https://www.mediawiki.org/wiki/Special:Code/MediaWiki/113977

Revision: 113977
Author:   tstarling
Date:     2012-03-15 23:34:18 +0000 (Thu, 15 Mar 2012)
Log Message:
-----------
Split off some more modules from luasandbox.c, it was getting a bit too long.

Modified Paths:
--------------
    trunk/php/luasandbox/config.m4
    trunk/php/luasandbox/luasandbox.c
    trunk/php/luasandbox/php_luasandbox.h

Added Paths:
-----------
    trunk/php/luasandbox/alloc.c
    trunk/php/luasandbox/data_conversion.c
    trunk/php/luasandbox/library.c

Added: trunk/php/luasandbox/alloc.c
===================================================================
--- trunk/php/luasandbox/alloc.c                                (rev 0)
+++ trunk/php/luasandbox/alloc.c        2012-03-15 23:34:18 UTC (rev 113977)
@@ -0,0 +1,126 @@
+/**
+ * The Lua allocator hook
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "php.h"
+#include "php_luasandbox.h"
+
+#if defined(LUA_JITLIBNAME) && (SSIZE_MAX >> 32) > 0
+#define LUASANDBOX_LJ_64
+#endif
+
+static inline int luasandbox_update_memory_accounting(php_luasandbox_alloc * 
obj, 
+       size_t osize, size_t nsize);
+static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t 
nsize);
+static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, 
size_t nsize);
+
+lua_State * luasandbox_alloc_new_state(php_luasandbox_alloc * alloc, 
php_luasandbox_obj * sandbox)
+{
+       lua_State * L;
+#ifdef LUASANDBOX_LJ_64
+       // The 64-bit version of LuaJIT needs to use its own allocator
+       L = luaL_newstate();
+       if (L) {
+               alloc->old_alloc = lua_getallocf(L, &alloc->old_alloc_ud);
+               lua_setallocf(L, luasandbox_passthru_alloc, sandbox);
+       }
+#else
+       L = lua_newstate(luasandbox_php_alloc, sandbox);
+#endif
+       return L;
+}
+
+void luasandbox_alloc_delete_state(php_luasandbox_alloc * alloc, lua_State * L)
+{
+       // In 64-bit LuaJIT mode, restore the old allocator before calling 
+       // lua_close() because lua_close() actually checks that the value of 
the 
+       // function pointer is unchanged before destroying the underlying 
+       // allocator. If the allocator has been changed, the mmap is not freed.
+#ifdef LUASANDBOX_LJ_64
+       lua_setallocf(alloc->state, alloc->old_alloc, alloc->old_alloc_ud);
+#endif
+
+       lua_close(L);
+}
+
+
+/** {{{ luasandbox_update_memory_accounting
+ *
+ * Update memory usage statistics for the given memory allocation request.
+ * Returns 1 if the allocation should be allowed, 0 if it should fail.
+ */
+static inline int luasandbox_update_memory_accounting(php_luasandbox_alloc * 
alloc, 
+       size_t osize, size_t nsize) 
+{
+       if (nsize > alloc->memory_limit
+               || alloc->memory_usage > alloc->memory_limit - nsize)
+       {
+               // Memory limit exceeded
+               return 0;
+       }
+
+       if (osize > nsize && alloc->memory_usage + nsize < osize) {
+               // Negative memory usage -- do not update
+               return 1;
+       }
+
+       alloc->memory_usage += nsize - osize;
+       return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_php_alloc
+ *
+ * The Lua allocator function. Use PHP's request-local allocator as a backend.
+ * Account for memory usage and deny the allocation request if the amount 
+ * allocated is above the user-specified limit.
+ */
+static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t 
nsize) 
+{
+       php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
+       void * nptr;
+       obj->in_php ++;
+       if (!luasandbox_update_memory_accounting(&obj->alloc, osize, nsize)) {
+               obj->in_php --;
+               return NULL;
+       }
+
+       if (nsize == 0) {
+               if (ptr) {
+                       efree(ptr);
+               }
+               nptr = NULL;
+       } else if (osize == 0) {
+               nptr = emalloc(nsize);
+       } else {
+               nptr = erealloc(ptr, nsize);
+       }
+       obj->in_php --;
+       return nptr;
+}
+/* }}} */
+
+/** {{{ luasandbox_passthru_alloc
+ *
+ * A Lua allocator function for use with LuaJIT on a 64-bit platform. Pass 
+ * allocation requests through to the standard allocator, which is customised
+ * on this platform to always return memory from the lower 2GB of address 
+ * space.
+ */
+static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, 
size_t nsize) 
+{
+       php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
+       if (!luasandbox_update_memory_accounting(&obj->alloc, osize, nsize)) {
+               return NULL;
+       }
+       return obj->alloc.old_alloc(obj->alloc.old_alloc_ud, ptr, osize, nsize);
+}
+/* }}} */
+


Property changes on: trunk/php/luasandbox/alloc.c
___________________________________________________________________
Added: svn:eol-style
   + native

Modified: trunk/php/luasandbox/config.m4
===================================================================
--- trunk/php/luasandbox/config.m4      2012-03-15 23:25:39 UTC (rev 113976)
+++ trunk/php/luasandbox/config.m4      2012-03-15 23:34:18 UTC (rev 113977)
@@ -26,5 +26,5 @@
        PHP_EVAL_LIBLINE("-lrt", LUASANDBOX_SHARED_LIBADD)
 
        PHP_SUBST(LUASANDBOX_SHARED_LIBADD)
-       PHP_NEW_EXTENSION(luasandbox, luasandbox.c timer.c, $ext_shared)
+       PHP_NEW_EXTENSION(luasandbox, alloc.c data_conversion.c library.c 
luasandbox.c timer.c, $ext_shared)
 fi

Added: trunk/php/luasandbox/data_conversion.c
===================================================================
--- trunk/php/luasandbox/data_conversion.c                              (rev 0)
+++ trunk/php/luasandbox/data_conversion.c      2012-03-15 23:34:18 UTC (rev 
113977)
@@ -0,0 +1,341 @@
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <limits.h>
+#include <float.h>
+
+#include "php.h"
+#include "php_luasandbox.h"
+
+static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
+       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
+static int luasandbox_free_zval_userdata(lua_State * L);
+static int luasandbox_push_hashtable(lua_State * L, HashTable * ht);
+
+extern zend_class_entry *luasandboxfunction_ce;
+extern zend_class_entry *luasandboxplaceholder_ce;
+
+/** {{{ luasandbox_data_conversion_init
+ *
+ * Set up a lua_State so that this module can work with it.
+ */
+void luasandbox_data_conversion_init(lua_State * L)
+{
+       // Create the metatable for zval destruction
+       lua_createtable(L, 0, 1);
+       lua_pushcfunction(L, luasandbox_free_zval_userdata);
+       lua_setfield(L, -2, "__gc");
+       lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
+}
+/* }}} */
+
+/** {{{ luasandbox_push_zval
+ *
+ * Convert a zval to an appropriate Lua type and push the resulting value on to
+ * the stack.
+ */
+int luasandbox_push_zval(lua_State * L, zval * z)
+{
+       switch (Z_TYPE_P(z)) {
+               case IS_NULL:
+                       lua_pushnil(L);
+                       break;
+               case IS_LONG:
+                       lua_pushinteger(L, Z_LVAL_P(z));
+                       break;
+               case IS_DOUBLE:
+                       lua_pushnumber(L, Z_DVAL_P(z));
+                       break;
+               case IS_BOOL:
+                       lua_pushboolean(L, Z_BVAL_P(z));
+                       break;
+               case IS_ARRAY:
+                       if (!luasandbox_push_hashtable(L, Z_ARRVAL_P(z))) {
+                               return 0;
+                       }
+                       break;
+               case IS_OBJECT: {
+                       zend_class_entry * objce;
+                       
+                       objce = Z_OBJCE_P(z);
+                       if (instanceof_function(objce, luasandboxfunction_ce 
TSRMLS_CC)) {
+                               php_luasandboxfunction_obj * func_obj;
+                               
+                               func_obj = (php_luasandboxfunction_obj 
*)zend_object_store_get_object(z TSRMLS_CC);
+                               
+                               lua_getfield(L, LUA_REGISTRYINDEX, 
"php_luasandbox_chunks");
+                               lua_rawgeti(L, -1, func_obj->index);
+                               lua_remove(L, -2);
+                               break;
+                       }
+               
+                       if (!luasandbox_push_hashtable(L, Z_OBJPROP_P(z))) {
+                               return 0;
+                       }
+                       break;
+               }
+               case IS_STRING:
+                       lua_pushlstring(L, Z_STRVAL_P(z), Z_STRLEN_P(z));
+                       break;
+               case IS_RESOURCE:
+               case IS_CONSTANT:
+               case IS_CONSTANT_ARRAY:
+               default:
+                       return 0;
+       }
+       return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_free_zval_userdata
+ *
+ * Free a zval given to Lua by luasandbox_push_zval_userdata.
+ */
+static int luasandbox_free_zval_userdata(lua_State * L)
+{
+       zval ** zpp = (zval**)lua_touserdata(L, 1);
+       php_luasandbox_obj * intern = luasandbox_get_php_obj(L);
+       luasandbox_enter_php(L, intern);
+       if (zpp && *zpp) {
+               zval_ptr_dtor(zpp);
+       }
+       *zpp = NULL;
+       luasandbox_leave_php(L, intern);
+       return 0;
+}
+/* }}} */
+
+/** {{{ luasandbox_push_zval_userdata
+ *
+ * Push a full userdata on to the stack, which stores a zval* in its block. 
+ * Increment its reference count and set its metatable so that it will be 
freed 
+ * at the appropriate time.
+ */
+void luasandbox_push_zval_userdata(lua_State * L, zval * z)
+{
+       zval ** ud;
+       ud = (zval**)lua_newuserdata(L, sizeof(zval*));
+       *ud = z;
+       Z_ADDREF_P(z);
+
+       lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
+       lua_setmetatable(L, -2);
+}
+/* }}} */
+
+/** {{{ luasandbox_push_hashtable
+ *
+ * Helper function for luasandbox_push_zval. Create a new table on the top of 
+ * the stack and add the zvals in the HashTable to it. 
+ */
+static int luasandbox_push_hashtable(lua_State * L, HashTable * ht)
+{
+       Bucket * p;
+
+       // Recursion requires an arbitrary amount of stack space so we have to 
+       // check the stack.
+       luaL_checkstack(L, 10, "converting PHP array to Lua");
+
+       lua_newtable(L);
+       if (!ht || !ht->nNumOfElements) {
+               return 1;
+       }
+       if (ht->nApplyCount) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion 
detected");
+               return 0;
+       }
+       ht->nApplyCount++;
+       for (p = ht->pListHead; p; p = p->pListNext) {
+               if (p->nKeyLength) {
+                       lua_pushlstring(L, p->arKey, p->nKeyLength - 1);
+               } else {
+                       lua_pushinteger(L, p->h);
+               }
+               
+               if (!luasandbox_push_zval(L, *(zval**)p->pData)) {
+                       // Failed to process that data value
+                       // Pop the key and the half-constructed table
+                       lua_pop(L, 2);
+                       ht->nApplyCount--;
+                       return 0;
+               }
+
+               lua_settable(L, -3);
+       }
+       ht->nApplyCount--;
+       return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_lua_to_zval
+ *
+ * Convert a lua value to a zval.
+ *
+ * If a value is encountered that can't be converted to a zval, a 
LuaPlaceholder
+ * object is returned instead.
+ *
+ * @param z A pointer to the destination zval
+ * @param L The lua state
+ * @param index The stack index to the input value
+ * @param sandbox_zval A zval poiting to a valid LuaSandbox object which will 
be
+ *     used for the parent object of any LuaSandboxFunction objects created.
+ * @param recursionGuard A hashtable for keeping track of tables that have 
been 
+ *     processed, to allow infinite recursion to be avoided. External callers 
+ *     should set this to NULL.
+ */
+void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, 
+       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC)
+{
+       switch (lua_type(L, index)) {
+               case LUA_TNIL:
+                       ZVAL_NULL(z);
+                       break;
+               case LUA_TNUMBER: {
+                       long i;
+                       double d, integerPart, fractionalPart;
+                       // Lua only provides a single number type
+                       // Convert it to a PHP integer if that can be done 
without loss
+                       // of precision
+                       d = lua_tonumber(L, index);
+                       fractionalPart = modf(d, &integerPart);
+                       if (fractionalPart == 0.0 && integerPart >= LONG_MIN && 
integerPart <= LONG_MAX) {
+                               // The number is small enough to fit inside an 
int. But has it already
+                               // been truncated by squeezing it into a 
double? This is only relevant
+                               // where the integer size is greater than the 
mantissa size.
+                               i = (long)integerPart;
+                               if (LONG_MAX < (1LL << DBL_MANT_DIG)
+                                       || labs(i) < (1L << DBL_MANT_DIG))
+                               {
+                                       ZVAL_LONG(z, i);
+                               } else {                                
+                                       ZVAL_DOUBLE(z, d);
+                               }
+                       } else {
+                               ZVAL_DOUBLE(z, d);
+                       }
+                       break;
+               }
+               case LUA_TBOOLEAN:
+                       ZVAL_BOOL(z, lua_toboolean(L, index));
+                       break;
+               case LUA_TSTRING: {
+                       const char * str;
+                       size_t length;
+                       str = lua_tolstring(L, index, &length);
+                       ZVAL_STRINGL(z, str, length, 1);
+                       break;
+               }
+               case LUA_TTABLE: {
+                       const void * ptr = lua_topointer(L, index);
+                       void * data = NULL;
+                       int allocated = 0;
+                       if (recursionGuard) {
+                               // Check for circular reference (infinite 
recursion)
+                               if (zend_hash_find(recursionGuard, (char*)&ptr, 
sizeof(void*), &data) == SUCCESS) {
+                                       // Found circular reference!
+                                       object_init_ex(z, 
luasandboxplaceholder_ce);
+                                       break;
+                               }
+                       } else {
+                               ALLOC_HASHTABLE(recursionGuard);
+                               zend_hash_init(recursionGuard, 1, NULL, NULL, 
0);
+                               allocated = 1;
+                       }
+
+                       // Add the current table to the recursion guard 
hashtable
+                       // Use the pointer as the key, zero-length data
+                       zend_hash_update(recursionGuard, (char*)&ptr, 
sizeof(void*), "", 1, NULL);
+
+                       // Process the array
+                       array_init(z);
+                       luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, 
sandbox_zval, recursionGuard TSRMLS_CC);
+
+                       if (allocated) {
+                               zend_hash_destroy(recursionGuard);
+                               FREE_HASHTABLE(recursionGuard);
+                       }
+                       break;
+               }
+               case LUA_TFUNCTION: {
+                       int func_index;
+                       php_luasandboxfunction_obj * func_obj;
+                       php_luasandbox_obj * sandbox = (php_luasandbox_obj*)
+                               zend_object_store_get_object(sandbox_zval);
+
+                       // Normalise the input index so that we can push 
without invalidating it.
+                       if (index < 0) {
+                               index += lua_gettop(L) + 1;
+                       }
+                       
+                       // Get the chunks table
+                       lua_getfield(L, LUA_REGISTRYINDEX, 
"php_luasandbox_chunks");
+
+                       // Get the next free index
+                       if (sandbox->function_index >= INT_MAX) {
+                               ZVAL_NULL(z);
+                               lua_pop(L, 1);
+                               break;
+                       }
+                       func_index = ++(sandbox->function_index);
+
+                       // Store it in the chunks table
+                       lua_pushvalue(L, index);
+                       lua_rawseti(L, -2, func_index);
+
+                       // Create a LuaSandboxFunction object to hold a 
reference to the function
+                       object_init_ex(z, luasandboxfunction_ce);
+                       func_obj = 
(php_luasandboxfunction_obj*)zend_object_store_get_object(z);
+                       func_obj->index = func_index;
+                       func_obj->sandbox = sandbox_zval;
+                       Z_ADDREF_P(sandbox_zval);
+
+                       // Balance the stack
+                       lua_pop(L, 1);
+                       break;
+               }
+               case LUA_TUSERDATA:
+               case LUA_TTHREAD:
+               case LUA_TLIGHTUSERDATA:
+               default:
+                       // TODO: provide derived classes for each type
+                       object_init_ex(z, luasandboxplaceholder_ce);
+       }
+}
+/* }}} */
+
+/** {{{ luasandbox_lua_to_array
+ *
+ * Append the elements of the table in the specified index to the given 
HashTable.
+ */
+static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
+       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC)
+{
+       const char * str;
+       size_t length;
+       zval *value;
+
+       // Normalise the input index so that we can push without invalidating 
it.
+       if (index < 0) {
+               index += lua_gettop(L) + 1;
+       }
+
+       lua_pushnil(L);
+       while (lua_next(L, index) != 0) {
+               MAKE_STD_ZVAL(value);
+               luasandbox_lua_to_zval(value, L, -1, sandbox_zval, 
recursionGuard TSRMLS_CC);
+               
+               // Make a copy of the key so that we can call lua_tolstring() 
which is destructive
+               lua_pushvalue(L, -2);
+               str = lua_tolstring(L, -1, &length);
+               zend_hash_update(ht, str, length + 1, (void*)&value, 
sizeof(zval*), NULL);
+
+               // Delete the copy and the value
+               lua_pop(L, 2);
+       }
+}
+/* }}} */
+


Property changes on: trunk/php/luasandbox/data_conversion.c
___________________________________________________________________
Added: svn:eol-style
   + native

Added: trunk/php/luasandbox/library.c
===================================================================
--- trunk/php/luasandbox/library.c                              (rev 0)
+++ trunk/php/luasandbox/library.c      2012-03-15 23:34:18 UTC (rev 113977)
@@ -0,0 +1,237 @@
+/**
+ * This file holds the library of functions which are written in C and exposed 
+ * to Lua code, and the code which manages registration of both the custom 
+ * library and the parts of the standard Lua library which we allow.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "php.h"
+#include "php_luasandbox.h"
+
+static HashTable * luasandbox_lib_get_allowed_globals(TSRMLS_D);
+
+static int luasandbox_base_tostring(lua_State * L);
+static int luasandbox_math_random(lua_State * L);
+static int luasandbox_math_randomseed(lua_State * L);
+
+/**
+ * Allowed global variables. Omissions are:
+ *   * pcall, xpcall: Changing the protected environment won't work with our
+ *     current timeout method.
+ *   * loadfile: insecure.
+ *   * load, loadstring: Probably creates a protected environment so has 
+ *     the same problem as pcall. Also omitting these makes analysis of the 
+ *     code for runtime etc. feasible.
+ *   * print: Not compatible with a sandbox environment
+ *   * tostring: Provides addresses of tables and functions, which provides an 
+ *     easy ASLR workaround or heap address discovery mechanism for a memory 
+ *     corruption exploit.
+ *   * Any new or undocumented functions like newproxy.
+ *   * package: cpath, loadlib etc. are insecure.
+ *   * coroutine: Not useful for our application so unreviewed at present.
+ *   * io, file, os: insecure
+ *   * debug: Provides various ways to break the sandbox, such as setupvalue()
+ *     and getregistry().
+ */
+char * luasandbox_allowed_globals[] = {
+       // base
+       "assert",
+       "error",
+       "getmetatable",
+       "getfenv",
+       "getmetatable",
+       "ipairs",
+       "next",
+       "pairs",
+       "rawequal",
+       "rawget",
+       "rawset",
+       "select",
+       "setmetatable",
+       "tonumber",
+       "type",
+       "unpack",
+       "_G",
+       "_VERSION",
+       // libs
+       "string",
+       "table",
+       "math"
+};
+#define LUASANDBOX_NUM_ALLOWED_GLOBALS (sizeof(luasandbox_allowed_globals) / 
sizeof(char*))
+
+ZEND_EXTERN_MODULE_GLOBALS(luasandbox);
+
+/** {{{  luasandbox_lib_register
+ */
+void luasandbox_lib_register(lua_State * L TSRMLS_DC)
+{
+       // Load some relatively safe standard libraries
+       lua_pushcfunction(L, luaopen_base);
+       lua_call(L, 0, 0);
+       lua_pushcfunction(L, luaopen_string);
+       lua_call(L, 0, 0);
+       lua_pushcfunction(L, luaopen_table);
+       lua_call(L, 0, 0);
+       lua_pushcfunction(L, luaopen_math);
+       lua_call(L, 0, 0);
+
+       // Remove any globals that aren't in a whitelist. This is mostly to 
remove 
+       // unsafe functions from the base library.
+       lua_pushnil(L);
+       while (lua_next(L, LUA_GLOBALSINDEX) != 0) {
+               const char * key;
+               size_t key_len;
+               void * data;
+               lua_pop(L, 1);
+               if (lua_type(L, -1) != LUA_TSTRING) {
+                       continue;
+               }
+               key = lua_tolstring(L, -1, &key_len);
+               if (zend_hash_find(luasandbox_lib_get_allowed_globals(), 
+                       (char*)key, key_len + 1, &data) == FAILURE) 
+               {
+                       // Not allowed, delete it
+                       lua_pushnil(L);
+                       lua_setglobal(L, key);
+               }
+       }
+
+       // Install our own version of tostring
+       lua_pushcfunction(L, luasandbox_base_tostring);
+       lua_setglobal(L, "tostring");
+
+       // Remove string.dump: may expose private data
+       lua_getglobal(L, "string");
+       lua_pushnil(L);
+       lua_setfield(L, -2, "dump");
+       lua_pop(L, 1);
+
+       // Install our own versions of math.random and math.randomseed
+       lua_getglobal(L, "math");
+       lua_pushcfunction(L, luasandbox_math_random);
+       lua_setfield(L, -2, "random");
+       lua_pushcfunction(L, luasandbox_math_randomseed);
+       lua_setfield(L, -2, "randomseed");
+       lua_pop(L, 1);
+}
+/* }}} */
+
+/** {{{ luasandbox_lib_shutdown */
+void luasandbox_lib_shutdown(TSRMLS_D)
+{
+       if (LUASANDBOX_G(allowed_globals)) {
+               zend_hash_destroy(LUASANDBOX_G(allowed_globals));
+               pefree(LUASANDBOX_G(allowed_globals), 1);
+       }
+}
+/* }}} */
+
+/** {{{ luasandbox_lib_get_allowed_globals
+ *
+ * Get a HashTable of allowed global variables
+ */
+static HashTable * luasandbox_lib_get_allowed_globals(TSRMLS_D)
+{
+       int i;
+       if (LUASANDBOX_G(allowed_globals)) {
+               return LUASANDBOX_G(allowed_globals);
+       }
+
+       LUASANDBOX_G(allowed_globals) = pemalloc(sizeof(HashTable), 1);
+       zend_hash_init(LUASANDBOX_G(allowed_globals), 
LUASANDBOX_NUM_ALLOWED_GLOBALS, NULL, NULL, 1);
+       for (i = 0; i < LUASANDBOX_NUM_ALLOWED_GLOBALS; i++) {
+               zend_hash_update(LUASANDBOX_G(allowed_globals), 
+                       luasandbox_allowed_globals[i], 
strlen(luasandbox_allowed_globals[i]) + 1,
+                       "", 1, NULL);
+       }
+
+       return LUASANDBOX_G(allowed_globals);
+}
+/* }}} */
+
+/** {{{ luasandbox_base_tostring
+ *
+ * This is identical to luaB_tostring except that it does not call 
lua_topointer().
+ */
+static int luasandbox_base_tostring(lua_State * L)
+{
+       luaL_checkany(L, 1);
+       if (luaL_callmeta(L, 1, "__tostring"))  /* is there a metafield? */
+               return 1;  /* use its value */
+       switch (lua_type(L, 1)) {
+               case LUA_TNUMBER:
+                       lua_pushstring(L, lua_tostring(L, 1));
+                       break;
+               case LUA_TSTRING:
+                       lua_pushvalue(L, 1);
+                       break;
+               case LUA_TBOOLEAN:
+                       lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : 
"false"));
+                       break;
+               case LUA_TNIL:
+                       lua_pushliteral(L, "nil");
+                       break;
+               default:
+                       lua_pushfstring(L, "%s", luaL_typename(L, 1));
+                       break;
+       }
+       return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_math_random
+ *
+ * A math.random implementation that doesn't share state with PHP's rand()
+ */
+static int luasandbox_math_random(lua_State * L)
+{
+       php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L);
+
+       int i = rand_r(&sandbox->random_seed);
+       if (i >= RAND_MAX) {
+               i -= RAND_MAX;
+       }
+       lua_Number r = (lua_Number)i / (lua_Number)RAND_MAX;
+       switch (lua_gettop(L)) {  /* check number of arguments */
+               case 0: {  /* no arguments */
+                       lua_pushnumber(L, r);  /* Number between 0 and 1 */
+                       break;
+               }
+               case 1: {  /* only upper limit */
+                       int u = luaL_checkint(L, 1);
+                       luaL_argcheck(L, 1<=u, 1, "interval is empty");
+                       lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and 
`u' */
+                       break;
+               }
+               case 2: {  /* lower and upper limits */
+                       int l = luaL_checkint(L, 1);
+                       int u = luaL_checkint(L, 2);
+                       luaL_argcheck(L, l<=u, 2, "interval is empty");
+                       lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between 
`l' and `u' */
+                       break;
+               }
+               default: return luaL_error(L, "wrong number of arguments");
+       }
+       return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_math_randomseed
+ *
+ * Set the seed for the custom math.random.
+ */
+static int luasandbox_math_randomseed(lua_State * L)
+{
+       php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L);
+       sandbox->random_seed = (unsigned int)luaL_checkint(L, 1);
+}
+/* }}} */
+


Property changes on: trunk/php/luasandbox/library.c
___________________________________________________________________
Added: svn:eol-style
   + native

Modified: trunk/php/luasandbox/luasandbox.c
===================================================================
--- trunk/php/luasandbox/luasandbox.c   2012-03-15 23:25:39 UTC (rev 113976)
+++ trunk/php/luasandbox/luasandbox.c   2012-03-15 23:34:18 UTC (rev 113977)
@@ -4,11 +4,7 @@
 
 #include <lua.h>
 #include <lauxlib.h>
-#include <lualib.h>
 #include <math.h>
-#include <limits.h>
-#include <float.h>
-#include <signal.h>
 #include <time.h>
 #include <stdlib.h>
 
@@ -20,10 +16,6 @@
 #include "luasandbox_timer.h"
 #include "ext/standard/php_smart_str.h"
 
-#if defined(LUA_JITLIBNAME) && (SSIZE_MAX >> 32) > 0
-#define LUASANDBOX_LJ_64
-#endif
-
 #define CHECK_VALID_STATE(state) \
        if (!state) { \
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid LuaSandbox 
state"); \
@@ -36,11 +28,6 @@
 static zend_object_value luasandboxfunction_new(zend_class_entry *ce 
TSRMLS_CC);
 static void luasandboxfunction_free_storage(void *object TSRMLS_DC);
 static void luasandboxfunction_destroy(void *object, zend_object_handle handle 
TSRMLS_DC);
-static int luasandbox_free_zval_userdata(lua_State * L);
-static inline int luasandbox_update_memory_accounting(php_luasandbox_obj* obj, 
-       size_t osize, size_t nsize);
-static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t 
nsize);
-static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, 
size_t nsize);
 static int luasandbox_panic(lua_State * L);
 static lua_State * luasandbox_state_from_zval(zval * this_ptr TSRMLS_DC);
 static void luasandbox_load_helper(int binary, INTERNAL_FUNCTION_PARAMETERS);
@@ -52,70 +39,13 @@
 static void luasandbox_call_helper(lua_State * L, zval * sandbox_zval, 
        php_luasandbox_obj * sandbox,
        zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC);
-static int luasandbox_push_zval(lua_State * L, zval * z);
-static void luasandbox_push_zval_userdata(lua_State * L, zval * z);
-static void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, 
-       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
-static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
-       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
-static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L);
 static void luasandbox_handle_error(lua_State * L, int status);
 static void luasandbox_handle_emergency_timeout(php_luasandbox_obj * sandbox);
-static int luasandbox_push_hashtable(lua_State * L, HashTable * ht);
 static int luasandbox_call_php(lua_State * L);
 static int luasandbox_dump_writer(lua_State * L, const void * p, size_t sz, 
void * ud);
-static int luasandbox_base_tostring(lua_State * L);
-static int luasandbox_math_random(lua_State * L);
-static int luasandbox_math_randomseed(lua_State * L);
 
 char luasandbox_timeout_message[] = "The maximum execution time for this 
script was exceeded";
 
-/**
- * Allowed global variables. Omissions are:
- *   * pcall, xpcall: Changing the protected environment won't work with our
- *     current timeout method.
- *   * loadfile: insecure.
- *   * load, loadstring: Probably creates a protected environment so has 
- *     the same problem as pcall. Also omitting these makes analysis of the 
- *     code for runtime etc. feasible.
- *   * print: Not compatible with a sandbox environment
- *   * tostring: Provides addresses of tables and functions, which provides an 
- *     easy ASLR workaround or heap address discovery mechanism for a memory 
- *     corruption exploit.
- *   * Any new or undocumented functions like newproxy.
- *   * package: cpath, loadlib etc. are insecure.
- *   * coroutine: Not useful for our application so unreviewed at present.
- *   * io, file, os: insecure
- *   * debug: Provides various ways to break the sandbox, such as setupvalue()
- *     and getregistry().
- */
-char * luasandbox_allowed_globals[] = {
-       // base
-       "assert",
-       "error",
-       "getmetatable",
-       "getfenv",
-       "getmetatable",
-       "ipairs",
-       "next",
-       "pairs",
-       "rawequal",
-       "rawget",
-       "rawset",
-       "select",
-       "setmetatable",
-       "tonumber",
-       "type",
-       "unpack",
-       "_G",
-       "_VERSION",
-       // libs
-       "string",
-       "table",
-       "math"
-};
-#define LUASANDBOX_NUM_ALLOWED_GLOBALS (sizeof(luasandbox_allowed_globals) / 
sizeof(char*))
-
 zend_class_entry *luasandbox_ce;
 zend_class_entry *luasandboxerror_ce;
 zend_class_entry *luasandboxemergencytimeout_ce;
@@ -227,7 +157,6 @@
  */
 PHP_MINIT_FUNCTION(luasandbox)
 {
-       int i;
        /* If you have INI entries, uncomment these lines 
        REGISTER_INI_ENTRIES();
        */
@@ -261,14 +190,7 @@
        luasandboxfunction_ce = zend_register_internal_class(&ce TSRMLS_CC);
        luasandboxfunction_ce->create_object = luasandboxfunction_new;
        
-       // Initialise LUASANDBOX_G(allowed_globals)
-       LUASANDBOX_G(allowed_globals) = pemalloc(sizeof(HashTable), 1);
-       zend_hash_init(LUASANDBOX_G(allowed_globals), 
LUASANDBOX_NUM_ALLOWED_GLOBALS, NULL, NULL, 1);
-       for (i = 0; i < LUASANDBOX_NUM_ALLOWED_GLOBALS; i++) {
-               zend_hash_update(LUASANDBOX_G(allowed_globals), 
-                       luasandbox_allowed_globals[i], 
strlen(luasandbox_allowed_globals[i]) + 1,
-                       "", 1, NULL);
-       }
+       LUASANDBOX_G(allowed_globals) = NULL;
        return SUCCESS;
 }
 /* }}} */
@@ -277,8 +199,7 @@
  */
 PHP_MSHUTDOWN_FUNCTION(luasandbox)
 {
-       zend_hash_destroy(LUASANDBOX_G(allowed_globals));
-       pefree(LUASANDBOX_G(allowed_globals), 1);
+       luasandbox_lib_shutdown(TSRMLS_C);
        return SUCCESS;
 }
 /* }}} */
@@ -303,37 +224,6 @@
 }
 /* }}} */
 
-/** {{{ luasandbox_enter_php
- *
- * This function must be called each time a C function is entered from Lua
- * and the PHP state needs to be accessed in any way. Before exiting the 
- * function, luasandbox_leave_php() must be called.
- *
- * This sets a flag which indicates to the timeout signal handler that it is 
- * unsafe to call longjmp() to return control to PHP. If the flag is not 
- * correctly set, memory may be corrupted and security compromised.
- */
-static inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * 
intern)
-{
-       intern->in_php ++;
-       if (intern->timed_out) {
-               intern->in_php --;
-               luaL_error(L, luasandbox_timeout_message);
-       }
-}
-/* }}} */
-
-/** {{{ luasandbox_leave_php
- *
- * This function must be called after luasandbox_enter_php, before the 
callback 
- * from Lua returns.
- */
-static inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * 
intern)
-{
-       intern->in_php --;
-}
-/* }}} */
-
 /** {{{ luasandbox_new
  *
  * "new" handler for the LuaSandbox class
@@ -347,7 +237,7 @@
        intern = emalloc(sizeof(php_luasandbox_obj));
        memset(intern, 0, sizeof(php_luasandbox_obj));
        zend_object_std_init(&intern->std, ce TSRMLS_CC);
-       intern->memory_limit = (size_t)-1;
+       intern->alloc.memory_limit = (size_t)-1;
 
        // Initialise the Lua state
        intern->state = luasandbox_newstate(intern);
@@ -370,73 +260,21 @@
  */
 static lua_State * luasandbox_newstate(php_luasandbox_obj * intern) 
 {
-       lua_State * L;
+       lua_State * L = luasandbox_alloc_new_state(&intern->alloc, intern);
 
-#ifdef LUASANDBOX_LJ_64
-       // The 64-bit version of LuaJIT needs to use its own allocator
-       L = luaL_newstate();
        if (L == NULL) {
                php_error_docref(NULL TSRMLS_CC, E_ERROR,
                        "Attempt to allocate a new Lua state failed");
        }
-       intern->old_alloc = lua_getallocf(L, &intern->old_alloc_ud);
-       lua_setallocf(L, luasandbox_passthru_alloc, intern);
-#else
-       L = lua_newstate(luasandbox_php_alloc, intern);
-#endif
 
        lua_atpanic(L, luasandbox_panic);
-
-       // Load some relatively safe standard libraries
-       lua_pushcfunction(L, luaopen_base);
-       lua_call(L, 0, 0);
-       lua_pushcfunction(L, luaopen_string);
-       lua_call(L, 0, 0);
-       lua_pushcfunction(L, luaopen_table);
-       lua_call(L, 0, 0);
-       lua_pushcfunction(L, luaopen_math);
-       lua_call(L, 0, 0);
-
-       // Remove any globals that aren't in a whitelist. This is mostly to 
remove 
-       // unsafe functions from the base library.
-       lua_pushnil(L);
-       while (lua_next(L, LUA_GLOBALSINDEX) != 0) {
-               const char * key;
-               size_t key_len;
-               void * data;
-               lua_pop(L, 1);
-               if (lua_type(L, -1) != LUA_TSTRING) {
-                       continue;
-               }
-               key = lua_tolstring(L, -1, &key_len);
-               if (zend_hash_find(LUASANDBOX_G(allowed_globals), 
-                       (char*)key, key_len + 1, &data) == FAILURE) 
-               {
-                       // Not allowed, delete it
-                       lua_pushnil(L);
-                       lua_setglobal(L, key);
-               }
-       }
-
-       // Install our own version of tostring
-       lua_pushcfunction(L, luasandbox_base_tostring);
-       lua_setglobal(L, "tostring");
-
-       // Remove string.dump: may expose private data
-       lua_getglobal(L, "string");
-       lua_pushnil(L);
-       lua_setfield(L, -2, "dump");
-       lua_pop(L, 1);
-
-       // Install our own versions of math.random and math.randomseed
-       lua_getglobal(L, "math");
-       lua_pushcfunction(L, luasandbox_math_random);
-       lua_setfield(L, -2, "random");
-       lua_pushcfunction(L, luasandbox_math_randomseed);
-       lua_setfield(L, -2, "randomseed");
-       lua_pop(L, 1);
        
+       // Register the standard library
+       luasandbox_lib_register(L TSRMLS_CC);
 
+       // Set up the data conversion module
+       luasandbox_data_conversion_init(L);
+
        // Create a table for storing chunks
        lua_newtable(L);
        lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_chunks");
@@ -445,12 +283,6 @@
        lua_pushlightuserdata(L, (void*)intern);
        lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj");
 
-       // Create the metatable for zval destruction
-       lua_createtable(L, 0, 1);
-       lua_pushcfunction(L, luasandbox_free_zval_userdata);
-       lua_setfield(L, -2, "__gc");
-       lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
-
        return L;
 }
 /* }}} */
@@ -464,16 +296,10 @@
        php_luasandbox_obj * intern = (php_luasandbox_obj*)object;
 
        if (intern->state) {
-               // In 64-bit LuaJIT mode, restore the old allocator before 
calling 
-               // lua_close() because lua_close() actually checks that the 
value of the 
-               // function pointer is unchanged before destroying the 
underlying 
-               // allocator. If the allocator has been changed, the mmap is 
not freed.
-#ifdef LUASANDBOX_LJ_64
-               lua_setallocf(intern->state, intern->old_alloc, 
intern->old_alloc_ud);
-#endif
-
-               lua_close(intern->state);
+               luasandbox_alloc_delete_state(&intern->alloc, intern->state);
                intern->state = NULL;
+               
+               intern->state = NULL;
        }
        zend_object_std_dtor(&intern->std);
        efree(object);
@@ -547,97 +373,6 @@
 }
 /* }}} */
 
-/** {{{ luasandbox_free_zval_userdata
- *
- * Free a zval given to Lua by luasandbox_push_zval_userdata.
- */
-static int luasandbox_free_zval_userdata(lua_State * L)
-{
-       zval ** zpp = (zval**)lua_touserdata(L, 1);
-       php_luasandbox_obj * intern = luasandbox_get_php_obj(L);
-       luasandbox_enter_php(L, intern);
-       if (zpp && *zpp) {
-               zval_ptr_dtor(zpp);
-       }
-       *zpp = NULL;
-       luasandbox_leave_php(L, intern);
-       return 0;
-}
-/* }}} */
-
-/** {{{ luasandbox_php_alloc
- *
- * The Lua allocator function. Use PHP's request-local allocator as a backend.
- * Account for memory usage and deny the allocation request if the amount 
- * allocated is above the user-specified limit.
- */
-static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t 
nsize) 
-{
-       php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
-       void * nptr;
-       obj->in_php ++;
-       if (!luasandbox_update_memory_accounting(obj, osize, nsize)) {
-               obj->in_php --;
-               return NULL;
-       }
-
-       if (nsize == 0) {
-               if (ptr) {
-                       efree(ptr);
-               }
-               nptr = NULL;
-       } else if (osize == 0) {
-               nptr = emalloc(nsize);
-       } else {
-               nptr = erealloc(ptr, nsize);
-       }
-       obj->in_php --;
-       return nptr;
-}
-/* }}} */
-
-/** {{{ luasandbox_passthru_alloc
- *
- * A Lua allocator function for use with LuaJIT on a 64-bit platform. Pass 
- * allocation requests through to the standard allocator, which is customised
- * on this platform to always return memory from the lower 2GB of address 
- * space.
- */
-static void *luasandbox_passthru_alloc(void *ud, void *ptr, size_t osize, 
size_t nsize) 
-{
-       php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
-       if (!luasandbox_update_memory_accounting(obj, osize, nsize)) {
-               return NULL;
-       }
-       return obj->old_alloc(obj->old_alloc_ud, ptr, osize, nsize);
-}
-/* }}} */
-
-/** {{{ luasandbox_update_memory_accounting
- *
- * Update memory usage statistics for the given memory allocation request.
- * Returns 1 if the allocation should be allowed, 0 if it should fail.
- */
-static inline int luasandbox_update_memory_accounting(php_luasandbox_obj* obj, 
-       size_t osize, size_t nsize) 
-{
-       if (nsize > obj->memory_limit
-               || obj->memory_usage > obj->memory_limit - nsize)
-       {
-               // Memory limit exceeded
-               return 0;
-       }
-
-       if (osize > nsize && obj->memory_usage + nsize < osize) {
-               // Negative memory usage -- do not update
-               return 1;
-       }
-
-       obj->memory_usage += nsize - osize;
-       return 1;
-}
-/* }}} */
-
 /** {{{ luasandbox_panic
  *
  * The Lua panic function. It is necessary to raise an E_ERROR, and thus do a
@@ -828,7 +563,7 @@
                RETURN_FALSE;
        }
 
-       intern->memory_limit = limit;
+       intern->alloc.memory_limit = limit;
 }
 /* }}} */
 
@@ -935,7 +670,7 @@
                RETURN_FALSE;
        }
 
-       RETURN_LONG(sandbox->memory_usage);
+       RETURN_LONG(sandbox->alloc.memory_usage);
 }
 /* }}} */
 
@@ -1034,6 +769,7 @@
 {
        php_error_docref(NULL TSRMLS_CC, E_ERROR, "LuaSandboxFunction cannot be 
constructed directly");
 }
+/* }}} */
 
 /** {{{ proto array LuaSandboxFunction::call(...)
  *
@@ -1216,299 +952,11 @@
 }
 /* }}} */
 
-/** {{{ luasandbox_push_zval
- *
- * Convert a zval to an appropriate Lua type and push the resulting value on to
- * the stack.
- */
-static int luasandbox_push_zval(lua_State * L, zval * z)
-{
-       switch (Z_TYPE_P(z)) {
-               case IS_NULL:
-                       lua_pushnil(L);
-                       break;
-               case IS_LONG:
-                       lua_pushinteger(L, Z_LVAL_P(z));
-                       break;
-               case IS_DOUBLE:
-                       lua_pushnumber(L, Z_DVAL_P(z));
-                       break;
-               case IS_BOOL:
-                       lua_pushboolean(L, Z_BVAL_P(z));
-                       break;
-               case IS_ARRAY:
-                       if (!luasandbox_push_hashtable(L, Z_ARRVAL_P(z))) {
-                               return 0;
-                       }
-                       break;
-               case IS_OBJECT: {
-                       zend_class_entry * objce;
-                       
-                       objce = Z_OBJCE_P(z);
-                       if (instanceof_function(objce, luasandboxfunction_ce 
TSRMLS_CC)) {
-                               php_luasandboxfunction_obj * func_obj;
-                               
-                               func_obj = (php_luasandboxfunction_obj 
*)zend_object_store_get_object(z TSRMLS_CC);
-                               
-                               lua_getfield(L, LUA_REGISTRYINDEX, 
"php_luasandbox_chunks");
-                               lua_rawgeti(L, -1, func_obj->index);
-                               lua_remove(L, -2);
-                               break;
-                       }
-               
-                       if (!luasandbox_push_hashtable(L, Z_OBJPROP_P(z))) {
-                               return 0;
-                       }
-                       break;
-               }
-               case IS_STRING:
-                       lua_pushlstring(L, Z_STRVAL_P(z), Z_STRLEN_P(z));
-                       break;
-               case IS_RESOURCE:
-               case IS_CONSTANT:
-               case IS_CONSTANT_ARRAY:
-               default:
-                       return 0;
-       }
-       return 1;
-}
-/* }}} */
-
-/** {{{ luasandbox_push_zval_userdata
- *
- * Push a full userdata on to the stack, which stores a zval* in its block. 
- * Increment its reference count and set its metatable so that it will be 
freed 
- * at the appropriate time.
- */
-static void luasandbox_push_zval_userdata(lua_State * L, zval * z)
-{
-       zval ** ud;
-       ud = (zval**)lua_newuserdata(L, sizeof(zval*));
-       *ud = z;
-       Z_ADDREF_P(z);
-
-       lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_zval_metatable");
-       lua_setmetatable(L, -2);
-}
-/* }}} */
-
-/** {{{ luasandbox_push_hashtable
- *
- * Helper function for luasandbox_push_zval. Create a new table on the top of 
- * the stack and add the zvals in the HashTable to it. 
- */
-static int luasandbox_push_hashtable(lua_State * L, HashTable * ht)
-{
-       Bucket * p;
-
-       // Recursion requires an arbitrary amount of stack space so we have to 
-       // check the stack.
-       luaL_checkstack(L, 10, "converting PHP array to Lua");
-
-       lua_newtable(L);
-       if (!ht || !ht->nNumOfElements) {
-               return 1;
-       }
-       if (ht->nApplyCount) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion 
detected");
-               return 0;
-       }
-       ht->nApplyCount++;
-       for (p = ht->pListHead; p; p = p->pListNext) {
-               if (p->nKeyLength) {
-                       lua_pushlstring(L, p->arKey, p->nKeyLength - 1);
-               } else {
-                       lua_pushinteger(L, p->h);
-               }
-               
-               if (!luasandbox_push_zval(L, *(zval**)p->pData)) {
-                       // Failed to process that data value
-                       // Pop the key and the half-constructed table
-                       lua_pop(L, 2);
-                       ht->nApplyCount--;
-                       return 0;
-               }
-
-               lua_settable(L, -3);
-       }
-       ht->nApplyCount--;
-       return 1;
-}
-/* }}} */
-
-/** {{{ luasandbox_lua_to_zval
- *
- * Convert a lua value to a zval.
- *
- * If a value is encountered that can't be converted to a zval, a 
LuaPlaceholder
- * object is returned instead.
- *
- * @param z A pointer to the destination zval
- * @param L The lua state
- * @param index The stack index to the input value
- * @param sandbox_zval A zval poiting to a valid LuaSandbox object which will 
be
- *     used for the parent object of any LuaSandboxFunction objects created.
- * @param recursionGuard A hashtable for keeping track of tables that have 
been 
- *     processed, to allow infinite recursion to be avoided. External callers 
- *     should set this to NULL.
- */
-static void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, 
-       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC)
-{
-       switch (lua_type(L, index)) {
-               case LUA_TNIL:
-                       ZVAL_NULL(z);
-                       break;
-               case LUA_TNUMBER: {
-                       long i;
-                       double d, integerPart, fractionalPart;
-                       // Lua only provides a single number type
-                       // Convert it to a PHP integer if that can be done 
without loss
-                       // of precision
-                       d = lua_tonumber(L, index);
-                       fractionalPart = modf(d, &integerPart);
-                       if (fractionalPart == 0.0 && integerPart >= LONG_MIN && 
integerPart <= LONG_MAX) {
-                               // The number is small enough to fit inside an 
int. But has it already
-                               // been truncated by squeezing it into a 
double? This is only relevant
-                               // where the integer size is greater than the 
mantissa size.
-                               i = (long)integerPart;
-                               if (LONG_MAX < (1LL << DBL_MANT_DIG)
-                                       || labs(i) < (1L << DBL_MANT_DIG))
-                               {
-                                       ZVAL_LONG(z, i);
-                               } else {                                
-                                       ZVAL_DOUBLE(z, d);
-                               }
-                       } else {
-                               ZVAL_DOUBLE(z, d);
-                       }
-                       break;
-               }
-               case LUA_TBOOLEAN:
-                       ZVAL_BOOL(z, lua_toboolean(L, index));
-                       break;
-               case LUA_TSTRING: {
-                       const char * str;
-                       size_t length;
-                       str = lua_tolstring(L, index, &length);
-                       ZVAL_STRINGL(z, str, length, 1);
-                       break;
-               }
-               case LUA_TTABLE: {
-                       const void * ptr = lua_topointer(L, index);
-                       void * data = NULL;
-                       int allocated = 0;
-                       if (recursionGuard) {
-                               // Check for circular reference (infinite 
recursion)
-                               if (zend_hash_find(recursionGuard, (char*)&ptr, 
sizeof(void*), &data) == SUCCESS) {
-                                       // Found circular reference!
-                                       object_init_ex(z, 
luasandboxplaceholder_ce);
-                                       break;
-                               }
-                       } else {
-                               ALLOC_HASHTABLE(recursionGuard);
-                               zend_hash_init(recursionGuard, 1, NULL, NULL, 
0);
-                               allocated = 1;
-                       }
-
-                       // Add the current table to the recursion guard 
hashtable
-                       // Use the pointer as the key, zero-length data
-                       zend_hash_update(recursionGuard, (char*)&ptr, 
sizeof(void*), "", 1, NULL);
-
-                       // Process the array
-                       array_init(z);
-                       luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, 
sandbox_zval, recursionGuard TSRMLS_CC);
-
-                       if (allocated) {
-                               zend_hash_destroy(recursionGuard);
-                               FREE_HASHTABLE(recursionGuard);
-                       }
-                       break;
-               }
-               case LUA_TFUNCTION: {
-                       int func_index;
-                       php_luasandboxfunction_obj * func_obj;
-                       php_luasandbox_obj * sandbox = (php_luasandbox_obj*)
-                               zend_object_store_get_object(sandbox_zval);
-
-                       // Normalise the input index so that we can push 
without invalidating it.
-                       if (index < 0) {
-                               index += lua_gettop(L) + 1;
-                       }
-                       
-                       // Get the chunks table
-                       lua_getfield(L, LUA_REGISTRYINDEX, 
"php_luasandbox_chunks");
-
-                       // Get the next free index
-                       if (sandbox->function_index >= INT_MAX) {
-                               ZVAL_NULL(z);
-                               lua_pop(L, 1);
-                               break;
-                       }
-                       func_index = ++(sandbox->function_index);
-
-                       // Store it in the chunks table
-                       lua_pushvalue(L, index);
-                       lua_rawseti(L, -2, func_index);
-
-                       // Create a LuaSandboxFunction object to hold a 
reference to the function
-                       object_init_ex(z, luasandboxfunction_ce);
-                       func_obj = 
(php_luasandboxfunction_obj*)zend_object_store_get_object(z);
-                       func_obj->index = func_index;
-                       func_obj->sandbox = sandbox_zval;
-                       Z_ADDREF_P(sandbox_zval);
-
-                       // Balance the stack
-                       lua_pop(L, 1);
-                       break;
-               }
-               case LUA_TUSERDATA:
-               case LUA_TTHREAD:
-               case LUA_TLIGHTUSERDATA:
-               default:
-                       // TODO: provide derived classes for each type
-                       object_init_ex(z, luasandboxplaceholder_ce);
-       }
-}
-/* }}} */
-
-/** {{{ luasandbox_lua_to_array
- *
- * Append the elements of the table in the specified index to the given 
HashTable.
- */
-static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
-       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC)
-{
-       const char * str;
-       size_t length;
-       zval *value;
-
-       // Normalise the input index so that we can push without invalidating 
it.
-       if (index < 0) {
-               index += lua_gettop(L) + 1;
-       }
-
-       lua_pushnil(L);
-       while (lua_next(L, index) != 0) {
-               MAKE_STD_ZVAL(value);
-               luasandbox_lua_to_zval(value, L, -1, sandbox_zval, 
recursionGuard TSRMLS_CC);
-               
-               // Make a copy of the key so that we can call lua_tolstring() 
which is destructive
-               lua_pushvalue(L, -2);
-               str = lua_tolstring(L, -1, &length);
-               zend_hash_update(ht, str, length + 1, (void*)&value, 
sizeof(zval*), NULL);
-
-               // Delete the copy and the value
-               lua_pop(L, 2);
-       }
-}
-/* }}} */
-
 /** {{{ luasandbox_get_php_obj
  *
  * Get the object data for a lua state.
  */
-static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L)
+php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L)
 {
        php_luasandbox_obj * obj;
        lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj");
@@ -1718,84 +1166,6 @@
        return 0;
 }
 /* }}} */
-
-/** {{{ luasandbox_base_tostring
- *
- * This is identical to luaB_tostring except that it does not call 
lua_topointer().
- */
-static int luasandbox_base_tostring(lua_State * L)
-{
-       luaL_checkany(L, 1);
-       if (luaL_callmeta(L, 1, "__tostring"))  /* is there a metafield? */
-               return 1;  /* use its value */
-       switch (lua_type(L, 1)) {
-               case LUA_TNUMBER:
-                       lua_pushstring(L, lua_tostring(L, 1));
-                       break;
-               case LUA_TSTRING:
-                       lua_pushvalue(L, 1);
-                       break;
-               case LUA_TBOOLEAN:
-                       lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : 
"false"));
-                       break;
-               case LUA_TNIL:
-                       lua_pushliteral(L, "nil");
-                       break;
-               default:
-                       lua_pushfstring(L, "%s", luaL_typename(L, 1));
-                       break;
-       }
-       return 1;
-}
-/* }}} */
-
-/** {{{ luasandbox_math_random
- *
- * A math.random implementation that doesn't share state with PHP's rand()
- */
-static int luasandbox_math_random(lua_State * L)
-{
-       php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L);
-
-       int i = rand_r(&sandbox->random_seed);
-       if (i >= RAND_MAX) {
-               i -= RAND_MAX;
-       }
-       lua_Number r = (lua_Number)i / (lua_Number)RAND_MAX;
-       switch (lua_gettop(L)) {  /* check number of arguments */
-               case 0: {  /* no arguments */
-                       lua_pushnumber(L, r);  /* Number between 0 and 1 */
-                       break;
-               }
-               case 1: {  /* only upper limit */
-                       int u = luaL_checkint(L, 1);
-                       luaL_argcheck(L, 1<=u, 1, "interval is empty");
-                       lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and 
`u' */
-                       break;
-               }
-               case 2: {  /* lower and upper limits */
-                       int l = luaL_checkint(L, 1);
-                       int u = luaL_checkint(L, 2);
-                       luaL_argcheck(L, l<=u, 2, "interval is empty");
-                       lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between 
`l' and `u' */
-                       break;
-               }
-               default: return luaL_error(L, "wrong number of arguments");
-       }
-       return 1;
-}
-/* }}} */
-
-/** {{{ luasandbox_math_randomseed
- *
- * Set the seed for the custom math.random.
- */
-static int luasandbox_math_randomseed(lua_State * L)
-{
-       php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L);
-       sandbox->random_seed = (unsigned int)luaL_checkint(L, 1);
-}
-/* }}} */
 /*
  * Local variables:
  * tab-width: 4

Modified: trunk/php/luasandbox/php_luasandbox.h
===================================================================
--- trunk/php/luasandbox/php_luasandbox.h       2012-03-15 23:25:39 UTC (rev 
113976)
+++ trunk/php/luasandbox/php_luasandbox.h       2012-03-15 23:34:18 UTC (rev 
113977)
@@ -3,7 +3,24 @@
 #define PHP_LUASANDBOX_H
 
 #include <lua.h>
+#include <signal.h>
 
+/* alloc.c */
+
+typedef struct {
+       lua_Alloc old_alloc;
+       void * old_alloc_ud;
+       size_t memory_limit;
+       size_t memory_usage;
+} php_luasandbox_alloc;
+
+struct _php_luasandbox_obj;
+
+lua_State * luasandbox_alloc_new_state(php_luasandbox_alloc * alloc, struct 
_php_luasandbox_obj * sandbox);
+void luasandbox_alloc_delete_state(php_luasandbox_alloc * alloc, lua_State * 
L);
+
+/* luasandbox.c */
+
 extern zend_module_entry luasandbox_module_entry;
 extern char luasandbox_timeout_message[];
 
@@ -53,10 +70,7 @@
 struct _php_luasandbox_obj {
        zend_object std;
        lua_State * state;
-       size_t memory_limit;
-       size_t memory_usage;
-       lua_Alloc old_alloc;
-       void * old_alloc_ud;
+       php_luasandbox_alloc alloc;
        int in_php;
        int in_lua;
        zval * current_zval; /* The zval for the LuaSandbox which is currently 
executing Lua code */
@@ -77,5 +91,53 @@
 };
 typedef struct _php_luasandboxfunction_obj php_luasandboxfunction_obj;
 
+
+php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L);
+
+/** {{{ luasandbox_enter_php
+ *
+ * This function must be called each time a C function is entered from Lua
+ * and the PHP state needs to be accessed in any way. Before exiting the 
+ * function, luasandbox_leave_php() must be called.
+ *
+ * This sets a flag which indicates to the timeout signal handler that it is 
+ * unsafe to call longjmp() to return control to PHP. If the flag is not 
+ * correctly set, memory may be corrupted and security compromised.
+ */
+inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern)
+{
+       intern->in_php ++;
+       if (intern->timed_out) {
+               intern->in_php --;
+               luaL_error(L, luasandbox_timeout_message);
+       }
+}
+/* }}} */
+
+/** {{{ luasandbox_leave_php
+ *
+ * This function must be called after luasandbox_enter_php, before the 
callback 
+ * from Lua returns.
+ */
+inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern)
+{
+       intern->in_php --;
+}
+/* }}} */
+
+/* library.c */
+
+void luasandbox_lib_register(lua_State * L TSRMLS_DC);
+void luasandbox_lib_shutdown(TSRMLS_D);
+
+/* data_conversion.c */
+
+void luasandbox_data_conversion_init(lua_State * L);
+
+int luasandbox_push_zval(lua_State * L, zval * z);
+void luasandbox_push_zval_userdata(lua_State * L, zval * z);
+void luasandbox_lua_to_zval(zval * z, lua_State * L, int index, 
+       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
+
 #endif /* PHP_LUASANDBOX_H */
 


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to