| 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 | |