Index: trunk/php/luasandbox/luasandbox.c |
— | — | @@ -23,6 +23,12 @@ |
24 | 24 | #define LUASANDBOX_LJ_64 |
25 | 25 | #endif |
26 | 26 | |
| 27 | +#define CHECK_VALID_STATE(state) \ |
| 28 | + if (!state) { \ |
| 29 | + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid LuaSandbox state"); \ |
| 30 | + RETURN_FALSE; \ |
| 31 | + } |
| 32 | + |
27 | 33 | static zend_object_value luasandbox_new(zend_class_entry *ce TSRMLS_DC); |
28 | 34 | static lua_State * luasandbox_newstate(php_luasandbox_obj * intern); |
29 | 35 | static void luasandbox_free_storage(void *object TSRMLS_DC); |
— | — | @@ -42,15 +48,18 @@ |
43 | 49 | static void luasandbox_set_timespec(struct timespec * dest, double source); |
44 | 50 | static int luasandbox_function_init(zval * this_ptr, php_luasandboxfunction_obj ** pfunc, |
45 | 51 | lua_State ** pstate, php_luasandbox_obj ** psandbox TSRMLS_DC); |
46 | | -static void luasandbox_call_helper(lua_State * L, php_luasandbox_obj * sandbox, |
| 52 | +static void luasandbox_call_helper(lua_State * L, zval * sandbox_zval, |
| 53 | + php_luasandbox_obj * sandbox, |
47 | 54 | zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC); |
48 | 55 | static int luasandbox_push_zval(lua_State * L, zval * z); |
49 | 56 | static void luasandbox_push_zval_userdata(lua_State * L, zval * z); |
50 | | -static zval * luasandbox_lua_to_zval(lua_State * L, int index, HashTable * recursionGuard); |
| 57 | +static void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
| 58 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
51 | 59 | static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index, |
52 | | - HashTable * recursionGuard); |
| 60 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
53 | 61 | static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L); |
54 | 62 | static void luasandbox_handle_error(lua_State * L, int status); |
| 63 | +static void luasandbox_handle_emergency_timeout(php_luasandbox_obj * sandbox); |
55 | 64 | static int luasandbox_push_hashtable(lua_State * L, HashTable * ht); |
56 | 65 | static int luasandbox_call_php(lua_State * L); |
57 | 66 | static int luasandbox_dump_writer(lua_State * L, const void * p, size_t sz, void * ud); |
— | — | @@ -145,6 +154,9 @@ |
146 | 155 | ZEND_ARG_INFO(0, functions) |
147 | 156 | ZEND_END_ARG_INFO() |
148 | 157 | |
| 158 | +ZEND_BEGIN_ARG_INFO(arginfo_luasandboxfunction___construct, 0) |
| 159 | +ZEND_END_ARG_INFO() |
| 160 | + |
149 | 161 | ZEND_BEGIN_ARG_INFO(arginfo_luasandboxfunction_call, 0) |
150 | 162 | ZEND_ARG_INFO(0, ...) |
151 | 163 | ZEND_END_ARG_INFO() |
— | — | @@ -171,6 +183,8 @@ |
172 | 184 | }; |
173 | 185 | |
174 | 186 | const zend_function_entry luasandboxfunction_methods[] = { |
| 187 | + PHP_ME(LuaSandboxFunction, __construct, arginfo_luasandboxfunction___construct, |
| 188 | + ZEND_ACC_PRIVATE | ZEND_ACC_FINAL) |
175 | 189 | PHP_ME(LuaSandboxFunction, call, arginfo_luasandboxfunction_call, 0) |
176 | 190 | PHP_ME(LuaSandboxFunction, dump, arginfo_luasandboxfunction_dump, 0) |
177 | 191 | {NULL, NULL, NULL} |
— | — | @@ -298,9 +312,9 @@ |
299 | 313 | */ |
300 | 314 | static inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern) |
301 | 315 | { |
302 | | - intern->in_php = 1; |
| 316 | + intern->in_php ++; |
303 | 317 | if (intern->timed_out) { |
304 | | - intern->in_php = 0; |
| 318 | + intern->in_php --; |
305 | 319 | luaL_error(L, luasandbox_timeout_message); |
306 | 320 | } |
307 | 321 | } |
— | — | @@ -313,7 +327,7 @@ |
314 | 328 | */ |
315 | 329 | static inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern) |
316 | 330 | { |
317 | | - intern->in_php = 0; |
| 331 | + intern->in_php --; |
318 | 332 | } |
319 | 333 | /* }}} */ |
320 | 334 | |
— | — | @@ -418,7 +432,7 @@ |
419 | 433 | // Register a pointer to the PHP object so that C functions can find it |
420 | 434 | lua_pushlightuserdata(L, (void*)intern); |
421 | 435 | lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj"); |
422 | | - |
| 436 | + |
423 | 437 | // Create the metatable for zval destruction |
424 | 438 | lua_createtable(L, 0, 1); |
425 | 439 | lua_pushcfunction(L, luasandbox_free_zval_userdata); |
— | — | @@ -437,16 +451,18 @@ |
438 | 452 | { |
439 | 453 | php_luasandbox_obj * intern = (php_luasandbox_obj*)object; |
440 | 454 | |
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. |
| 455 | + if (intern->state) { |
| 456 | + // In 64-bit LuaJIT mode, restore the old allocator before calling |
| 457 | + // lua_close() because lua_close() actually checks that the value of the |
| 458 | + // function pointer is unchanged before destroying the underlying |
| 459 | + // allocator. If the allocator has been changed, the mmap is not freed. |
445 | 460 | #ifdef LUASANDBOX_LJ_64 |
446 | | - lua_setallocf(intern->state, intern->old_alloc, intern->old_alloc_ud); |
| 461 | + lua_setallocf(intern->state, intern->old_alloc, intern->old_alloc_ud); |
447 | 462 | #endif |
448 | 463 | |
449 | | - lua_close(intern->state); |
450 | | - intern->state = NULL; |
| 464 | + lua_close(intern->state); |
| 465 | + intern->state = NULL; |
| 466 | + } |
451 | 467 | zend_object_std_dtor(&intern->std); |
452 | 468 | efree(object); |
453 | 469 | } |
— | — | @@ -455,9 +471,6 @@ |
456 | 472 | /** {{{ luasandboxfunction_new |
457 | 473 | * |
458 | 474 | * "new" handler for the LuaSandboxFunction class. |
459 | | - * |
460 | | - * TODO: Make it somehow impossible to construct these objects from user code. |
461 | | - * Only LuaSandbox methods should be constructing them. |
462 | 475 | */ |
463 | 476 | static zend_object_value luasandboxfunction_new(zend_class_entry *ce TSRMLS_CC) |
464 | 477 | { |
— | — | @@ -492,14 +505,16 @@ |
493 | 506 | if (func->sandbox) { |
494 | 507 | php_luasandbox_obj * sandbox = (php_luasandbox_obj*) |
495 | 508 | zend_object_store_get_object(func->sandbox TSRMLS_CC); |
496 | | - lua_State * L = sandbox->state; |
| 509 | + if (sandbox && sandbox->state) { |
| 510 | + lua_State * L = sandbox->state; |
497 | 511 | |
498 | | - // Delete the chunk |
499 | | - if (func->index) { |
500 | | - lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
501 | | - lua_pushnil(L); |
502 | | - lua_rawseti(L, -2, func->index); |
503 | | - lua_pop(L, 1); |
| 512 | + // Delete the chunk |
| 513 | + if (func->index) { |
| 514 | + lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
| 515 | + lua_pushnil(L); |
| 516 | + lua_rawseti(L, -2, func->index); |
| 517 | + lua_pop(L, 1); |
| 518 | + } |
504 | 519 | } |
505 | 520 | |
506 | 521 | // Delete the parent reference |
— | — | @@ -548,9 +563,9 @@ |
549 | 564 | { |
550 | 565 | php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
551 | 566 | void * nptr; |
552 | | - obj->in_php = 1; |
| 567 | + obj->in_php ++; |
553 | 568 | if (!luasandbox_update_memory_accounting(obj, osize, nsize)) { |
554 | | - obj->in_php = 0; |
| 569 | + obj->in_php --; |
555 | 570 | return NULL; |
556 | 571 | } |
557 | 572 | |
— | — | @@ -564,7 +579,7 @@ |
565 | 580 | } else { |
566 | 581 | nptr = erealloc(ptr, nsize); |
567 | 582 | } |
568 | | - obj->in_php = 0; |
| 583 | + obj->in_php --; |
569 | 584 | return nptr; |
570 | 585 | } |
571 | 586 | /* }}} */ |
— | — | @@ -668,20 +683,8 @@ |
669 | 684 | sandbox = (php_luasandbox_obj*) |
670 | 685 | zend_object_store_get_object(this_ptr TSRMLS_CC); |
671 | 686 | L = sandbox->state; |
| 687 | + CHECK_VALID_STATE(L); |
672 | 688 | |
673 | | - // The following code puts zval of the sandbox object into the register. |
674 | | - // Why here? It should have been done at the constructor, but there was |
675 | | - // no getThis() available at that point. Let's hope user will not run any |
676 | | - // code requiring this register until he actually loads the code. |
677 | | - // Why put it? Because when creating function object we need a zval, |
678 | | - // not just php_luasandbox_obj, since we are going to reference to it from |
679 | | - // the function object, and such referencing is possible only through zvalues. |
680 | | - // Let us hope putting zval into Lua register won't corrupt any PHP internal |
681 | | - // mechanisms. |
682 | | - // FIXME: there should be a better way of doing this. |
683 | | - lua_pushlightuserdata(L, (void*)getThis()); |
684 | | - lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj_zval"); |
685 | | - |
686 | 689 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", |
687 | 690 | &code, &codeLength, &chunkName, &chunkNameLength) == FAILURE) { |
688 | 691 | RETURN_FALSE; |
— | — | @@ -713,33 +716,20 @@ |
714 | 717 | RETURN_FALSE; |
715 | 718 | } |
716 | 719 | |
717 | | - // Get the chunks table |
718 | | - lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
719 | | - |
720 | | - // Get the next free index |
721 | | - index = ++(sandbox->function_index); |
722 | | - if (index >= INT_MAX) { |
723 | | - php_error_docref(NULL TSRMLS_CC, E_WARNING, |
724 | | - "too many chunks loaded already"); |
725 | | - RETURN_FALSE; |
726 | | - } |
727 | | - |
728 | 720 | // Parse the string into a function on the stack |
729 | 721 | status = luaL_loadbuffer(L, code, codeLength, chunkName); |
730 | 722 | if (status != 0) { |
731 | 723 | luasandbox_handle_error(L, status); |
732 | 724 | return; |
733 | 725 | } |
734 | | - |
735 | | - // Store the resulting function to the chunks table |
736 | | - lua_rawseti(L, -2, (int)index); |
737 | 726 | |
738 | | - // Create a LuaSandboxFunction object to hold a reference to the function |
739 | | - object_init_ex(return_value, luasandboxfunction_ce); |
740 | | - func_obj = (php_luasandboxfunction_obj*)zend_object_store_get_object(return_value); |
741 | | - func_obj->index = index; |
742 | | - func_obj->sandbox = getThis(); |
743 | | - Z_ADDREF_P(getThis()); |
| 727 | + // Make a zval out of it, and return false on error |
| 728 | + luasandbox_lua_to_zval(return_value, L, lua_gettop(L), this_ptr, NULL TSRMLS_CC); |
| 729 | + if (Z_TYPE_P(return_value) == IS_NULL) { |
| 730 | + php_error_docref(NULL TSRMLS_CC, E_WARNING, |
| 731 | + "too many chunks loaded already"); |
| 732 | + RETVAL_FALSE; |
| 733 | + } |
744 | 734 | |
745 | 735 | // Balance the stack |
746 | 736 | lua_pop(L, 1); |
— | — | @@ -773,6 +763,23 @@ |
774 | 764 | } |
775 | 765 | /* }}} */ |
776 | 766 | |
| 767 | +/** {{{ luasandbox_handle_emergency_timeout |
| 768 | + * |
| 769 | + * Handle the situation where the emergency_timeout flag is set. Throws an |
| 770 | + * appropriate exception and destroys the state. |
| 771 | + */ |
| 772 | +static void luasandbox_handle_emergency_timeout(php_luasandbox_obj * sandbox) |
| 773 | +{ |
| 774 | + lua_close(sandbox->state); |
| 775 | + sandbox->state = NULL; |
| 776 | + sandbox->emergency_timed_out = 0; |
| 777 | + zend_throw_exception(luasandboxemergencytimeout_ce, |
| 778 | + "The maximum execution time was exceeded " |
| 779 | + "and the current Lua statement failed to return, leading to " |
| 780 | + "destruction of the Lua state", LUA_ERRRUN); |
| 781 | +} |
| 782 | +/* }}} */ |
| 783 | + |
777 | 784 | /** {{{ luasandbox_handle_error |
778 | 785 | * |
779 | 786 | * Handles the error return situation from lua_pcall() and lua_load(), where a |
— | — | @@ -945,6 +952,7 @@ |
946 | 953 | php_luasandbox_obj * sandbox = (php_luasandbox_obj*) |
947 | 954 | zend_object_store_get_object(getThis() TSRMLS_CC); |
948 | 955 | lua_State * L = sandbox->state; |
| 956 | + CHECK_VALID_STATE(L); |
949 | 957 | |
950 | 958 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s*", |
951 | 959 | &name, &nameLength, &args, &numArgs) == FAILURE) |
— | — | @@ -959,7 +967,7 @@ |
960 | 968 | RETVAL_FALSE; |
961 | 969 | } else { |
962 | 970 | // Call it |
963 | | - luasandbox_call_helper(L, sandbox, args, numArgs, return_value TSRMLS_CC); |
| 971 | + luasandbox_call_helper(L, getThis(), sandbox, args, numArgs, return_value TSRMLS_CC); |
964 | 972 | } |
965 | 973 | |
966 | 974 | // Delete varargs |
— | — | @@ -980,6 +988,8 @@ |
981 | 989 | *pfunc = (php_luasandboxfunction_obj *) |
982 | 990 | zend_object_store_get_object(this_ptr TSRMLS_CC); |
983 | 991 | if (!*pfunc || !(*pfunc)->sandbox || !(*pfunc)->index) { |
| 992 | + php_error_docref(NULL TSRMLS_CC, E_WARNING, |
| 993 | + "attempt to call uninitialized LuaSandboxFunction object" ); |
984 | 994 | return 0; |
985 | 995 | } |
986 | 996 | |
— | — | @@ -987,6 +997,11 @@ |
988 | 998 | zend_object_store_get_object((*pfunc)->sandbox TSRMLS_CC); |
989 | 999 | *pstate = (*psandbox)->state; |
990 | 1000 | |
| 1001 | + if (!*pstate) { |
| 1002 | + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid LuaSandbox state"); |
| 1003 | + return 0; |
| 1004 | + } |
| 1005 | + |
991 | 1006 | // Find the function |
992 | 1007 | lua_getfield(*pstate, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
993 | 1008 | lua_rawgeti(*pstate, -1, (*pfunc)->index); |
— | — | @@ -998,6 +1013,16 @@ |
999 | 1014 | } |
1000 | 1015 | /* }}} */ |
1001 | 1016 | |
| 1017 | +/* {{{ proto private final LuaSandboxFunction::__construct() |
| 1018 | + * |
| 1019 | + * Construct a LuaSandboxFunction object. Do not call this directly, use |
| 1020 | + * LuaSandbox::loadString(). |
| 1021 | + */ |
| 1022 | +PHP_METHOD(LuaSandboxFunction, __construct) |
| 1023 | +{ |
| 1024 | + php_error_docref(NULL TSRMLS_CC, E_ERROR, "LuaSandboxFunction cannot be constructed directly"); |
| 1025 | +} |
| 1026 | + |
1002 | 1027 | /** {{{ proto array LuaSandboxFunction::call(...) |
1003 | 1028 | * |
1004 | 1029 | * Call a LuaSandboxFunction. The arguments to this function are passed through |
— | — | @@ -1023,8 +1048,6 @@ |
1024 | 1049 | php_luasandbox_obj * sandbox; |
1025 | 1050 | |
1026 | 1051 | if (!luasandbox_function_init(getThis(), &func, &L, &sandbox TSRMLS_CC)) { |
1027 | | - php_error_docref(NULL TSRMLS_CC, E_WARNING, |
1028 | | - "attempt to call uninitialized LuaSandboxFunction object" ); |
1029 | 1052 | RETURN_FALSE; |
1030 | 1053 | } |
1031 | 1054 | |
— | — | @@ -1035,7 +1058,7 @@ |
1036 | 1059 | } |
1037 | 1060 | |
1038 | 1061 | // Call the function |
1039 | | - luasandbox_call_helper(L, sandbox, args, numArgs, return_value TSRMLS_CC); |
| 1062 | + luasandbox_call_helper(L, func->sandbox, sandbox, args, numArgs, return_value TSRMLS_CC); |
1040 | 1063 | |
1041 | 1064 | // Delete varargs |
1042 | 1065 | if (numArgs) { |
— | — | @@ -1049,7 +1072,7 @@ |
1050 | 1073 | * Call the function at the top of the stack and then pop it. Set return_value |
1051 | 1074 | * to an array containing all the results. |
1052 | 1075 | */ |
1053 | | -static void luasandbox_call_helper(lua_State * L, php_luasandbox_obj * sandbox, |
| 1076 | +static void luasandbox_call_helper(lua_State * L, zval * sandbox_zval, php_luasandbox_obj * sandbox, |
1054 | 1077 | zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC) |
1055 | 1078 | { |
1056 | 1079 | // Save the top position |
— | — | @@ -1058,6 +1081,7 @@ |
1059 | 1082 | int cpu_limited = 0; |
1060 | 1083 | luasandbox_timer_set t; |
1061 | 1084 | int i, numResults; |
| 1085 | + zval * old_zval; |
1062 | 1086 | |
1063 | 1087 | // Check to see if the value is a valid function |
1064 | 1088 | if (lua_type(L, -1) != LUA_TFUNCTION) { |
— | — | @@ -1078,7 +1102,7 @@ |
1079 | 1103 | } |
1080 | 1104 | |
1081 | 1105 | // Initialise the CPU limit timer |
1082 | | - if (sandbox->is_cpu_limited) { |
| 1106 | + if (!sandbox->in_lua && sandbox->is_cpu_limited) { |
1083 | 1107 | cpu_limited = 1; |
1084 | 1108 | if (!LUASANDBOX_G(signal_handler_installed)) { |
1085 | 1109 | luasandbox_timer_install_handler(&LUASANDBOX_G(old_handler)); |
— | — | @@ -1087,26 +1111,26 @@ |
1088 | 1112 | &sandbox->cpu_emergency_limit); |
1089 | 1113 | } |
1090 | 1114 | |
| 1115 | + // Save the current zval for later use in luasandbox_call_php. Restore it |
| 1116 | + // after execution finishes, to support re-entrancy. |
| 1117 | + old_zval = sandbox->current_zval; |
| 1118 | + sandbox->current_zval = sandbox_zval; |
| 1119 | + |
1091 | 1120 | // Call the function |
| 1121 | + sandbox->in_lua++; |
1092 | 1122 | status = lua_pcall(L, numArgs, LUA_MULTRET, 0); |
| 1123 | + sandbox->in_lua--; |
| 1124 | + sandbox->current_zval = old_zval; |
1093 | 1125 | |
1094 | 1126 | // Stop the timer |
1095 | 1127 | if (cpu_limited) { |
1096 | 1128 | luasandbox_timer_stop(&t); |
1097 | 1129 | } |
1098 | | - |
1099 | | - // If there was an emergency timeout, destroy the state |
1100 | 1130 | if (sandbox->emergency_timed_out) { |
1101 | | - lua_close(L); |
1102 | | - L = sandbox->state = luasandbox_newstate(sandbox); |
1103 | | - sandbox->emergency_timed_out = 0; |
1104 | | - zend_throw_exception(luasandboxemergencytimeout_ce, |
1105 | | - "The maximum execution time was exceeded " |
1106 | | - "and the current Lua statement failed to return, leading to " |
1107 | | - "destruction of the Lua state", LUA_ERRRUN); |
| 1131 | + luasandbox_handle_emergency_timeout(sandbox); |
1108 | 1132 | return; |
1109 | 1133 | } |
1110 | | - |
| 1134 | + |
1111 | 1135 | // Handle normal errors |
1112 | 1136 | if (status) { |
1113 | 1137 | luasandbox_handle_error(L, status); |
— | — | @@ -1120,7 +1144,9 @@ |
1121 | 1145 | |
1122 | 1146 | // Fill the array with the results |
1123 | 1147 | for (i = 0; i < numResults; i++) { |
1124 | | - zval * element = luasandbox_lua_to_zval(L, origTop + i, NULL); |
| 1148 | + zval * element; |
| 1149 | + MAKE_STD_ZVAL(element); |
| 1150 | + luasandbox_lua_to_zval(element, L, origTop + i, sandbox_zval, NULL TSRMLS_CC); |
1125 | 1151 | zend_hash_next_index_insert(Z_ARRVAL_P(return_value), |
1126 | 1152 | (void*)&element, |
1127 | 1153 | sizeof(zval*), NULL); |
— | — | @@ -1300,23 +1326,23 @@ |
1301 | 1327 | |
1302 | 1328 | /** {{{ luasandbox_lua_to_zval |
1303 | 1329 | * |
1304 | | - * Convert a lua value to a zval. Allocates the zval on the heap and returns |
1305 | | - * a pointer to it. |
| 1330 | + * Convert a lua value to a zval. |
1306 | 1331 | * |
1307 | 1332 | * If a value is encountered that can't be converted to a zval, a LuaPlaceholder |
1308 | 1333 | * object is returned instead. |
1309 | 1334 | * |
| 1335 | + * @param z A pointer to the destination zval |
1310 | 1336 | * @param L The lua state |
1311 | 1337 | * @param index The stack index to the input value |
| 1338 | + * @param sandbox_zval A zval poiting to a valid LuaSandbox object which will be |
| 1339 | + * used for the parent object of any LuaSandboxFunction objects created. |
1312 | 1340 | * @param recursionGuard A hashtable for keeping track of tables that have been |
1313 | 1341 | * processed, to allow infinite recursion to be avoided. External callers |
1314 | 1342 | * should set this to NULL. |
1315 | 1343 | */ |
1316 | | -static zval * luasandbox_lua_to_zval(lua_State * L, int index, HashTable * recursionGuard) |
| 1344 | +static void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
| 1345 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC) |
1317 | 1346 | { |
1318 | | - zval * z; |
1319 | | - MAKE_STD_ZVAL(z); |
1320 | | - |
1321 | 1347 | switch (lua_type(L, index)) { |
1322 | 1348 | case LUA_TNIL: |
1323 | 1349 | ZVAL_NULL(z); |
— | — | @@ -1379,7 +1405,7 @@ |
1380 | 1406 | |
1381 | 1407 | // Process the array |
1382 | 1408 | array_init(z); |
1383 | | - luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, recursionGuard); |
| 1409 | + luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, sandbox_zval, recursionGuard TSRMLS_CC); |
1384 | 1410 | |
1385 | 1411 | if (allocated) { |
1386 | 1412 | zend_hash_destroy(recursionGuard); |
— | — | @@ -1390,37 +1416,35 @@ |
1391 | 1417 | case LUA_TFUNCTION: { |
1392 | 1418 | int func_index; |
1393 | 1419 | php_luasandboxfunction_obj * func_obj; |
1394 | | - php_luasandbox_obj * sandbox; |
1395 | | - zval * sandbox_zval; |
| 1420 | + php_luasandbox_obj * sandbox = (php_luasandbox_obj*) |
| 1421 | + zend_object_store_get_object(sandbox_zval); |
| 1422 | + |
| 1423 | + // Normalise the input index so that we can push without invalidating it. |
| 1424 | + if (index < 0) { |
| 1425 | + index += lua_gettop(L) + 1; |
| 1426 | + } |
1396 | 1427 | |
1397 | | - // Get the sandbox object and its zval |
1398 | | - sandbox = luasandbox_get_php_obj(L); |
1399 | | - lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj_zval"); |
1400 | | - sandbox_zval = (zval*)lua_touserdata(L, -1); |
1401 | | - assert(sandbox_zval != NULL); |
1402 | | - lua_pop(L, 1); |
1403 | | - |
1404 | 1428 | // Get the chunks table |
1405 | 1429 | lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
1406 | 1430 | |
1407 | 1431 | // Get the next free index |
1408 | | - func_index = ++(sandbox->function_index); |
1409 | | - if (func_index >= INT_MAX) { |
1410 | | - php_error_docref(NULL TSRMLS_CC, E_WARNING, |
1411 | | - "too many chunks loaded already"); |
| 1432 | + if (sandbox->function_index >= INT_MAX) { |
1412 | 1433 | ZVAL_NULL(z); |
| 1434 | + lua_pop(L, 1); |
| 1435 | + break; |
1413 | 1436 | } |
| 1437 | + func_index = ++(sandbox->function_index); |
1414 | 1438 | |
1415 | | - // Put the function together with other chunks |
1416 | | - lua_pushvalue(L, index - 1); |
1417 | | - lua_rawseti(L, -2, (int)func_index); |
| 1439 | + // Store it in the chunks table |
| 1440 | + lua_pushvalue(L, index); |
| 1441 | + lua_rawseti(L, -2, func_index); |
1418 | 1442 | |
1419 | 1443 | // Create a LuaSandboxFunction object to hold a reference to the function |
1420 | 1444 | object_init_ex(z, luasandboxfunction_ce); |
1421 | 1445 | func_obj = (php_luasandboxfunction_obj*)zend_object_store_get_object(z); |
1422 | 1446 | func_obj->index = func_index; |
1423 | 1447 | func_obj->sandbox = sandbox_zval; |
1424 | | - Z_ADDREF_P(func_obj->sandbox); |
| 1448 | + Z_ADDREF_P(sandbox_zval); |
1425 | 1449 | |
1426 | 1450 | // Balance the stack |
1427 | 1451 | lua_pop(L, 1); |
— | — | @@ -1433,7 +1457,6 @@ |
1434 | 1458 | // TODO: provide derived classes for each type |
1435 | 1459 | object_init_ex(z, luasandboxplaceholder_ce); |
1436 | 1460 | } |
1437 | | - return z; |
1438 | 1461 | } |
1439 | 1462 | /* }}} */ |
1440 | 1463 | |
— | — | @@ -1442,7 +1465,7 @@ |
1443 | 1466 | * Append the elements of the table in the specified index to the given HashTable. |
1444 | 1467 | */ |
1445 | 1468 | static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index, |
1446 | | - HashTable * recursionGuard) |
| 1469 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC) |
1447 | 1470 | { |
1448 | 1471 | const char * str; |
1449 | 1472 | size_t length; |
— | — | @@ -1455,7 +1478,8 @@ |
1456 | 1479 | |
1457 | 1480 | lua_pushnil(L); |
1458 | 1481 | while (lua_next(L, index) != 0) { |
1459 | | - value = luasandbox_lua_to_zval(L, -1, recursionGuard); |
| 1482 | + MAKE_STD_ZVAL(value); |
| 1483 | + luasandbox_lua_to_zval(value, L, -1, sandbox_zval, recursionGuard TSRMLS_CC); |
1460 | 1484 | |
1461 | 1485 | // Make a copy of the key so that we can call lua_tolstring() which is destructive |
1462 | 1486 | lua_pushvalue(L, -2); |
— | — | @@ -1509,6 +1533,8 @@ |
1510 | 1534 | HashTable * functions; |
1511 | 1535 | Bucket * p; |
1512 | 1536 | |
| 1537 | + CHECK_VALID_STATE(L); |
| 1538 | + |
1513 | 1539 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", |
1514 | 1540 | &libname, &libname_len, &zfunctions) == FAILURE) |
1515 | 1541 | { |
— | — | @@ -1577,6 +1603,7 @@ |
1578 | 1604 | zval **pointers; |
1579 | 1605 | zval ***double_pointers; |
1580 | 1606 | int num_results = 0; |
| 1607 | + TSRMLS_FETCH(); |
1581 | 1608 | |
1582 | 1609 | // Based on zend_parse_arg_impl() |
1583 | 1610 | if (zend_fcall_info_init(*callback_pp, 0, &fci, &fcc, NULL, |
— | — | @@ -1599,7 +1626,8 @@ |
1600 | 1627 | double_pointers = (zval***)temp; |
1601 | 1628 | pointers = (zval**)(temp + top); |
1602 | 1629 | for (i = 0; i < top; i++ ) { |
1603 | | - pointers[i] = luasandbox_lua_to_zval(L, i + 1, NULL); |
| 1630 | + MAKE_STD_ZVAL(pointers[i]); |
| 1631 | + luasandbox_lua_to_zval(pointers[i], L, i + 1, intern->current_zval, NULL TSRMLS_CC); |
1604 | 1632 | double_pointers[i] = &(pointers[i]); |
1605 | 1633 | } |
1606 | 1634 | |
— | — | @@ -1649,8 +1677,6 @@ |
1650 | 1678 | smart_str buf = {0}; |
1651 | 1679 | |
1652 | 1680 | if (!luasandbox_function_init(getThis(), &func, &L, &sandbox TSRMLS_CC)) { |
1653 | | - php_error_docref(NULL TSRMLS_CC, E_WARNING, |
1654 | | - "attempt to call uninitialized LuaSandboxFunction object" ); |
1655 | 1681 | RETURN_FALSE; |
1656 | 1682 | } |
1657 | 1683 | |
Index: trunk/php/luasandbox/tests/invalid_state.phpt |
— | — | @@ -0,0 +1,27 @@ |
| 2 | +--TEST-- |
| 3 | +Functions called on an invalid LuaSandbox, after emergency timeout |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | +$sandbox = new LuaSandbox; |
| 7 | +$sandbox->setCPULimit(100, 0.1); |
| 8 | +$f = $sandbox->loadString('while true do end'); |
| 9 | +$dump = $f->dump(); |
| 10 | +try { |
| 11 | + $f->call(); |
| 12 | +} catch (LuaSandboxEmergencyTimeout $e) { |
| 13 | + print $e->getMessage() . "\n"; |
| 14 | +} |
| 15 | +$f->call(); |
| 16 | +$sandbox->loadString('foo()'); |
| 17 | +$sandbox->loadBinary($dump); |
| 18 | +$sandbox->callFunction('foo'); |
| 19 | +--EXPECTF-- |
| 20 | +The maximum execution time was exceeded and the current Lua statement failed to return, leading to destruction of the Lua state |
| 21 | + |
| 22 | +Warning: LuaSandboxFunction::call(): invalid LuaSandbox state in %s on line %d |
| 23 | + |
| 24 | +Warning: LuaSandbox::loadString(): invalid LuaSandbox state in %s on line %d |
| 25 | + |
| 26 | +Warning: LuaSandbox::loadBinary(): invalid LuaSandbox state in %s on line %d |
| 27 | + |
| 28 | +Warning: LuaSandbox::callFunction(): invalid LuaSandbox state in %s on line %d |
Index: trunk/php/luasandbox/tests/loadString.phpt |
— | — | @@ -0,0 +1,17 @@ |
| 2 | +--TEST-- |
| 3 | +loadString 1 |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | +var_dump($sandbox = new LuaSandbox); |
| 7 | +var_dump($f = $sandbox->loadString('foo()')); |
| 8 | +try { |
| 9 | + $f = $sandbox->loadString('foo'); |
| 10 | +} catch( Exception $e ) { |
| 11 | + print $e->getMessage(); |
| 12 | +} |
| 13 | +--EXPECTF-- |
| 14 | +object(LuaSandbox)#1 (0) { |
| 15 | +} |
| 16 | +object(LuaSandboxFunction)#2 (0) { |
| 17 | +} |
| 18 | +[string ""]:1: '=' expected near '<eof>' |
Index: trunk/php/luasandbox/tests/LuaSandboxFunction_construct.phpt |
— | — | @@ -0,0 +1,10 @@ |
| 2 | +--TEST-- |
| 3 | +LuaSandboxFunction::construct() is private |
| 4 | +--SKIPIF-- |
| 5 | +<?php if (!extension_loaded("luasandbox")) print "skip"; ?> |
| 6 | +--FILE-- |
| 7 | +<?php |
| 8 | +new LuaSandboxFunction; |
| 9 | +?> |
| 10 | +--EXPECTF-- |
| 11 | +Fatal error: Call to private LuaSandboxFunction::__construct() from invalid context in %s on line %d |
Index: trunk/php/luasandbox/tests/callback_exception.phpt |
— | — | @@ -0,0 +1,18 @@ |
| 2 | +--TEST-- |
| 3 | +Exception in a PHP function called from Lua |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | + |
| 7 | +function throw_exception() { |
| 8 | + throw new Exception('message'); |
| 9 | +} |
| 10 | +$sandbox = new LuaSandbox; |
| 11 | +$sandbox->registerLibrary( 'test', array( 'throw_exception' => 'throw_exception' ) ); |
| 12 | +$f = $sandbox->loadString('test.throw_exception()'); |
| 13 | +try { |
| 14 | + $f->call(); |
| 15 | +} catch ( Exception $e ) { |
| 16 | + print $e->getMessage(); |
| 17 | +} |
| 18 | +--EXPECT-- |
| 19 | +message |
Index: trunk/php/luasandbox/tests/call.phpt |
— | — | @@ -0,0 +1,11 @@ |
| 2 | +--TEST-- |
| 3 | +LuaSandboxFunction::call |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | +$sandbox = new LuaSandbox; |
| 7 | +var_dump( $sandbox->loadString( 'return 1' )->call() ); |
| 8 | +--EXPECT-- |
| 9 | +array(1) { |
| 10 | + [0]=> |
| 11 | + int(1) |
| 12 | +} |
Index: trunk/php/luasandbox/tests/reentrant.phpt |
— | — | @@ -0,0 +1,53 @@ |
| 2 | +--TEST-- |
| 3 | +Re-entering Lua during a callback to PHP |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | +$sandbox = new LuaSandbox; |
| 7 | +$chunk = $sandbox->loadString(' |
| 8 | + function factorial(n) |
| 9 | + if n <= 1 then |
| 10 | + return 1 |
| 11 | + else |
| 12 | + return n * test.factorial(n - 1) |
| 13 | + end |
| 14 | + end |
| 15 | + |
| 16 | + return factorial |
| 17 | +'); |
| 18 | + |
| 19 | +$ret = $chunk->call(); |
| 20 | +$luaFactorial = $ret[0]; |
| 21 | + |
| 22 | +$sandbox->registerLibrary( 'test', array( 'factorial' => 'factorial' ) ); |
| 23 | + |
| 24 | +function factorial($n) { |
| 25 | + global $luaFactorial; |
| 26 | + if ($n <= 1) { |
| 27 | + return 1; |
| 28 | + } else { |
| 29 | + $ret = $luaFactorial->call($n - 1); |
| 30 | + return $n * $ret[0]; |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +print factorial(10) . "\n"; |
| 35 | +var_dump( $luaFactorial->call(10) ); |
| 36 | + |
| 37 | +try { |
| 38 | + $luaFactorial->call(1000000000); |
| 39 | +} catch ( LuaSandboxError $e ) { |
| 40 | + print $e->getMessage() . "\n"; |
| 41 | +} |
| 42 | +try { |
| 43 | + factorial(1000000000); |
| 44 | +} catch ( LuaSandboxError $e ) { |
| 45 | + print $e->getMessage() . "\n"; |
| 46 | +} |
| 47 | +--EXPECT-- |
| 48 | +3628800 |
| 49 | +array(1) { |
| 50 | + [0]=> |
| 51 | + int(3628800) |
| 52 | +} |
| 53 | +C stack overflow |
| 54 | +C stack overflow |
Index: trunk/php/luasandbox/tests/dump_loadBinary_call.phpt |
— | — | @@ -0,0 +1,21 @@ |
| 2 | +--TEST-- |
| 3 | +dump -> loadBinary -> call |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | + |
| 7 | +var_dump( $sandbox = new LuaSandbox ); |
| 8 | +var_dump( $f = $sandbox->loadString( 'return 1' ) ); |
| 9 | +$dump = $f->dump(); |
| 10 | +var_dump( $restore = $sandbox->loadBinary( $dump ) ); |
| 11 | +var_dump( $restore->call() ); |
| 12 | +--EXPECT-- |
| 13 | +object(LuaSandbox)#1 (0) { |
| 14 | +} |
| 15 | +object(LuaSandboxFunction)#2 (0) { |
| 16 | +} |
| 17 | +object(LuaSandboxFunction)#3 (0) { |
| 18 | +} |
| 19 | +array(1) { |
| 20 | + [0]=> |
| 21 | + int(1) |
| 22 | +} |
Index: trunk/php/luasandbox/php_luasandbox.h |
— | — | @@ -34,6 +34,7 @@ |
35 | 35 | PHP_METHOD(LuaSandbox, callFunction); |
36 | 36 | PHP_METHOD(LuaSandbox, registerLibrary); |
37 | 37 | |
| 38 | +PHP_METHOD(LuaSandboxFunction, __construct); |
38 | 39 | PHP_METHOD(LuaSandboxFunction, call); |
39 | 40 | PHP_METHOD(LuaSandboxFunction, dump); |
40 | 41 | |
— | — | @@ -57,6 +58,8 @@ |
58 | 59 | lua_Alloc old_alloc; |
59 | 60 | void * old_alloc_ud; |
60 | 61 | int in_php; |
| 62 | + int in_lua; |
| 63 | + zval * current_zval; /* The zval for the LuaSandbox which is currently executing Lua code */ |
61 | 64 | volatile int timed_out; |
62 | 65 | volatile int emergency_timed_out; |
63 | 66 | int is_cpu_limited; |