r94730 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r94729‎ | r94730 | r94731 >
Date:06:51, 17 August 2011
Author:tstarling
Status:deferred
Tags:
Comment:
* Moved sandbox.scripts to the registry, and don't provide access to it to user scripts, to avoid the potential for a panic in LuaSandbox::loadString() if sandbox.scripts is overwritten.

* Removed LuaSandbox::callScript(). Instead, have LuaSandbox::loadString() return a LuaSandboxFunction object with a call() method. Store the script functions in a table indexed by integer, instead of using the chunk name, so that nothing strange will happen if there are duplicate chunk names. Delete the function from the Lua state when the object is destroyed.

* Added a method for registering PHP callbacks (LuaSandbox::register())

* Added CPU time limits per the previously outlined plan. It will only work on platforms that support the POSIX function timer_create(), i.e. not Mac OS X. It should compile correctly on other platforms, it just won't do anything. SIGRTMIN+1 is used where available, to reduce the chance of signal conflicts with users of the standard signals.

* Removed the sandbox global. It was intended to be a set of Lua functions implemented in C, but it's looking at this point like it will be easier to implement any that we need in PHP. Besides, that gives full control over the environment to the PHP app.

* Moved MINIT etc. to near the top. It seems like a good place for them as long as you're committed to having function prototypes, which I am.
Modified paths:
  • /trunk/php/luasandbox/config.m4 (modified) (history)
  • /trunk/php/luasandbox/luasandbox.c (modified) (history)
  • /trunk/php/luasandbox/luasandbox_timer.h (added) (history)
  • /trunk/php/luasandbox/php_luasandbox.h (modified) (history)
  • /trunk/php/luasandbox/timer.c (added) (history)

Diff [purge]

Index: trunk/php/luasandbox/luasandbox.c
@@ -1,22 +1,10 @@
22
33 /*
44 * To do:
5 - * * Move sandbox.scripts to the registry, and don't provide access to it to
6 - * user scripts, to avoid the potential for a panic in
7 - * LuaSandbox::loadString() if sandbox.scripts is overwritten.
8 - *
9 - * * Remove LuaSandbox::callScript(). Instead, have LuaSandbox::loadString()
10 - * return a LuaSandboxFunction object with a call() method. Store the
11 - * script functions in a table indexed by integer, instead of using the
12 - * chunk name, so that nothing strange will happen if there are duplicate
13 - * chunk names. Delete the function from the Lua state when the object is
14 - * destroyed.
15 - *
165 * * Provide a LuaSandbox::doString() as a convenience wrapper for
176 * LuaSandbox::loadString()->call(), analogous to luaL_dostring().
187 *
19 - * * Add PHP methods for registering callback functions, both individually
20 - * and in bulk (a library creation function).
 8+ * * Add a PHP method for registering callback functions individually.
219 *
2210 * * Provide a wrapper around the base library, which allows only a whitelist
2311 * of functions, not including e.g. loadfile(). This is slightly tricky
@@ -25,22 +13,8 @@
2614 * copy each function pointer to the destination lua_State using
2715 * lua_tocfunction().
2816 *
29 - * * Add CPU time limits.
30 - *
31 - * Doing a longjmp() from a signal handler destroys anything that the
32 - * call stack may have been modifying at the time. Allowing continued
33 - * access to such state will allow security vulnerabilities (SIG32-C).
34 - *
35 - * So I propose having two timeouts. When the first expires, a debug hook
36 - * is set which calls lua_error(), and a flag is set prohibiting dispatch
37 - * of any PHP callback. When the second expires, emergency action is
38 - * taken. If a PHP callback has been dispatched and we are waiting for it
39 - * to return, zend_error() will need to be called with E_ERROR, to safely
40 - * destroy the PHP state. This mirrors the behaviour of normal Zend timeouts.
41 - * Otherwise, lua_error() should be called, to return control to the
42 - * lua_pcall() caller, which should then destroy the lua state.
43 - *
44 - * * Add LuaSandbox::getMemoryUsage().
 17+ * * Support lua_dump()
 18+ * * Protect LuaSandbox->loadString() against malicious binaries
4519 */
4620
4721 #ifdef HAVE_CONFIG_H
@@ -49,38 +23,54 @@
5024
5125 #include <lua.h>
5226 #include <lauxlib.h>
 27+#include <lualib.h>
5328 #include <math.h>
5429 #include <limits.h>
5530 #include <float.h>
 31+#include <signal.h>
 32+#include <time.h>
5633
5734 #include "php.h"
5835 #include "php_ini.h"
5936 #include "ext/standard/info.h"
 37+#include "zend_exceptions.h"
6038 #include "php_luasandbox.h"
61 -#include "zend_exceptions.h"
 39+#include "luasandbox_timer.h"
6240
6341 static zend_object_value luasandbox_new(zend_class_entry *ce TSRMLS_DC);
 42+static lua_State * luasandbox_newstate(php_luasandbox_obj * intern);
6443 static void luasandbox_free_storage(void *object TSRMLS_DC);
 44+static zend_object_value luasandboxfunction_new(zend_class_entry *ce TSRMLS_CC);
 45+static void luasandboxfunction_free_storage(void *object TSRMLS_DC);
 46+static void luasandboxfunction_destroy(void *object, zend_object_handle handle TSRMLS_DC);
 47+static int luasandbox_free_zval_userdata(lua_State * L);
6548 static void *luasandbox_alloc(void *ud, void *ptr, size_t osize, size_t nsize);
6649 static int luasandbox_panic(lua_State * L);
67 -static lua_State * luasandbox_newstate();
68 -static lua_State * getLuaState(zval * this_ptr);
 50+static lua_State * luasandbox_state_from_zval(zval * this_ptr TSRMLS_DC);
6951 static int luasandbox_find_field(lua_State * L, int index,
7052 char * spec, int specLength);
71 -static void luasandbox_call_helper(INTERNAL_FUNCTION_PARAMETERS, int index);
 53+static void luasandbox_set_timespec(struct timespec * dest, double source);
 54+static void luasandbox_call_helper(lua_State * L, php_luasandbox_obj * sandbox,
 55+ zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC);
7256 static int luasandbox_push_zval(lua_State * L, zval * z);
 57+static void luasandbox_push_zval_userdata(lua_State * L, zval * z);
7358 static zval * luasandbox_lua_to_zval(lua_State * L, int index, HashTable * recursionGuard);
7459 static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
7560 HashTable * recursionGuard);
7661 static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L);
77 -static int luasandbox_method_getMemoryUsage(lua_State * L);
7862 static void luasandbox_handle_error(lua_State * L, int status);
7963 static int luasandbox_push_hashtable(lua_State * L, HashTable * ht);
 64+static int luasandbox_call_php(lua_State * L);
8065
 66+char luasandbox_timeout_message[] = "The maximum execution time for this script was exceeded";
 67+
8168 zend_class_entry *luasandbox_ce;
8269 zend_class_entry *luasandboxerror_ce;
8370 zend_class_entry *luasandboxplaceholder_ce;
 71+zend_class_entry *luasandboxfunction_ce;
8472
 73+ZEND_DECLARE_MODULE_GLOBALS(luasandbox);
 74+
8575 /** {{{ arginfo */
8676 ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_loadString, 0)
8777 ZEND_ARG_INFO(0, code)
@@ -91,13 +81,22 @@
9282 ZEND_ARG_INFO(0, limit)
9383 ZEND_END_ARG_INFO()
9484
 85+ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_setCPULimit, 0)
 86+ ZEND_ARG_INFO(0, normal_limit)
 87+ ZEND_ARG_INFO(0, emergency_limit)
 88+ZEND_END_ARG_INFO()
 89+
9590 ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_callFunction, 0)
9691 ZEND_ARG_INFO(0, name)
9792 ZEND_ARG_INFO(0, ...)
9893 ZEND_END_ARG_INFO()
9994
100 -ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_callScript, 0)
101 - ZEND_ARG_INFO(0, name)
 95+ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_register, 0)
 96+ ZEND_ARG_INFO(0, libname)
 97+ ZEND_ARG_INFO(0, functions)
 98+ZEND_END_ARG_INFO()
 99+
 100+ZEND_BEGIN_ARG_INFO(arginfo_luasandboxfunction_call, 0)
102101 ZEND_ARG_INFO(0, ...)
103102 ZEND_END_ARG_INFO()
104103 /* }}} */
@@ -110,19 +109,19 @@
111110 const zend_function_entry luasandbox_methods[] = {
112111 PHP_ME(LuaSandbox, loadString, arginfo_luasandbox_loadString, 0)
113112 PHP_ME(LuaSandbox, setMemoryLimit, arginfo_luasandbox_setMemoryLimit, 0)
 113+ PHP_ME(LuaSandbox, setCPULimit, arginfo_luasandbox_setCPULimit, 0)
114114 PHP_ME(LuaSandbox, callFunction, arginfo_luasandbox_callFunction, 0)
115 - PHP_ME(LuaSandbox, callScript, arginfo_luasandbox_callScript, 0)
 115+ PHP_ME(LuaSandbox, register, arginfo_luasandbox_register, 0)
116116 {NULL, NULL, NULL}
117117 };
118118
119 -const zend_function_entry luasandbox_empty_methods[] = {
 119+const zend_function_entry luasandboxfunction_methods[] = {
 120+ PHP_ME(LuaSandboxFunction, call, arginfo_luasandboxfunction_call, 0)
120121 {NULL, NULL, NULL}
121122 };
122123
123 -/** sandbox method names and their C functions */
124 -static const luaL_Reg luasandbox_lua_methods[] = {
125 - {"getMemoryUsage", luasandbox_method_getMemoryUsage},
126 - {NULL, NULL}
 124+const zend_function_entry luasandbox_empty_methods[] = {
 125+ {NULL, NULL, NULL}
127126 };
128127
129128 /* }}} */
@@ -138,7 +137,7 @@
139138 PHP_MINIT(luasandbox),
140139 PHP_MSHUTDOWN(luasandbox),
141140 NULL, /* RINIT */
142 - NULL, /* RSHUTDOWN */
 141+ PHP_RSHUTDOWN(luasandbox), /* RSHUTDOWN */
143142 PHP_MINFO(luasandbox),
144143 #if ZEND_MODULE_API_NO >= 20010901
145144 "0.1",
@@ -151,6 +150,98 @@
152151 ZEND_GET_MODULE(luasandbox)
153152 #endif
154153
 154+/* {{{ PHP_MINIT_FUNCTION
 155+ */
 156+PHP_MINIT_FUNCTION(luasandbox)
 157+{
 158+ /* If you have INI entries, uncomment these lines
 159+ REGISTER_INI_ENTRIES();
 160+ */
 161+
 162+ zend_class_entry ce;
 163+ INIT_CLASS_ENTRY(ce, "LuaSandbox", luasandbox_methods);
 164+ luasandbox_ce = zend_register_internal_class(&ce TSRMLS_CC);
 165+ luasandbox_ce->create_object = luasandbox_new;
 166+
 167+ INIT_CLASS_ENTRY(ce, "LuaSandboxError", luasandbox_empty_methods);
 168+ luasandboxerror_ce = zend_register_internal_class_ex(
 169+ &ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
 170+
 171+ zend_declare_class_constant_long(luasandboxerror_ce,
 172+ "RUN", sizeof("RUN"), LUA_ERRRUN);
 173+ zend_declare_class_constant_long(luasandboxerror_ce,
 174+ "SYNTAX", sizeof("SYNTAX"), LUA_ERRSYNTAX);
 175+ zend_declare_class_constant_long(luasandboxerror_ce,
 176+ "MEM", sizeof("MEM"), LUA_ERRMEM);
 177+ zend_declare_class_constant_long(luasandboxerror_ce,
 178+ "ERR", sizeof("ERR"), LUA_ERRERR);
 179+
 180+ INIT_CLASS_ENTRY(ce, "LuaSandboxPlaceholder", luasandbox_empty_methods);
 181+ luasandboxplaceholder_ce = zend_register_internal_class(&ce TSRMLS_CC);
 182+
 183+ INIT_CLASS_ENTRY(ce, "LuaSandboxFunction", luasandboxfunction_methods);
 184+ luasandboxfunction_ce = zend_register_internal_class(&ce TSRMLS_CC);
 185+ luasandboxfunction_ce->create_object = luasandboxfunction_new;
 186+
 187+ return SUCCESS;
 188+}
 189+/* }}} */
 190+
 191+/* {{{ PHP_MSHUTDOWN_FUNCTION
 192+ */
 193+PHP_MSHUTDOWN_FUNCTION(luasandbox)
 194+{
 195+ return SUCCESS;
 196+}
 197+/* }}} */
 198+
 199+/* {{{ PHP_RSHUTDOWN_FUNCTION */
 200+PHP_RSHUTDOWN_FUNCTION(luasandbox)
 201+{
 202+ if (LUASANDBOX_G(signal_handler_installed)) {
 203+ luasandbox_timer_remove_handler(&LUASANDBOX_G(old_handler));
 204+ }
 205+ return SUCCESS;
 206+}
 207+/* }}} */
 208+
 209+/* {{{ PHP_MINFO_FUNCTION
 210+ */
 211+PHP_MINFO_FUNCTION(luasandbox)
 212+{
 213+ php_info_print_table_start();
 214+ php_info_print_table_header(2, "luasandbox support", "enabled");
 215+ php_info_print_table_end();
 216+}
 217+/* }}} */
 218+
 219+/** {{{ luasandbox_enter_php
 220+ * This function must be called each time a C function is entered from Lua
 221+ * and the PHP state needs to be accessed in any way. Before exiting the
 222+ * function, luasandbox_leave_php() must be called.
 223+ *
 224+ * This sets a flag which indicates to the timeout signal handler that it is
 225+ * unsafe to call longjmp() to return control to PHP. If the flag is not
 226+ * correctly set, memory may be corrupted and security compromised.
 227+ */
 228+static inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern)
 229+{
 230+ intern->in_php = 1;
 231+ if (intern->timed_out) {
 232+ intern->in_php = 0;
 233+ luaL_error(L, luasandbox_timeout_message);
 234+ }
 235+}
 236+/* }}} */
 237+
 238+/** {{{ luasandbox_leave_php
 239+ */
 240+static inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern)
 241+{
 242+ intern->in_php = 0;
 243+}
 244+/* }}} */
 245+
155246 /** {{{ luasandbox_new */
156247 static zend_object_value luasandbox_new(zend_class_entry *ce TSRMLS_DC)
157248 {
@@ -167,10 +258,10 @@
168259
169260 // Put the object into the store
170261 retval.handle = zend_objects_store_put(
171 - intern,
172 - (zend_objects_store_dtor_t)zend_objects_destroy_object,
173 - (zend_objects_free_object_storage_t)luasandbox_free_storage,
174 - NULL TSRMLS_CC);
 262+ intern,
 263+ (zend_objects_store_dtor_t)zend_objects_destroy_object,
 264+ (zend_objects_free_object_storage_t)luasandbox_free_storage,
 265+ NULL TSRMLS_CC);
175266 retval.handlers = zend_get_std_object_handlers();
176267 return retval;
177268 }
@@ -189,22 +280,20 @@
190281 luaopen_table(L);
191282 luaopen_math(L);
192283
193 - // Create the "sandbox" global
 284+ // Create a table for storing chunks
194285 lua_newtable(L);
195 - // Add its methods
196 - luaL_register(L, NULL, luasandbox_lua_methods);
197 -
198 - // Add its properties
199 - lua_newtable(L);
200 - lua_setfield(L, -2, "scripts");
201 -
202 - // Move the new table from the stack to the sandbox global
203 - lua_setglobal(L, "sandbox");
 286+ lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
204287
205288 // Register a pointer to the PHP object so that C functions can find it
206289 lua_pushlightuserdata(L, (void*)intern);
207290 lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj");
208291
 292+ // Create the metatable for zval destruction
 293+ lua_createtable(L, 0, 1);
 294+ lua_pushcfunction(L, luasandbox_free_zval_userdata);
 295+ lua_setfield(L, -2, "__gc");
 296+ lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
 297+
209298 return L;
210299 }
211300 /* }}} */
@@ -220,64 +309,89 @@
221310 }
222311 /* }}} */
223312
224 -/* {{{ PHP_MINIT_FUNCTION
225 - */
226 -PHP_MINIT_FUNCTION(luasandbox)
 313+/** {{{ luasandboxfunction_new */
 314+static zend_object_value luasandboxfunction_new(zend_class_entry *ce TSRMLS_CC)
227315 {
228 - /* If you have INI entries, uncomment these lines
229 - REGISTER_INI_ENTRIES();
230 - */
 316+ php_luasandboxfunction_obj * intern;
 317+ zend_object_value retval;
 318+
 319+ // Create the internal object
 320+ intern = emalloc(sizeof(php_luasandboxfunction_obj));
 321+ memset(intern, 0, sizeof(php_luasandboxfunction_obj));
 322+ zend_object_std_init(&intern->std, ce TSRMLS_CC);
231323
232 - zend_class_entry ce;
233 - INIT_CLASS_ENTRY(ce, "LuaSandbox", luasandbox_methods);
234 - luasandbox_ce = zend_register_internal_class(&ce TSRMLS_CC);
235 - luasandbox_ce->create_object = luasandbox_new;
 324+ // Put the object into the store
 325+ retval.handle = zend_objects_store_put(
 326+ intern,
 327+ (zend_objects_store_dtor_t)luasandboxfunction_destroy,
 328+ (zend_objects_free_object_storage_t)luasandboxfunction_free_storage,
 329+ NULL TSRMLS_CC);
 330+ retval.handlers = zend_get_std_object_handlers();
 331+ return retval;
 332+}
 333+/* }}} */
236334
237 - INIT_CLASS_ENTRY(ce, "LuaSandboxError", luasandbox_empty_methods);
238 - luasandboxerror_ce = zend_register_internal_class_ex(
239 - &ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
 335+/** {{{ luasandboxfunction_destroy */
 336+static void luasandboxfunction_destroy(void *object, zend_object_handle handle TSRMLS_DC)
 337+{
 338+ php_luasandboxfunction_obj * func = (php_luasandboxfunction_obj*)object;
 339+ if (func->sandbox) {
 340+ php_luasandbox_obj * sandbox = (php_luasandbox_obj*)
 341+ zend_object_store_get_object(func->sandbox TSRMLS_CC);
 342+ lua_State * L = sandbox->state;
240343
241 - zend_declare_class_constant_long(luasandboxerror_ce,
242 - "RUN", sizeof("RUN"), LUA_ERRRUN);
243 - zend_declare_class_constant_long(luasandboxerror_ce,
244 - "SYNTAX", sizeof("SYNTAX"), LUA_ERRSYNTAX);
245 - zend_declare_class_constant_long(luasandboxerror_ce,
246 - "MEM", sizeof("MEM"), LUA_ERRMEM);
247 - zend_declare_class_constant_long(luasandboxerror_ce,
248 - "ERR", sizeof("ERR"), LUA_ERRERR);
 344+ // Delete the chunk
 345+ if (func->index) {
 346+ lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
 347+ lua_pushnil(L);
 348+ lua_rawseti(L, -2, func->index);
 349+ lua_pop(L, 1);
 350+ }
249351
250 - INIT_CLASS_ENTRY(ce, "LuaSandboxPlaceholder", luasandbox_empty_methods);
251 - luasandboxplaceholder_ce = zend_register_internal_class(&ce TSRMLS_CC);
252 - return SUCCESS;
 352+ // Delete the parent reference
 353+ zval_ptr_dtor(&func->sandbox);
 354+ }
253355 }
254356 /* }}} */
255357
256 -/* {{{ PHP_MSHUTDOWN_FUNCTION
257 - */
258 -PHP_MSHUTDOWN_FUNCTION(luasandbox)
 358+/** {{{ luasandboxfunction_free_storage */
 359+static void luasandboxfunction_free_storage(void *object TSRMLS_DC)
259360 {
260 - return SUCCESS;
 361+ php_luasandboxfunction_obj * func = (php_luasandboxfunction_obj*)object;
 362+ zend_object_std_dtor(&func->std);
 363+ efree(object);
261364 }
262365 /* }}} */
263366
264 -/* {{{ PHP_MINFO_FUNCTION
 367+/** {{{ luasandbox_free_zval_userdata
 368+ * Free a zval given to Lua by luasandbox_push_zval_userdata.
265369 */
266 -PHP_MINFO_FUNCTION(luasandbox)
 370+static int luasandbox_free_zval_userdata(lua_State * L)
267371 {
268 - php_info_print_table_start();
269 - php_info_print_table_header(2, "luasandbox support", "enabled");
270 - php_info_print_table_end();
 372+ zval ** zpp = (zval**)lua_touserdata(L, 1);
 373+ php_luasandbox_obj * intern = luasandbox_get_php_obj(L);
 374+ luasandbox_enter_php(L, intern);
 375+ if (zpp && *zpp) {
 376+ zval_ptr_dtor(zpp);
 377+ }
 378+ *zpp = NULL;
 379+ luasandbox_leave_php(L, intern);
 380+ return 0;
271381 }
272382 /* }}} */
273383
274384 /** {{{ luasandbox_alloc */
275385 static void *luasandbox_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
276386 {
 387+ php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
 388+ void * nptr;
 389+ obj->in_php = 1;
 390+
277391 // Update memory usage accounting
278 - php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
279392 if (obj->memory_usage + nsize < obj->memory_usage) {
280393 // Overflow
281394 // No PHP error because it's plausible that untrusted scripts could do this.
 395+ obj->in_php = 0;
282396 return NULL;
283397 }
284398
@@ -287,12 +401,14 @@
288402 if (ptr) {
289403 efree(ptr);
290404 }
291 - return NULL;
 405+ nptr = NULL;
292406 } else if (osize == 0) {
293 - return emalloc(nsize);
 407+ nptr = emalloc(nsize);
294408 } else {
295 - return erealloc(ptr, nsize);
 409+ nptr = erealloc(ptr, nsize);
296410 }
 411+ obj->in_php = 0;
 412+ return nptr;
297413 }
298414 /* }}} */
299415
@@ -306,8 +422,8 @@
307423 }
308424 /* }}} */
309425
310 -/** {{{ getLuaState */
311 -static lua_State * getLuaState(zval * this_ptr)
 426+/** {{{ luasandbox_state_from_zval */
 427+static lua_State * luasandbox_state_from_zval(zval * this_ptr TSRMLS_DC)
312428 {
313429 php_luasandbox_obj * intern = (php_luasandbox_obj*)
314430 zend_object_store_get_object(this_ptr TSRMLS_CC);
@@ -318,27 +434,41 @@
319435 /** {{{ proto int LuaSandbox::loadString(string code, string chunkName) */
320436 PHP_METHOD(LuaSandbox, loadString)
321437 {
322 - char *code, *chunkName;
 438+ char *code, *chunkName = NULL;
323439 int codeLength, chunkNameLength;
324440 int status;
325 - lua_State * L = getLuaState(getThis());
 441+ lua_State * L = luasandbox_state_from_zval(getThis() TSRMLS_CC);
 442+ size_t index;
 443+ php_luasandboxfunction_obj * func_obj;
326444
327 - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
 445+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
328446 &code, &codeLength, &chunkName, &chunkNameLength) == FAILURE) {
329447 RETURN_FALSE;
330448 }
331449
332 - // Check chunkName for nulls
333 - if (strlen(chunkName) != chunkNameLength) {
334 - php_error_docref(NULL TSRMLS_CC, E_WARNING,
335 - "chunk name may not contain null characters");
 450+ if (chunkName == NULL) {
 451+ chunkName = "";
 452+ } else {
 453+ // Check chunkName for nulls
 454+ if (strlen(chunkName) != chunkNameLength) {
 455+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 456+ "chunk name may not contain null characters");
 457+ RETURN_FALSE;
 458+ }
 459+ }
 460+
 461+ // Get the chunks table
 462+ lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
 463+
 464+ // Get the next free index
 465+ index = lua_objlen(L, -1);
 466+ if (index >= INT_MAX) {
 467+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 468+ "too many chunks loaded already");
336469 RETURN_FALSE;
337470 }
 471+ index++;
338472
339 - // Find sandbox.scripts
340 - lua_getglobal(L, "sandbox");
341 - lua_getfield(L, -1, "scripts");
342 -
343473 // Parse the string into a function on the stack
344474 status = luaL_loadbuffer(L, code, codeLength, chunkName);
345475 if (status != 0) {
@@ -346,12 +476,18 @@
347477 return;
348478 }
349479
350 - // Store the resulting function as a member of sandbox.scripts
351 - lua_setfield(L, -2, chunkName);
 480+ // Store the resulting function to the chunks table
 481+ lua_rawseti(L, -2, (int)index);
352482
 483+ // Create a LuaSandboxFunction object to hold a reference to the function
 484+ object_init_ex(return_value, luasandboxfunction_ce);
 485+ func_obj = (php_luasandboxfunction_obj*)zend_object_store_get_object(return_value);
 486+ func_obj->index = index;
 487+ func_obj->sandbox = getThis();
 488+ Z_ADDREF_P(getThis());
 489+
353490 // Balance the stack
354 - lua_pop(L, 2);
355 - RETURN_TRUE;
 491+ lua_pop(L, 1);
356492 }
357493
358494 /* }}} */
@@ -387,92 +523,241 @@
388524 }
389525 /* }}} */
390526
391 -/*** {{{ proto array LuaSandbox::callFunction(string name, ...)
392 - */
393 -PHP_METHOD(LuaSandbox, callFunction)
 527+
 528+/** {{{ proto void LuaSandbox::setCPULimit(mixed normal_limit, mixed emergency_limit = false) */
 529+PHP_METHOD(LuaSandbox, setCPULimit)
394530 {
395 - luasandbox_call_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, LUA_GLOBALSINDEX);
 531+ zval **zpp_normal = NULL, **zpp_emergency = NULL;
 532+
 533+ php_luasandbox_obj * sandbox = (php_luasandbox_obj*)
 534+ zend_object_store_get_object(getThis() TSRMLS_CC);
 535+
 536+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|Z",
 537+ &zpp_normal, &zpp_emergency) == FAILURE)
 538+ {
 539+ RETURN_FALSE;
 540+ }
 541+
 542+ if (!zpp_normal
 543+ || (Z_TYPE_PP(zpp_normal) == IS_BOOL && Z_BVAL_PP(zpp_normal) == 0))
 544+ {
 545+ // No limit
 546+ sandbox->is_cpu_limited = 0;
 547+ } else {
 548+ convert_scalar_to_number_ex(zpp_normal);
 549+ if (Z_TYPE_PP(zpp_normal) == IS_LONG) {
 550+ convert_to_double_ex(zpp_normal);
 551+ }
 552+ if (Z_TYPE_PP(zpp_normal) == IS_DOUBLE) {
 553+ luasandbox_set_timespec(&sandbox->cpu_normal_limit, Z_DVAL_PP(zpp_normal));
 554+ sandbox->is_cpu_limited = 1;
 555+ } else {
 556+ sandbox->is_cpu_limited = 0;
 557+ }
 558+ }
 559+
 560+ if (!zpp_emergency
 561+ || (Z_TYPE_PP(zpp_emergency) == IS_BOOL && Z_BVAL_PP(zpp_emergency) == 0))
 562+ {
 563+ // No emergency limit
 564+ sandbox->cpu_emergency_limit.tv_sec = 0;
 565+ sandbox->cpu_emergency_limit.tv_nsec = 0;
 566+ } else {
 567+ convert_scalar_to_number_ex(zpp_emergency);
 568+ if (Z_TYPE_PP(zpp_emergency) == IS_LONG) {
 569+ convert_to_double_ex(zpp_emergency);
 570+ }
 571+ if (Z_TYPE_PP(zpp_normal) == IS_DOUBLE) {
 572+ luasandbox_set_timespec(&sandbox->cpu_emergency_limit, Z_DVAL_PP(zpp_emergency));
 573+ } else {
 574+ sandbox->cpu_emergency_limit.tv_sec = 0;
 575+ sandbox->cpu_emergency_limit.tv_nsec = 0;
 576+ }
 577+ }
396578 }
397579 /* }}} */
398580
399 -/*** {{{ proto array LuaSandbox::callScript(string name, ...)
400 - */
401 -PHP_METHOD(LuaSandbox, callScript)
 581+/** {{{ luasandbox_set_timespec */
 582+static void luasandbox_set_timespec(struct timespec * dest, double source)
402583 {
403 - lua_State * L = getLuaState(getThis());
404 - lua_getglobal(L, "sandbox");
405 - lua_getfield(L, -1, "scripts");
406 - luasandbox_call_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1);
407 - lua_pop(L, 2);
 584+ double fractional, integral;
 585+ if (source < 0) {
 586+ dest->tv_sec = dest->tv_nsec = 0;
 587+ return;
 588+ }
 589+
 590+ fractional = modf(source, &integral);
 591+ dest->tv_sec = (time_t)integral;
 592+ dest->tv_nsec = (long)(fractional * 1000000000.0);
 593+ if (dest->tv_nsec >= 1000000000L) {
 594+ dest->tv_nsec -= 1000000000L;
 595+ dest->tv_sec ++;
 596+ }
408597 }
409 -/** }}} */
410598
411 -/** {{{ luasandbox_call_helper
412 - * Call a field or subfield of the table at the given index. Set return_value
413 - * to an array containing all the results.
 599+/* }}} */
 600+
 601+/*** {{{ proto array LuaSandbox::callFunction(string name, ...)
414602 */
415 -static void luasandbox_call_helper(INTERNAL_FUNCTION_PARAMETERS, int index)
 603+PHP_METHOD(LuaSandbox, callFunction)
416604 {
417605 char *name;
418 - int nameLength = 0, status, origTop;
419 - zend_uint numArgs = 0, numResults, i;
 606+ int nameLength = 0;
 607+ zend_uint numArgs = 0;
420608 zval *** args = NULL;
421 - lua_State * L = getLuaState(getThis());
422609
 610+ php_luasandbox_obj * sandbox = (php_luasandbox_obj*)
 611+ zend_object_store_get_object(getThis() TSRMLS_CC);
 612+ lua_State * L = sandbox->state;
 613+
423614 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s*",
424615 &name, &nameLength, &args, &numArgs) == FAILURE)
425616 {
426617 RETURN_FALSE;
427618 }
428619
429 - // Save the top position
430 - origTop = lua_gettop(L);
431 -
432620 // Find the function
433 - if (!luasandbox_find_field(L, index, name, nameLength)) {
 621+ if (!luasandbox_find_field(L, LUA_GLOBALSINDEX, name, nameLength)) {
434622 php_error_docref(NULL TSRMLS_CC, E_WARNING,
435623 "The specified lua function does not exist");
436624 RETVAL_FALSE;
437 - goto cleanup;
 625+ } else {
 626+ // Call it
 627+ luasandbox_call_helper(L, sandbox, args, numArgs, return_value TSRMLS_CC);
438628 }
439629
 630+ // Delete varargs
 631+ if (numArgs) {
 632+ efree(args);
 633+ }
 634+}
 635+/* }}} */
 636+
 637+/*** {{{ proto array LuaSandboxFunction::call(...)
 638+ */
 639+PHP_METHOD(LuaSandboxFunction, call)
 640+{
 641+ zend_uint numArgs = 0;
 642+ zval *** args = NULL;
 643+
 644+ php_luasandboxfunction_obj * func = (php_luasandboxfunction_obj *)
 645+ zend_object_store_get_object(getThis() TSRMLS_CC);
 646+ lua_State * L;
 647+ php_luasandbox_obj * sandbox;
 648+
 649+ if (!func || !func->sandbox || !func->index) {
 650+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 651+ "attempt to call uninitialized LuaSandboxFunction object" );
 652+ RETURN_FALSE;
 653+ }
 654+
 655+ sandbox = (php_luasandbox_obj*)
 656+ zend_object_store_get_object(func->sandbox TSRMLS_CC);
 657+ L = sandbox->state;
 658+
 659+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*",
 660+ &args, &numArgs) == FAILURE)
 661+ {
 662+ RETURN_FALSE;
 663+ }
 664+
 665+ // Find the function
 666+ lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
 667+ lua_rawgeti(L, -1, func->index);
 668+
 669+ // Call it
 670+ luasandbox_call_helper(L, sandbox, args, numArgs, return_value TSRMLS_CC);
 671+
 672+ // Delete varargs
 673+ if (numArgs) {
 674+ efree(args);
 675+ }
 676+}
 677+/** }}} */
 678+
 679+/** {{{ luasandbox_call_helper
 680+ * Call the function at the top of the stack and then pop it. Set return_value
 681+ * to an array containing all the results.
 682+ */
 683+static void luasandbox_call_helper(lua_State * L, php_luasandbox_obj * sandbox,
 684+ zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC)
 685+{
 686+ // Save the top position
 687+ int origTop = lua_gettop(L);
 688+ int status;
 689+ int cpu_limited = 0;
 690+ luasandbox_timer_set t;
 691+ int i, numResults;
 692+
 693+ // Check to see if the value is a valid function
 694+ if (lua_type(L, -1) != LUA_TFUNCTION) {
 695+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 696+ "the specified Lua value is not a valid function");
 697+ lua_settop(L, origTop - 1);
 698+ RETURN_FALSE;
 699+ }
 700+
440701 // Push the arguments
441702 for (i = 0; i < numArgs; i++) {
442703 if (!luasandbox_push_zval(L, *(args[i]))) {
443704 php_error_docref(NULL TSRMLS_CC, E_WARNING,
444 - "Unable to convert argument %d to a lua value", i + 1);
445 - RETVAL_FALSE;
446 - goto cleanup;
 705+ "unable to convert argument %d to a lua value", i + 1);
 706+ lua_settop(L, origTop - 1);
 707+ RETURN_FALSE;
447708 }
448709 }
449710
450 - // Call it
 711+ // Initialise the CPU limit timer
 712+ if (sandbox->is_cpu_limited) {
 713+ cpu_limited = 1;
 714+ if (!LUASANDBOX_G(signal_handler_installed)) {
 715+ luasandbox_timer_install_handler(&LUASANDBOX_G(old_handler));
 716+ }
 717+ luasandbox_timer_start(&t, sandbox, &sandbox->cpu_normal_limit,
 718+ &sandbox->cpu_emergency_limit);
 719+ }
 720+
 721+ // Call the function
451722 status = lua_pcall(L, numArgs, LUA_MULTRET, 0);
 723+
 724+ // Stop the timer
 725+ if (cpu_limited) {
 726+ luasandbox_timer_stop(&t);
 727+ }
 728+
 729+ // If there was an emergency timeout, destroy the state
 730+ if (sandbox->emergency_timed_out) {
 731+ lua_close(L);
 732+ L = sandbox->state = luasandbox_newstate(sandbox);
 733+ sandbox->emergency_timed_out = 0;
 734+ zend_throw_exception(luasandboxerror_ce,
 735+ "The maximum execution time was exceeded "
 736+ "and the current Lua statement failed to return, leading to "
 737+ "destruction of the Lua state", LUA_ERRRUN);
 738+ return;
 739+ }
 740+
 741+ // Handle normal errors
452742 if (status) {
453743 luasandbox_handle_error(L, status);
454 - goto cleanup;
 744+ lua_settop(L, origTop - 1);
 745+ RETURN_FALSE;
455746 }
456747
457748 // Calculate the number of results and create an array of that capacity
458 - numResults = lua_gettop(L) - origTop;
 749+ numResults = lua_gettop(L) - origTop + 1;
459750 array_init_size(return_value, numResults);
460751
461752 // Fill the array with the results
462753 for (i = 0; i < numResults; i++) {
463 - zval * element = luasandbox_lua_to_zval(L, origTop + i + 1, NULL);
 754+ zval * element = luasandbox_lua_to_zval(L, origTop + i, NULL);
464755 zend_hash_next_index_insert(Z_ARRVAL_P(return_value),
465756 (void*)&element,
466757 sizeof(zval*), NULL);
467758 }
468759
469 -cleanup:
470 -
471760 // Balance the stack
472 - lua_pop(L, lua_gettop(L) - origTop);
473 - // Delete varargs
474 - if (numArgs) {
475 - efree(args);
476 - }
 761+ lua_settop(L, origTop - 1);
477762 }
478763 /* }}} */
479764
@@ -499,7 +784,7 @@
500785 if (i == specLength || spec[i] == '.') {
501786 // Put the next item into top+2
502787 lua_pushlstring(L, spec + tokenStart, i - tokenStart);
503 - lua_gettable(L, top + 1);
 788+ lua_rawget(L, top + 1);
504789
505790 // Not found?
506791 if (lua_isnil(L, top + 2)) {
@@ -522,7 +807,10 @@
523808 }
524809 /* }}} */
525810
526 -/** {{{ luasandbox_push_zval */
 811+/** {{{ luasandbox_push_zval
 812+ * Convert a zval to an appropriate Lua type and push the resulting value on to
 813+ * the stack.
 814+ */
527815 static int luasandbox_push_zval(lua_State * L, zval * z)
528816 {
529817 switch (Z_TYPE_P(z)) {
@@ -561,6 +849,23 @@
562850 }
563851 /* }}} */
564852
 853+/** {{{ luasandbox_push_zval_userdata
 854+ * Push a full userdata on to the stack, which stores a zval* in its block.
 855+ * Increment its reference count and set its metatable so that it will be freed
 856+ * at the appropriate time.
 857+ */
 858+static void luasandbox_push_zval_userdata(lua_State * L, zval * z)
 859+{
 860+ zval ** ud;
 861+ ud = (zval**)lua_newuserdata(L, sizeof(zval*));
 862+ *ud = z;
 863+ Z_ADDREF_P(z);
 864+
 865+ lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
 866+ lua_setmetatable(L, -2);
 867+}
 868+/* }}} */
 869+
565870 /** {{{ luasandbox_push_hashtable */
566871 static int luasandbox_push_hashtable(lua_State * L, HashTable * ht)
567872 {
@@ -581,7 +886,7 @@
582887 ht->nApplyCount++;
583888 for (p = ht->pListHead; p; p = p->pListNext) {
584889 if (p->nKeyLength) {
585 - lua_pushlstring(L, p->arKey, p->nKeyLength);
 890+ lua_pushlstring(L, p->arKey, p->nKeyLength - 1);
586891 } else {
587892 lua_pushinteger(L, p->h);
588893 }
@@ -746,19 +1051,136 @@
7471052 }
7481053 /* }}} */
7491054
750 -/** {{{ luasandbox_method_getMemoryUsage */
751 -static int luasandbox_method_getMemoryUsage(lua_State * L)
 1055+/** {{{ proto void LuaSandbox::register(string libname, array functions)
 1056+ */
 1057+PHP_METHOD(LuaSandbox, register)
7521058 {
 1059+ lua_State * L = luasandbox_state_from_zval(getThis() TSRMLS_CC);
 1060+ char * libname = NULL;
 1061+ int libname_len = 0;
 1062+ zval * zfunctions = NULL;
 1063+ HashTable * functions;
 1064+ Bucket * p;
 1065+
 1066+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa",
 1067+ &libname, &libname_len, &zfunctions) == FAILURE)
 1068+ {
 1069+ RETURN_FALSE;
 1070+ }
 1071+
 1072+ functions = Z_ARRVAL_P(zfunctions);
 1073+
 1074+ // Determine if the library exists already
 1075+ // Make a copy of the library name on the stack for rawset later
 1076+ lua_pushlstring(L, libname, libname_len);
 1077+ lua_pushvalue(L, -1);
 1078+ lua_rawget(L, LUA_GLOBALSINDEX);
 1079+ if (lua_type(L, -1) != LUA_TNIL) {
 1080+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 1081+ "library \"%s\" already exists", libname);
 1082+ RETURN_FALSE;
 1083+ }
 1084+ // Remove the nil
 1085+ lua_pop(L, 1);
 1086+
 1087+ // Create the new table
 1088+ lua_createtable(L, 0, functions->nNumOfElements);
 1089+
 1090+ for (p = functions->pListHead; p; p = p->pListNext) {
 1091+ // Push the key
 1092+ if (p->nKeyLength) {
 1093+ lua_pushlstring(L, p->arKey, p->nKeyLength - 1);
 1094+ } else {
 1095+ lua_pushinteger(L, p->h);
 1096+ }
 1097+
 1098+ // Push the callback zval and create the closure
 1099+ luasandbox_push_zval_userdata(L, *(zval**)p->pData);
 1100+ lua_pushcclosure(L, luasandbox_call_php, 1);
 1101+
 1102+ // Add it to the table
 1103+ lua_rawset(L, -3);
 1104+ }
 1105+
 1106+ // Move the new table to the global namespace
 1107+ // The key is on the stack already
 1108+ lua_rawset(L, LUA_GLOBALSINDEX);
 1109+}
 1110+/* }}} */
 1111+
 1112+/** {{{ luasandbox_call_php
 1113+ */
 1114+static int luasandbox_call_php(lua_State * L)
 1115+{
7531116 php_luasandbox_obj * intern = luasandbox_get_php_obj(L);
754 - if ((size_t)(lua_Integer)intern->memory_usage != intern->memory_usage) {
755 - lua_pushnumber(L, (lua_Number)intern->memory_usage);
756 - } else {
757 - lua_pushinteger(L, (lua_Integer)intern->memory_usage);
 1117+
 1118+ luasandbox_enter_php(L, intern);
 1119+
 1120+ zval ** callback_pp = lua_touserdata(L, lua_upvalueindex(1));
 1121+ zval *retval_ptr = NULL;
 1122+ zend_fcall_info fci;
 1123+ zend_fcall_info_cache fcc;
 1124+ char *is_callable_error = NULL;
 1125+ int top = lua_gettop(L);
 1126+ int i;
 1127+ void **temp;
 1128+ zval **pointers;
 1129+ zval ***double_pointers;
 1130+ int num_results = 0;
 1131+
 1132+ // Based on zend_parse_arg_impl()
 1133+ if (zend_fcall_info_init(*callback_pp, 0, &fci, &fcc, NULL,
 1134+ &is_callable_error TSRMLS_CC) != SUCCESS)
 1135+ {
 1136+ // Handle errors similar to the way PHP does it: show a warning and
 1137+ // return nil
 1138+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 1139+ "to be a valid callback, %s", is_callable_error);
 1140+ efree(is_callable_error);
 1141+ lua_pushnil(L);
 1142+ luasandbox_leave_php(L, intern);
 1143+ return 1;
7581144 }
759 - return 1;
 1145+
 1146+ fci.retval_ptr_ptr = &retval_ptr;
 1147+
 1148+ // Make an array of zval double-pointers to hold the arguments
 1149+ temp = ecalloc(top, sizeof(void*) * 2);
 1150+ double_pointers = (zval***)temp;
 1151+ pointers = (zval**)(temp + top);
 1152+ for (i = 0; i < top; i++ ) {
 1153+ pointers[i] = luasandbox_lua_to_zval(L, i + 1, NULL);
 1154+ double_pointers[i] = &(pointers[i]);
 1155+ }
 1156+
 1157+ // Initialise the fci. Use zend_fcall_info_args_restore() since that's an
 1158+ // almost-legitimate way to avoid the extra malloc that we'd get from
 1159+ // zend_fcall_info_argp()
 1160+ zend_fcall_info_args_restore(&fci, top, double_pointers);
 1161+
 1162+ // Call the function
 1163+ if (zend_call_function(&fci, &fcc TSRMLS_CC) == SUCCESS
 1164+ && fci.retval_ptr_ptr && *fci.retval_ptr_ptr)
 1165+ {
 1166+ // Push the return value back to Lua
 1167+ luasandbox_push_zval(L, *fci.retval_ptr_ptr);
 1168+ num_results = 1;
 1169+ }
 1170+
 1171+ // Free the zvals
 1172+ for (i = 0; i < top; i++) {
 1173+ zval_ptr_dtor(&(pointers[i]));
 1174+ }
 1175+ zval_ptr_dtor(&retval_ptr);
 1176+
 1177+ // Free the pointer arrays
 1178+ efree(temp);
 1179+ luasandbox_leave_php(L, intern);
 1180+ return num_results;
7601181 }
7611182 /* }}} */
7621183
 1184+/* }}} */
7631185 /*
7641186 * Local variables:
7651187 * tab-width: 4
Index: trunk/php/luasandbox/timer.c
@@ -0,0 +1,143 @@
 2+#ifdef HAVE_CONFIG_H
 3+#include "config.h"
 4+#endif
 5+
 6+#include <signal.h>
 7+#include <time.h>
 8+#include <lua.h>
 9+#include <lauxlib.h>
 10+
 11+#include "php.h"
 12+#include "php_luasandbox.h"
 13+#include "luasandbox_timer.h"
 14+
 15+#ifdef LUASANDBOX_NO_CLOCK
 16+
 17+void luasandbox_timer_install_handler(struct sigaction * oldact) {}
 18+void luasandbox_timer_remove_handler(struct sigaction * oldact) {}
 19+void luasandbox_timer_start(luasandbox_timer_set * lts,
 20+ php_luasandbox_obj * sandbox,
 21+ struct timespec * normal_timeout,
 22+ struct timespec * emergency_timeout) {}
 23+void luasandbox_timer_stop(luasandbox_timer_set * lts) {}
 24+
 25+#else
 26+
 27+static void luasandbox_timer_handle(int signo, siginfo_t * info, void * context);
 28+static void luasandbox_timer_create(luasandbox_timer * lt, php_luasandbox_obj * sandbox,
 29+ int emergency);
 30+static void luasandbox_timer_timeout_hook(lua_State *L, lua_Debug *ar);
 31+static void luasandbox_timer_settime(luasandbox_timer * lt, struct timespec * ts);
 32+
 33+void luasandbox_timer_install_handler(struct sigaction * oldact)
 34+{
 35+ struct sigaction newact;
 36+ newact.sa_sigaction = luasandbox_timer_handle;
 37+ newact.sa_flags = SA_SIGINFO;
 38+ sigprocmask(SIG_BLOCK, NULL, &newact.sa_mask);
 39+ sigaction(LUASANDBOX_SIGNAL, &newact, oldact);
 40+}
 41+
 42+void luasandbox_timer_remove_handler(struct sigaction * oldact)
 43+{
 44+ sigaction(LUASANDBOX_SIGNAL, oldact, NULL);
 45+}
 46+
 47+static void luasandbox_timer_handle(int signo, siginfo_t * info, void * context)
 48+{
 49+ luasandbox_timer_callback_data * data;
 50+
 51+ if (signo != LUASANDBOX_SIGNAL
 52+ || info->si_code != SI_TIMER
 53+ || !info->si_value.sival_ptr)
 54+ {
 55+ return;
 56+ }
 57+
 58+ data = (luasandbox_timer_callback_data*)info->si_value.sival_ptr;
 59+ data->sandbox->timed_out = 1;
 60+ if (data->emergency) {
 61+ sigset_t set;
 62+ sigemptyset(&set);
 63+ sigprocmask(SIG_SETMASK, &set, NULL);
 64+ data->sandbox->emergency_timed_out = 1;
 65+ if (data->sandbox->in_php) {
 66+ // The whole PHP request context is dirty now. We need to kill it,
 67+ // like what happens if there is a max_execution_time timeout.
 68+ zend_error(E_ERROR, "The maximum execution time for a Lua sandbox script "
 69+ "was exceeded and a PHP callback failed to return");
 70+ } else {
 71+ // The Lua state is dirty now and can't be used again.
 72+ luaL_error(data->sandbox->state, "emergency timeout");
 73+ }
 74+ } else {
 75+ // Set a hook which will terminate the script execution in a graceful way
 76+ lua_sethook(data->sandbox->state, luasandbox_timer_timeout_hook,
 77+ LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 0);
 78+ }
 79+}
 80+
 81+static void luasandbox_timer_timeout_hook(lua_State *L, lua_Debug *ar)
 82+{
 83+ // Avoid infinite recursion
 84+ lua_sethook(L, luasandbox_timer_timeout_hook, 0, 0);
 85+ // Do a longjmp to report the timeout error
 86+ luaL_error(L, luasandbox_timeout_message);
 87+}
 88+
 89+void luasandbox_timer_start(luasandbox_timer_set * lts,
 90+ php_luasandbox_obj * sandbox,
 91+ struct timespec * normal_timeout,
 92+ struct timespec * emergency_timeout)
 93+{
 94+ // Create normal timer
 95+ luasandbox_timer_create(&lts->normal_timer, sandbox, 0);
 96+ luasandbox_timer_settime(&lts->normal_timer, normal_timeout);
 97+
 98+ // Create emergency timer if requested
 99+ if (emergency_timeout->tv_sec || emergency_timeout->tv_nsec) {
 100+ lts->use_emergency = 1;
 101+ luasandbox_timer_create(&lts->emergency_timer, sandbox, 1);
 102+ luasandbox_timer_settime(&lts->emergency_timer, emergency_timeout);
 103+ } else {
 104+ lts->use_emergency = 0;
 105+ }
 106+}
 107+
 108+static void luasandbox_timer_create(luasandbox_timer * lt, php_luasandbox_obj * sandbox,
 109+ int emergency)
 110+{
 111+ struct sigevent ev;
 112+
 113+ lt->cbdata.emergency = emergency;
 114+ lt->cbdata.sandbox = sandbox;
 115+
 116+ ev.sigev_notify = SIGEV_SIGNAL;
 117+ ev.sigev_signo = LUASANDBOX_SIGNAL;
 118+ ev.sigev_value.sival_ptr = (void*)&lt->cbdata;
 119+
 120+ timer_create(LUASANDBOX_CLOCK_ID, &ev, &lt->timer);
 121+}
 122+
 123+static void luasandbox_timer_settime(luasandbox_timer * lt, struct timespec * ts)
 124+{
 125+ struct itimerspec its;
 126+ its.it_interval.tv_sec = 0;
 127+ its.it_interval.tv_nsec = 0;
 128+ its.it_value = *ts;
 129+ timer_settime(lt->timer, 0, &its, NULL);
 130+}
 131+
 132+void luasandbox_timer_stop(luasandbox_timer_set * lts)
 133+{
 134+ struct timespec zero = {0, 0};
 135+ if (lts->use_emergency) {
 136+ luasandbox_timer_settime(&lts->emergency_timer, &zero);
 137+ timer_delete(lts->emergency_timer.timer);
 138+ }
 139+
 140+ luasandbox_timer_settime(&lts->normal_timer, &zero);
 141+ timer_delete(lts->normal_timer.timer);
 142+}
 143+
 144+#endif
Property changes on: trunk/php/luasandbox/timer.c
___________________________________________________________________
Added: svn:eol-style
1145 + native
Index: trunk/php/luasandbox/config.m4
@@ -26,5 +26,5 @@
2727 AC_MSG_NOTICE(LUA: found LUA_LIBS: $LUA_LIBS)
2828
2929 PHP_SUBST(LUASANDBOX_SHARED_LIBADD)
30 - PHP_NEW_EXTENSION(luasandbox, luasandbox.c, $ext_shared)
 30+ PHP_NEW_EXTENSION(luasandbox, luasandbox.c timer.c, $ext_shared)
3131 fi
Index: trunk/php/luasandbox/luasandbox_timer.h
@@ -0,0 +1,53 @@
 2+#ifndef LUASANDBOX_TIMER_H
 3+#define LUASANDBOX_TIMER_H
 4+
 5+#ifdef CLOCK_REALTIME
 6+
 7+#ifdef CLOCK_PROCESS_CPUTIME_ID
 8+#define LUASANDBOX_CLOCK_ID CLOCK_PROCESS_CPUTIME_ID
 9+#else
 10+#define LUASANDBOX_CLOCK_ID CLOCK_REALTIME
 11+#endif
 12+
 13+#ifdef SIGRTMIN
 14+#define LUASANDBOX_SIGNAL (SIGRTMIN+1)
 15+#else
 16+#define LUASANDBOX_SIGNAL SIGUSR2
 17+#endif
 18+
 19+typedef struct {
 20+ int emergency;
 21+ php_luasandbox_obj * sandbox;
 22+} luasandbox_timer_callback_data;
 23+
 24+typedef struct {
 25+ timer_t timer;
 26+ luasandbox_timer_callback_data cbdata;
 27+} luasandbox_timer;
 28+
 29+typedef struct {
 30+ luasandbox_timer normal_timer;
 31+ luasandbox_timer emergency_timer;
 32+ int use_emergency;
 33+ php_luasandbox_obj * sandbox;
 34+} luasandbox_timer_set;
 35+
 36+
 37+#else
 38+
 39+#define LUASANDBOX_NO_CLOCK
 40+
 41+typedef struct {} luasandbox_timer;
 42+
 43+#endif
 44+
 45+void luasandbox_timer_install_handler(struct sigaction * oldact);
 46+void luasandbox_timer_remove_handler(struct sigaction * oldact);
 47+void luasandbox_timer_start(luasandbox_timer_set * lts,
 48+ php_luasandbox_obj * sandbox,
 49+ struct timespec * normal_timeout,
 50+ struct timespec * emergency_timeout);
 51+void luasandbox_timer_stop(luasandbox_timer_set * lts);
 52+
 53+
 54+#endif
Property changes on: trunk/php/luasandbox/luasandbox_timer.h
___________________________________________________________________
Added: svn:eol-style
155 + native
Index: trunk/php/luasandbox/php_luasandbox.h
@@ -5,6 +5,8 @@
66 #include <lua.h>
77
88 extern zend_module_entry luasandbox_module_entry;
 9+extern char luasandbox_timeout_message[];
 10+
911 #define phpext_luasandbox_ptr &luasandbox_module_entry
1012
1113 #ifdef PHP_WIN32
@@ -21,34 +23,23 @@
2224
2325 PHP_MINIT_FUNCTION(luasandbox);
2426 PHP_MSHUTDOWN_FUNCTION(luasandbox);
 27+PHP_RSHUTDOWN_FUNCTION(luasandbox);
2528 PHP_MINFO_FUNCTION(luasandbox);
2629
2730 PHP_METHOD(LuaSandbox, loadString);
 31+PHP_METHOD(LuaSandbox, doString);
2832 PHP_METHOD(LuaSandbox, setMemoryLimit);
 33+PHP_METHOD(LuaSandbox, setCPULimit);
2934 PHP_METHOD(LuaSandbox, callFunction);
30 -PHP_METHOD(LuaSandbox, callScript);
31 -PHP_METHOD(LuaSandbox, createFunction);
 35+PHP_METHOD(LuaSandbox, register);
3236
33 -/*
34 - Declare any global variables you may need between the BEGIN
35 - and END macros here:
 37+PHP_METHOD(LuaSandboxFunction, call);
3638
3739 ZEND_BEGIN_MODULE_GLOBALS(luasandbox)
38 - long global_value;
39 - char *global_string;
 40+ int signal_handler_installed;
 41+ struct sigaction old_handler;
4042 ZEND_END_MODULE_GLOBALS(luasandbox)
41 -*/
4243
43 -/* In every utility function you add that needs to use variables
44 - in php_luasandbox_globals, call TSRMLS_FETCH(); after declaring other
45 - variables used by that function, or better yet, pass in TSRMLS_CC
46 - after the last function argument and declare your utility function
47 - with TSRMLS_DC after the last declared argument. Always refer to
48 - the globals in your function as LUASANDBOX_G(variable). You are
49 - encouraged to rename these macros something shorter, see
50 - examples in any other php module directory.
51 -*/
52 -
5344 #ifdef ZTS
5445 #define LUASANDBOX_G(v) TSRMG(luasandbox_globals_id, zend_luasandbox_globals *, v)
5546 #else
@@ -60,8 +51,21 @@
6152 lua_State * state;
6253 size_t memory_limit;
6354 size_t memory_usage;
 55+ int in_php;
 56+ volatile int timed_out;
 57+ volatile int emergency_timed_out;
 58+ int is_cpu_limited;
 59+ struct timespec cpu_normal_limit;
 60+ struct timespec cpu_emergency_limit;
6461 };
6562 typedef struct _php_luasandbox_obj php_luasandbox_obj;
6663
 64+struct _php_luasandboxfunction_obj {
 65+ zend_object std;
 66+ zval * sandbox;
 67+ int index;
 68+};
 69+typedef struct _php_luasandboxfunction_obj php_luasandboxfunction_obj;
 70+
6771 #endif /* PHP_LUASANDBOX_H */
6872

Status & tagging log