r94402 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r94401‎ | r94402 | r94403 >
Date:10:11, 13 August 2011
Author:tstarling
Status:deferred
Tags:
Comment:
Extension for running untrusted Lua scripts inside PHP. Work in progress.
Modified paths:
  • /trunk/php/luasandbox (added) (history)
  • /trunk/php/luasandbox/.svnignore (added) (history)
  • /trunk/php/luasandbox/CREDITS (added) (history)
  • /trunk/php/luasandbox/EXPERIMENTAL (added) (history)
  • /trunk/php/luasandbox/config.m4 (added) (history)
  • /trunk/php/luasandbox/luasandbox.c (added) (history)
  • /trunk/php/luasandbox/luasandbox.php (added) (history)
  • /trunk/php/luasandbox/php_luasandbox.h (added) (history)
  • /trunk/php/luasandbox/tests (added) (history)

Diff [purge]

Index: trunk/php/luasandbox/luasandbox.c
@@ -0,0 +1,756 @@
 2+
 3+/*
 4+ * To do:
 5+ * * Move sandbox.scripts to the registry, and don't provide access to it to
 6+ * user scripts, to avoid the potential for a panic in
 7+ * LuaSandbox::loadString() if sandbox.scripts is overwritten.
 8+ *
 9+ * * Remove LuaSandbox::callScript(). Instead, have LuaSandbox::loadString()
 10+ * return a LuaSandboxFunction object with a call() method. Store the
 11+ * script functions in a table indexed by integer, instead of using the
 12+ * chunk name, so that nothing strange will happen if there are duplicate
 13+ * chunk names. Delete the function from the Lua state when the object is
 14+ * destroyed.
 15+ *
 16+ * * Provide a LuaSandbox::doString() as a convenience wrapper for
 17+ * LuaSandbox::loadString()->call(), analogous to luaL_dostring().
 18+ *
 19+ * * Add PHP methods for registering callback functions, both individually
 20+ * and in bulk (a library creation function).
 21+ *
 22+ * * Provide a wrapper around the base library, which allows only a whitelist
 23+ * of functions, not including e.g. loadfile(). This is slightly tricky
 24+ * because the functions are not exported from the shared library. It's
 25+ * necessary to call luaopen_base() on a separate lua_State, and then to
 26+ * copy each function pointer to the destination lua_State using
 27+ * lua_tocfunction().
 28+ *
 29+ * * Add CPU time limits.
 30+ * * Add LuaSandbox::getMemoryUsage().
 31+ * * Fix memory leak probably in
 32+ */
 33+
 34+#ifdef HAVE_CONFIG_H
 35+#include "config.h"
 36+#endif
 37+
 38+#include <lua.h>
 39+#include <lauxlib.h>
 40+#include <math.h>
 41+#include <limits.h>
 42+#include <float.h>
 43+
 44+#include "php.h"
 45+#include "php_ini.h"
 46+#include "ext/standard/info.h"
 47+#include "php_luasandbox.h"
 48+#include "zend_exceptions.h"
 49+
 50+static zend_object_value luasandbox_new(zend_class_entry *ce TSRMLS_DC);
 51+static void luasandbox_free_storage(void *object TSRMLS_DC);
 52+static void *luasandbox_alloc(void *ud, void *ptr, size_t osize, size_t nsize);
 53+static int luasandbox_panic(lua_State * L);
 54+static lua_State * luasandbox_newstate();
 55+static lua_State * getLuaState(zval * this_ptr);
 56+static int luasandbox_find_field(lua_State * L, int index,
 57+ char * spec, int specLength);
 58+static void luasandbox_call_helper(INTERNAL_FUNCTION_PARAMETERS, int index);
 59+static int luasandbox_push_zval(lua_State * L, zval * z);
 60+static zval * luasandbox_lua_to_zval(lua_State * L, int index, HashTable * recursionGuard);
 61+static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
 62+ HashTable * recursionGuard);
 63+static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L);
 64+static int luasandbox_method_getMemoryUsage(lua_State * L);
 65+static void luasandbox_handle_error(lua_State * L, int status);
 66+static int luasandbox_push_hashtable(lua_State * L, HashTable * ht);
 67+
 68+zend_class_entry *luasandbox_ce;
 69+zend_class_entry *luasandboxerror_ce;
 70+zend_class_entry *luasandboxplaceholder_ce;
 71+
 72+/** {{{ arginfo */
 73+ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_loadString, 0)
 74+ ZEND_ARG_INFO(0, code)
 75+ ZEND_ARG_INFO(0, chunkName)
 76+ZEND_END_ARG_INFO()
 77+
 78+ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_setMemoryLimit, 0)
 79+ ZEND_ARG_INFO(0, limit)
 80+ZEND_END_ARG_INFO()
 81+
 82+ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_callFunction, 0)
 83+ ZEND_ARG_INFO(0, name)
 84+ ZEND_ARG_INFO(0, ...)
 85+ZEND_END_ARG_INFO()
 86+
 87+ZEND_BEGIN_ARG_INFO(arginfo_luasandbox_callScript, 0)
 88+ ZEND_ARG_INFO(0, name)
 89+ ZEND_ARG_INFO(0, ...)
 90+ZEND_END_ARG_INFO()
 91+/* }}} */
 92+
 93+/** {{{ function entries */
 94+const zend_function_entry luasandbox_functions[] = {
 95+ {NULL, NULL, NULL} /* Must be the last line in luasandbox_functions[] */
 96+};
 97+
 98+const zend_function_entry luasandbox_methods[] = {
 99+ PHP_ME(LuaSandbox, loadString, arginfo_luasandbox_loadString, 0)
 100+ PHP_ME(LuaSandbox, setMemoryLimit, arginfo_luasandbox_setMemoryLimit, 0)
 101+ PHP_ME(LuaSandbox, callFunction, arginfo_luasandbox_callFunction, 0)
 102+ PHP_ME(LuaSandbox, callScript, arginfo_luasandbox_callScript, 0)
 103+ {NULL, NULL, NULL}
 104+};
 105+
 106+const zend_function_entry luasandbox_empty_methods[] = {
 107+ {NULL, NULL, NULL}
 108+};
 109+
 110+/** sandbox method names and their C functions */
 111+static const luaL_Reg luasandbox_lua_methods[] = {
 112+ {"getMemoryUsage", luasandbox_method_getMemoryUsage},
 113+ {NULL, NULL}
 114+};
 115+
 116+/* }}} */
 117+
 118+/* {{{ luasandbox_module_entry
 119+ */
 120+zend_module_entry luasandbox_module_entry = {
 121+#if ZEND_MODULE_API_NO >= 20010901
 122+ STANDARD_MODULE_HEADER,
 123+#endif
 124+ "luasandbox",
 125+ luasandbox_functions,
 126+ PHP_MINIT(luasandbox),
 127+ PHP_MSHUTDOWN(luasandbox),
 128+ NULL, /* RINIT */
 129+ NULL, /* RSHUTDOWN */
 130+ PHP_MINFO(luasandbox),
 131+#if ZEND_MODULE_API_NO >= 20010901
 132+ "0.1",
 133+#endif
 134+ STANDARD_MODULE_PROPERTIES
 135+};
 136+/* }}} */
 137+
 138+#ifdef COMPILE_DL_LUASANDBOX
 139+ZEND_GET_MODULE(luasandbox)
 140+#endif
 141+
 142+/** {{{ luasandbox_new */
 143+static zend_object_value luasandbox_new(zend_class_entry *ce TSRMLS_DC)
 144+{
 145+ php_luasandbox_obj * intern;
 146+ zend_object_value retval;
 147+
 148+ // Create the internal object
 149+ intern = emalloc(sizeof(php_luasandbox_obj));
 150+ memset(intern, 0, sizeof(php_luasandbox_obj));
 151+ zend_object_std_init(&intern->std, ce TSRMLS_CC);
 152+
 153+ // Initialise the Lua state
 154+ intern->state = luasandbox_newstate(intern);
 155+
 156+ // Put the object into the store
 157+ retval.handle = zend_objects_store_put(
 158+ intern,
 159+ (zend_objects_store_dtor_t)zend_objects_destroy_object,
 160+ (zend_objects_free_object_storage_t)luasandbox_free_storage,
 161+ NULL TSRMLS_CC);
 162+ retval.handlers = zend_get_std_object_handlers();
 163+ return retval;
 164+}
 165+/* }}} */
 166+
 167+/** {{{ luasandbox_newstate */
 168+static lua_State * luasandbox_newstate(php_luasandbox_obj * intern)
 169+{
 170+ lua_State * L;
 171+
 172+ L = lua_newstate(luasandbox_alloc, intern);
 173+ lua_atpanic(L, luasandbox_panic);
 174+
 175+ // Load some relatively safe standard libraries
 176+ luaopen_string(L);
 177+ luaopen_table(L);
 178+ luaopen_math(L);
 179+
 180+ // Create the "sandbox" global
 181+ lua_newtable(L);
 182+ // Add its methods
 183+ luaL_register(L, NULL, luasandbox_lua_methods);
 184+
 185+ // Add its properties
 186+ lua_newtable(L);
 187+ lua_setfield(L, -2, "scripts");
 188+
 189+ // Move the new table from the stack to the sandbox global
 190+ lua_setglobal(L, "sandbox");
 191+
 192+ // Register a pointer to the PHP object so that C functions can find it
 193+ lua_pushlightuserdata(L, (void*)intern);
 194+ lua_setfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj");
 195+
 196+ return L;
 197+}
 198+/* }}} */
 199+
 200+/** {{{ luasandbox_free_storage */
 201+static void luasandbox_free_storage(void *object TSRMLS_DC)
 202+{
 203+ php_luasandbox_obj * intern = (php_luasandbox_obj*)object;
 204+ lua_close(intern->state);
 205+ intern->state = NULL;
 206+ zend_object_std_dtor(&intern->std);
 207+ efree(object);
 208+}
 209+/* }}} */
 210+
 211+/* {{{ PHP_MINIT_FUNCTION
 212+ */
 213+PHP_MINIT_FUNCTION(luasandbox)
 214+{
 215+ /* If you have INI entries, uncomment these lines
 216+ REGISTER_INI_ENTRIES();
 217+ */
 218+
 219+ zend_class_entry ce;
 220+ INIT_CLASS_ENTRY(ce, "LuaSandbox", luasandbox_methods);
 221+ luasandbox_ce = zend_register_internal_class(&ce TSRMLS_CC);
 222+ luasandbox_ce->create_object = luasandbox_new;
 223+
 224+ INIT_CLASS_ENTRY(ce, "LuaSandboxError", luasandbox_empty_methods);
 225+ luasandboxerror_ce = zend_register_internal_class_ex(
 226+ &ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
 227+
 228+ zend_declare_class_constant_long(luasandboxerror_ce,
 229+ "RUN", sizeof("RUN"), LUA_ERRRUN);
 230+ zend_declare_class_constant_long(luasandboxerror_ce,
 231+ "SYNTAX", sizeof("SYNTAX"), LUA_ERRSYNTAX);
 232+ zend_declare_class_constant_long(luasandboxerror_ce,
 233+ "MEM", sizeof("MEM"), LUA_ERRMEM);
 234+ zend_declare_class_constant_long(luasandboxerror_ce,
 235+ "ERR", sizeof("ERR"), LUA_ERRERR);
 236+
 237+ INIT_CLASS_ENTRY(ce, "LuaSandboxPlaceholder", luasandbox_empty_methods);
 238+ luasandboxplaceholder_ce = zend_register_internal_class(&ce TSRMLS_CC);
 239+ return SUCCESS;
 240+}
 241+/* }}} */
 242+
 243+/* {{{ PHP_MSHUTDOWN_FUNCTION
 244+ */
 245+PHP_MSHUTDOWN_FUNCTION(luasandbox)
 246+{
 247+ return SUCCESS;
 248+}
 249+/* }}} */
 250+
 251+/* {{{ PHP_MINFO_FUNCTION
 252+ */
 253+PHP_MINFO_FUNCTION(luasandbox)
 254+{
 255+ php_info_print_table_start();
 256+ php_info_print_table_header(2, "luasandbox support", "enabled");
 257+ php_info_print_table_end();
 258+}
 259+/* }}} */
 260+
 261+/** {{{ luasandbox_alloc */
 262+static void *luasandbox_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
 263+{
 264+ // Update memory usage accounting
 265+ php_luasandbox_obj * obj = (php_luasandbox_obj*)ud;
 266+ if (obj->memory_usage + nsize < obj->memory_usage) {
 267+ // Overflow
 268+ // No PHP error because it's plausible that untrusted scripts could do this.
 269+ return NULL;
 270+ }
 271+
 272+ obj->memory_usage += nsize - osize;
 273+
 274+ if (nsize == 0) {
 275+ if (ptr) {
 276+ efree(ptr);
 277+ }
 278+ return NULL;
 279+ } else if (osize == 0) {
 280+ return emalloc(nsize);
 281+ } else {
 282+ return erealloc(ptr, nsize);
 283+ }
 284+}
 285+/* }}} */
 286+
 287+/** {{{ luasandbox_panic */
 288+static int luasandbox_panic(lua_State * L)
 289+{
 290+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
 291+ "PANIC: unprotected error in call to Lua API (%s)",
 292+ lua_tostring(L, -1));
 293+ return 0;
 294+}
 295+/* }}} */
 296+
 297+/** {{{ getLuaState */
 298+static lua_State * getLuaState(zval * this_ptr)
 299+{
 300+ php_luasandbox_obj * intern = (php_luasandbox_obj*)
 301+ zend_object_store_get_object(this_ptr TSRMLS_CC);
 302+ return intern->state;
 303+}
 304+/* }}} */
 305+
 306+/** {{{ proto int LuaSandbox::loadString(string code, string chunkName) */
 307+PHP_METHOD(LuaSandbox, loadString)
 308+{
 309+ char *code, *chunkName;
 310+ int codeLength, chunkNameLength;
 311+ int status;
 312+ lua_State * L = getLuaState(getThis());
 313+
 314+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
 315+ &code, &codeLength, &chunkName, &chunkNameLength) == FAILURE) {
 316+ RETURN_FALSE;
 317+ }
 318+
 319+ // Check chunkName for nulls
 320+ if (strlen(chunkName) != chunkNameLength) {
 321+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 322+ "chunk name may not contain null characters");
 323+ RETURN_FALSE;
 324+ }
 325+
 326+ // Find sandbox.scripts
 327+ lua_getglobal(L, "sandbox");
 328+ lua_getfield(L, -1, "scripts");
 329+
 330+ // Parse the string into a function on the stack
 331+ status = luaL_loadbuffer(L, code, codeLength, chunkName);
 332+ if (status != 0) {
 333+ luasandbox_handle_error(L, status);
 334+ return;
 335+ }
 336+
 337+ // Store the resulting function as a member of sandbox.scripts
 338+ lua_setfield(L, -2, chunkName);
 339+
 340+ // Balance the stack
 341+ lua_pop(L, 2);
 342+ RETURN_TRUE;
 343+}
 344+
 345+/* }}} */
 346+
 347+/** {{{ luasandbox_handle_error
 348+ *
 349+ * Handles the error return situation from lua_pcall() and lua_load(), where a
 350+ * status is returned and an error message pushed to the stack. Throws a suitable
 351+ * exception.
 352+ */
 353+static void luasandbox_handle_error(lua_State * L, int status)
 354+{
 355+ const char * errorMsg = lua_tostring(L, -1);
 356+ lua_pop(L, 1);
 357+ zend_throw_exception(luasandboxerror_ce, (char*)errorMsg, status);
 358+}
 359+/* }}} */
 360+
 361+/** {{{ proto void LuaSandbox::setMemoryLimit(int limit) */
 362+PHP_METHOD(LuaSandbox, setMemoryLimit)
 363+{
 364+ long limit;
 365+ php_luasandbox_obj * intern = (php_luasandbox_obj*)
 366+ zend_object_store_get_object(getThis() TSRMLS_CC);
 367+
 368+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
 369+ &limit) == FAILURE)
 370+ {
 371+ RETURN_FALSE;
 372+ }
 373+
 374+ intern->memory_limit = limit;
 375+}
 376+/* }}} */
 377+
 378+/*** {{{ proto array LuaSandbox::callFunction(string name, ...)
 379+ */
 380+PHP_METHOD(LuaSandbox, callFunction)
 381+{
 382+ luasandbox_call_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, LUA_GLOBALSINDEX);
 383+}
 384+/* }}} */
 385+
 386+/*** {{{ proto array LuaSandbox::callScript(string name, ...)
 387+ */
 388+PHP_METHOD(LuaSandbox, callScript)
 389+{
 390+ lua_State * L = getLuaState(getThis());
 391+ lua_getglobal(L, "sandbox");
 392+ lua_getfield(L, -1, "scripts");
 393+ luasandbox_call_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1);
 394+ lua_pop(L, 2);
 395+}
 396+/** }}} */
 397+
 398+/** {{{ luasandbox_call_helper
 399+ * Call a field or subfield of the table at the given index. Set return_value
 400+ * to an array containing all the results.
 401+ */
 402+static void luasandbox_call_helper(INTERNAL_FUNCTION_PARAMETERS, int index)
 403+{
 404+ char *name;
 405+ int nameLength = 0, status, origTop;
 406+ zend_uint numArgs = 0, numResults, i;
 407+ zval *** args = NULL;
 408+ lua_State * L = getLuaState(getThis());
 409+
 410+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s*",
 411+ &name, &nameLength, &args, &numArgs) == FAILURE)
 412+ {
 413+ RETURN_FALSE;
 414+ }
 415+
 416+ // Save the top position
 417+ origTop = lua_gettop(L);
 418+
 419+ // Find the function
 420+ if (!luasandbox_find_field(L, index, name, nameLength)) {
 421+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 422+ "The specified lua function does not exist");
 423+ RETVAL_FALSE;
 424+ goto cleanup;
 425+ }
 426+
 427+ // Push the arguments
 428+ for (i = 0; i < numArgs; i++) {
 429+ if (!luasandbox_push_zval(L, *(args[i]))) {
 430+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
 431+ "Unable to convert argument %d to a lua value", i + 1);
 432+ RETVAL_FALSE;
 433+ goto cleanup;
 434+ }
 435+ }
 436+
 437+ // Call it
 438+ status = lua_pcall(L, numArgs, LUA_MULTRET, 0);
 439+ if (status) {
 440+ luasandbox_handle_error(L, status);
 441+ goto cleanup;
 442+ }
 443+
 444+ // Calculate the number of results and create an array of that capacity
 445+ numResults = lua_gettop(L) - origTop;
 446+ array_init_size(return_value, numResults);
 447+
 448+ // Fill the array with the results
 449+ for (i = 0; i < numResults; i++) {
 450+ zval * element = luasandbox_lua_to_zval(L, origTop + i + 1, NULL);
 451+ zend_hash_next_index_insert(Z_ARRVAL_P(return_value),
 452+ (void*)&element,
 453+ sizeof(zval*), NULL);
 454+ }
 455+
 456+cleanup:
 457+
 458+ // Balance the stack
 459+ lua_pop(L, lua_gettop(L) - origTop);
 460+ // Delete varargs
 461+ if (numArgs) {
 462+ efree(args);
 463+ }
 464+}
 465+/* }}} */
 466+
 467+/** {{{ luasandbox_find_field
 468+ * Given a string in the format "a.b.c.d" find the relevant variable in the
 469+ * table at the given stack position. If it is found, 1 is returned
 470+ * and the variable will be pushed to the stack. If not, 0 is returned
 471+ * and the stack will be in the original state.
 472+ */
 473+static int luasandbox_find_field(lua_State * L, int index,
 474+ char * spec, int specLength)
 475+{
 476+ int i;
 477+ int tokenStart = 0;
 478+ const int top = lua_gettop(L);
 479+
 480+ // Put a copy of the input table into top+1, this will be the index for
 481+ // the parent table in the loop.
 482+ lua_pushvalue(L, index);
 483+
 484+ spec = estrndup(spec, specLength);
 485+
 486+ for (i = 0; i <= specLength; i++) {
 487+ if (i == specLength || spec[i] == '.') {
 488+ // Put the next item into top+2
 489+ lua_pushlstring(L, spec + tokenStart, i - tokenStart);
 490+ lua_gettable(L, top + 1);
 491+
 492+ // Not found?
 493+ if (lua_isnil(L, top + 2)) {
 494+ // Remove the two items we put on the stack and return
 495+ lua_pop(L, 2);
 496+ efree(spec);
 497+ return 0;
 498+ }
 499+
 500+ // Record this position
 501+ tokenStart = i + 1;
 502+
 503+ // Shift the new item down to top+1
 504+ lua_remove(L, top+1);
 505+ }
 506+ }
 507+
 508+ efree(spec);
 509+ return 1;
 510+}
 511+/* }}} */
 512+
 513+/** {{{ luasandbox_push_zval */
 514+static int luasandbox_push_zval(lua_State * L, zval * z)
 515+{
 516+ switch (Z_TYPE_P(z)) {
 517+ case IS_NULL:
 518+ lua_pushnil(L);
 519+ break;
 520+ case IS_LONG:
 521+ lua_pushinteger(L, Z_LVAL_P(z));
 522+ break;
 523+ case IS_DOUBLE:
 524+ lua_pushnumber(L, Z_DVAL_P(z));
 525+ break;
 526+ case IS_BOOL:
 527+ lua_pushboolean(L, Z_BVAL_P(z));
 528+ break;
 529+ case IS_ARRAY:
 530+ if (!luasandbox_push_hashtable(L, Z_ARRVAL_P(z))) {
 531+ return 0;
 532+ }
 533+ break;
 534+ case IS_OBJECT:
 535+ if (!luasandbox_push_hashtable(L, Z_OBJPROP_P(z))) {
 536+ return 0;
 537+ }
 538+ break;
 539+ case IS_STRING:
 540+ lua_pushlstring(L, Z_STRVAL_P(z), Z_STRLEN_P(z));
 541+ break;
 542+ case IS_RESOURCE:
 543+ case IS_CONSTANT:
 544+ case IS_CONSTANT_ARRAY:
 545+ default:
 546+ return 0;
 547+ }
 548+ return 1;
 549+}
 550+/* }}} */
 551+
 552+/** {{{ luasandbox_push_hashtable */
 553+static int luasandbox_push_hashtable(lua_State * L, HashTable * ht)
 554+{
 555+ Bucket * p;
 556+
 557+ // Recursion requires an arbitrary amount of stack space so we have to
 558+ // check the stack.
 559+ luaL_checkstack(L, 10, "converting PHP array to Lua");
 560+
 561+ lua_newtable(L);
 562+ if (!ht || !ht->nNumOfElements) {
 563+ return 1;
 564+ }
 565+ if (ht->nApplyCount) {
 566+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
 567+ return 0;
 568+ }
 569+ ht->nApplyCount++;
 570+ for (p = ht->pListHead; p; p = p->pListNext) {
 571+ if (p->nKeyLength) {
 572+ lua_pushlstring(L, p->arKey, p->nKeyLength);
 573+ } else {
 574+ lua_pushinteger(L, p->h);
 575+ }
 576+
 577+ if (!luasandbox_push_zval(L, *(zval**)p->pData)) {
 578+ // Failed to process that data value
 579+ // Pop the key and the half-constructed table
 580+ lua_pop(L, 2);
 581+ ht->nApplyCount--;
 582+ return 0;
 583+ }
 584+
 585+ lua_settable(L, -3);
 586+ }
 587+ ht->nApplyCount--;
 588+ return 1;
 589+}
 590+/* }}} */
 591+
 592+/** {{{ luasandbox_lua_to_zval
 593+ *
 594+ * Convert a lua value to a zval. Allocates the zval on the heap and returns
 595+ * a pointer to it.
 596+ *
 597+ * If a value is encountered that can't be converted to a zval, a LuaPlaceholder
 598+ * object is returned instead.
 599+ *
 600+ * @param L The lua state
 601+ * @param index The stack index to the input value
 602+ * @param recursionGuard A hashtable for keeping track of tables that have been
 603+ * processed, to allow infinite recursion to be avoided. External callers
 604+ * should set this to NULL.
 605+ */
 606+static zval * luasandbox_lua_to_zval(lua_State * L, int index, HashTable * recursionGuard)
 607+{
 608+ zval * z;
 609+ MAKE_STD_ZVAL(z);
 610+
 611+ switch (lua_type(L, index)) {
 612+ case LUA_TNIL:
 613+ ZVAL_NULL(z);
 614+ break;
 615+ case LUA_TNUMBER: {
 616+ long i;
 617+ double d, integerPart, fractionalPart;
 618+ // Lua only provides a single number type
 619+ // Convert it to a PHP integer if that can be done without loss
 620+ // of precision
 621+ d = lua_tonumber(L, index);
 622+ fractionalPart = modf(d, &integerPart);
 623+ if (fractionalPart == 0.0 && integerPart >= LONG_MIN && integerPart <= LONG_MAX) {
 624+ // The number is small enough to fit inside an int. But has it already
 625+ // been truncated by squeezing it into a double? This is only relevant
 626+ // where the integer size is greater than the mantissa size.
 627+ i = (long)integerPart;
 628+ if (LONG_MAX < (1LL << DBL_MANT_DIG)
 629+ || labs(i) < (1L << DBL_MANT_DIG))
 630+ {
 631+ ZVAL_LONG(z, i);
 632+ } else {
 633+ ZVAL_DOUBLE(z, d);
 634+ }
 635+ } else {
 636+ ZVAL_DOUBLE(z, d);
 637+ }
 638+ break;
 639+ }
 640+ case LUA_TBOOLEAN:
 641+ ZVAL_BOOL(z, lua_toboolean(L, index));
 642+ break;
 643+ case LUA_TSTRING: {
 644+ const char * str;
 645+ size_t length;
 646+ str = lua_tolstring(L, index, &length);
 647+ ZVAL_STRINGL(z, str, length, 1);
 648+ break;
 649+ }
 650+ case LUA_TTABLE: {
 651+ const void * ptr = lua_topointer(L, index);
 652+ void * data = NULL;
 653+ int allocated = 0;
 654+ if (recursionGuard) {
 655+ // Check for circular reference (infinite recursion)
 656+ if (zend_hash_find(recursionGuard, (char*)&ptr, sizeof(void*), &data) == SUCCESS) {
 657+ // Found circular reference!
 658+ object_init_ex(z, luasandboxplaceholder_ce);
 659+ break;
 660+ }
 661+ } else {
 662+ ALLOC_HASHTABLE(recursionGuard);
 663+ zend_hash_init(recursionGuard, 1, NULL, NULL, 0);
 664+ allocated = 1;
 665+ }
 666+
 667+ // Add the current table to the recursion guard hashtable
 668+ // Use the pointer as the key, zero-length data
 669+ zend_hash_update(recursionGuard, (char*)&ptr, sizeof(void*), &data, 0, NULL);
 670+
 671+ // Process the array
 672+ array_init(z);
 673+ luasandbox_lua_to_array(Z_ARRVAL_P(z), L, index, recursionGuard);
 674+
 675+ if (allocated) {
 676+ zend_hash_destroy(recursionGuard);
 677+ FREE_HASHTABLE(recursionGuard);
 678+ }
 679+ break;
 680+ }
 681+ case LUA_TFUNCTION:
 682+ case LUA_TUSERDATA:
 683+ case LUA_TTHREAD:
 684+ case LUA_TLIGHTUSERDATA:
 685+ default:
 686+ // TODO: provide derived classes for each type
 687+ object_init_ex(z, luasandboxplaceholder_ce);
 688+ }
 689+ return z;
 690+}
 691+/* }}} */
 692+
 693+/** {{{ luasandbox_lua_to_array
 694+ * Append the elements of the table in the specified index to the given HashTable.
 695+ */
 696+static void luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
 697+ HashTable * recursionGuard)
 698+{
 699+ const char * str;
 700+ size_t length;
 701+ zval *value;
 702+
 703+ // Normalise the input index so that we can push without invalidating it.
 704+ if (index < 0) {
 705+ index += lua_gettop(L) + 1;
 706+ }
 707+
 708+ lua_pushnil(L);
 709+ while (lua_next(L, index) != 0) {
 710+ value = luasandbox_lua_to_zval(L, -1, recursionGuard);
 711+
 712+ // Make a copy of the key so that we can call lua_tolstring() which is destructive
 713+ lua_pushvalue(L, -2);
 714+ str = lua_tolstring(L, -1, &length);
 715+ zend_hash_update(ht, str, length + 1, (void*)&value, sizeof(zval*), NULL);
 716+
 717+ // Delete the copy and the value
 718+ lua_pop(L, 2);
 719+ }
 720+}
 721+/* }}} */
 722+
 723+/** {{{ luasandbox_get_php_obj
 724+ * Get the object data for a lua state.
 725+ */
 726+static php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L)
 727+{
 728+ php_luasandbox_obj * obj;
 729+ lua_getfield(L, LUA_REGISTRYINDEX, "php_luasandbox_obj");
 730+ obj = (php_luasandbox_obj*)lua_touserdata(L, -1);
 731+ assert(obj != NULL);
 732+ lua_pop(L, 1);
 733+ return obj;
 734+}
 735+/* }}} */
 736+
 737+/** {{{ luasandbox_method_getMemoryUsage */
 738+static int luasandbox_method_getMemoryUsage(lua_State * L)
 739+{
 740+ php_luasandbox_obj * intern = luasandbox_get_php_obj(L);
 741+ if ((size_t)(lua_Integer)intern->memory_usage != intern->memory_usage) {
 742+ lua_pushnumber(L, (lua_Number)intern->memory_usage);
 743+ } else {
 744+ lua_pushinteger(L, (lua_Integer)intern->memory_usage);
 745+ }
 746+ return 1;
 747+}
 748+/* }}} */
 749+
 750+/*
 751+ * Local variables:
 752+ * tab-width: 4
 753+ * c-basic-offset: 4
 754+ * End:
 755+ * vim600: noet sw=4 ts=4 fdm=marker
 756+ * vim<600: noet sw=4 ts=4
 757+ */
Property changes on: trunk/php/luasandbox/luasandbox.c
___________________________________________________________________
Added: svn:eol-style
1758 + native
Index: trunk/php/luasandbox/config.m4
@@ -0,0 +1,59 @@
 2+dnl $Id$
 3+dnl config.m4 for extension luasandbox
 4+
 5+PHP_ARG_WITH(luasandbox, for luasandbox support,
 6+[ --with-luasandbox Include luasandbox support])
 7+
 8+dnl TODO: make this find either lua or luajit as it is installed in various
 9+dnl distros. Make a define available to C to indicate luajit is available
 10+dnl as opposed to lua. Maybe use pkgconfig since the library file is in an
 11+dnl odd place. Allow the user to specify the include directory manually.
 12+dnl -- TS
 13+
 14+if test "$PHP_LUASANDBOX" != "no"; then
 15+ SEARCH_PREFIX="/usr/local /usr"
 16+ SEARCH_INCLUDE_SUBDIR="lua5.1 lua"
 17+ LUA_LIB_NAME=lua5.1
 18+ LUA_LIB_FILE=liblua5.1.so
 19+
 20+ SEARCH_INCLUDE=
 21+ for prefix in $SEARCH_PREFIX; do
 22+ for subdir in $SEARCH_INCLUDE_SUBDIR; do
 23+ SEARCH_INCLUDE="$SEARCH_INCLUDE $prefix/include/$subdir"
 24+ done
 25+ done
 26+
 27+ LUA_INCLUDE_DIR=
 28+ AC_MSG_CHECKING([for lua include files in default path])
 29+ for i in $SEARCH_INCLUDE; do
 30+ if test -r "$i/lua.h"; then
 31+ LUA_INCLUDE_DIR=$i
 32+ AC_MSG_RESULT(found in $i)
 33+ fi
 34+ done
 35+
 36+ if test -z "$LUA_INCLUDE_DIR"; then
 37+ AC_MSG_RESULT([not found])
 38+ AC_MSG_ERROR([Please reinstall the lua distribution])
 39+ fi
 40+
 41+ PHP_ADD_INCLUDE($LUA_INCLUDE_DIR)
 42+
 43+ LUA_LIB_PREFIX=
 44+ AC_MSG_CHECKING([for lua library files])
 45+ for i in $SEARCH_PREFIX; do
 46+ if test -r "$i/lib/$LUA_LIB_FILE"; then
 47+ AC_MSG_RESULT([found in $i])
 48+ LUA_LIB_PREFIX=$i
 49+ fi
 50+ done
 51+
 52+ if test -z "$LUA_LIB_PREFIX"; then
 53+ AC_MSG_RESULT([not found])
 54+ AC_MSG_ERROR([Please reinstall the lua distribution])
 55+ fi
 56+
 57+ PHP_ADD_LIBRARY_WITH_PATH($LUA_LIB_NAME, $LUA_LIB_PREFIX/lib, LUASANDBOX_SHARED_LIBADD)
 58+ PHP_SUBST(LUASANDBOX_SHARED_LIBADD)
 59+ PHP_NEW_EXTENSION(luasandbox, luasandbox.c, $ext_shared)
 60+fi
Index: trunk/php/luasandbox/luasandbox.php
@@ -0,0 +1,21 @@
 2+<?php
 3+$br = (php_sapi_name() == "cli")? "":"<br>";
 4+
 5+if(!extension_loaded('luasandbox')) {
 6+ dl('luasandbox.' . PHP_SHLIB_SUFFIX);
 7+}
 8+$module = 'luasandbox';
 9+$functions = get_extension_funcs($module);
 10+echo "Functions available in the test extension:$br\n";
 11+foreach($functions as $func) {
 12+ echo $func."$br\n";
 13+}
 14+echo "$br\n";
 15+$function = 'confirm_' . $module . '_compiled';
 16+if (extension_loaded($module)) {
 17+ $str = $function($module);
 18+} else {
 19+ $str = "Module $module is not compiled into PHP";
 20+}
 21+echo "$str\n";
 22+?>
Property changes on: trunk/php/luasandbox/luasandbox.php
___________________________________________________________________
Added: svn:eol-style
123 + native
Index: trunk/php/luasandbox/.svnignore
@@ -0,0 +1,3 @@
 2+.deps
 3+*.lo
 4+*.la
Index: trunk/php/luasandbox/CREDITS
@@ -0,0 +1 @@
 2+luasandbox
\ No newline at end of file
Index: trunk/php/luasandbox/EXPERIMENTAL
Index: trunk/php/luasandbox/php_luasandbox.h
@@ -0,0 +1,67 @@
 2+
 3+#ifndef PHP_LUASANDBOX_H
 4+#define PHP_LUASANDBOX_H
 5+
 6+#include <lua.h>
 7+
 8+extern zend_module_entry luasandbox_module_entry;
 9+#define phpext_luasandbox_ptr &luasandbox_module_entry
 10+
 11+#ifdef PHP_WIN32
 12+# define PHP_LUASANDBOX_API __declspec(dllexport)
 13+#elif defined(__GNUC__) && __GNUC__ >= 4
 14+# define PHP_LUASANDBOX_API __attribute__ ((visibility("default")))
 15+#else
 16+# define PHP_LUASANDBOX_API
 17+#endif
 18+
 19+#ifdef ZTS
 20+#include "TSRM.h"
 21+#endif
 22+
 23+PHP_MINIT_FUNCTION(luasandbox);
 24+PHP_MSHUTDOWN_FUNCTION(luasandbox);
 25+PHP_MINFO_FUNCTION(luasandbox);
 26+
 27+PHP_METHOD(LuaSandbox, loadString);
 28+PHP_METHOD(LuaSandbox, setMemoryLimit);
 29+PHP_METHOD(LuaSandbox, callFunction);
 30+PHP_METHOD(LuaSandbox, callScript);
 31+PHP_METHOD(LuaSandbox, createFunction);
 32+
 33+/*
 34+ Declare any global variables you may need between the BEGIN
 35+ and END macros here:
 36+
 37+ZEND_BEGIN_MODULE_GLOBALS(luasandbox)
 38+ long global_value;
 39+ char *global_string;
 40+ZEND_END_MODULE_GLOBALS(luasandbox)
 41+*/
 42+
 43+/* In every utility function you add that needs to use variables
 44+ in php_luasandbox_globals, call TSRMLS_FETCH(); after declaring other
 45+ variables used by that function, or better yet, pass in TSRMLS_CC
 46+ after the last function argument and declare your utility function
 47+ with TSRMLS_DC after the last declared argument. Always refer to
 48+ the globals in your function as LUASANDBOX_G(variable). You are
 49+ encouraged to rename these macros something shorter, see
 50+ examples in any other php module directory.
 51+*/
 52+
 53+#ifdef ZTS
 54+#define LUASANDBOX_G(v) TSRMG(luasandbox_globals_id, zend_luasandbox_globals *, v)
 55+#else
 56+#define LUASANDBOX_G(v) (luasandbox_globals.v)
 57+#endif
 58+
 59+struct _php_luasandbox_obj {
 60+ zend_object std;
 61+ lua_State * state;
 62+ size_t memory_limit;
 63+ size_t memory_usage;
 64+};
 65+typedef struct _php_luasandbox_obj php_luasandbox_obj;
 66+
 67+#endif /* PHP_LUASANDBOX_H */
 68+
Property changes on: trunk/php/luasandbox/php_luasandbox.h
___________________________________________________________________
Added: svn:eol-style
169 + native

Status & tagging log