Index: trunk/php/luasandbox/php_luasandbox.h |
— | — | @@ -29,6 +29,7 @@ |
30 | 30 | PHP_METHOD(LuaSandbox, loadString); |
31 | 31 | PHP_METHOD(LuaSandbox, loadBinary); |
32 | 32 | PHP_METHOD(LuaSandbox, setMemoryLimit); |
| 33 | +PHP_METHOD(LuaSandbox, getMemoryUsage); |
33 | 34 | PHP_METHOD(LuaSandbox, setCPULimit); |
34 | 35 | PHP_METHOD(LuaSandbox, callFunction); |
35 | 36 | PHP_METHOD(LuaSandbox, registerLibrary); |
— | — | @@ -53,6 +54,8 @@ |
54 | 55 | lua_State * state; |
55 | 56 | size_t memory_limit; |
56 | 57 | size_t memory_usage; |
| 58 | + lua_Alloc old_alloc; |
| 59 | + void * old_alloc_ud; |
57 | 60 | int in_php; |
58 | 61 | volatile int timed_out; |
59 | 62 | volatile int emergency_timed_out; |
Index: trunk/php/luasandbox/luasandbox.c |
— | — | @@ -19,6 +19,10 @@ |
20 | 20 | #include "luasandbox_timer.h" |
21 | 21 | #include "ext/standard/php_smart_str.h" |
22 | 22 | |
| 23 | +#if defined(LUA_JITLIBNAME) && (SSIZE_MAX >> 32) > 0 |
| 24 | +#define LUASANDBOX_LJ_64 |
| 25 | +#endif |
| 26 | + |
23 | 27 | static zend_object_value luasandbox_new(zend_class_entry *ce TSRMLS_DC); |
24 | 28 | static lua_State * luasandbox_newstate(php_luasandbox_obj * intern); |
25 | 29 | static void luasandbox_free_storage(void *object TSRMLS_DC); |
— | — | @@ -26,7 +30,10 @@ |
27 | 31 | static void luasandboxfunction_free_storage(void *object TSRMLS_DC); |
28 | 32 | static void luasandboxfunction_destroy(void *object, zend_object_handle handle TSRMLS_DC); |
29 | 33 | static int luasandbox_free_zval_userdata(lua_State * L); |
30 | | -static void *luasandbox_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
| 34 | +static inline int luasandbox_update_memory_accounting(php_luasandbox_obj* obj, |
| 35 | + size_t osize, size_t nsize); |
| 36 | +static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
| 37 | +static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
31 | 38 | static int luasandbox_panic(lua_State * L); |
32 | 39 | static lua_State * luasandbox_state_from_zval(zval * this_ptr TSRMLS_DC); |
33 | 40 | static void luasandbox_load_helper(int binary, INTERNAL_FUNCTION_PARAMETERS); |
— | — | @@ -120,6 +127,9 @@ |
121 | 128 | ZEND_ARG_INFO(0, limit) |
122 | 129 | ZEND_END_ARG_INFO() |
123 | 130 | |
| 131 | +ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_getMemoryUsage, 0) |
| 132 | +ZEND_END_ARG_INFO() |
| 133 | + |
124 | 134 | ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_setCPULimit, 0) |
125 | 135 | ZEND_ARG_INFO(0, normal_limit) |
126 | 136 | ZEND_ARG_INFO(0, emergency_limit) |
— | — | @@ -153,6 +163,7 @@ |
154 | 164 | PHP_ME(LuaSandbox, loadString, arginfo_luasandbox_loadString, 0) |
155 | 165 | PHP_ME(LuaSandbox, loadBinary, arginfo_luasandbox_loadBinary, 0) |
156 | 166 | PHP_ME(LuaSandbox, setMemoryLimit, arginfo_luasandbox_setMemoryLimit, 0) |
| 167 | + PHP_ME(LuaSandbox, getMemoryUsage, arginfo_luasandbox_getMemoryUsage, 0) |
157 | 168 | PHP_ME(LuaSandbox, setCPULimit, arginfo_luasandbox_setCPULimit, 0) |
158 | 169 | PHP_ME(LuaSandbox, callFunction, arginfo_luasandbox_callFunction, 0) |
159 | 170 | PHP_ME(LuaSandbox, registerLibrary, arginfo_luasandbox_registerLibrary, 0) |
— | — | @@ -319,7 +330,8 @@ |
320 | 331 | intern = emalloc(sizeof(php_luasandbox_obj)); |
321 | 332 | memset(intern, 0, sizeof(php_luasandbox_obj)); |
322 | 333 | zend_object_std_init(&intern->std, ce TSRMLS_CC); |
323 | | - |
| 334 | + intern->memory_limit = (size_t)-1; |
| 335 | + |
324 | 336 | // Initialise the Lua state |
325 | 337 | intern->state = luasandbox_newstate(intern); |
326 | 338 | |
— | — | @@ -343,14 +355,30 @@ |
344 | 356 | { |
345 | 357 | lua_State * L; |
346 | 358 | |
347 | | - L = lua_newstate(luasandbox_alloc, intern); |
| 359 | +#ifdef LUASANDBOX_LJ_64 |
| 360 | + // The 64-bit version of LuaJIT needs to use its own allocator |
| 361 | + L = luaL_newstate(); |
| 362 | + if (L == NULL) { |
| 363 | + php_error_docref(NULL TSRMLS_CC, E_ERROR, |
| 364 | + "Attempt to allocate a new Lua state failed"); |
| 365 | + } |
| 366 | + intern->old_alloc = lua_getallocf(L, &intern->old_alloc_ud); |
| 367 | + lua_setallocf(L, luasandbox_passthru_alloc, intern); |
| 368 | +#else |
| 369 | + L = lua_newstate(luasandbox_php_alloc, intern); |
| 370 | +#endif |
| 371 | + |
348 | 372 | lua_atpanic(L, luasandbox_panic); |
349 | 373 | |
350 | 374 | // Load some relatively safe standard libraries |
351 | | - luaopen_base(L); |
352 | | - luaopen_string(L); |
353 | | - luaopen_table(L); |
354 | | - luaopen_math(L); |
| 375 | + lua_pushcfunction(L, luaopen_base); |
| 376 | + lua_call(L, 0, 0); |
| 377 | + lua_pushcfunction(L, luaopen_string); |
| 378 | + lua_call(L, 0, 0); |
| 379 | + lua_pushcfunction(L, luaopen_table); |
| 380 | + lua_call(L, 0, 0); |
| 381 | + lua_pushcfunction(L, luaopen_math); |
| 382 | + lua_call(L, 0, 0); |
355 | 383 | |
356 | 384 | // Remove any globals that aren't in a whitelist. This is mostly to remove |
357 | 385 | // unsafe functions from the base library. |
— | — | @@ -408,6 +436,15 @@ |
409 | 437 | static void luasandbox_free_storage(void *object TSRMLS_DC) |
410 | 438 | { |
411 | 439 | php_luasandbox_obj * intern = (php_luasandbox_obj*)object; |
| 440 | + |
| 441 | + // In 64-bit LuaJIT mode, restore the old allocator before calling |
| 442 | + // lua_close() because lua_close() actually checks that the value of the |
| 443 | + // function pointer is unchanged before destroying the underlying |
| 444 | + // allocator. If the allocator has been changed, the mmap is not freed. |
| 445 | +#ifdef LUASANDBOX_LJ_64 |
| 446 | + lua_setallocf(intern->state, intern->old_alloc, intern->old_alloc_ud); |
| 447 | +#endif |
| 448 | + |
412 | 449 | lua_close(intern->state); |
413 | 450 | intern->state = NULL; |
414 | 451 | zend_object_std_dtor(&intern->std); |
— | — | @@ -501,28 +538,22 @@ |
502 | 539 | } |
503 | 540 | /* }}} */ |
504 | 541 | |
505 | | -/** {{{ luasandbox_alloc |
| 542 | +/** {{{ luasandbox_php_alloc |
506 | 543 | * |
507 | 544 | * The Lua allocator function. Use PHP's request-local allocator as a backend. |
508 | 545 | * Account for memory usage and deny the allocation request if the amount |
509 | 546 | * allocated is above the user-specified limit. |
510 | 547 | */ |
511 | | -static void *luasandbox_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
| 548 | +static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
512 | 549 | { |
513 | 550 | php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
514 | 551 | void * nptr; |
515 | 552 | obj->in_php = 1; |
516 | | - |
517 | | - // Update memory usage accounting |
518 | | - if (obj->memory_usage + nsize < obj->memory_usage) { |
519 | | - // Overflow |
520 | | - // No PHP error because it's plausible that untrusted scripts could do this. |
| 553 | + if (!luasandbox_update_memory_accounting(obj, osize, nsize)) { |
521 | 554 | obj->in_php = 0; |
522 | 555 | return NULL; |
523 | 556 | } |
524 | 557 | |
525 | | - obj->memory_usage += nsize - osize; |
526 | | - |
527 | 558 | if (nsize == 0) { |
528 | 559 | if (ptr) { |
529 | 560 | efree(ptr); |
— | — | @@ -538,6 +569,48 @@ |
539 | 570 | } |
540 | 571 | /* }}} */ |
541 | 572 | |
| 573 | +/** {{{ luasandbox_passthru_alloc |
| 574 | + * |
| 575 | + * A Lua allocator function for use with LuaJIT on a 64-bit platform. Pass |
| 576 | + * allocation requests through to the standard allocator, which is customised |
| 577 | + * on this platform to always return memory from the lower 2GB of address |
| 578 | + * space. |
| 579 | + */ |
| 580 | +static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
| 581 | +{ |
| 582 | + php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
| 583 | + if (!luasandbox_update_memory_accounting(obj, osize, nsize)) { |
| 584 | + return NULL; |
| 585 | + } |
| 586 | + return obj->old_alloc(obj->old_alloc_ud, ptr, osize, nsize); |
| 587 | +} |
| 588 | +/* }}} */ |
| 589 | + |
| 590 | +/** {{{ luasandbox_update_memory_accounting |
| 591 | + * |
| 592 | + * Update memory usage statistics for the given memory allocation request. |
| 593 | + * Returns 1 if the allocation should be allowed, 0 if it should fail. |
| 594 | + */ |
| 595 | +static inline int luasandbox_update_memory_accounting(php_luasandbox_obj* obj, |
| 596 | + size_t osize, size_t nsize) |
| 597 | +{ |
| 598 | + if (nsize > obj->memory_limit |
| 599 | + || obj->memory_usage > obj->memory_limit - nsize) |
| 600 | + { |
| 601 | + // Memory limit exceeded |
| 602 | + return 0; |
| 603 | + } |
| 604 | + |
| 605 | + if (osize > nsize && obj->memory_usage + nsize < osize) { |
| 606 | + // Negative memory usage -- do not update |
| 607 | + return 1; |
| 608 | + } |
| 609 | + |
| 610 | + obj->memory_usage += nsize - osize; |
| 611 | + return 1; |
| 612 | +} |
| 613 | +/* }}} */ |
| 614 | + |
542 | 615 | /** {{{ luasandbox_panic |
543 | 616 | * |
544 | 617 | * The Lua panic function. It is necessary to raise an E_ERROR, and thus do a |
— | — | @@ -818,6 +891,20 @@ |
819 | 892 | |
820 | 893 | /* }}} */ |
821 | 894 | |
| 895 | +/** {{{ LuaSandbox::getMemoryUsage */ |
| 896 | +PHP_METHOD(LuaSandbox, getMemoryUsage) |
| 897 | +{ |
| 898 | + php_luasandbox_obj * sandbox = (php_luasandbox_obj*) |
| 899 | + zend_object_store_get_object(getThis() TSRMLS_CC); |
| 900 | + |
| 901 | + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) { |
| 902 | + RETURN_FALSE; |
| 903 | + } |
| 904 | + |
| 905 | + RETURN_LONG(sandbox->memory_usage); |
| 906 | +} |
| 907 | +/* }}} */ |
| 908 | + |
822 | 909 | /** {{{ proto array LuaSandbox::callFunction(string name, ...) |
823 | 910 | * |
824 | 911 | * Call a function in the global variable with the given name. The name may |
Index: trunk/php/luasandbox/timer.c |
— | — | @@ -73,7 +73,7 @@ |
74 | 74 | } else { |
75 | 75 | // Set a hook which will terminate the script execution in a graceful way |
76 | 76 | lua_sethook(data->sandbox->state, luasandbox_timer_timeout_hook, |
77 | | - LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 0); |
| 77 | + LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 1); |
78 | 78 | } |
79 | 79 | } |
80 | 80 | |