Index: trunk/php/luasandbox/php_luasandbox.h |
— | — | @@ -22,7 +22,6 @@ |
23 | 23 | /* luasandbox.c */ |
24 | 24 | |
25 | 25 | extern zend_module_entry luasandbox_module_entry; |
26 | | -extern char luasandbox_timeout_message[]; |
27 | 26 | |
28 | 27 | #define phpext_luasandbox_ptr &luasandbox_module_entry |
29 | 28 | |
— | — | @@ -104,12 +103,12 @@ |
105 | 104 | * unsafe to call longjmp() to return control to PHP. If the flag is not |
106 | 105 | * correctly set, memory may be corrupted and security compromised. |
107 | 106 | */ |
108 | | -inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern) |
| 107 | +static inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern) |
109 | 108 | { |
110 | 109 | intern->in_php ++; |
111 | 110 | if (intern->timed_out) { |
112 | 111 | intern->in_php --; |
113 | | - luaL_error(L, luasandbox_timeout_message); |
| 112 | + luasandbox_timer_timeout_error(L); |
114 | 113 | } |
115 | 114 | } |
116 | 115 | /* }}} */ |
— | — | @@ -119,7 +118,7 @@ |
120 | 119 | * This function must be called after luasandbox_enter_php, before the callback |
121 | 120 | * from Lua returns. |
122 | 121 | */ |
123 | | -inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern) |
| 122 | +static inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern) |
124 | 123 | { |
125 | 124 | intern->in_php --; |
126 | 125 | } |
— | — | @@ -138,6 +137,10 @@ |
139 | 138 | void luasandbox_push_zval_userdata(lua_State * L, zval * z); |
140 | 139 | void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
141 | 140 | zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
| 141 | +void luasandbox_wrap_fatal(lua_State * L); |
| 142 | +int luasandbox_is_fatal(lua_State * L, int index); |
| 143 | +char * luasandbox_error_to_string(lua_State * L, int index); |
142 | 144 | |
| 145 | + |
143 | 146 | #endif /* PHP_LUASANDBOX_H */ |
144 | 147 | |
Index: trunk/php/luasandbox/data_conversion.c |
— | — | @@ -19,6 +19,13 @@ |
20 | 20 | extern zend_class_entry *luasandboxfunction_ce; |
21 | 21 | extern zend_class_entry *luasandboxplaceholder_ce; |
22 | 22 | |
| 23 | +/** |
| 24 | + * An int, the address of which is used as a fatal error marker. The value is |
| 25 | + * not used. |
| 26 | + */ |
| 27 | +int luasandbox_fatal_error_marker = 0; |
| 28 | + |
| 29 | + |
23 | 30 | /** {{{ luasandbox_data_conversion_init |
24 | 31 | * |
25 | 32 | * Set up a lua_State so that this module can work with it. |
— | — | @@ -339,3 +346,79 @@ |
340 | 347 | } |
341 | 348 | /* }}} */ |
342 | 349 | |
| 350 | +/** {{{ luasandbox_wrap_fatal |
| 351 | + * |
| 352 | + * Pop a value off the top of the stack, and push a fatal error wrapper |
| 353 | + * containing the value. |
| 354 | + */ |
| 355 | +void luasandbox_wrap_fatal(lua_State * L) |
| 356 | +{ |
| 357 | + // Create the table and put the marker in it as element 1 |
| 358 | + lua_createtable(L, 0, 2); |
| 359 | + lua_pushlightuserdata(L, &luasandbox_fatal_error_marker); |
| 360 | + lua_rawseti(L, -2, 1); |
| 361 | + |
| 362 | + // Swap the table with the input value, so that the value is on the top, |
| 363 | + // then put the value in the table as element 2 |
| 364 | + lua_insert(L, -2); |
| 365 | + lua_rawseti(L, -2, 2); |
| 366 | +} |
| 367 | +/* }}} */ |
| 368 | + |
| 369 | +/** {{{ luasandbox_is_fatal |
| 370 | + * |
| 371 | + * Check if the value at the given stack index is a fatal error wrapper |
| 372 | + * created by luasandbox_wrap_fatal(). Return 1 if it is, 0 otherwise. |
| 373 | + */ |
| 374 | +int luasandbox_is_fatal(lua_State * L, int index) |
| 375 | +{ |
| 376 | + void * ud; |
| 377 | + int haveIndex2 = 0; |
| 378 | + |
| 379 | + if (!lua_istable(L, index)) { |
| 380 | + return 0; |
| 381 | + } |
| 382 | + |
| 383 | + lua_rawgeti(L, index, 1); |
| 384 | + ud = lua_touserdata(L, -1); |
| 385 | + lua_pop(L, 1); |
| 386 | + if (ud != &luasandbox_fatal_error_marker) { |
| 387 | + return 0; |
| 388 | + } |
| 389 | + |
| 390 | + lua_rawgeti(L, index, 2); |
| 391 | + haveIndex2 = !lua_isnil(L, -1); |
| 392 | + lua_pop(L, 1); |
| 393 | + return haveIndex2; |
| 394 | +} |
| 395 | +/* }}} */ |
| 396 | + |
| 397 | +/** {{{ |
| 398 | + * |
| 399 | + * If the value at the given stack index is a fatal error wrapper, convert |
| 400 | + * the error object it wraps to a string. If the value is anything else, |
| 401 | + * convert it directly to a string. If the error object is not convertible |
| 402 | + * to a string, return "unknown error". |
| 403 | + * |
| 404 | + * This calls lua_tolstring() and will corrupt the value on the stack as |
| 405 | + * described in that function's documentation. The string is valid until the |
| 406 | + * Lua value is destroyed. |
| 407 | + */ |
| 408 | +char * luasandbox_error_to_string(lua_State * L, int index) |
| 409 | +{ |
| 410 | + char * s; |
| 411 | + if (luasandbox_is_fatal(L, index)) { |
| 412 | + lua_rawgeti(L, index, 2); |
| 413 | + s = lua_tostring(L, -1); |
| 414 | + lua_pop(L, 1); |
| 415 | + } else { |
| 416 | + s = lua_tostring(L, index); |
| 417 | + } |
| 418 | + if (!s) { |
| 419 | + return "unknown error"; |
| 420 | + } else { |
| 421 | + return s; |
| 422 | + } |
| 423 | +} |
| 424 | +/* }}} */ |
| 425 | + |
Index: trunk/php/luasandbox/luasandbox.c |
— | — | @@ -44,8 +44,6 @@ |
45 | 45 | static int luasandbox_call_php(lua_State * L); |
46 | 46 | static int luasandbox_dump_writer(lua_State * L, const void * p, size_t sz, void * ud); |
47 | 47 | |
48 | | -char luasandbox_timeout_message[] = "The maximum execution time for this script was exceeded"; |
49 | | - |
50 | 48 | zend_class_entry *luasandbox_ce; |
51 | 49 | zend_class_entry *luasandboxerror_ce; |
52 | 50 | zend_class_entry *luasandboxemergencytimeout_ce; |
— | — | @@ -393,7 +391,7 @@ |
394 | 392 | { |
395 | 393 | php_error_docref(NULL TSRMLS_CC, E_ERROR, |
396 | 394 | "PANIC: unprotected error in call to Lua API (%s)", |
397 | | - lua_tostring(L, -1)); |
| 395 | + luasandbox_error_to_string(L, -1)); |
398 | 396 | return 0; |
399 | 397 | } |
400 | 398 | /* }}} */ |
— | — | @@ -535,7 +533,7 @@ |
536 | 534 | */ |
537 | 535 | static void luasandbox_handle_error(lua_State * L, int status) |
538 | 536 | { |
539 | | - const char * errorMsg = lua_tostring(L, -1); |
| 537 | + const char * errorMsg = luasandbox_error_to_string(L, -1); |
540 | 538 | lua_pop(L, 1); |
541 | 539 | if (!EG(exception)) { |
542 | 540 | zend_throw_exception(luasandboxerror_ce, (char*)errorMsg, status); |
— | — | @@ -1117,7 +1115,9 @@ |
1118 | 1116 | |
1119 | 1117 | // If an exception occurred, convert it to a Lua error (just to unwind the stack) |
1120 | 1118 | if (EG(exception)) { |
1121 | | - luaL_error(L, "[exception]"); |
| 1119 | + lua_pushstring(L, "[exception]"); |
| 1120 | + luasandbox_wrap_fatal(L); |
| 1121 | + lua_error(L); |
1122 | 1122 | } |
1123 | 1123 | return num_results; |
1124 | 1124 | } |
Index: trunk/php/luasandbox/timer.c |
— | — | @@ -20,6 +20,7 @@ |
21 | 21 | struct timespec * normal_timeout, |
22 | 22 | struct timespec * emergency_timeout) {} |
23 | 23 | void luasandbox_timer_stop(luasandbox_timer_set * lts) {} |
| 24 | +void luasandbox_timer_timeout_error(lua_State *L) {} |
24 | 25 | |
25 | 26 | #else |
26 | 27 | |
— | — | @@ -29,6 +30,8 @@ |
30 | 31 | static void luasandbox_timer_timeout_hook(lua_State *L, lua_Debug *ar); |
31 | 32 | static void luasandbox_timer_settime(luasandbox_timer * lt, struct timespec * ts); |
32 | 33 | |
| 34 | +char luasandbox_timeout_message[] = "The maximum execution time for this script was exceeded"; |
| 35 | + |
33 | 36 | void luasandbox_timer_install_handler(struct sigaction * oldact) |
34 | 37 | { |
35 | 38 | struct sigaction newact; |
— | — | @@ -46,6 +49,7 @@ |
47 | 50 | static void luasandbox_timer_handle(int signo, siginfo_t * info, void * context) |
48 | 51 | { |
49 | 52 | luasandbox_timer_callback_data * data; |
| 53 | + lua_State * L; |
50 | 54 | |
51 | 55 | if (signo != LUASANDBOX_SIGNAL |
52 | 56 | || info->si_code != SI_TIMER |
— | — | @@ -56,6 +60,7 @@ |
57 | 61 | |
58 | 62 | data = (luasandbox_timer_callback_data*)info->si_value.sival_ptr; |
59 | 63 | data->sandbox->timed_out = 1; |
| 64 | + L = data->sandbox->state; |
60 | 65 | if (data->emergency) { |
61 | 66 | sigset_t set; |
62 | 67 | sigemptyset(&set); |
— | — | @@ -68,11 +73,13 @@ |
69 | 74 | "was exceeded and a PHP callback failed to return"); |
70 | 75 | } else { |
71 | 76 | // The Lua state is dirty now and can't be used again. |
72 | | - luaL_error(data->sandbox->state, "emergency timeout"); |
| 77 | + lua_pushstring(L, "emergency timeout"); |
| 78 | + luasandbox_wrap_fatal(L); |
| 79 | + lua_error(L); |
73 | 80 | } |
74 | 81 | } else { |
75 | 82 | // Set a hook which will terminate the script execution in a graceful way |
76 | | - lua_sethook(data->sandbox->state, luasandbox_timer_timeout_hook, |
| 83 | + lua_sethook(L, luasandbox_timer_timeout_hook, |
77 | 84 | LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 1); |
78 | 85 | } |
79 | 86 | } |
— | — | @@ -82,9 +89,16 @@ |
83 | 90 | // Avoid infinite recursion |
84 | 91 | lua_sethook(L, luasandbox_timer_timeout_hook, 0, 0); |
85 | 92 | // Do a longjmp to report the timeout error |
86 | | - luaL_error(L, luasandbox_timeout_message); |
| 93 | + luasandbox_timer_timeout_error(L); |
87 | 94 | } |
88 | 95 | |
| 96 | +void luasandbox_timer_timeout_error(lua_State *L) |
| 97 | +{ |
| 98 | + lua_pushstring(L, luasandbox_timeout_message); |
| 99 | + luasandbox_wrap_fatal(L); |
| 100 | + lua_error(L); |
| 101 | +} |
| 102 | + |
89 | 103 | void luasandbox_timer_start(luasandbox_timer_set * lts, |
90 | 104 | php_luasandbox_obj * sandbox, |
91 | 105 | struct timespec * normal_timeout, |
Index: trunk/php/luasandbox/tests/xpcall.phpt |
— | — | @@ -0,0 +1,102 @@ |
| 2 | +--TEST-- |
| 3 | +xpcall() basic behaviour |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | + |
| 7 | +$lua = <<<LUA |
| 8 | + function xpcall_test(f, err) |
| 9 | + local status, msg |
| 10 | + status, msg = xpcall(f, err) |
| 11 | + if not status then |
| 12 | + return msg |
| 13 | + else |
| 14 | + return "success" |
| 15 | + end |
| 16 | + end |
| 17 | +LUA; |
| 18 | + |
| 19 | +$xperr = 'return "xp: " .. msg'; |
| 20 | + |
| 21 | +$tests = array( |
| 22 | + 'Normal' => array( |
| 23 | + 'return 1', |
| 24 | + $xperr |
| 25 | + ), |
| 26 | + 'User error' => array( |
| 27 | + 'error("runtime error")', |
| 28 | + $xperr |
| 29 | + ), |
| 30 | + 'Error in error handler' => array( |
| 31 | + 'error("original error")', |
| 32 | + 'error("error in handler")' |
| 33 | + ), |
| 34 | + 'Unconvertible error in error handler' => array( |
| 35 | + 'error("original error")', |
| 36 | + 'error({})' |
| 37 | + ), |
| 38 | + 'Numeric error in error handler' => array( |
| 39 | + 'error("original error")', |
| 40 | + 'error(2)', |
| 41 | + ), |
| 42 | + 'Argument check error' => array( |
| 43 | + 'string.byte()', |
| 44 | + $xperr |
| 45 | + ), |
| 46 | + 'Protected infinite recursion' => array( |
| 47 | + 'function foo() foo() end foo()', |
| 48 | + $xperr |
| 49 | + ), |
| 50 | + 'Infinite recursion in handler' => array( |
| 51 | + 'error("x")', |
| 52 | + 'function foo() foo() end foo()' |
| 53 | + ), |
| 54 | + 'Protected infinite loop' => array( |
| 55 | + 'while true do end', |
| 56 | + $xperr, |
| 57 | + ), |
| 58 | + 'Infinite loop in handler' => array( |
| 59 | + 'error("x")', |
| 60 | + 'while true do end', |
| 61 | + ), |
| 62 | + 'Out of memory in handler' => array( |
| 63 | + 'error("x")', |
| 64 | + 'string.rep("x", 1000000)' |
| 65 | + ), |
| 66 | +); |
| 67 | + |
| 68 | +$sandbox = new LuaSandbox; |
| 69 | +$sandbox->loadString( $lua )->call(); |
| 70 | +$sandbox->setCPULimit( 0.25 ); |
| 71 | +$sandbox->setMemoryLimit( 100000 ); |
| 72 | + |
| 73 | +foreach ( $tests as $desc => $info ) { |
| 74 | + $sandbox = new LuaSandbox; |
| 75 | + $sandbox->loadString( $lua )->call(); |
| 76 | + $sandbox->setCPULimit( 0.25 ); |
| 77 | + $sandbox->setMemoryLimit( 100000 ); |
| 78 | + echo "$desc: "; |
| 79 | + list( $code, $errorCode ) = $info; |
| 80 | + $func = $sandbox->loadString( $code ); |
| 81 | + $errorCode = "return function(msg) $errorCode end"; |
| 82 | + $ret = $sandbox->loadString( $errorCode )->call(); |
| 83 | + $errorFunc = $ret[0]; |
| 84 | + |
| 85 | + try { |
| 86 | + print implode("\n", |
| 87 | + $sandbox->callFunction( 'xpcall_test', $func, $errorFunc ) ) . "\n"; |
| 88 | + } catch ( LuaSandboxError $e ) { |
| 89 | + echo "LuaSandboxError: " . $e->getMessage() . "\n"; |
| 90 | + } |
| 91 | +} |
| 92 | +--EXPECT-- |
| 93 | +Normal: success |
| 94 | +User error: xp: [string ""]:1: runtime error |
| 95 | +Error in error handler: LuaSandboxError: [string ""]:1: error in handler |
| 96 | +Unconvertible error in error handler: LuaSandboxError: unknown error |
| 97 | +Numeric error in error handler: LuaSandboxError: [string ""]:1: 2 |
| 98 | +Argument check error: xp: [string ""]:1: bad argument #1 to 'byte' (string expected, got no value) |
| 99 | +Protected infinite recursion: LuaSandboxError: not enough memory |
| 100 | +Infinite recursion in handler: LuaSandboxError: not enough memory |
| 101 | +Protected infinite loop: LuaSandboxError: The maximum execution time for this script was exceeded |
| 102 | +Infinite loop in handler: LuaSandboxError: The maximum execution time for this script was exceeded |
| 103 | +Out of memory in handler: LuaSandboxError: not enough memory |
Index: trunk/php/luasandbox/tests/pcall.phpt |
— | — | @@ -0,0 +1,46 @@ |
| 2 | +--TEST-- |
| 3 | +pcall() catching various errors |
| 4 | +--FILE-- |
| 5 | +<?php |
| 6 | +$sandbox = new LuaSandbox; |
| 7 | +$lua = <<<LUA |
| 8 | + function pcall_test(f) |
| 9 | + local status, msg |
| 10 | + status, msg = pcall(f) |
| 11 | + if not status then |
| 12 | + return "Caught: " .. msg |
| 13 | + else |
| 14 | + return "success" |
| 15 | + end |
| 16 | + end |
| 17 | +LUA; |
| 18 | + |
| 19 | +$tests = array( |
| 20 | + 'Normal' => 'return 1', |
| 21 | + 'User error' => 'error("runtime error")', |
| 22 | + 'Argument check error' => 'string.byte()', |
| 23 | + 'Infinite recursion' => 'function foo() foo() end foo()', |
| 24 | + 'Infinite loop (timeout)' => 'while true do end', |
| 25 | + 'Out of memory' => 'string.rep("x", 1000000)' |
| 26 | +); |
| 27 | + |
| 28 | +$sandbox->loadString( $lua )->call(); |
| 29 | +$sandbox->setCPULimit( 0.25 ); |
| 30 | +$sandbox->setMemoryLimit( 100000 ); |
| 31 | + |
| 32 | +foreach ( $tests as $desc => $code ) { |
| 33 | + echo "$desc: "; |
| 34 | + try { |
| 35 | + print implode("\n", |
| 36 | + $sandbox->callFunction( 'pcall_test', $sandbox->loadString( $code ) ) ) . "\n"; |
| 37 | + } catch ( LuaSandboxError $e ) { |
| 38 | + echo "LuaSandboxError: " . $e->getMessage() . "\n"; |
| 39 | + } |
| 40 | +} |
| 41 | +--EXPECT-- |
| 42 | +Normal: success |
| 43 | +User error: Caught: [string ""]:1: runtime error |
| 44 | +Argument check error: Caught: [string ""]:1: bad argument #1 to 'byte' (string expected, got no value) |
| 45 | +Infinite recursion: LuaSandboxError: not enough memory |
| 46 | +Infinite loop (timeout): LuaSandboxError: The maximum execution time for this script was exceeded |
| 47 | +Out of memory: LuaSandboxError: not enough memory |
Index: trunk/php/luasandbox/library.c |
— | — | @@ -20,11 +20,13 @@ |
21 | 21 | static int luasandbox_base_tostring(lua_State * L); |
22 | 22 | static int luasandbox_math_random(lua_State * L); |
23 | 23 | static int luasandbox_math_randomseed(lua_State * L); |
| 24 | +static int luasandbox_base_pcall(lua_State * L); |
| 25 | +static int luasandbox_base_xpcall (lua_State *L); |
24 | 26 | |
25 | 27 | /** |
26 | 28 | * Allowed global variables. Omissions are: |
27 | | - * * pcall, xpcall: Changing the protected environment won't work with our |
28 | | - * current timeout method. |
| 29 | + * * pcall, xpcall: We have our own versions which don't allow interception of |
| 30 | + * timeout etc. errors. |
29 | 31 | * * loadfile: insecure. |
30 | 32 | * * load, loadstring: Probably creates a protected environment so has |
31 | 33 | * the same problem as pcall. Also omitting these makes analysis of the |
— | — | @@ -32,7 +34,7 @@ |
33 | 35 | * * print: Not compatible with a sandbox environment |
34 | 36 | * * tostring: Provides addresses of tables and functions, which provides an |
35 | 37 | * easy ASLR workaround or heap address discovery mechanism for a memory |
36 | | - * corruption exploit. |
| 38 | + * corruption exploit. We have our own version. |
37 | 39 | * * Any new or undocumented functions like newproxy. |
38 | 40 | * * package: cpath, loadlib etc. are insecure. |
39 | 41 | * * coroutine: Not useful for our application so unreviewed at present. |
— | — | @@ -104,10 +106,15 @@ |
105 | 107 | } |
106 | 108 | } |
107 | 109 | |
108 | | - // Install our own version of tostring |
| 110 | + // Install our own version of tostring, pcall, xpcall |
109 | 111 | lua_pushcfunction(L, luasandbox_base_tostring); |
110 | 112 | lua_setglobal(L, "tostring"); |
| 113 | + lua_pushcfunction(L, luasandbox_base_pcall); |
| 114 | + lua_setglobal(L, "pcall"); |
| 115 | + lua_pushcfunction(L, luasandbox_base_xpcall); |
| 116 | + lua_setglobal(L, "xpcall"); |
111 | 117 | |
| 118 | + |
112 | 119 | // Remove string.dump: may expose private data |
113 | 120 | lua_getglobal(L, "string"); |
114 | 121 | lua_pushnil(L); |
— | — | @@ -235,3 +242,105 @@ |
236 | 243 | } |
237 | 244 | /* }}} */ |
238 | 245 | |
| 246 | +/** {{{ luasandbox_lib_rethrow_fatal |
| 247 | + * |
| 248 | + * If the error on the top of the stack with the error return code given as a |
| 249 | + * parameter is a fatal, rethrow the error. If the error is rethrown, the |
| 250 | + * function does not return. |
| 251 | + */ |
| 252 | +static void luasandbox_lib_rethrow_fatal(lua_State * L, int status) |
| 253 | +{ |
| 254 | + switch (status) { |
| 255 | + case 0: |
| 256 | + // No error |
| 257 | + return; |
| 258 | + case LUA_ERRRUN: |
| 259 | + // A runtime error which we can rethrow in a normal way |
| 260 | + if (luasandbox_is_fatal(L, -1)) { |
| 261 | + lua_error(L); |
| 262 | + } |
| 263 | + break; |
| 264 | + case LUA_ERRMEM: |
| 265 | + case LUA_ERRERR: |
| 266 | + // Lua doesn't provide a public API for rethrowing these, so we |
| 267 | + // have to convert them to our own fatal error type |
| 268 | + if (!luasandbox_is_fatal(L, -1)) { |
| 269 | + luasandbox_wrap_fatal(L); |
| 270 | + } |
| 271 | + lua_error(L); |
| 272 | + break; // not reached |
| 273 | + } |
| 274 | +} |
| 275 | +/* }}} */ |
| 276 | + |
| 277 | +/** {{{ luasandbox_lib_error_wrapper |
| 278 | + * |
| 279 | + * Wrapper for the xpcall error function |
| 280 | + */ |
| 281 | +static int luasandbox_lib_error_wrapper(lua_State * L) |
| 282 | +{ |
| 283 | + int status; |
| 284 | + luaL_checkany(L, 1); |
| 285 | + |
| 286 | + // This function is only called from luaG_errormsg(), which will later |
| 287 | + // unconditionally set the status code to LUA_ERRRUN, so we can assume |
| 288 | + // that the error type is equivalent to LUA_ERRRUN. |
| 289 | + if (luasandbox_is_fatal(L, 1)) { |
| 290 | + // Just return to whatever called lua_pcall(), don't call the user |
| 291 | + // function |
| 292 | + return lua_gettop(L); |
| 293 | + } |
| 294 | + |
| 295 | + // Put the user error function at the bottom of the stack |
| 296 | + lua_pushvalue(L, lua_upvalueindex(1)); |
| 297 | + lua_insert(L, 1); |
| 298 | + // Call it, passing through the arguments to this function |
| 299 | + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); |
| 300 | + if (status != 0) { |
| 301 | + luasandbox_lib_rethrow_fatal(L, LUA_ERRERR); |
| 302 | + } |
| 303 | + return lua_gettop(L); |
| 304 | +} |
| 305 | +/* }}} */ |
| 306 | + |
| 307 | +/** {{{ luasandbox_base_pcall |
| 308 | + * |
| 309 | + * This is our implementation of the Lua function pcall(). It allows Lua code |
| 310 | + * to handle its own errors, but forces internal errors to be rethrown. |
| 311 | + */ |
| 312 | +static int luasandbox_base_pcall(lua_State * L) |
| 313 | +{ |
| 314 | + int status; |
| 315 | + luaL_checkany(L, 1); |
| 316 | + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); |
| 317 | + luasandbox_lib_rethrow_fatal(L, status); |
| 318 | + lua_pushboolean(L, (status == 0)); |
| 319 | + lua_insert(L, 1); |
| 320 | + return lua_gettop(L); // return status + all results |
| 321 | +} |
| 322 | +/* }}} */ |
| 323 | + |
| 324 | +/** {{{ luasandbox_base_xpcall |
| 325 | + * |
| 326 | + * This is our implementation of the Lua function xpcall(). It allows Lua code |
| 327 | + * to handle its own errors, but forces internal errors to be rethrown. |
| 328 | + */ |
| 329 | +static int luasandbox_base_xpcall (lua_State *L) |
| 330 | +{ |
| 331 | + int status; |
| 332 | + luaL_checkany(L, 2); |
| 333 | + lua_settop(L, 2); |
| 334 | + |
| 335 | + // We wrap the error function in a C closure. The error function already |
| 336 | + // happens to be at the top of the stack, so we don't need to push it before |
| 337 | + // calling lua_pushcfunction to make it an upvalue |
| 338 | + lua_pushcclosure(L, luasandbox_lib_error_wrapper, 1); |
| 339 | + lua_insert(L, 1); // put error function under function to be called |
| 340 | + |
| 341 | + status = lua_pcall(L, 0, LUA_MULTRET, 1); |
| 342 | + luasandbox_lib_rethrow_fatal(L, status); |
| 343 | + lua_pushboolean(L, (status == 0)); |
| 344 | + lua_replace(L, 1); |
| 345 | + return lua_gettop(L); // return status + all results |
| 346 | +} |
| 347 | +/* }}} */ |