Index: trunk/php/luasandbox/data_conversion.c |
— | — | @@ -0,0 +1,341 @@ |
| 2 | + |
| 3 | +#ifdef HAVE_CONFIG_H |
| 4 | +#include "config.h" |
| 5 | +#endif |
| 6 | + |
| 7 | +#include <lua.h> |
| 8 | +#include <lauxlib.h> |
| 9 | +#include <limits.h> |
| 10 | +#include <float.h> |
| 11 | + |
| 12 | +#include "php.h" |
| 13 | +#include "php_luasandbox.h" |
| 14 | + |
| 15 | +static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index, |
| 16 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
| 17 | +static int luasandbox_free_zval_userdata(lua_State * L); |
| 18 | +static int luasandbox_push_hashtable(lua_State * L, HashTable * ht); |
| 19 | + |
| 20 | +extern zend_class_entry *luasandboxfunction_ce; |
| 21 | +extern zend_class_entry *luasandboxplaceholder_ce; |
| 22 | + |
| 23 | +/** {{{ luasandbox_data_conversion_init |
| 24 | + * |
| 25 | + * Set up a lua_State so that this module can work with it. |
| 26 | + */ |
| 27 | +void luasandbox_data_conversion_init(lua_State * L) |
| 28 | +{ |
| 29 | + // Create the metatable for zval destruction |
| 30 | + lua_createtable(L, 0, 1); |
| 31 | + lua_pushcfunction(L, luasandbox_free_zval_userdata); |
| 32 | + lua_setfield(L, -2, "__gc"); |
| 33 | + lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable"); |
| 34 | +} |
| 35 | +/* }}} */ |
| 36 | + |
| 37 | +/** {{{ luasandbox_push_zval |
| 38 | + * |
| 39 | + * Convert a zval to an appropriate Lua type and push the resulting value on to |
| 40 | + * the stack. |
| 41 | + */ |
| 42 | +int luasandbox_push_zval(lua_State * L, zval * z) |
| 43 | +{ |
| 44 | + switch (Z_TYPE_P(z)) { |
| 45 | + case IS_NULL: |
| 46 | + lua_pushnil(L); |
| 47 | + break; |
| 48 | + case IS_LONG: |
| 49 | + lua_pushinteger(L, Z_LVAL_P(z)); |
| 50 | + break; |
| 51 | + case IS_DOUBLE: |
| 52 | + lua_pushnumber(L, Z_DVAL_P(z)); |
| 53 | + break; |
| 54 | + case IS_BOOL: |
| 55 | + lua_pushboolean(L, Z_BVAL_P(z)); |
| 56 | + break; |
| 57 | + case IS_ARRAY: |
| 58 | + if (!luasandbox_push_hashtable(L, Z_ARRVAL_P(z))) { |
| 59 | + return 0; |
| 60 | + } |
| 61 | + break; |
| 62 | + case IS_OBJECT: { |
| 63 | + zend_class_entry * objce; |
| 64 | + |
| 65 | + objce = Z_OBJCE_P(z); |
| 66 | + if (instanceof_function(objce, luasandboxfunction_ce TSRMLS_CC)) { |
| 67 | + php_luasandboxfunction_obj * func_obj; |
| 68 | + |
| 69 | + func_obj = (php_luasandboxfunction_obj *)zend_object_store_get_object(z TSRMLS_CC); |
| 70 | + |
| 71 | + lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
| 72 | + lua_rawgeti(L, -1, func_obj->index); |
| 73 | + lua_remove(L, -2); |
| 74 | + break; |
| 75 | + } |
| 76 | + |
| 77 | + if (!luasandbox_push_hashtable(L, Z_OBJPROP_P(z))) { |
| 78 | + return 0; |
| 79 | + } |
| 80 | + break; |
| 81 | + } |
| 82 | + case IS_STRING: |
| 83 | + lua_pushlstring(L, Z_STRVAL_P(z), Z_STRLEN_P(z)); |
| 84 | + break; |
| 85 | + case IS_RESOURCE: |
| 86 | + case IS_CONSTANT: |
| 87 | + case IS_CONSTANT_ARRAY: |
| 88 | + default: |
| 89 | + return 0; |
| 90 | + } |
| 91 | + return 1; |
| 92 | +} |
| 93 | +/* }}} */ |
| 94 | + |
| 95 | +/** {{{ luasandbox_free_zval_userdata |
| 96 | + * |
| 97 | + * Free a zval given to Lua by luasandbox_push_zval_userdata. |
| 98 | + */ |
| 99 | +static int luasandbox_free_zval_userdata(lua_State * L) |
| 100 | +{ |
| 101 | + zval ** zpp = (zval**)lua_touserdata(L, 1); |
| 102 | + php_luasandbox_obj * intern = luasandbox_get_php_obj(L); |
| 103 | + luasandbox_enter_php(L, intern); |
| 104 | + if (zpp && *zpp) { |
| 105 | + zval_ptr_dtor(zpp); |
| 106 | + } |
| 107 | + *zpp = NULL; |
| 108 | + luasandbox_leave_php(L, intern); |
| 109 | + return 0; |
| 110 | +} |
| 111 | +/* }}} */ |
| 112 | + |
| 113 | +/** {{{ luasandbox_push_zval_userdata |
| 114 | + * |
| 115 | + * Push a full userdata on to the stack, which stores a zval* in its block. |
| 116 | + * Increment its reference count and set its metatable so that it will be freed |
| 117 | + * at the appropriate time. |
| 118 | + */ |
| 119 | +void luasandbox_push_zval_userdata(lua_State * L, zval * z) |
| 120 | +{ |
| 121 | + zval ** ud; |
| 122 | + ud = (zval**)lua_newuserdata(L, sizeof(zval*)); |
| 123 | + *ud = z; |
| 124 | + Z_ADDREF_P(z); |
| 125 | + |
| 126 | + lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable"); |
| 127 | + lua_setmetatable(L, -2); |
| 128 | +} |
| 129 | +/* }}} */ |
| 130 | + |
| 131 | +/** {{{ luasandbox_push_hashtable |
| 132 | + * |
| 133 | + * Helper function for luasandbox_push_zval. Create a new table on the top of |
| 134 | + * the stack and add the zvals in the HashTable to it. |
| 135 | + */ |
| 136 | +static int luasandbox_push_hashtable(lua_State * L, HashTable * ht) |
| 137 | +{ |
| 138 | + Bucket * p; |
| 139 | + |
| 140 | + // Recursion requires an arbitrary amount of stack space so we have to |
| 141 | + // check the stack. |
| 142 | + luaL_checkstack(L, 10, "converting PHP array to Lua"); |
| 143 | + |
| 144 | + lua_newtable(L); |
| 145 | + if (!ht || !ht->nNumOfElements) { |
| 146 | + return 1; |
| 147 | + } |
| 148 | + if (ht->nApplyCount) { |
| 149 | + php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); |
| 150 | + return 0; |
| 151 | + } |
| 152 | + ht->nApplyCount++; |
| 153 | + for (p = ht->pListHead; p; p = p->pListNext) { |
| 154 | + if (p->nKeyLength) { |
| 155 | + lua_pushlstring(L, p->arKey, p->nKeyLength - 1); |
| 156 | + } else { |
| 157 | + lua_pushinteger(L, p->h); |
| 158 | + } |
| 159 | + |
| 160 | + if (!luasandbox_push_zval(L, *(zval**)p->pData)) { |
| 161 | + // Failed to process that data value |
| 162 | + // Pop the key and the half-constructed table |
| 163 | + lua_pop(L, 2); |
| 164 | + ht->nApplyCount--; |
| 165 | + return 0; |
| 166 | + } |
| 167 | + |
| 168 | + lua_settable(L, -3); |
| 169 | + } |
| 170 | + ht->nApplyCount--; |
| 171 | + return 1; |
| 172 | +} |
| 173 | +/* }}} */ |
| 174 | + |
| 175 | +/** {{{ luasandbox_lua_to_zval |
| 176 | + * |
| 177 | + * Convert a lua value to a zval. |
| 178 | + * |
| 179 | + * If a value is encountered that can't be converted to a zval, a LuaPlaceholder |
| 180 | + * object is returned instead. |
| 181 | + * |
| 182 | + * @param z A pointer to the destination zval |
| 183 | + * @param L The lua state |
| 184 | + * @param index The stack index to the input value |
| 185 | + * @param sandbox_zval A zval poiting to a valid LuaSandbox object which will be |
| 186 | + * used for the parent object of any LuaSandboxFunction objects created. |
| 187 | + * @param recursionGuard A hashtable for keeping track of tables that have been |
| 188 | + * processed, to allow infinite recursion to be avoided. External callers |
| 189 | + * should set this to NULL. |
| 190 | + */ |
| 191 | +void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
| 192 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC) |
| 193 | +{ |
| 194 | + switch (lua_type(L, index)) { |
| 195 | + case LUA_TNIL: |
| 196 | + ZVAL_NULL(z); |
| 197 | + break; |
| 198 | + case LUA_TNUMBER: { |
| 199 | + long i; |
| 200 | + double d, integerPart, fractionalPart; |
| 201 | + // Lua only provides a single number type |
| 202 | + // Convert it to a PHP integer if that can be done without loss |
| 203 | + // of precision |
| 204 | + d = lua_tonumber(L, index); |
| 205 | + fractionalPart = modf(d, &integerPart); |
| 206 | + if (fractionalPart == 0.0 && integerPart >= LONG_MIN && integerPart <= LONG_MAX) { |
| 207 | + // The number is small enough to fit inside an int. But has it already |
| 208 | + // been truncated by squeezing it into a double? This is only relevant |
| 209 | + // where the integer size is greater than the mantissa size. |
| 210 | + i = (long)integerPart; |
| 211 | + if (LONG_MAX < (1LL << DBL_MANT_DIG) |
| 212 | + || labs(i) < (1L << DBL_MANT_DIG)) |
| 213 | + { |
| 214 | + ZVAL_LONG(z, i); |
| 215 | + } else { |
| 216 | + ZVAL_DOUBLE(z, d); |
| 217 | + } |
| 218 | + } else { |
| 219 | + ZVAL_DOUBLE(z, d); |
| 220 | + } |
| 221 | + break; |
| 222 | + } |
| 223 | + case LUA_TBOOLEAN: |
| 224 | + ZVAL_BOOL(z, lua_toboolean(L, index)); |
| 225 | + break; |
| 226 | + case LUA_TSTRING: { |
| 227 | + const char * str; |
| 228 | + size_t length; |
| 229 | + str = lua_tolstring(L, index, &length); |
| 230 | + ZVAL_STRINGL(z, str, length, 1); |
| 231 | + break; |
| 232 | + } |
| 233 | + case LUA_TTABLE: { |
| 234 | + const void * ptr = lua_topointer(L, index); |
| 235 | + void * data = NULL; |
| 236 | + int allocated = 0; |
| 237 | + if (recursionGuard) { |
| 238 | + // Check for circular reference (infinite recursion) |
| 239 | + if (zend_hash_find(recursionGuard, (char*)&ptr, sizeof(void*), &data) == SUCCESS) { |
| 240 | + // Found circular reference! |
| 241 | + object_init_ex(z, luasandboxplaceholder_ce); |
| 242 | + break; |
| 243 | + } |
| 244 | + } else { |
| 245 | + ALLOC_HASHTABLE(recursionGuard); |
| 246 | + zend_hash_init(recursionGuard, 1, NULL, NULL, 0); |
| 247 | + allocated = 1; |
| 248 | + } |
| 249 | + |
| 250 | + // Add the current table to the recursion guard hashtable |
| 251 | + // Use the pointer as the key, zero-length data |
| 252 | + zend_hash_update(recursionGuard, (char*)&ptr, sizeof(void*), "", 1, NULL); |
| 253 | + |
| 254 | + // Process the array |
| 255 | + array_init(z); |
| 256 | + luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, sandbox_zval, recursionGuard TSRMLS_CC); |
| 257 | + |
| 258 | + if (allocated) { |
| 259 | + zend_hash_destroy(recursionGuard); |
| 260 | + FREE_HASHTABLE(recursionGuard); |
| 261 | + } |
| 262 | + break; |
| 263 | + } |
| 264 | + case LUA_TFUNCTION: { |
| 265 | + int func_index; |
| 266 | + php_luasandboxfunction_obj * func_obj; |
| 267 | + php_luasandbox_obj * sandbox = (php_luasandbox_obj*) |
| 268 | + zend_object_store_get_object(sandbox_zval); |
| 269 | + |
| 270 | + // Normalise the input index so that we can push without invalidating it. |
| 271 | + if (index < 0) { |
| 272 | + index += lua_gettop(L) + 1; |
| 273 | + } |
| 274 | + |
| 275 | + // Get the chunks table |
| 276 | + lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
| 277 | + |
| 278 | + // Get the next free index |
| 279 | + if (sandbox->function_index >= INT_MAX) { |
| 280 | + ZVAL_NULL(z); |
| 281 | + lua_pop(L, 1); |
| 282 | + break; |
| 283 | + } |
| 284 | + func_index = ++(sandbox->function_index); |
| 285 | + |
| 286 | + // Store it in the chunks table |
| 287 | + lua_pushvalue(L, index); |
| 288 | + lua_rawseti(L, -2, func_index); |
| 289 | + |
| 290 | + // Create a LuaSandboxFunction object to hold a reference to the function |
| 291 | + object_init_ex(z, luasandboxfunction_ce); |
| 292 | + func_obj = (php_luasandboxfunction_obj*)zend_object_store_get_object(z); |
| 293 | + func_obj->index = func_index; |
| 294 | + func_obj->sandbox = sandbox_zval; |
| 295 | + Z_ADDREF_P(sandbox_zval); |
| 296 | + |
| 297 | + // Balance the stack |
| 298 | + lua_pop(L, 1); |
| 299 | + break; |
| 300 | + } |
| 301 | + case LUA_TUSERDATA: |
| 302 | + case LUA_TTHREAD: |
| 303 | + case LUA_TLIGHTUSERDATA: |
| 304 | + default: |
| 305 | + // TODO: provide derived classes for each type |
| 306 | + object_init_ex(z, luasandboxplaceholder_ce); |
| 307 | + } |
| 308 | +} |
| 309 | +/* }}} */ |
| 310 | + |
| 311 | +/** {{{ luasandbox_lua_to_array |
| 312 | + * |
| 313 | + * Append the elements of the table in the specified index to the given HashTable. |
| 314 | + */ |
| 315 | +static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index, |
| 316 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC) |
| 317 | +{ |
| 318 | + const char * str; |
| 319 | + size_t length; |
| 320 | + zval *value; |
| 321 | + |
| 322 | + // Normalise the input index so that we can push without invalidating it. |
| 323 | + if (index < 0) { |
| 324 | + index += lua_gettop(L) + 1; |
| 325 | + } |
| 326 | + |
| 327 | + lua_pushnil(L); |
| 328 | + while (lua_next(L, index) != 0) { |
| 329 | + MAKE_STD_ZVAL(value); |
| 330 | + luasandbox_lua_to_zval(value, L, -1, sandbox_zval, recursionGuard TSRMLS_CC); |
| 331 | + |
| 332 | + // Make a copy of the key so that we can call lua_tolstring() which is destructive |
| 333 | + lua_pushvalue(L, -2); |
| 334 | + str = lua_tolstring(L, -1, &length); |
| 335 | + zend_hash_update(ht, str, length + 1, (void*)&value, sizeof(zval*), NULL); |
| 336 | + |
| 337 | + // Delete the copy and the value |
| 338 | + lua_pop(L, 2); |
| 339 | + } |
| 340 | +} |
| 341 | +/* }}} */ |
| 342 | + |
Property changes on: trunk/php/luasandbox/data_conversion.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 343 | + native |
Index: trunk/php/luasandbox/luasandbox.c |
— | — | @@ -4,11 +4,7 @@ |
5 | 5 | |
6 | 6 | #include <lua.h> |
7 | 7 | #include <lauxlib.h> |
8 | | -#include <lualib.h> |
9 | 8 | #include <math.h> |
10 | | -#include <limits.h> |
11 | | -#include <float.h> |
12 | | -#include <signal.h> |
13 | 9 | #include <time.h> |
14 | 10 | #include <stdlib.h> |
15 | 11 | |
— | — | @@ -20,10 +16,6 @@ |
21 | 17 | #include "luasandbox_timer.h" |
22 | 18 | #include "ext/standard/php_smart_str.h" |
23 | 19 | |
24 | | -#if defined(LUA_JITLIBNAME) && (SSIZE_MAX >> 32) > 0 |
25 | | -#define LUASANDBOX_LJ_64 |
26 | | -#endif |
27 | | - |
28 | 20 | #define CHECK_VALID_STATE(state) \ |
29 | 21 | if (!state) { \ |
30 | 22 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid LuaSandbox state"); \ |
— | — | @@ -36,11 +28,6 @@ |
37 | 29 | static zend_object_value luasandboxfunction_new(zend_class_entry *ce TSRMLS_CC); |
38 | 30 | static void luasandboxfunction_free_storage(void *object TSRMLS_DC); |
39 | 31 | static void luasandboxfunction_destroy(void *object, zend_object_handle handle TSRMLS_DC); |
40 | | -static int luasandbox_free_zval_userdata(lua_State * L); |
41 | | -static inline int luasandbox_update_memory_accounting(php_luasandbox_obj* obj, |
42 | | - size_t osize, size_t nsize); |
43 | | -static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
44 | | -static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
45 | 32 | static int luasandbox_panic(lua_State * L); |
46 | 33 | static lua_State * luasandbox_state_from_zval(zval * this_ptr TSRMLS_DC); |
47 | 34 | static void luasandbox_load_helper(int binary, INTERNAL_FUNCTION_PARAMETERS); |
— | — | @@ -52,70 +39,13 @@ |
53 | 40 | static void luasandbox_call_helper(lua_State * L, zval * sandbox_zval, |
54 | 41 | php_luasandbox_obj * sandbox, |
55 | 42 | zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC); |
56 | | -static int luasandbox_push_zval(lua_State * L, zval * z); |
57 | | -static void luasandbox_push_zval_userdata(lua_State * L, zval * z); |
58 | | -static void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
59 | | - zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
60 | | -static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index, |
61 | | - zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
62 | | -static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L); |
63 | 43 | static void luasandbox_handle_error(lua_State * L, int status); |
64 | 44 | static void luasandbox_handle_emergency_timeout(php_luasandbox_obj * sandbox); |
65 | | -static int luasandbox_push_hashtable(lua_State * L, HashTable * ht); |
66 | 45 | static int luasandbox_call_php(lua_State * L); |
67 | 46 | static int luasandbox_dump_writer(lua_State * L, const void * p, size_t sz, void * ud); |
68 | | -static int luasandbox_base_tostring(lua_State * L); |
69 | | -static int luasandbox_math_random(lua_State * L); |
70 | | -static int luasandbox_math_randomseed(lua_State * L); |
71 | 47 | |
72 | 48 | char luasandbox_timeout_message[] = "The maximum execution time for this script was exceeded"; |
73 | 49 | |
74 | | -/** |
75 | | - * Allowed global variables. Omissions are: |
76 | | - * * pcall, xpcall: Changing the protected environment won't work with our |
77 | | - * current timeout method. |
78 | | - * * loadfile: insecure. |
79 | | - * * load, loadstring: Probably creates a protected environment so has |
80 | | - * the same problem as pcall. Also omitting these makes analysis of the |
81 | | - * code for runtime etc. feasible. |
82 | | - * * print: Not compatible with a sandbox environment |
83 | | - * * tostring: Provides addresses of tables and functions, which provides an |
84 | | - * easy ASLR workaround or heap address discovery mechanism for a memory |
85 | | - * corruption exploit. |
86 | | - * * Any new or undocumented functions like newproxy. |
87 | | - * * package: cpath, loadlib etc. are insecure. |
88 | | - * * coroutine: Not useful for our application so unreviewed at present. |
89 | | - * * io, file, os: insecure |
90 | | - * * debug: Provides various ways to break the sandbox, such as setupvalue() |
91 | | - * and getregistry(). |
92 | | - */ |
93 | | -char * luasandbox_allowed_globals[] = { |
94 | | - // base |
95 | | - "assert", |
96 | | - "error", |
97 | | - "getmetatable", |
98 | | - "getfenv", |
99 | | - "getmetatable", |
100 | | - "ipairs", |
101 | | - "next", |
102 | | - "pairs", |
103 | | - "rawequal", |
104 | | - "rawget", |
105 | | - "rawset", |
106 | | - "select", |
107 | | - "setmetatable", |
108 | | - "tonumber", |
109 | | - "type", |
110 | | - "unpack", |
111 | | - "_G", |
112 | | - "_VERSION", |
113 | | - // libs |
114 | | - "string", |
115 | | - "table", |
116 | | - "math" |
117 | | -}; |
118 | | -#define LUASANDBOX_NUM_ALLOWED_GLOBALS (sizeof(luasandbox_allowed_globals) / sizeof(char*)) |
119 | | - |
120 | 50 | zend_class_entry *luasandbox_ce; |
121 | 51 | zend_class_entry *luasandboxerror_ce; |
122 | 52 | zend_class_entry *luasandboxemergencytimeout_ce; |
— | — | @@ -227,7 +157,6 @@ |
228 | 158 | */ |
229 | 159 | PHP_MINIT_FUNCTION(luasandbox) |
230 | 160 | { |
231 | | - int i; |
232 | 161 | /* If you have INI entries, uncomment these lines |
233 | 162 | REGISTER_INI_ENTRIES(); |
234 | 163 | */ |
— | — | @@ -261,14 +190,7 @@ |
262 | 191 | luasandboxfunction_ce = zend_register_internal_class(&ce TSRMLS_CC); |
263 | 192 | luasandboxfunction_ce->create_object = luasandboxfunction_new; |
264 | 193 | |
265 | | - // Initialise LUASANDBOX_G(allowed_globals) |
266 | | - LUASANDBOX_G(allowed_globals) = pemalloc(sizeof(HashTable), 1); |
267 | | - zend_hash_init(LUASANDBOX_G(allowed_globals), LUASANDBOX_NUM_ALLOWED_GLOBALS, NULL, NULL, 1); |
268 | | - for (i = 0; i < LUASANDBOX_NUM_ALLOWED_GLOBALS; i++) { |
269 | | - zend_hash_update(LUASANDBOX_G(allowed_globals), |
270 | | - luasandbox_allowed_globals[i], strlen(luasandbox_allowed_globals[i]) + 1, |
271 | | - "", 1, NULL); |
272 | | - } |
| 194 | + LUASANDBOX_G(allowed_globals) = NULL; |
273 | 195 | return SUCCESS; |
274 | 196 | } |
275 | 197 | /* }}} */ |
— | — | @@ -277,8 +199,7 @@ |
278 | 200 | */ |
279 | 201 | PHP_MSHUTDOWN_FUNCTION(luasandbox) |
280 | 202 | { |
281 | | - zend_hash_destroy(LUASANDBOX_G(allowed_globals)); |
282 | | - pefree(LUASANDBOX_G(allowed_globals), 1); |
| 203 | + luasandbox_lib_shutdown(TSRMLS_C); |
283 | 204 | return SUCCESS; |
284 | 205 | } |
285 | 206 | /* }}} */ |
— | — | @@ -303,37 +224,6 @@ |
304 | 225 | } |
305 | 226 | /* }}} */ |
306 | 227 | |
307 | | -/** {{{ luasandbox_enter_php |
308 | | - * |
309 | | - * This function must be called each time a C function is entered from Lua |
310 | | - * and the PHP state needs to be accessed in any way. Before exiting the |
311 | | - * function, luasandbox_leave_php() must be called. |
312 | | - * |
313 | | - * This sets a flag which indicates to the timeout signal handler that it is |
314 | | - * unsafe to call longjmp() to return control to PHP. If the flag is not |
315 | | - * correctly set, memory may be corrupted and security compromised. |
316 | | - */ |
317 | | -static inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern) |
318 | | -{ |
319 | | - intern->in_php ++; |
320 | | - if (intern->timed_out) { |
321 | | - intern->in_php --; |
322 | | - luaL_error(L, luasandbox_timeout_message); |
323 | | - } |
324 | | -} |
325 | | -/* }}} */ |
326 | | - |
327 | | -/** {{{ luasandbox_leave_php |
328 | | - * |
329 | | - * This function must be called after luasandbox_enter_php, before the callback |
330 | | - * from Lua returns. |
331 | | - */ |
332 | | -static inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern) |
333 | | -{ |
334 | | - intern->in_php --; |
335 | | -} |
336 | | -/* }}} */ |
337 | | - |
338 | 228 | /** {{{ luasandbox_new |
339 | 229 | * |
340 | 230 | * "new" handler for the LuaSandbox class |
— | — | @@ -347,7 +237,7 @@ |
348 | 238 | intern = emalloc(sizeof(php_luasandbox_obj)); |
349 | 239 | memset(intern, 0, sizeof(php_luasandbox_obj)); |
350 | 240 | zend_object_std_init(&intern->std, ce TSRMLS_CC); |
351 | | - intern->memory_limit = (size_t)-1; |
| 241 | + intern->alloc.memory_limit = (size_t)-1; |
352 | 242 | |
353 | 243 | // Initialise the Lua state |
354 | 244 | intern->state = luasandbox_newstate(intern); |
— | — | @@ -370,73 +260,21 @@ |
371 | 261 | */ |
372 | 262 | static lua_State * luasandbox_newstate(php_luasandbox_obj * intern) |
373 | 263 | { |
374 | | - lua_State * L; |
| 264 | + lua_State * L = luasandbox_alloc_new_state(&intern->alloc, intern); |
375 | 265 | |
376 | | -#ifdef LUASANDBOX_LJ_64 |
377 | | - // The 64-bit version of LuaJIT needs to use its own allocator |
378 | | - L = luaL_newstate(); |
379 | 266 | if (L == NULL) { |
380 | 267 | php_error_docref(NULL TSRMLS_CC, E_ERROR, |
381 | 268 | "Attempt to allocate a new Lua state failed"); |
382 | 269 | } |
383 | | - intern->old_alloc = lua_getallocf(L, &intern->old_alloc_ud); |
384 | | - lua_setallocf(L, luasandbox_passthru_alloc, intern); |
385 | | -#else |
386 | | - L = lua_newstate(luasandbox_php_alloc, intern); |
387 | | -#endif |
388 | 270 | |
389 | 271 | lua_atpanic(L, luasandbox_panic); |
390 | | - |
391 | | - // Load some relatively safe standard libraries |
392 | | - lua_pushcfunction(L, luaopen_base); |
393 | | - lua_call(L, 0, 0); |
394 | | - lua_pushcfunction(L, luaopen_string); |
395 | | - lua_call(L, 0, 0); |
396 | | - lua_pushcfunction(L, luaopen_table); |
397 | | - lua_call(L, 0, 0); |
398 | | - lua_pushcfunction(L, luaopen_math); |
399 | | - lua_call(L, 0, 0); |
400 | | - |
401 | | - // Remove any globals that aren't in a whitelist. This is mostly to remove |
402 | | - // unsafe functions from the base library. |
403 | | - lua_pushnil(L); |
404 | | - while (lua_next(L, LUA_GLOBALSINDEX) != 0) { |
405 | | - const char * key; |
406 | | - size_t key_len; |
407 | | - void * data; |
408 | | - lua_pop(L, 1); |
409 | | - if (lua_type(L, -1) != LUA_TSTRING) { |
410 | | - continue; |
411 | | - } |
412 | | - key = lua_tolstring(L, -1, &key_len); |
413 | | - if (zend_hash_find(LUASANDBOX_G(allowed_globals), |
414 | | - (char*)key, key_len + 1, &data) == FAILURE) |
415 | | - { |
416 | | - // Not allowed, delete it |
417 | | - lua_pushnil(L); |
418 | | - lua_setglobal(L, key); |
419 | | - } |
420 | | - } |
421 | | - |
422 | | - // Install our own version of tostring |
423 | | - lua_pushcfunction(L, luasandbox_base_tostring); |
424 | | - lua_setglobal(L, "tostring"); |
425 | | - |
426 | | - // Remove string.dump: may expose private data |
427 | | - lua_getglobal(L, "string"); |
428 | | - lua_pushnil(L); |
429 | | - lua_setfield(L, -2, "dump"); |
430 | | - lua_pop(L, 1); |
431 | | - |
432 | | - // Install our own versions of math.random and math.randomseed |
433 | | - lua_getglobal(L, "math"); |
434 | | - lua_pushcfunction(L, luasandbox_math_random); |
435 | | - lua_setfield(L, -2, "random"); |
436 | | - lua_pushcfunction(L, luasandbox_math_randomseed); |
437 | | - lua_setfield(L, -2, "randomseed"); |
438 | | - lua_pop(L, 1); |
439 | 272 | |
| 273 | + // Register the standard library |
| 274 | + luasandbox_lib_register(L TSRMLS_CC); |
440 | 275 | |
| 276 | + // Set up the data conversion module |
| 277 | + luasandbox_data_conversion_init(L); |
| 278 | + |
441 | 279 | // Create a table for storing chunks |
442 | 280 | lua_newtable(L); |
443 | 281 | lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
— | — | @@ -445,12 +283,6 @@ |
446 | 284 | lua_pushlightuserdata(L, (void*)intern); |
447 | 285 | lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj"); |
448 | 286 | |
449 | | - // Create the metatable for zval destruction |
450 | | - lua_createtable(L, 0, 1); |
451 | | - lua_pushcfunction(L, luasandbox_free_zval_userdata); |
452 | | - lua_setfield(L, -2, "__gc"); |
453 | | - lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable"); |
454 | | - |
455 | 287 | return L; |
456 | 288 | } |
457 | 289 | /* }}} */ |
— | — | @@ -464,16 +296,10 @@ |
465 | 297 | php_luasandbox_obj * intern = (php_luasandbox_obj*)object; |
466 | 298 | |
467 | 299 | if (intern->state) { |
468 | | - // In 64-bit LuaJIT mode, restore the old allocator before calling |
469 | | - // lua_close() because lua_close() actually checks that the value of the |
470 | | - // function pointer is unchanged before destroying the underlying |
471 | | - // allocator. If the allocator has been changed, the mmap is not freed. |
472 | | -#ifdef LUASANDBOX_LJ_64 |
473 | | - lua_setallocf(intern->state, intern->old_alloc, intern->old_alloc_ud); |
474 | | -#endif |
475 | | - |
476 | | - lua_close(intern->state); |
| 300 | + luasandbox_alloc_delete_state(&intern->alloc, intern->state); |
477 | 301 | intern->state = NULL; |
| 302 | + |
| 303 | + intern->state = NULL; |
478 | 304 | } |
479 | 305 | zend_object_std_dtor(&intern->std); |
480 | 306 | efree(object); |
— | — | @@ -547,97 +373,6 @@ |
548 | 374 | } |
549 | 375 | /* }}} */ |
550 | 376 | |
551 | | -/** {{{ luasandbox_free_zval_userdata |
552 | | - * |
553 | | - * Free a zval given to Lua by luasandbox_push_zval_userdata. |
554 | | - */ |
555 | | -static int luasandbox_free_zval_userdata(lua_State * L) |
556 | | -{ |
557 | | - zval ** zpp = (zval**)lua_touserdata(L, 1); |
558 | | - php_luasandbox_obj * intern = luasandbox_get_php_obj(L); |
559 | | - luasandbox_enter_php(L, intern); |
560 | | - if (zpp && *zpp) { |
561 | | - zval_ptr_dtor(zpp); |
562 | | - } |
563 | | - *zpp = NULL; |
564 | | - luasandbox_leave_php(L, intern); |
565 | | - return 0; |
566 | | -} |
567 | | -/* }}} */ |
568 | | - |
569 | | -/** {{{ luasandbox_php_alloc |
570 | | - * |
571 | | - * The Lua allocator function. Use PHP's request-local allocator as a backend. |
572 | | - * Account for memory usage and deny the allocation request if the amount |
573 | | - * allocated is above the user-specified limit. |
574 | | - */ |
575 | | -static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
576 | | -{ |
577 | | - php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
578 | | - void * nptr; |
579 | | - obj->in_php ++; |
580 | | - if (!luasandbox_update_memory_accounting(obj, osize, nsize)) { |
581 | | - obj->in_php --; |
582 | | - return NULL; |
583 | | - } |
584 | | - |
585 | | - if (nsize == 0) { |
586 | | - if (ptr) { |
587 | | - efree(ptr); |
588 | | - } |
589 | | - nptr = NULL; |
590 | | - } else if (osize == 0) { |
591 | | - nptr = emalloc(nsize); |
592 | | - } else { |
593 | | - nptr = erealloc(ptr, nsize); |
594 | | - } |
595 | | - obj->in_php --; |
596 | | - return nptr; |
597 | | -} |
598 | | -/* }}} */ |
599 | | - |
600 | | -/** {{{ luasandbox_passthru_alloc |
601 | | - * |
602 | | - * A Lua allocator function for use with LuaJIT on a 64-bit platform. Pass |
603 | | - * allocation requests through to the standard allocator, which is customised |
604 | | - * on this platform to always return memory from the lower 2GB of address |
605 | | - * space. |
606 | | - */ |
607 | | -static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
608 | | -{ |
609 | | - php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
610 | | - if (!luasandbox_update_memory_accounting(obj, osize, nsize)) { |
611 | | - return NULL; |
612 | | - } |
613 | | - return obj->old_alloc(obj->old_alloc_ud, ptr, osize, nsize); |
614 | | -} |
615 | | -/* }}} */ |
616 | | - |
617 | | -/** {{{ luasandbox_update_memory_accounting |
618 | | - * |
619 | | - * Update memory usage statistics for the given memory allocation request. |
620 | | - * Returns 1 if the allocation should be allowed, 0 if it should fail. |
621 | | - */ |
622 | | -static inline int luasandbox_update_memory_accounting(php_luasandbox_obj* obj, |
623 | | - size_t osize, size_t nsize) |
624 | | -{ |
625 | | - if (nsize > obj->memory_limit |
626 | | - || obj->memory_usage > obj->memory_limit - nsize) |
627 | | - { |
628 | | - // Memory limit exceeded |
629 | | - return 0; |
630 | | - } |
631 | | - |
632 | | - if (osize > nsize && obj->memory_usage + nsize < osize) { |
633 | | - // Negative memory usage -- do not update |
634 | | - return 1; |
635 | | - } |
636 | | - |
637 | | - obj->memory_usage += nsize - osize; |
638 | | - return 1; |
639 | | -} |
640 | | -/* }}} */ |
641 | | - |
642 | 377 | /** {{{ luasandbox_panic |
643 | 378 | * |
644 | 379 | * The Lua panic function. It is necessary to raise an E_ERROR, and thus do a |
— | — | @@ -828,7 +563,7 @@ |
829 | 564 | RETURN_FALSE; |
830 | 565 | } |
831 | 566 | |
832 | | - intern->memory_limit = limit; |
| 567 | + intern->alloc.memory_limit = limit; |
833 | 568 | } |
834 | 569 | /* }}} */ |
835 | 570 | |
— | — | @@ -935,7 +670,7 @@ |
936 | 671 | RETURN_FALSE; |
937 | 672 | } |
938 | 673 | |
939 | | - RETURN_LONG(sandbox->memory_usage); |
| 674 | + RETURN_LONG(sandbox->alloc.memory_usage); |
940 | 675 | } |
941 | 676 | /* }}} */ |
942 | 677 | |
— | — | @@ -1034,6 +769,7 @@ |
1035 | 770 | { |
1036 | 771 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "LuaSandboxFunction cannot be constructed directly"); |
1037 | 772 | } |
| 773 | +/* }}} */ |
1038 | 774 | |
1039 | 775 | /** {{{ proto array LuaSandboxFunction::call(...) |
1040 | 776 | * |
— | — | @@ -1216,299 +952,11 @@ |
1217 | 953 | } |
1218 | 954 | /* }}} */ |
1219 | 955 | |
1220 | | -/** {{{ luasandbox_push_zval |
1221 | | - * |
1222 | | - * Convert a zval to an appropriate Lua type and push the resulting value on to |
1223 | | - * the stack. |
1224 | | - */ |
1225 | | -static int luasandbox_push_zval(lua_State * L, zval * z) |
1226 | | -{ |
1227 | | - switch (Z_TYPE_P(z)) { |
1228 | | - case IS_NULL: |
1229 | | - lua_pushnil(L); |
1230 | | - break; |
1231 | | - case IS_LONG: |
1232 | | - lua_pushinteger(L, Z_LVAL_P(z)); |
1233 | | - break; |
1234 | | - case IS_DOUBLE: |
1235 | | - lua_pushnumber(L, Z_DVAL_P(z)); |
1236 | | - break; |
1237 | | - case IS_BOOL: |
1238 | | - lua_pushboolean(L, Z_BVAL_P(z)); |
1239 | | - break; |
1240 | | - case IS_ARRAY: |
1241 | | - if (!luasandbox_push_hashtable(L, Z_ARRVAL_P(z))) { |
1242 | | - return 0; |
1243 | | - } |
1244 | | - break; |
1245 | | - case IS_OBJECT: { |
1246 | | - zend_class_entry * objce; |
1247 | | - |
1248 | | - objce = Z_OBJCE_P(z); |
1249 | | - if (instanceof_function(objce, luasandboxfunction_ce TSRMLS_CC)) { |
1250 | | - php_luasandboxfunction_obj * func_obj; |
1251 | | - |
1252 | | - func_obj = (php_luasandboxfunction_obj *)zend_object_store_get_object(z TSRMLS_CC); |
1253 | | - |
1254 | | - lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
1255 | | - lua_rawgeti(L, -1, func_obj->index); |
1256 | | - lua_remove(L, -2); |
1257 | | - break; |
1258 | | - } |
1259 | | - |
1260 | | - if (!luasandbox_push_hashtable(L, Z_OBJPROP_P(z))) { |
1261 | | - return 0; |
1262 | | - } |
1263 | | - break; |
1264 | | - } |
1265 | | - case IS_STRING: |
1266 | | - lua_pushlstring(L, Z_STRVAL_P(z), Z_STRLEN_P(z)); |
1267 | | - break; |
1268 | | - case IS_RESOURCE: |
1269 | | - case IS_CONSTANT: |
1270 | | - case IS_CONSTANT_ARRAY: |
1271 | | - default: |
1272 | | - return 0; |
1273 | | - } |
1274 | | - return 1; |
1275 | | -} |
1276 | | -/* }}} */ |
1277 | | - |
1278 | | -/** {{{ luasandbox_push_zval_userdata |
1279 | | - * |
1280 | | - * Push a full userdata on to the stack, which stores a zval* in its block. |
1281 | | - * Increment its reference count and set its metatable so that it will be freed |
1282 | | - * at the appropriate time. |
1283 | | - */ |
1284 | | -static void luasandbox_push_zval_userdata(lua_State * L, zval * z) |
1285 | | -{ |
1286 | | - zval ** ud; |
1287 | | - ud = (zval**)lua_newuserdata(L, sizeof(zval*)); |
1288 | | - *ud = z; |
1289 | | - Z_ADDREF_P(z); |
1290 | | - |
1291 | | - lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable"); |
1292 | | - lua_setmetatable(L, -2); |
1293 | | -} |
1294 | | -/* }}} */ |
1295 | | - |
1296 | | -/** {{{ luasandbox_push_hashtable |
1297 | | - * |
1298 | | - * Helper function for luasandbox_push_zval. Create a new table on the top of |
1299 | | - * the stack and add the zvals in the HashTable to it. |
1300 | | - */ |
1301 | | -static int luasandbox_push_hashtable(lua_State * L, HashTable * ht) |
1302 | | -{ |
1303 | | - Bucket * p; |
1304 | | - |
1305 | | - // Recursion requires an arbitrary amount of stack space so we have to |
1306 | | - // check the stack. |
1307 | | - luaL_checkstack(L, 10, "converting PHP array to Lua"); |
1308 | | - |
1309 | | - lua_newtable(L); |
1310 | | - if (!ht || !ht->nNumOfElements) { |
1311 | | - return 1; |
1312 | | - } |
1313 | | - if (ht->nApplyCount) { |
1314 | | - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); |
1315 | | - return 0; |
1316 | | - } |
1317 | | - ht->nApplyCount++; |
1318 | | - for (p = ht->pListHead; p; p = p->pListNext) { |
1319 | | - if (p->nKeyLength) { |
1320 | | - lua_pushlstring(L, p->arKey, p->nKeyLength - 1); |
1321 | | - } else { |
1322 | | - lua_pushinteger(L, p->h); |
1323 | | - } |
1324 | | - |
1325 | | - if (!luasandbox_push_zval(L, *(zval**)p->pData)) { |
1326 | | - // Failed to process that data value |
1327 | | - // Pop the key and the half-constructed table |
1328 | | - lua_pop(L, 2); |
1329 | | - ht->nApplyCount--; |
1330 | | - return 0; |
1331 | | - } |
1332 | | - |
1333 | | - lua_settable(L, -3); |
1334 | | - } |
1335 | | - ht->nApplyCount--; |
1336 | | - return 1; |
1337 | | -} |
1338 | | -/* }}} */ |
1339 | | - |
1340 | | -/** {{{ luasandbox_lua_to_zval |
1341 | | - * |
1342 | | - * Convert a lua value to a zval. |
1343 | | - * |
1344 | | - * If a value is encountered that can't be converted to a zval, a LuaPlaceholder |
1345 | | - * object is returned instead. |
1346 | | - * |
1347 | | - * @param z A pointer to the destination zval |
1348 | | - * @param L The lua state |
1349 | | - * @param index The stack index to the input value |
1350 | | - * @param sandbox_zval A zval poiting to a valid LuaSandbox object which will be |
1351 | | - * used for the parent object of any LuaSandboxFunction objects created. |
1352 | | - * @param recursionGuard A hashtable for keeping track of tables that have been |
1353 | | - * processed, to allow infinite recursion to be avoided. External callers |
1354 | | - * should set this to NULL. |
1355 | | - */ |
1356 | | -static void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
1357 | | - zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC) |
1358 | | -{ |
1359 | | - switch (lua_type(L, index)) { |
1360 | | - case LUA_TNIL: |
1361 | | - ZVAL_NULL(z); |
1362 | | - break; |
1363 | | - case LUA_TNUMBER: { |
1364 | | - long i; |
1365 | | - double d, integerPart, fractionalPart; |
1366 | | - // Lua only provides a single number type |
1367 | | - // Convert it to a PHP integer if that can be done without loss |
1368 | | - // of precision |
1369 | | - d = lua_tonumber(L, index); |
1370 | | - fractionalPart = modf(d, &integerPart); |
1371 | | - if (fractionalPart == 0.0 && integerPart >= LONG_MIN && integerPart <= LONG_MAX) { |
1372 | | - // The number is small enough to fit inside an int. But has it already |
1373 | | - // been truncated by squeezing it into a double? This is only relevant |
1374 | | - // where the integer size is greater than the mantissa size. |
1375 | | - i = (long)integerPart; |
1376 | | - if (LONG_MAX < (1LL << DBL_MANT_DIG) |
1377 | | - || labs(i) < (1L << DBL_MANT_DIG)) |
1378 | | - { |
1379 | | - ZVAL_LONG(z, i); |
1380 | | - } else { |
1381 | | - ZVAL_DOUBLE(z, d); |
1382 | | - } |
1383 | | - } else { |
1384 | | - ZVAL_DOUBLE(z, d); |
1385 | | - } |
1386 | | - break; |
1387 | | - } |
1388 | | - case LUA_TBOOLEAN: |
1389 | | - ZVAL_BOOL(z, lua_toboolean(L, index)); |
1390 | | - break; |
1391 | | - case LUA_TSTRING: { |
1392 | | - const char * str; |
1393 | | - size_t length; |
1394 | | - str = lua_tolstring(L, index, &length); |
1395 | | - ZVAL_STRINGL(z, str, length, 1); |
1396 | | - break; |
1397 | | - } |
1398 | | - case LUA_TTABLE: { |
1399 | | - const void * ptr = lua_topointer(L, index); |
1400 | | - void * data = NULL; |
1401 | | - int allocated = 0; |
1402 | | - if (recursionGuard) { |
1403 | | - // Check for circular reference (infinite recursion) |
1404 | | - if (zend_hash_find(recursionGuard, (char*)&ptr, sizeof(void*), &data) == SUCCESS) { |
1405 | | - // Found circular reference! |
1406 | | - object_init_ex(z, luasandboxplaceholder_ce); |
1407 | | - break; |
1408 | | - } |
1409 | | - } else { |
1410 | | - ALLOC_HASHTABLE(recursionGuard); |
1411 | | - zend_hash_init(recursionGuard, 1, NULL, NULL, 0); |
1412 | | - allocated = 1; |
1413 | | - } |
1414 | | - |
1415 | | - // Add the current table to the recursion guard hashtable |
1416 | | - // Use the pointer as the key, zero-length data |
1417 | | - zend_hash_update(recursionGuard, (char*)&ptr, sizeof(void*), "", 1, NULL); |
1418 | | - |
1419 | | - // Process the array |
1420 | | - array_init(z); |
1421 | | - luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, sandbox_zval, recursionGuard TSRMLS_CC); |
1422 | | - |
1423 | | - if (allocated) { |
1424 | | - zend_hash_destroy(recursionGuard); |
1425 | | - FREE_HASHTABLE(recursionGuard); |
1426 | | - } |
1427 | | - break; |
1428 | | - } |
1429 | | - case LUA_TFUNCTION: { |
1430 | | - int func_index; |
1431 | | - php_luasandboxfunction_obj * func_obj; |
1432 | | - php_luasandbox_obj * sandbox = (php_luasandbox_obj*) |
1433 | | - zend_object_store_get_object(sandbox_zval); |
1434 | | - |
1435 | | - // Normalise the input index so that we can push without invalidating it. |
1436 | | - if (index < 0) { |
1437 | | - index += lua_gettop(L) + 1; |
1438 | | - } |
1439 | | - |
1440 | | - // Get the chunks table |
1441 | | - lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks"); |
1442 | | - |
1443 | | - // Get the next free index |
1444 | | - if (sandbox->function_index >= INT_MAX) { |
1445 | | - ZVAL_NULL(z); |
1446 | | - lua_pop(L, 1); |
1447 | | - break; |
1448 | | - } |
1449 | | - func_index = ++(sandbox->function_index); |
1450 | | - |
1451 | | - // Store it in the chunks table |
1452 | | - lua_pushvalue(L, index); |
1453 | | - lua_rawseti(L, -2, func_index); |
1454 | | - |
1455 | | - // Create a LuaSandboxFunction object to hold a reference to the function |
1456 | | - object_init_ex(z, luasandboxfunction_ce); |
1457 | | - func_obj = (php_luasandboxfunction_obj*)zend_object_store_get_object(z); |
1458 | | - func_obj->index = func_index; |
1459 | | - func_obj->sandbox = sandbox_zval; |
1460 | | - Z_ADDREF_P(sandbox_zval); |
1461 | | - |
1462 | | - // Balance the stack |
1463 | | - lua_pop(L, 1); |
1464 | | - break; |
1465 | | - } |
1466 | | - case LUA_TUSERDATA: |
1467 | | - case LUA_TTHREAD: |
1468 | | - case LUA_TLIGHTUSERDATA: |
1469 | | - default: |
1470 | | - // TODO: provide derived classes for each type |
1471 | | - object_init_ex(z, luasandboxplaceholder_ce); |
1472 | | - } |
1473 | | -} |
1474 | | -/* }}} */ |
1475 | | - |
1476 | | -/** {{{ luasandbox_lua_to_array |
1477 | | - * |
1478 | | - * Append the elements of the table in the specified index to the given HashTable. |
1479 | | - */ |
1480 | | -static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index, |
1481 | | - zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC) |
1482 | | -{ |
1483 | | - const char * str; |
1484 | | - size_t length; |
1485 | | - zval *value; |
1486 | | - |
1487 | | - // Normalise the input index so that we can push without invalidating it. |
1488 | | - if (index < 0) { |
1489 | | - index += lua_gettop(L) + 1; |
1490 | | - } |
1491 | | - |
1492 | | - lua_pushnil(L); |
1493 | | - while (lua_next(L, index) != 0) { |
1494 | | - MAKE_STD_ZVAL(value); |
1495 | | - luasandbox_lua_to_zval(value, L, -1, sandbox_zval, recursionGuard TSRMLS_CC); |
1496 | | - |
1497 | | - // Make a copy of the key so that we can call lua_tolstring() which is destructive |
1498 | | - lua_pushvalue(L, -2); |
1499 | | - str = lua_tolstring(L, -1, &length); |
1500 | | - zend_hash_update(ht, str, length + 1, (void*)&value, sizeof(zval*), NULL); |
1501 | | - |
1502 | | - // Delete the copy and the value |
1503 | | - lua_pop(L, 2); |
1504 | | - } |
1505 | | -} |
1506 | | -/* }}} */ |
1507 | | - |
1508 | 956 | /** {{{ luasandbox_get_php_obj |
1509 | 957 | * |
1510 | 958 | * Get the object data for a lua state. |
1511 | 959 | */ |
1512 | | -static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L) |
| 960 | +php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L) |
1513 | 961 | { |
1514 | 962 | php_luasandbox_obj * obj; |
1515 | 963 | lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj"); |
— | — | @@ -1718,84 +1166,6 @@ |
1719 | 1167 | return 0; |
1720 | 1168 | } |
1721 | 1169 | /* }}} */ |
1722 | | - |
1723 | | -/** {{{ luasandbox_base_tostring |
1724 | | - * |
1725 | | - * This is identical to luaB_tostring except that it does not call lua_topointer(). |
1726 | | - */ |
1727 | | -static int luasandbox_base_tostring(lua_State * L) |
1728 | | -{ |
1729 | | - luaL_checkany(L, 1); |
1730 | | - if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ |
1731 | | - return 1; /* use its value */ |
1732 | | - switch (lua_type(L, 1)) { |
1733 | | - case LUA_TNUMBER: |
1734 | | - lua_pushstring(L, lua_tostring(L, 1)); |
1735 | | - break; |
1736 | | - case LUA_TSTRING: |
1737 | | - lua_pushvalue(L, 1); |
1738 | | - break; |
1739 | | - case LUA_TBOOLEAN: |
1740 | | - lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); |
1741 | | - break; |
1742 | | - case LUA_TNIL: |
1743 | | - lua_pushliteral(L, "nil"); |
1744 | | - break; |
1745 | | - default: |
1746 | | - lua_pushfstring(L, "%s", luaL_typename(L, 1)); |
1747 | | - break; |
1748 | | - } |
1749 | | - return 1; |
1750 | | -} |
1751 | | -/* }}} */ |
1752 | | - |
1753 | | -/** {{{ luasandbox_math_random |
1754 | | - * |
1755 | | - * A math.random implementation that doesn't share state with PHP's rand() |
1756 | | - */ |
1757 | | -static int luasandbox_math_random(lua_State * L) |
1758 | | -{ |
1759 | | - php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); |
1760 | | - |
1761 | | - int i = rand_r(&sandbox->random_seed); |
1762 | | - if (i >= RAND_MAX) { |
1763 | | - i -= RAND_MAX; |
1764 | | - } |
1765 | | - lua_Number r = (lua_Number)i / (lua_Number)RAND_MAX; |
1766 | | - switch (lua_gettop(L)) { /* check number of arguments */ |
1767 | | - case 0: { /* no arguments */ |
1768 | | - lua_pushnumber(L, r); /* Number between 0 and 1 */ |
1769 | | - break; |
1770 | | - } |
1771 | | - case 1: { /* only upper limit */ |
1772 | | - int u = luaL_checkint(L, 1); |
1773 | | - luaL_argcheck(L, 1<=u, 1, "interval is empty"); |
1774 | | - lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ |
1775 | | - break; |
1776 | | - } |
1777 | | - case 2: { /* lower and upper limits */ |
1778 | | - int l = luaL_checkint(L, 1); |
1779 | | - int u = luaL_checkint(L, 2); |
1780 | | - luaL_argcheck(L, l<=u, 2, "interval is empty"); |
1781 | | - lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ |
1782 | | - break; |
1783 | | - } |
1784 | | - default: return luaL_error(L, "wrong number of arguments"); |
1785 | | - } |
1786 | | - return 1; |
1787 | | -} |
1788 | | -/* }}} */ |
1789 | | - |
1790 | | -/** {{{ luasandbox_math_randomseed |
1791 | | - * |
1792 | | - * Set the seed for the custom math.random. |
1793 | | - */ |
1794 | | -static int luasandbox_math_randomseed(lua_State * L) |
1795 | | -{ |
1796 | | - php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); |
1797 | | - sandbox->random_seed = (unsigned int)luaL_checkint(L, 1); |
1798 | | -} |
1799 | | -/* }}} */ |
1800 | 1170 | /* |
1801 | 1171 | * Local variables: |
1802 | 1172 | * tab-width: 4 |
Index: trunk/php/luasandbox/config.m4 |
— | — | @@ -26,5 +26,5 @@ |
27 | 27 | PHP_EVAL_LIBLINE("-lrt", LUASANDBOX_SHARED_LIBADD) |
28 | 28 | |
29 | 29 | PHP_SUBST(LUASANDBOX_SHARED_LIBADD) |
30 | | - PHP_NEW_EXTENSION(luasandbox, luasandbox.c timer.c, $ext_shared) |
| 30 | + PHP_NEW_EXTENSION(luasandbox, alloc.c data_conversion.c library.c luasandbox.c timer.c, $ext_shared) |
31 | 31 | fi |
Index: trunk/php/luasandbox/library.c |
— | — | @@ -0,0 +1,237 @@ |
| 2 | +/** |
| 3 | + * This file holds the library of functions which are written in C and exposed |
| 4 | + * to Lua code, and the code which manages registration of both the custom |
| 5 | + * library and the parts of the standard Lua library which we allow. |
| 6 | + */ |
| 7 | + |
| 8 | +#ifdef HAVE_CONFIG_H |
| 9 | +#include "config.h" |
| 10 | +#endif |
| 11 | + |
| 12 | +#include <lua.h> |
| 13 | +#include <lauxlib.h> |
| 14 | +#include <lualib.h> |
| 15 | + |
| 16 | +#include "php.h" |
| 17 | +#include "php_luasandbox.h" |
| 18 | + |
| 19 | +static HashTable * luasandbox_lib_get_allowed_globals(TSRMLS_D); |
| 20 | + |
| 21 | +static int luasandbox_base_tostring(lua_State * L); |
| 22 | +static int luasandbox_math_random(lua_State * L); |
| 23 | +static int luasandbox_math_randomseed(lua_State * L); |
| 24 | + |
| 25 | +/** |
| 26 | + * Allowed global variables. Omissions are: |
| 27 | + * * pcall, xpcall: Changing the protected environment won't work with our |
| 28 | + * current timeout method. |
| 29 | + * * loadfile: insecure. |
| 30 | + * * load, loadstring: Probably creates a protected environment so has |
| 31 | + * the same problem as pcall. Also omitting these makes analysis of the |
| 32 | + * code for runtime etc. feasible. |
| 33 | + * * print: Not compatible with a sandbox environment |
| 34 | + * * tostring: Provides addresses of tables and functions, which provides an |
| 35 | + * easy ASLR workaround or heap address discovery mechanism for a memory |
| 36 | + * corruption exploit. |
| 37 | + * * Any new or undocumented functions like newproxy. |
| 38 | + * * package: cpath, loadlib etc. are insecure. |
| 39 | + * * coroutine: Not useful for our application so unreviewed at present. |
| 40 | + * * io, file, os: insecure |
| 41 | + * * debug: Provides various ways to break the sandbox, such as setupvalue() |
| 42 | + * and getregistry(). |
| 43 | + */ |
| 44 | +char * luasandbox_allowed_globals[] = { |
| 45 | + // base |
| 46 | + "assert", |
| 47 | + "error", |
| 48 | + "getmetatable", |
| 49 | + "getfenv", |
| 50 | + "getmetatable", |
| 51 | + "ipairs", |
| 52 | + "next", |
| 53 | + "pairs", |
| 54 | + "rawequal", |
| 55 | + "rawget", |
| 56 | + "rawset", |
| 57 | + "select", |
| 58 | + "setmetatable", |
| 59 | + "tonumber", |
| 60 | + "type", |
| 61 | + "unpack", |
| 62 | + "_G", |
| 63 | + "_VERSION", |
| 64 | + // libs |
| 65 | + "string", |
| 66 | + "table", |
| 67 | + "math" |
| 68 | +}; |
| 69 | +#define LUASANDBOX_NUM_ALLOWED_GLOBALS (sizeof(luasandbox_allowed_globals) / sizeof(char*)) |
| 70 | + |
| 71 | +ZEND_EXTERN_MODULE_GLOBALS(luasandbox); |
| 72 | + |
| 73 | +/** {{{ luasandbox_lib_register |
| 74 | + */ |
| 75 | +void luasandbox_lib_register(lua_State * L TSRMLS_DC) |
| 76 | +{ |
| 77 | + // Load some relatively safe standard libraries |
| 78 | + lua_pushcfunction(L, luaopen_base); |
| 79 | + lua_call(L, 0, 0); |
| 80 | + lua_pushcfunction(L, luaopen_string); |
| 81 | + lua_call(L, 0, 0); |
| 82 | + lua_pushcfunction(L, luaopen_table); |
| 83 | + lua_call(L, 0, 0); |
| 84 | + lua_pushcfunction(L, luaopen_math); |
| 85 | + lua_call(L, 0, 0); |
| 86 | + |
| 87 | + // Remove any globals that aren't in a whitelist. This is mostly to remove |
| 88 | + // unsafe functions from the base library. |
| 89 | + lua_pushnil(L); |
| 90 | + while (lua_next(L, LUA_GLOBALSINDEX) != 0) { |
| 91 | + const char * key; |
| 92 | + size_t key_len; |
| 93 | + void * data; |
| 94 | + lua_pop(L, 1); |
| 95 | + if (lua_type(L, -1) != LUA_TSTRING) { |
| 96 | + continue; |
| 97 | + } |
| 98 | + key = lua_tolstring(L, -1, &key_len); |
| 99 | + if (zend_hash_find(luasandbox_lib_get_allowed_globals(), |
| 100 | + (char*)key, key_len + 1, &data) == FAILURE) |
| 101 | + { |
| 102 | + // Not allowed, delete it |
| 103 | + lua_pushnil(L); |
| 104 | + lua_setglobal(L, key); |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + // Install our own version of tostring |
| 109 | + lua_pushcfunction(L, luasandbox_base_tostring); |
| 110 | + lua_setglobal(L, "tostring"); |
| 111 | + |
| 112 | + // Remove string.dump: may expose private data |
| 113 | + lua_getglobal(L, "string"); |
| 114 | + lua_pushnil(L); |
| 115 | + lua_setfield(L, -2, "dump"); |
| 116 | + lua_pop(L, 1); |
| 117 | + |
| 118 | + // Install our own versions of math.random and math.randomseed |
| 119 | + lua_getglobal(L, "math"); |
| 120 | + lua_pushcfunction(L, luasandbox_math_random); |
| 121 | + lua_setfield(L, -2, "random"); |
| 122 | + lua_pushcfunction(L, luasandbox_math_randomseed); |
| 123 | + lua_setfield(L, -2, "randomseed"); |
| 124 | + lua_pop(L, 1); |
| 125 | +} |
| 126 | +/* }}} */ |
| 127 | + |
| 128 | +/** {{{ luasandbox_lib_shutdown */ |
| 129 | +void luasandbox_lib_shutdown(TSRMLS_D) |
| 130 | +{ |
| 131 | + if (LUASANDBOX_G(allowed_globals)) { |
| 132 | + zend_hash_destroy(LUASANDBOX_G(allowed_globals)); |
| 133 | + pefree(LUASANDBOX_G(allowed_globals), 1); |
| 134 | + } |
| 135 | +} |
| 136 | +/* }}} */ |
| 137 | + |
| 138 | +/** {{{ luasandbox_lib_get_allowed_globals |
| 139 | + * |
| 140 | + * Get a HashTable of allowed global variables |
| 141 | + */ |
| 142 | +static HashTable * luasandbox_lib_get_allowed_globals(TSRMLS_D) |
| 143 | +{ |
| 144 | + int i; |
| 145 | + if (LUASANDBOX_G(allowed_globals)) { |
| 146 | + return LUASANDBOX_G(allowed_globals); |
| 147 | + } |
| 148 | + |
| 149 | + LUASANDBOX_G(allowed_globals) = pemalloc(sizeof(HashTable), 1); |
| 150 | + zend_hash_init(LUASANDBOX_G(allowed_globals), LUASANDBOX_NUM_ALLOWED_GLOBALS, NULL, NULL, 1); |
| 151 | + for (i = 0; i < LUASANDBOX_NUM_ALLOWED_GLOBALS; i++) { |
| 152 | + zend_hash_update(LUASANDBOX_G(allowed_globals), |
| 153 | + luasandbox_allowed_globals[i], strlen(luasandbox_allowed_globals[i]) + 1, |
| 154 | + "", 1, NULL); |
| 155 | + } |
| 156 | + |
| 157 | + return LUASANDBOX_G(allowed_globals); |
| 158 | +} |
| 159 | +/* }}} */ |
| 160 | + |
| 161 | +/** {{{ luasandbox_base_tostring |
| 162 | + * |
| 163 | + * This is identical to luaB_tostring except that it does not call lua_topointer(). |
| 164 | + */ |
| 165 | +static int luasandbox_base_tostring(lua_State * L) |
| 166 | +{ |
| 167 | + luaL_checkany(L, 1); |
| 168 | + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ |
| 169 | + return 1; /* use its value */ |
| 170 | + switch (lua_type(L, 1)) { |
| 171 | + case LUA_TNUMBER: |
| 172 | + lua_pushstring(L, lua_tostring(L, 1)); |
| 173 | + break; |
| 174 | + case LUA_TSTRING: |
| 175 | + lua_pushvalue(L, 1); |
| 176 | + break; |
| 177 | + case LUA_TBOOLEAN: |
| 178 | + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); |
| 179 | + break; |
| 180 | + case LUA_TNIL: |
| 181 | + lua_pushliteral(L, "nil"); |
| 182 | + break; |
| 183 | + default: |
| 184 | + lua_pushfstring(L, "%s", luaL_typename(L, 1)); |
| 185 | + break; |
| 186 | + } |
| 187 | + return 1; |
| 188 | +} |
| 189 | +/* }}} */ |
| 190 | + |
| 191 | +/** {{{ luasandbox_math_random |
| 192 | + * |
| 193 | + * A math.random implementation that doesn't share state with PHP's rand() |
| 194 | + */ |
| 195 | +static int luasandbox_math_random(lua_State * L) |
| 196 | +{ |
| 197 | + php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); |
| 198 | + |
| 199 | + int i = rand_r(&sandbox->random_seed); |
| 200 | + if (i >= RAND_MAX) { |
| 201 | + i -= RAND_MAX; |
| 202 | + } |
| 203 | + lua_Number r = (lua_Number)i / (lua_Number)RAND_MAX; |
| 204 | + switch (lua_gettop(L)) { /* check number of arguments */ |
| 205 | + case 0: { /* no arguments */ |
| 206 | + lua_pushnumber(L, r); /* Number between 0 and 1 */ |
| 207 | + break; |
| 208 | + } |
| 209 | + case 1: { /* only upper limit */ |
| 210 | + int u = luaL_checkint(L, 1); |
| 211 | + luaL_argcheck(L, 1<=u, 1, "interval is empty"); |
| 212 | + lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ |
| 213 | + break; |
| 214 | + } |
| 215 | + case 2: { /* lower and upper limits */ |
| 216 | + int l = luaL_checkint(L, 1); |
| 217 | + int u = luaL_checkint(L, 2); |
| 218 | + luaL_argcheck(L, l<=u, 2, "interval is empty"); |
| 219 | + lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ |
| 220 | + break; |
| 221 | + } |
| 222 | + default: return luaL_error(L, "wrong number of arguments"); |
| 223 | + } |
| 224 | + return 1; |
| 225 | +} |
| 226 | +/* }}} */ |
| 227 | + |
| 228 | +/** {{{ luasandbox_math_randomseed |
| 229 | + * |
| 230 | + * Set the seed for the custom math.random. |
| 231 | + */ |
| 232 | +static int luasandbox_math_randomseed(lua_State * L) |
| 233 | +{ |
| 234 | + php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); |
| 235 | + sandbox->random_seed = (unsigned int)luaL_checkint(L, 1); |
| 236 | +} |
| 237 | +/* }}} */ |
| 238 | + |
Property changes on: trunk/php/luasandbox/library.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 239 | + native |
Index: trunk/php/luasandbox/alloc.c |
— | — | @@ -0,0 +1,126 @@ |
| 2 | +/** |
| 3 | + * The Lua allocator hook |
| 4 | + */ |
| 5 | + |
| 6 | +#ifdef HAVE_CONFIG_H |
| 7 | +#include "config.h" |
| 8 | +#endif |
| 9 | + |
| 10 | +#include <lua.h> |
| 11 | +#include <lauxlib.h> |
| 12 | + |
| 13 | +#include "php.h" |
| 14 | +#include "php_luasandbox.h" |
| 15 | + |
| 16 | +#if defined(LUA_JITLIBNAME) && (SSIZE_MAX >> 32) > 0 |
| 17 | +#define LUASANDBOX_LJ_64 |
| 18 | +#endif |
| 19 | + |
| 20 | +static inline int luasandbox_update_memory_accounting(php_luasandbox_alloc * obj, |
| 21 | + size_t osize, size_t nsize); |
| 22 | +static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
| 23 | +static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, size_t nsize); |
| 24 | + |
| 25 | +lua_State * luasandbox_alloc_new_state(php_luasandbox_alloc * alloc, php_luasandbox_obj * sandbox) |
| 26 | +{ |
| 27 | + lua_State * L; |
| 28 | +#ifdef LUASANDBOX_LJ_64 |
| 29 | + // The 64-bit version of LuaJIT needs to use its own allocator |
| 30 | + L = luaL_newstate(); |
| 31 | + if (L) { |
| 32 | + alloc->old_alloc = lua_getallocf(L, &alloc->old_alloc_ud); |
| 33 | + lua_setallocf(L, luasandbox_passthru_alloc, sandbox); |
| 34 | + } |
| 35 | +#else |
| 36 | + L = lua_newstate(luasandbox_php_alloc, sandbox); |
| 37 | +#endif |
| 38 | + return L; |
| 39 | +} |
| 40 | + |
| 41 | +void luasandbox_alloc_delete_state(php_luasandbox_alloc * alloc, lua_State * L) |
| 42 | +{ |
| 43 | + // In 64-bit LuaJIT mode, restore the old allocator before calling |
| 44 | + // lua_close() because lua_close() actually checks that the value of the |
| 45 | + // function pointer is unchanged before destroying the underlying |
| 46 | + // allocator. If the allocator has been changed, the mmap is not freed. |
| 47 | +#ifdef LUASANDBOX_LJ_64 |
| 48 | + lua_setallocf(alloc->state, alloc->old_alloc, alloc->old_alloc_ud); |
| 49 | +#endif |
| 50 | + |
| 51 | + lua_close(L); |
| 52 | +} |
| 53 | + |
| 54 | + |
| 55 | +/** {{{ luasandbox_update_memory_accounting |
| 56 | + * |
| 57 | + * Update memory usage statistics for the given memory allocation request. |
| 58 | + * Returns 1 if the allocation should be allowed, 0 if it should fail. |
| 59 | + */ |
| 60 | +static inline int luasandbox_update_memory_accounting(php_luasandbox_alloc * alloc, |
| 61 | + size_t osize, size_t nsize) |
| 62 | +{ |
| 63 | + if (nsize > alloc->memory_limit |
| 64 | + || alloc->memory_usage > alloc->memory_limit - nsize) |
| 65 | + { |
| 66 | + // Memory limit exceeded |
| 67 | + return 0; |
| 68 | + } |
| 69 | + |
| 70 | + if (osize > nsize && alloc->memory_usage + nsize < osize) { |
| 71 | + // Negative memory usage -- do not update |
| 72 | + return 1; |
| 73 | + } |
| 74 | + |
| 75 | + alloc->memory_usage += nsize - osize; |
| 76 | + return 1; |
| 77 | +} |
| 78 | +/* }}} */ |
| 79 | + |
| 80 | +/** {{{ luasandbox_php_alloc |
| 81 | + * |
| 82 | + * The Lua allocator function. Use PHP's request-local allocator as a backend. |
| 83 | + * Account for memory usage and deny the allocation request if the amount |
| 84 | + * allocated is above the user-specified limit. |
| 85 | + */ |
| 86 | +static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
| 87 | +{ |
| 88 | + php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
| 89 | + void * nptr; |
| 90 | + obj->in_php ++; |
| 91 | + if (!luasandbox_update_memory_accounting(&obj->alloc, osize, nsize)) { |
| 92 | + obj->in_php --; |
| 93 | + return NULL; |
| 94 | + } |
| 95 | + |
| 96 | + if (nsize == 0) { |
| 97 | + if (ptr) { |
| 98 | + efree(ptr); |
| 99 | + } |
| 100 | + nptr = NULL; |
| 101 | + } else if (osize == 0) { |
| 102 | + nptr = emalloc(nsize); |
| 103 | + } else { |
| 104 | + nptr = erealloc(ptr, nsize); |
| 105 | + } |
| 106 | + obj->in_php --; |
| 107 | + return nptr; |
| 108 | +} |
| 109 | +/* }}} */ |
| 110 | + |
| 111 | +/** {{{ luasandbox_passthru_alloc |
| 112 | + * |
| 113 | + * A Lua allocator function for use with LuaJIT on a 64-bit platform. Pass |
| 114 | + * allocation requests through to the standard allocator, which is customised |
| 115 | + * on this platform to always return memory from the lower 2GB of address |
| 116 | + * space. |
| 117 | + */ |
| 118 | +static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
| 119 | +{ |
| 120 | + php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; |
| 121 | + if (!luasandbox_update_memory_accounting(&obj->alloc, osize, nsize)) { |
| 122 | + return NULL; |
| 123 | + } |
| 124 | + return obj->alloc.old_alloc(obj->alloc.old_alloc_ud, ptr, osize, nsize); |
| 125 | +} |
| 126 | +/* }}} */ |
| 127 | + |
Property changes on: trunk/php/luasandbox/alloc.c |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 128 | + native |
Index: trunk/php/luasandbox/php_luasandbox.h |
— | — | @@ -3,7 +3,24 @@ |
4 | 4 | #define PHP_LUASANDBOX_H |
5 | 5 | |
6 | 6 | #include <lua.h> |
| 7 | +#include <signal.h> |
7 | 8 | |
| 9 | +/* alloc.c */ |
| 10 | + |
| 11 | +typedef struct { |
| 12 | + lua_Alloc old_alloc; |
| 13 | + void * old_alloc_ud; |
| 14 | + size_t memory_limit; |
| 15 | + size_t memory_usage; |
| 16 | +} php_luasandbox_alloc; |
| 17 | + |
| 18 | +struct _php_luasandbox_obj; |
| 19 | + |
| 20 | +lua_State * luasandbox_alloc_new_state(php_luasandbox_alloc * alloc, struct _php_luasandbox_obj * sandbox); |
| 21 | +void luasandbox_alloc_delete_state(php_luasandbox_alloc * alloc, lua_State * L); |
| 22 | + |
| 23 | +/* luasandbox.c */ |
| 24 | + |
8 | 25 | extern zend_module_entry luasandbox_module_entry; |
9 | 26 | extern char luasandbox_timeout_message[]; |
10 | 27 | |
— | — | @@ -53,10 +70,7 @@ |
54 | 71 | struct _php_luasandbox_obj { |
55 | 72 | zend_object std; |
56 | 73 | lua_State * state; |
57 | | - size_t memory_limit; |
58 | | - size_t memory_usage; |
59 | | - lua_Alloc old_alloc; |
60 | | - void * old_alloc_ud; |
| 74 | + php_luasandbox_alloc alloc; |
61 | 75 | int in_php; |
62 | 76 | int in_lua; |
63 | 77 | zval * current_zval; /* The zval for the LuaSandbox which is currently executing Lua code */ |
— | — | @@ -77,5 +91,53 @@ |
78 | 92 | }; |
79 | 93 | typedef struct _php_luasandboxfunction_obj php_luasandboxfunction_obj; |
80 | 94 | |
| 95 | + |
| 96 | +php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L); |
| 97 | + |
| 98 | +/** {{{ luasandbox_enter_php |
| 99 | + * |
| 100 | + * This function must be called each time a C function is entered from Lua |
| 101 | + * and the PHP state needs to be accessed in any way. Before exiting the |
| 102 | + * function, luasandbox_leave_php() must be called. |
| 103 | + * |
| 104 | + * This sets a flag which indicates to the timeout signal handler that it is |
| 105 | + * unsafe to call longjmp() to return control to PHP. If the flag is not |
| 106 | + * correctly set, memory may be corrupted and security compromised. |
| 107 | + */ |
| 108 | +inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern) |
| 109 | +{ |
| 110 | + intern->in_php ++; |
| 111 | + if (intern->timed_out) { |
| 112 | + intern->in_php --; |
| 113 | + luaL_error(L, luasandbox_timeout_message); |
| 114 | + } |
| 115 | +} |
| 116 | +/* }}} */ |
| 117 | + |
| 118 | +/** {{{ luasandbox_leave_php |
| 119 | + * |
| 120 | + * This function must be called after luasandbox_enter_php, before the callback |
| 121 | + * from Lua returns. |
| 122 | + */ |
| 123 | +inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern) |
| 124 | +{ |
| 125 | + intern->in_php --; |
| 126 | +} |
| 127 | +/* }}} */ |
| 128 | + |
| 129 | +/* library.c */ |
| 130 | + |
| 131 | +void luasandbox_lib_register(lua_State * L TSRMLS_DC); |
| 132 | +void luasandbox_lib_shutdown(TSRMLS_D); |
| 133 | + |
| 134 | +/* data_conversion.c */ |
| 135 | + |
| 136 | +void luasandbox_data_conversion_init(lua_State * L); |
| 137 | + |
| 138 | +int luasandbox_push_zval(lua_State * L, zval * z); |
| 139 | +void luasandbox_push_zval_userdata(lua_State * L, zval * z); |
| 140 | +void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, |
| 141 | + zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC); |
| 142 | + |
81 | 143 | #endif /* PHP_LUASANDBOX_H */ |
82 | 144 | |