Line data Source code
1 :
2 : #ifdef HAVE_CONFIG_H
3 : #include "config.h"
4 : #endif
5 :
6 : #include <lua.h>
7 : #include <lauxlib.h>
8 : #include <limits.h>
9 : #include <float.h>
10 : #include <math.h>
11 : #include <inttypes.h>
12 :
13 : #include "php.h"
14 : #include "php_luasandbox.h"
15 : #include "zend_exceptions.h"
16 :
17 : #include "luasandbox_compat.h"
18 :
19 : static void luasandbox_throw_runtimeerror(lua_State * L, zval * sandbox_zval, const char *message);
20 :
21 : static inline int luasandbox_protect_recursion(zval * z, HashTable ** recursionGuard, int * allocated);
22 : static inline void luasandbox_unprotect_recursion(zval * z, HashTable * recursionGuard, int allocated);
23 :
24 : static int luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
25 : zval * sandbox_zval, HashTable * recursionGuard);
26 : static int luasandbox_lua_pair_to_array(HashTable *ht, lua_State *L,
27 : zval * sandbox_zval, HashTable * recursionGuard);
28 : static int luasandbox_free_zval_userdata(lua_State * L);
29 : static int luasandbox_push_hashtable(lua_State * L, HashTable * ht, HashTable * recursionGuard);
30 : static int luasandbox_has_error_marker(lua_State * L, int index, void * marker);
31 :
32 : extern zend_class_entry *luasandboxfunction_ce;
33 : extern zend_class_entry *luasandboxruntimeerror_ce;
34 :
35 : /**
36 : * An int, the address of which is used as a fatal error marker. The value is
37 : * not used.
38 : */
39 : int luasandbox_fatal_error_marker = 0;
40 :
41 : /**
42 : * Same as luasandbox_fatal_error_marker but for trace errors
43 : */
44 : int luasandbox_trace_error_marker = 0;
45 :
46 : /** {{{ luasandbox_data_conversion_init
47 : *
48 : * Set up a lua_State so that this module can work with it.
49 : */
50 125 : void luasandbox_data_conversion_init(lua_State * L)
51 : {
52 : // Create the metatable for zval destruction
53 125 : lua_createtable(L, 0, 1);
54 125 : lua_pushcfunction(L, luasandbox_free_zval_userdata);
55 125 : lua_setfield(L, -2, "__gc");
56 125 : lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
57 125 : }
58 : /* }}} */
59 :
60 : /** {{{ luasandbox_push_zval
61 : *
62 : * Convert a zval to an appropriate Lua type and push the resulting value on to
63 : * the stack.
64 : */
65 1826 : int luasandbox_push_zval(lua_State * L, zval * z, HashTable * recursionGuard)
66 : {
67 1826 : switch (Z_TYPE_P(z)) {
68 : #ifdef IS_UNDEF
69 1 : case IS_UNDEF: // Close enough to IS_NULL
70 : #endif
71 : case IS_NULL:
72 1 : lua_pushnil(L);
73 1 : break;
74 223 : case IS_LONG:
75 223 : lua_pushinteger(L, Z_LVAL_P(z));
76 223 : break;
77 3 : case IS_DOUBLE:
78 3 : lua_pushnumber(L, Z_DVAL_P(z));
79 3 : break;
80 : #ifdef IS_BOOL
81 : case IS_BOOL:
82 : lua_pushboolean(L, Z_BVAL_P(z));
83 : break;
84 : #endif
85 : #ifdef IS_TRUE
86 1 : case IS_TRUE:
87 1 : lua_pushboolean(L, 1);
88 1 : break;
89 1 : case IS_FALSE:
90 1 : lua_pushboolean(L, 0);
91 1 : break;
92 : #endif
93 509 : case IS_ARRAY: {
94 509 : int ret, allocated = 0;
95 509 : if (!luasandbox_protect_recursion(z, &recursionGuard, &allocated)) {
96 0 : return 0;
97 : }
98 509 : ret = luasandbox_push_hashtable(L, Z_ARRVAL_P(z), recursionGuard);
99 509 : luasandbox_unprotect_recursion(z, recursionGuard, allocated);
100 509 : return ret;
101 : }
102 45 : case IS_OBJECT: {
103 : zend_class_entry * objce;
104 :
105 45 : objce = Z_OBJCE_P(z);
106 45 : if (instanceof_function(objce, luasandboxfunction_ce)) {
107 : php_luasandboxfunction_obj * func_obj;
108 :
109 43 : func_obj = GET_LUASANDBOXFUNCTION_OBJ(z);
110 :
111 43 : lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
112 43 : lua_rawgeti(L, -1, func_obj->index);
113 43 : lua_remove(L, -2);
114 43 : break;
115 : }
116 :
117 2 : php_error_docref(NULL, E_WARNING, "Unable to convert object of type %s",
118 2 : ZSTR_VAL(objce->name)
119 : );
120 :
121 2 : return 0;
122 : }
123 1038 : case IS_STRING:
124 1038 : lua_pushlstring(L, Z_STRVAL_P(z), Z_STRLEN_P(z));
125 1036 : break;
126 : #ifdef IS_REFERENCE
127 5 : case IS_REFERENCE: {
128 5 : int ret, allocated = 0;
129 5 : if (!luasandbox_protect_recursion(z, &recursionGuard, &allocated)) {
130 2 : return 0;
131 : }
132 3 : ret = luasandbox_push_zval(L, Z_REFVAL_P(z), recursionGuard);
133 3 : luasandbox_unprotect_recursion(z, recursionGuard, allocated);
134 3 : return ret;
135 : }
136 : #endif
137 :
138 0 : case IS_RESOURCE:
139 : default:
140 0 : return 0;
141 : }
142 1308 : return 1;
143 : }
144 : /* }}} */
145 :
146 : /** {{{ luasandbox_free_zval_userdata
147 : *
148 : * Free a zval given to Lua by luasandbox_push_zval_userdata.
149 : */
150 125 : static int luasandbox_free_zval_userdata(lua_State * L)
151 : {
152 125 : zval * ud = (zval*)lua_touserdata(L, 1);
153 125 : php_luasandbox_obj * intern = luasandbox_get_php_obj(L);
154 :
155 : // Don't abort if the request has timed out, we need to be able to clean up
156 125 : luasandbox_enter_php_ignore_timeouts(L, intern);
157 :
158 250 : if (ud && !Z_ISUNDEF_P(ud)) {
159 125 : zval_ptr_dtor(ud);
160 125 : ZVAL_UNDEF(ud);
161 : }
162 :
163 125 : luasandbox_leave_php(L, intern);
164 125 : return 0;
165 : }
166 : /* }}} */
167 :
168 : /** {{{ luasandbox_push_zval_userdata
169 : *
170 : * Push a full userdata on to the stack, which stores a zval* in its block.
171 : * Increment its reference count and set its metatable so that it will be freed
172 : * at the appropriate time.
173 : */
174 125 : void luasandbox_push_zval_userdata(lua_State * L, zval * z)
175 : {
176 125 : zval * ud = (zval*)lua_newuserdata(L, sizeof(zval));
177 125 : ZVAL_COPY(ud, z);
178 :
179 125 : lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
180 125 : lua_setmetatable(L, -2);
181 125 : }
182 : /* }}} */
183 :
184 : /** {{{ luasandbox_push_hashtable
185 : *
186 : * Helper function for luasandbox_push_zval. Create a new table on the top of
187 : * the stack and add the zvals in the HashTable to it.
188 : */
189 509 : static int luasandbox_push_hashtable(lua_State * L, HashTable * ht, HashTable * recursionGuard)
190 : {
191 : #if SIZEOF_LONG > 4
192 : char buffer[MAX_LENGTH_OF_LONG + 1];
193 : #endif
194 :
195 : // Recursion requires an arbitrary amount of stack space so we have to
196 : // check the stack.
197 509 : luaL_checkstack(L, 10, "converting PHP array to Lua");
198 :
199 509 : lua_newtable(L);
200 509 : if (!ht || !zend_hash_num_elements(ht)) {
201 0 : return 1;
202 : }
203 :
204 : zend_ulong lkey;
205 : zend_string *key;
206 : zval *value;
207 1537 : ZEND_HASH_FOREACH_KEY_VAL(ht, lkey, key, value)
208 : {
209 : // Lua doesn't represent most integers with absolute value over 2**53,
210 : // so stringify them.
211 : #if SIZEOF_LONG > 4
212 516 : if (!key &&
213 511 : ((int64_t)lkey > INT64_C(9007199254740992) || (int64_t)lkey < INT64_C(-9007199254740992))
214 2 : ) {
215 2 : size_t len = snprintf(buffer, sizeof(buffer), "%" PRId64, (int64_t)lkey);
216 2 : lua_pushlstring(L, buffer, len);
217 : } else
218 : #endif
219 514 : if (key) {
220 5 : lua_pushlstring(L, ZSTR_VAL(key), ZSTR_LEN(key));
221 : } else {
222 509 : lua_pushinteger(L, lkey);
223 : }
224 :
225 516 : if (!luasandbox_push_zval(L, value, recursionGuard)) {
226 : // Failed to process that data value
227 : // Pop the key and the half-constructed table
228 4 : lua_pop(L, 2);
229 4 : return 0;
230 : }
231 :
232 512 : lua_settable(L, -3);
233 : } ZEND_HASH_FOREACH_END();
234 :
235 505 : return 1;
236 : }
237 : /* }}} */
238 :
239 : /** {{{ luasandbox_lua_to_zval
240 : *
241 : * Convert a lua value to a zval.
242 : *
243 : * If a value is encountered that can't be converted to a zval, an exception is
244 : * thrown.
245 : *
246 : * @param z A pointer to the destination zval
247 : * @param L The lua state
248 : * @param index The stack index to the input value
249 : * @param sandbox_zval A zval poiting to a valid LuaSandbox object which will be
250 : * used for the parent object of any LuaSandboxFunction objects created.
251 : * @param recursionGuard A hashtable for keeping track of tables that have been
252 : * processed, to allow infinite recursion to be avoided. External callers
253 : * should set this to NULL.
254 : * @return int 0 (and a PHP exception) on failure
255 : */
256 190502 : int luasandbox_lua_to_zval(zval * z, lua_State * L, int index,
257 : zval * sandbox_zval, HashTable * recursionGuard)
258 : {
259 190502 : switch (lua_type(L, index)) {
260 1 : case LUA_TNIL:
261 1 : ZVAL_NULL(z);
262 1 : break;
263 60698 : case LUA_TNUMBER: {
264 : long i;
265 : double d, integerPart, fractionalPart;
266 : // Lua only provides a single number type
267 : // Convert it to a PHP integer if that can be done without loss
268 : // of precision
269 60698 : d = lua_tonumber(L, index);
270 60698 : fractionalPart = modf(d, &integerPart);
271 60698 : if (fractionalPart == 0.0 && integerPart >= LONG_MIN && integerPart <= LONG_MAX) {
272 : // The number is small enough to fit inside an int. But has it already
273 : // been truncated by squeezing it into a double? This is only relevant
274 : // where the integer size is greater than the mantissa size.
275 60695 : i = (long)integerPart;
276 121390 : if (LONG_MAX < (1LL << DBL_MANT_DIG)
277 60695 : || labs(i) < (1LL << DBL_MANT_DIG))
278 : {
279 60695 : ZVAL_LONG(z, i);
280 : } else {
281 0 : ZVAL_DOUBLE(z, d);
282 : }
283 : } else {
284 3 : ZVAL_DOUBLE(z, d);
285 : }
286 60698 : break;
287 : }
288 2 : case LUA_TBOOLEAN:
289 2 : ZVAL_BOOL(z, lua_toboolean(L, index));
290 2 : break;
291 99163 : case LUA_TSTRING: {
292 : const char * str;
293 : size_t length;
294 99163 : str = lua_tolstring(L, index, &length);
295 99163 : ZVAL_STRINGL(z, str, length);
296 99163 : break;
297 : }
298 30474 : case LUA_TTABLE: {
299 30474 : const void * ptr = lua_topointer(L, index);
300 30474 : int allocated = 0;
301 30474 : int success = 1;
302 30474 : if (recursionGuard) {
303 : // Check for circular reference (infinite recursion)
304 30234 : if (zend_hash_str_exists(recursionGuard, (char*)&ptr, sizeof(void*))) {
305 : // Found circular reference!
306 4 : luasandbox_throw_runtimeerror(L, sandbox_zval, "Cannot pass circular reference to PHP");
307 :
308 4 : ZVAL_NULL(z); // Need to set something to prevent a segfault
309 16 : return 0;
310 : }
311 : } else {
312 240 : ALLOC_HASHTABLE(recursionGuard);
313 240 : zend_hash_init(recursionGuard, 1, NULL, NULL, 0);
314 240 : allocated = 1;
315 : }
316 :
317 : // Add the current table to the recursion guard hashtable
318 : // Use the pointer as the key, zero-length data
319 : zval zv;
320 30470 : ZVAL_TRUE(&zv);
321 30470 : zend_hash_str_update(recursionGuard, (char*)&ptr, sizeof(void*), &zv);
322 :
323 : // Process the array
324 30470 : array_init(z);
325 30470 : success = luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, sandbox_zval, recursionGuard);
326 :
327 30470 : if (allocated) {
328 240 : zend_hash_destroy(recursionGuard);
329 240 : FREE_HASHTABLE(recursionGuard);
330 : }
331 :
332 30470 : if (!success) {
333 : // free the array created by array_init() above.
334 : zval_dtor(z);
335 12 : ZVAL_NULL(z);
336 12 : return 0;
337 : }
338 30458 : break;
339 : }
340 164 : case LUA_TFUNCTION: {
341 : int func_index;
342 : php_luasandboxfunction_obj * func_obj;
343 164 : php_luasandbox_obj * sandbox = GET_LUASANDBOX_OBJ(sandbox_zval);
344 :
345 : // Normalise the input index so that we can push without invalidating it.
346 164 : if (index < 0) {
347 0 : index += lua_gettop(L) + 1;
348 : }
349 :
350 : // Get the chunks table
351 164 : lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
352 :
353 : // Get the next free index
354 164 : if (sandbox->function_index >= INT_MAX) {
355 0 : ZVAL_NULL(z);
356 0 : lua_pop(L, 1);
357 0 : break;
358 : }
359 164 : func_index = ++(sandbox->function_index);
360 :
361 : // Store it in the chunks table
362 164 : lua_pushvalue(L, index);
363 164 : lua_rawseti(L, -2, func_index);
364 :
365 : // Create a LuaSandboxFunction object to hold a reference to the function
366 164 : object_init_ex(z, luasandboxfunction_ce);
367 164 : func_obj = GET_LUASANDBOXFUNCTION_OBJ(z);
368 164 : func_obj->index = func_index;
369 164 : ZVAL_COPY(&func_obj->sandbox, sandbox_zval);
370 :
371 : // Balance the stack
372 164 : lua_pop(L, 1);
373 164 : break;
374 : }
375 0 : case LUA_TUSERDATA:
376 : case LUA_TTHREAD:
377 : case LUA_TLIGHTUSERDATA:
378 : // TODO: provide derived classes for each type
379 : default: {
380 : char *message;
381 0 : spprintf(&message, 0, "Cannot pass %s to PHP", lua_typename(L, lua_type(L, index)));
382 0 : luasandbox_throw_runtimeerror(L, sandbox_zval, message);
383 0 : efree(message);
384 :
385 0 : ZVAL_NULL(z); // Need to set something to prevent a segfault
386 0 : return 0;
387 : }
388 : }
389 190486 : return 1;
390 : }
391 : /* }}} */
392 :
393 : /** {{{ luasandbox_lua_to_array
394 : *
395 : * Append the elements of the table in the specified index to the given HashTable.
396 : */
397 30470 : static int luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
398 : zval * sandbox_zval, HashTable * recursionGuard)
399 : {
400 : php_luasandbox_obj * sandbox;
401 30470 : int top = lua_gettop(L);
402 :
403 : // Recursion requires an arbitrary amount of stack space so we have to
404 : // check the stack.
405 30470 : luaL_checkstack(L, 15, "converting Lua table to PHP");
406 :
407 : // Normalise the input index so that we can push without invalidating it.
408 30470 : if (index < 0) {
409 30240 : index += top + 1;
410 : }
411 :
412 : // If the input table has a __pairs function, we need to use that instead
413 : // of lua_next.
414 30470 : if (luaL_getmetafield(L, index, "__pairs")) {
415 3 : sandbox = luasandbox_get_php_obj(L);
416 :
417 : // Put the error handler function onto the stack
418 3 : lua_pushcfunction(L, luasandbox_attach_trace);
419 3 : lua_insert(L, top + 1);
420 :
421 : // Call __pairs
422 3 : lua_pushvalue(L, index);
423 3 : if (!luasandbox_call_lua(sandbox, sandbox_zval, 1, 3, top + 1)) {
424 : // Failed to call __pairs. Cleanup stack and return failure.
425 1 : lua_settop(L, top);
426 1 : return 0;
427 : }
428 : while (1) {
429 : // We need to copy static-data and func so we can reuse them for
430 : // each iteration.
431 4 : lua_pushvalue(L, -3);
432 4 : lua_insert(L, -2);
433 4 : lua_pushvalue(L, -3);
434 4 : lua_insert(L, -2);
435 :
436 : // Call the custom 'next' function from __pairs
437 4 : if (!luasandbox_call_lua(sandbox, sandbox_zval, 2, 2, top + 1)) {
438 : // Failed. Cleanup stack and return failure.
439 1 : lua_settop(L, top);
440 1 : return 0;
441 : }
442 :
443 3 : if (lua_isnil(L, -2)) {
444 : // Nil key == end. Cleanup stack and exit loop.
445 1 : lua_settop(L, top);
446 1 : break;
447 : }
448 2 : if (!luasandbox_lua_pair_to_array(ht, L, sandbox_zval, recursionGuard)) {
449 : // Failed to convert value. Cleanup stack and return failure.
450 0 : lua_settop(L, top);
451 0 : return 0;
452 : }
453 : }
454 : } else {
455 : // No __pairs, we can use lua_next
456 30467 : lua_pushnil(L);
457 219290 : while (lua_next(L, index) != 0) {
458 188833 : if (!luasandbox_lua_pair_to_array(ht, L, sandbox_zval, recursionGuard)) {
459 : // Failed to convert value. Cleanup stack and return failure.
460 10 : lua_settop(L, top);
461 10 : return 0;
462 : }
463 : }
464 : }
465 30458 : return 1;
466 : }
467 : /* }}} */
468 :
469 : /** {{{ luasandbox_lua_pair_to_array
470 : *
471 : * Take the lua key-value pair at the top of the Lua stack and add it to the given HashTable.
472 : * On success the value is popped, but the key remains on the stack.
473 : */
474 188835 : static int luasandbox_lua_pair_to_array(HashTable *ht, lua_State *L,
475 : zval * sandbox_zval, HashTable * recursionGuard)
476 : {
477 : const char * str;
478 : size_t length;
479 : lua_Number n;
480 : zend_ulong zn;
481 :
482 188835 : zval value, *valp = &value;
483 188835 : ZVAL_NULL(&value);
484 :
485 : // Convert value, then remove it
486 188835 : if (!luasandbox_lua_to_zval(valp, L, -1, sandbox_zval, recursionGuard)) {
487 4 : zval_ptr_dtor(&value);
488 4 : return 0;
489 : }
490 188831 : lua_pop(L, 1);
491 :
492 : // Convert key, but leave it there
493 188831 : if (lua_type(L, -1) == LUA_TNUMBER) {
494 30243 : n = lua_tonumber(L, -1);
495 30243 : if (isfinite(n) && n == floor(n) && n >= LONG_MIN && n <= LONG_MAX) {
496 30239 : zn = (long)n;
497 30239 : goto add_int_key;
498 : }
499 : }
500 :
501 : // Make a copy of the key so that we can call lua_tolstring() which is destructive
502 158592 : lua_pushvalue(L, -1);
503 158592 : str = lua_tolstring(L, -1, &length);
504 158592 : if ( str == NULL ) {
505 : // Only strings and integers may be used as keys
506 : char *message;
507 3 : spprintf(&message, 0, "Cannot use %s as an array key when passing data from Lua to PHP",
508 : lua_typename(L, lua_type(L, -2))
509 : );
510 3 : zval_ptr_dtor(&value);
511 3 : luasandbox_throw_runtimeerror(L, sandbox_zval, message);
512 3 : efree(message);
513 3 : return 0;
514 : }
515 158589 : lua_pop(L, 1);
516 :
517 : // See if the string is convertable to a number
518 317178 : if (ZEND_HANDLE_NUMERIC_STR(str, length, zn)) {
519 6 : goto add_int_key;
520 : }
521 :
522 : // Nope, use it as a string
523 317166 : if (zend_hash_str_exists(ht, str, length)) {
524 : // Collision, probably the key is an integer-like string
525 : char *message;
526 2 : spprintf(&message, 0, "Collision for array key %s when passing data from Lua to PHP", str );
527 2 : zval_ptr_dtor(&value);
528 2 : luasandbox_throw_runtimeerror(L, sandbox_zval, message);
529 2 : efree(message);
530 2 : return 0;
531 : }
532 158581 : zend_hash_str_update(ht, str, length, valp);
533 :
534 158581 : return 1;
535 :
536 30245 : add_int_key:
537 60490 : if (zend_hash_index_exists(ht, zn)) {
538 : // Collision, probably with a integer-like string
539 : char *message;
540 1 : spprintf(&message, 0, "Collision for array key %" PRId64 " when passing data from Lua to PHP",
541 : (int64_t)zn
542 : );
543 1 : zval_ptr_dtor(&value);
544 1 : luasandbox_throw_runtimeerror(L, sandbox_zval, message);
545 1 : efree(message);
546 1 : return 0;
547 : }
548 30244 : zend_hash_index_update(ht, zn, valp);
549 :
550 30244 : return 1;
551 : }
552 : /* }}} */
553 :
554 : /** {{{ luasandbox_wrap_fatal
555 : *
556 : * Pop a value off the top of the stack, and push a fatal error wrapper
557 : * containing the value.
558 : */
559 37 : void luasandbox_wrap_fatal(lua_State * L)
560 : {
561 : // Create the table and put the marker in it as element 1
562 37 : lua_createtable(L, 0, 2);
563 37 : lua_pushlightuserdata(L, &luasandbox_fatal_error_marker);
564 37 : lua_rawseti(L, -2, 1);
565 :
566 : // Swap the table with the input value, so that the value is on the top,
567 : // then put the value in the table as element 2
568 37 : lua_insert(L, -2);
569 37 : lua_rawseti(L, -2, 2);
570 37 : }
571 : /* }}} */
572 :
573 : /** {{{ luasandbox_is_fatal
574 : *
575 : * Check if the value at the given stack index is a fatal error wrapper
576 : * created by luasandbox_wrap_fatal(). Return 1 if it is, 0 otherwise.
577 : *
578 : * This function cannot raise Lua errors.
579 : */
580 751 : int luasandbox_is_fatal(lua_State * L, int index)
581 : {
582 751 : return luasandbox_has_error_marker(L, index, &luasandbox_fatal_error_marker);
583 : }
584 : /* }}} */
585 :
586 : /** {{{ luasandbox_is_trace_error
587 : *
588 : * Check if the value at the given stack index is an error wrapper created by
589 : * luasandbox_attach_trace(). Return 1 if it is, 0 otherwise.
590 : *
591 : * This function cannot raise Lua errors.
592 : */
593 450 : int luasandbox_is_trace_error(lua_State * L, int index)
594 : {
595 450 : return luasandbox_has_error_marker(L, index, &luasandbox_trace_error_marker);
596 : }
597 : /* }}} */
598 :
599 : /** {{{
600 : *
601 : * Check if the error at the given stack index has a given marker userdata
602 : */
603 1201 : static int luasandbox_has_error_marker(lua_State * L, int index, void * marker)
604 : {
605 : void * ud;
606 1201 : if (!lua_istable(L, index)) {
607 247 : return 0;
608 : }
609 954 : lua_rawgeti(L, index, 1);
610 954 : ud = lua_touserdata(L, -1);
611 954 : lua_pop(L, 1);
612 954 : return ud == marker;
613 : }
614 : /* }}} */
615 :
616 : /** {{{
617 : *
618 : * If the value at the given stack index is a fatal error wrapper, convert
619 : * the error object it wraps to a string. If the value is anything else,
620 : * convert it directly to a string. If the error object is not convertible
621 : * to a string, return "unknown error".
622 : *
623 : * This calls lua_tolstring() and will corrupt the value on the stack as
624 : * described in that function's documentation. The string is valid until the
625 : * Lua value is destroyed.
626 : *
627 : * This function can raise Lua memory errors, but no other Lua errors.
628 : */
629 237 : const char * luasandbox_error_to_string(lua_State * L, int index)
630 : {
631 : const char * s;
632 237 : if (index < 0) {
633 237 : index += lua_gettop(L) + 1;
634 : }
635 237 : if (luasandbox_is_fatal(L, index) || luasandbox_is_trace_error(L, index)) {
636 230 : lua_rawgeti(L, index, 2);
637 230 : s = lua_tostring(L, -1);
638 230 : lua_pop(L, 1);
639 : } else {
640 7 : s = lua_tostring(L, index);
641 : }
642 237 : if (!s) {
643 1 : return "unknown error";
644 : } else {
645 236 : return s;
646 : }
647 : }
648 : /* }}} */
649 :
650 : /** {{{ luasandbox_attach_trace
651 : *
652 : * Error callback function for lua_pcall(): wrap the error value in a table that
653 : * includes backtrace information.
654 : */
655 245 : int luasandbox_attach_trace(lua_State * L)
656 : {
657 245 : if (luasandbox_is_fatal(L, 1)) {
658 : // Pass fatals through unaltered
659 37 : return 1;
660 : }
661 :
662 : // Create the table and put the marker in it as element 1
663 208 : lua_createtable(L, 0, 3);
664 208 : lua_pushlightuserdata(L, &luasandbox_trace_error_marker);
665 208 : lua_rawseti(L, -2, 1);
666 :
667 : // Swap the table with the input value, so that the value is on the top,
668 : // then put the value in the table as element 2
669 208 : lua_insert(L, -2);
670 208 : lua_rawseti(L, -2, 2);
671 :
672 : // Put the backtrace in element 3
673 208 : luasandbox_push_structured_trace(L, 1);
674 208 : lua_rawseti(L, -2, 3);
675 :
676 208 : return 1;
677 : }
678 : /* }}} */
679 :
680 : /** {{{ luasandbox_push_structured_trace
681 : *
682 : * Make a table representing the current backtrace and push it to the stack.
683 : * "level" is the call stack level to start at, 1 for the current function.
684 : */
685 218 : void luasandbox_push_structured_trace(lua_State * L, int level)
686 : {
687 : lua_Debug ar;
688 218 : lua_newtable(L);
689 : int i;
690 :
691 30545 : for (i = level; lua_getstack(L, i, &ar); i++) {
692 30327 : lua_getinfo(L, "nSl", &ar);
693 30327 : lua_createtable(L, 0, 8);
694 30327 : lua_pushstring(L, ar.short_src);
695 30327 : lua_setfield(L, -2, "short_src");
696 30327 : lua_pushstring(L, ar.what);
697 30327 : lua_setfield(L, -2, "what");
698 30327 : lua_pushnumber(L, ar.currentline);
699 30327 : lua_setfield(L, -2, "currentline");
700 30327 : lua_pushstring(L, ar.name);
701 30327 : lua_setfield(L, -2, "name");
702 30327 : lua_pushstring(L, ar.namewhat);
703 30327 : lua_setfield(L, -2, "namewhat");
704 30327 : lua_pushnumber(L, ar.linedefined);
705 30327 : lua_setfield(L, -2, "linedefined");
706 30327 : lua_rawseti(L, -2, i - level + 1);
707 : }
708 218 : }
709 : /* }}} */
710 :
711 : /** {{{ luasandbox_throw_runtimeerror
712 : *
713 : * Create and throw a luasandboxruntimeerror
714 : */
715 10 : void luasandbox_throw_runtimeerror(lua_State * L, zval * sandbox_zval, const char *message)
716 : {
717 : zval *zex, *ztrace;
718 :
719 : zval zvex, zvtrace;
720 10 : zex = &zvex;
721 10 : ztrace = &zvtrace;
722 10 : ZVAL_NULL(ztrace); // IS_NULL if lua_to_zval fails.
723 :
724 10 : object_init_ex(zex, luasandboxruntimeerror_ce);
725 :
726 10 : luasandbox_push_structured_trace(L, 1);
727 10 : luasandbox_lua_to_zval(ztrace, L, -1, sandbox_zval, NULL);
728 10 : luasandbox_update_property(luasandboxruntimeerror_ce, zex, "luaTrace", sizeof("luaTrace")-1, ztrace);
729 10 : zval_ptr_dtor(&zvtrace);
730 :
731 10 : lua_pop(L, 1);
732 :
733 10 : luasandbox_update_property_string(luasandboxruntimeerror_ce, zex,
734 : "message", sizeof("message")-1, message);
735 10 : luasandbox_update_property_long(luasandboxruntimeerror_ce, zex, "code", sizeof("code")-1, -1);
736 10 : zend_throw_exception_object(zex);
737 10 : }
738 : /* }}} */
739 :
740 : /** {{{ luasandbox_protect_recursion
741 : *
742 : * Check that the zval isn't already in recursionGuard, and if so add it. May
743 : * allocate recursionGuard if necessary.
744 : *
745 : * Returns 1 if recursion is not detected, 0 if it was.
746 : */
747 514 : static inline int luasandbox_protect_recursion(zval * z, HashTable ** recursionGuard, int * allocated) {
748 514 : if (!*recursionGuard) {
749 8 : *allocated = 1;
750 8 : ALLOC_HASHTABLE(*recursionGuard);
751 8 : zend_hash_init(*recursionGuard, 1, NULL, NULL, 0);
752 1012 : } else if (zend_hash_str_exists(*recursionGuard, (char*)&z, sizeof(void*))) {
753 2 : php_error_docref(NULL, E_WARNING, "Cannot pass circular reference to Lua");
754 2 : return 0;
755 : }
756 :
757 : zval zv;
758 512 : ZVAL_TRUE(&zv);
759 512 : zend_hash_str_update(*recursionGuard, (char*)&z, sizeof(void*), &zv);
760 :
761 512 : return 1;
762 : }
763 : /* }}} */
764 :
765 : /** {{{ luasandbox_unprotect_recursion
766 : *
767 : * Undoes what luasandbox_protect_recursion did.
768 : */
769 512 : static inline void luasandbox_unprotect_recursion(zval * z, HashTable * recursionGuard, int allocated) {
770 512 : if (allocated) {
771 8 : zend_hash_destroy(recursionGuard);
772 8 : FREE_HASHTABLE(recursionGuard);
773 : } else {
774 504 : zend_hash_str_del(recursionGuard, (char*)&z, sizeof(void*));
775 : }
776 512 : }
777 : /* }}} */
|