├── .gitignore ├── .gitreview ├── COPYING ├── CREDITS ├── Doxyfile ├── FindLua51cpp.cmake ├── README.Documentation ├── README.md ├── alloc.c ├── config.m4 ├── config.w32 ├── data_conversion.c ├── docbook ├── book.xml ├── configure.xml ├── constants.xml ├── differences.xml ├── examples.xml ├── luasandbox.xml ├── luasandbox │ ├── callfunction.xml │ ├── disableprofiler.xml │ ├── enableprofiler.xml │ ├── getcpuusage.xml │ ├── getmemoryusage.xml │ ├── getpeakmemoryusage.xml │ ├── getprofilerfunctionreport.xml │ ├── getversioninfo.xml │ ├── loadbinary.xml │ ├── loadstring.xml │ ├── pauseusagetimer.xml │ ├── registerlibrary.xml │ ├── setcpulimit.xml │ ├── setmemorylimit.xml │ ├── unpauseusagetimer.xml │ └── wrapphpfunction.xml ├── luasandboxerror.xml ├── luasandboxerrorerror.xml ├── luasandboxfatalerror.xml ├── luasandboxfunction.xml ├── luasandboxfunction │ ├── call.xml │ ├── construct.xml │ └── dump.xml ├── luasandboxmemoryerror.xml ├── luasandboxruntimeerror.xml ├── luasandboxsyntaxerror.xml ├── luasandboxtimeouterror.xml ├── reference.xml ├── setup.xml └── versions.xml ├── library.c ├── luasandbox.c ├── luasandbox_compat.h ├── luasandbox_lstrlib.c ├── luasandbox_lstrlib.patch ├── luasandbox_timer.h ├── luasandbox_types.h ├── luasandbox_version.h ├── m4 └── pkg.m4 ├── package.xml ├── php_luasandbox.h ├── stubs ├── Exceptions.php ├── LuaSandbox.php └── LuaSandboxFunction.php ├── tests ├── LuaSandboxFunction_construct.phpt ├── array-key-conversion.phpt ├── call.phpt ├── callback_exception.phpt ├── datatypes-unsupported.phpt ├── datatypes.phpt ├── dump_loadBinary_call.phpt ├── errors-at-call-boundaries.phpt ├── extending-LuaSandbox.phpt ├── ipairs.phpt ├── loadString.phpt ├── lua_catches_php_exception.phpt ├── oom-init.phpt ├── pairs.phpt ├── pcall.phpt ├── profiler-sorting.phpt ├── profiler.phpt ├── reentrant.phpt ├── timer.phpt └── xpcall.phpt └── timer.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors 2 | *~ 3 | .*.swp 4 | 5 | # Auto-generated files from building 6 | .deps 7 | .libs/ 8 | Makefile* 9 | build/ 10 | modules/ 11 | tests/*.diff 12 | tests/*.out 13 | tests/*.php 14 | tests/*.exp 15 | tests/*.log 16 | tests/*.sh 17 | install-sh 18 | libtool 19 | ltmain.sh 20 | missing 21 | run-tests.php 22 | mkinstalldirs 23 | configure* 24 | acinclude.m4 25 | aclocal.m4 26 | autom4te.cache/ 27 | config.guess 28 | config.h* 29 | config.log 30 | config.nice 31 | config.status 32 | config.sub 33 | *.lo 34 | *.la 35 | 36 | # Doxygen output 37 | /doc/ 38 | 39 | # Docbook generated files, just in case 40 | docbook/entities.*.xml 41 | 42 | # Override the "configure*" rule above 43 | !docbook/configure.xml 44 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=gerrit.wikimedia.org 3 | port=29418 4 | project=mediawiki/php/luasandbox 5 | defaultbranch=master 6 | defaultrebase=0 -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | (C) 2011-2014 Tim Starling 2 | (C) 2011-2014 Wikimedia Foundation 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | ;Lua Sandbox library for PHP 2 | 3 | Tim Starling [tstarling] (lead) 4 | Brad Jorsch 5 | Kunal Mehta 6 | Victor Vasiliev 7 | Ori Livneh 8 | -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile for LuaSandbox 2 | # 3 | # See 4 | # for help on how to use this file to configure Doxygen. 5 | 6 | PROJECT_NAME = "LuaSandbox" 7 | PROJECT_BRIEF = "extension to run Lua 5.1 code from PHP" 8 | OUTPUT_DIRECTORY = doc 9 | JAVADOC_AUTOBRIEF = YES 10 | QT_AUTOBRIEF = YES 11 | WARN_NO_PARAMDOC = YES 12 | INPUT = README.md stubs/ 13 | USE_MDFILE_AS_MAINPAGE = README.md 14 | FILE_PATTERNS = *.php 15 | RECURSIVE = YES 16 | HTML_DYNAMIC_SECTIONS = YES 17 | GENERATE_TREEVIEW = YES 18 | TREEVIEW_WIDTH = 250 19 | GENERATE_LATEX = NO 20 | HAVE_DOT = YES 21 | DOT_FONTNAME = Helvetica 22 | DOT_FONTSIZE = 10 23 | TEMPLATE_RELATIONS = YES 24 | CALL_GRAPH = NO 25 | CALLER_GRAPH = NO 26 | DOT_MULTI_TARGETS = YES 27 | -------------------------------------------------------------------------------- /FindLua51cpp.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUA51_FOUND, if false, do not try to link to Lua 4 | # LUA_LIBRARIES 5 | # LUA_INCLUDE_DIR, where to find lua.h 6 | # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) 7 | # 8 | # Note that the expected include convention is 9 | # #include "lua.h" 10 | # and not 11 | # #include 12 | # This is because, the lua location is not standardized and may exist 13 | # in locations other than lua/ 14 | 15 | #============================================================================= 16 | # Copyright 2007-2009 Kitware, Inc. 17 | # 18 | # Distributed under the OSI-approved BSD License (the "License"); 19 | # see accompanying file Copyright.txt for details. 20 | # 21 | # This software is distributed WITHOUT ANY WARRANTY; without even the 22 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 23 | # See the License for more information. 24 | #============================================================================= 25 | 26 | find_path(LUA_INCLUDE_DIR lua.h 27 | HINTS 28 | ENV LUA_DIR 29 | PATH_SUFFIXES include/lua51 include/lua5.1 include/lua-5.1 include/lua include 30 | PATHS 31 | ~/Library/Frameworks 32 | /Library/Frameworks 33 | /sw # Fink 34 | /opt/local # DarwinPorts 35 | /opt/csw # Blastwave 36 | /opt 37 | ) 38 | 39 | find_library(LUA_LIBRARY 40 | NAMES lua5.1-c++ 41 | HINTS 42 | ENV LUA_DIR 43 | PATH_SUFFIXES lib 44 | PATHS 45 | ~/Library/Frameworks 46 | /Library/Frameworks 47 | /sw 48 | /opt/local 49 | /opt/csw 50 | /opt 51 | ) 52 | 53 | if(LUA_LIBRARY) 54 | # include the math library for Unix 55 | if(UNIX AND NOT APPLE AND NOT BEOS) 56 | find_library(LUA_MATH_LIBRARY m) 57 | set( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 58 | # For Windows and Mac, don't need to explicitly include the math library 59 | else() 60 | set( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 61 | endif() 62 | endif() 63 | 64 | if(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") 65 | file(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") 66 | 67 | string(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") 68 | unset(lua_version_str) 69 | endif() 70 | 71 | include(FindPackageHandleStandardArgs) 72 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 73 | # all listed variables are TRUE 74 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua51 75 | REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR 76 | VERSION_VAR LUA_VERSION_STRING) 77 | 78 | mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) 79 | 80 | -------------------------------------------------------------------------------- /README.Documentation: -------------------------------------------------------------------------------- 1 | This extension has documentation in three different formats. When updating 2 | the documentation in one place, remember to update the others as well. 3 | 4 | == On-wiki documentation == 5 | 6 | Installation of the extension and some examples are documented at 7 | . 8 | 9 | == PHPdoc style documentation == 10 | 11 | PHP interface documentation is provided via stub class definitions in 12 | the stubs/ directory. These stubs may be used with IDEs that understand 13 | PHPdoc documentation. 14 | 15 | These stubs, along with a brief introduction in README.md, may be used 16 | to generate online documentation using Doxygen. It should suffice to run 17 | `doxygen` with no arguments. 18 | 19 | == PHP DocBook documentation == 20 | 21 | Documentation in the DocBook format used by php.net is included in the 22 | docbook/ directory. The intention here is that that directory can be 23 | copied directly into PHP's documentation Git repository. 24 | 25 | To generate the documentation locally: 26 | 27 | * Get the PhD docbook builder: 28 | git clone https://github.com/php/phd phd 29 | * Check out the PHP documentation: 30 | git clone https://github.com/php/doc-en en 31 | git clone https://github.com/php/doc-base doc-base 32 | * In the en directory, 33 | * Copy the docbook/ directory here to reference/luasandbox/. 34 | * Build the manual (two commands): 35 | php ../doc-base/configure.php --enable-xml-details 36 | php ../phd/render.php --docbook ../doc-base/.manual.xml --package PHP --format xhtml 37 | * Open output/php-chunked-xhtml/book.luasandbox.html in your browser. 38 | 39 | The changes can then be committed and submitted as a pull request against 40 | the php/doc-en repository on GitHub. Make sure to check if anyone has made 41 | changes to the LuaSandbox documentation there that should be copied back 42 | into this repository. 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LuaSandbox is an extension for PHP 7 and PHP 8 to allow safely 2 | running untrusted Lua 5.1 code from within PHP, which will generally 3 | be faster than shelling out to a Lua binary and using inter-process 4 | communication. 5 | 6 | For more details see . 7 | -------------------------------------------------------------------------------- /alloc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The Lua allocator hook 3 | */ 4 | 5 | #ifdef HAVE_CONFIG_H 6 | #include "config.h" 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "php.h" 15 | #include "php_luasandbox.h" 16 | 17 | static inline int luasandbox_update_memory_accounting(php_luasandbox_alloc * obj, 18 | size_t osize, size_t nsize); 19 | static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize); 20 | 21 | lua_State * luasandbox_alloc_new_state(php_luasandbox_alloc * alloc, php_luasandbox_obj * sandbox) 22 | { 23 | lua_State * L; 24 | L = lua_newstate(luasandbox_php_alloc, sandbox); 25 | return L; 26 | } 27 | 28 | void luasandbox_alloc_delete_state(php_luasandbox_alloc * alloc, lua_State * L) 29 | { 30 | lua_close(L); 31 | } 32 | 33 | 34 | /** {{{ luasandbox_update_memory_accounting 35 | * 36 | * Update memory usage statistics for the given memory allocation request. 37 | * Returns 1 if the allocation should be allowed, 0 if it should fail. 38 | */ 39 | static inline int luasandbox_update_memory_accounting(php_luasandbox_alloc * alloc, 40 | size_t osize, size_t nsize) 41 | { 42 | if (nsize > osize && (nsize > alloc->memory_limit 43 | || alloc->memory_usage + nsize > alloc->memory_limit)) 44 | { 45 | // Memory limit exceeded 46 | return 0; 47 | } 48 | 49 | if (osize > nsize && alloc->memory_usage + nsize < osize) { 50 | // Negative memory usage -- do not update 51 | return 1; 52 | } 53 | 54 | alloc->memory_usage += nsize - osize; 55 | if (alloc->memory_usage > alloc->peak_memory_usage) { 56 | alloc->peak_memory_usage = alloc->memory_usage; 57 | } 58 | return 1; 59 | } 60 | /* }}} */ 61 | 62 | /** {{{ luasandbox_update_gc_pause 63 | * Scale the GC pause size so that collection will start before an OOM occurs (T349462) 64 | */ 65 | static inline void luasandbox_update_gc_pause(lua_State * L, php_luasandbox_alloc * alloc) 66 | { 67 | size_t limit = alloc->memory_limit; 68 | size_t usage = alloc->memory_usage; 69 | 70 | // Guard against overflow and division by zero 71 | if (limit >= SIZE_MAX / 90 || usage == 0) { 72 | return; 73 | } 74 | size_t pause = limit * 90 / usage; 75 | if (pause > 200) { 76 | pause = 200; 77 | } 78 | lua_gc(L, LUA_GCSETPAUSE, (int)pause); 79 | } 80 | /* }}} */ 81 | 82 | /** {{{ luasandbox_php_alloc 83 | * 84 | * The Lua allocator function. Use PHP's request-local allocator as a backend. 85 | * Account for memory usage and deny the allocation request if the amount 86 | * allocated is above the user-specified limit. 87 | */ 88 | static void *luasandbox_php_alloc(void *ud, void *ptr, size_t osize, size_t nsize) 89 | { 90 | php_luasandbox_obj * obj = (php_luasandbox_obj*)ud; 91 | void * nptr; 92 | obj->in_php ++; 93 | if (!luasandbox_update_memory_accounting(&obj->alloc, osize, nsize)) { 94 | obj->in_php --; 95 | return NULL; 96 | } 97 | 98 | luasandbox_update_gc_pause(obj->state, &obj->alloc); 99 | 100 | if (nsize == 0) { 101 | if (ptr) { 102 | efree(ptr); 103 | } 104 | nptr = NULL; 105 | } else if (osize == 0) { 106 | nptr = ecalloc(1, nsize); 107 | } else { 108 | nptr = erealloc(ptr, nsize); 109 | if (nsize > osize) { 110 | memset(nptr + osize, 0, nsize - osize); 111 | } 112 | } 113 | obj->in_php --; 114 | return nptr; 115 | } 116 | /* }}} */ 117 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension luasandbox 3 | 4 | AC_PREREQ(2.50) 5 | 6 | PHP_ARG_WITH(luasandbox, for luasandbox support, 7 | [ --with-luasandbox Include luasandbox support]) 8 | 9 | if test "$PHP_LUASANDBOX" != "no"; then 10 | dnl Include pkg-config macros definitions: 11 | m4_include([m4/pkg.m4]) 12 | PKG_PROG_PKG_CONFIG 13 | 14 | dnl We need lua 15 | dnl Under debian package is known as 'lua5.1' 16 | dnl Under freebsd package is known as 'lua-5.1' 17 | PKG_CHECK_MODULES([LUA], [lua >= 5.1 lua < 5.2],, [ 18 | PKG_CHECK_MODULES([LUA], [lua5.1],, [ 19 | PKG_CHECK_MODULES([LUA], [lua-5.1]) 20 | ]) 21 | ]) 22 | 23 | dnl Timers require real-time and pthread library on Linux and not 24 | dnl supported on other platforms 25 | AC_SEARCH_LIBS([timer_create], [rt], [ 26 | PHP_EVAL_LIBLINE($LIBS, LUASANDBOX_SHARED_LIBADD) 27 | ]) 28 | AC_SEARCH_LIBS([sem_init], [pthread], [ 29 | PHP_EVAL_LIBLINE($LIBS, LUASANDBOX_SHARED_LIBADD) 30 | ]) 31 | 32 | dnl LUA_LIBS and LUA_CFLAGS interprets them: 33 | PHP_EVAL_INCLINE($LUA_CFLAGS) 34 | PHP_EVAL_LIBLINE($LUA_LIBS, LUASANDBOX_SHARED_LIBADD) 35 | 36 | PHP_SUBST(LUASANDBOX_SHARED_LIBADD) 37 | PHP_NEW_EXTENSION(luasandbox, alloc.c data_conversion.c library.c luasandbox.c timer.c luasandbox_lstrlib.c, $ext_shared) 38 | PHP_ADD_MAKEFILE_FRAGMENT 39 | fi 40 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_WITH("luasandbox", "Include luasandbox support", "no"); 4 | 5 | if (PHP_LUASANDBOX != "no") { 6 | if (CHECK_LIB("lua5.1.lib", "luasandbox", PHP_LUASANDBOX) && 7 | CHECK_HEADER_ADD_INCLUDE("lua.h", "CFLAGS_LUASANDBOX", PHP_PHP_BUILD + "\\include;" + PHP_LUASANDBOX)) { 8 | EXTENSION("luasandbox", "alloc.c data_conversion.c library.c luasandbox.c timer.c luasandbox_lstrlib.c", PHP_LUASANDBOX_SHARED); 9 | } else { 10 | WARNING("luasandbox not enabled; libraries and headers not found"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docbook/book.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LuaSandbox 6 | LuaSandbox 7 | 8 | 9 | &reftitle.intro; 10 | 11 | LuaSandbox is an extension for PHP 7 and PHP 8 to allow safely 12 | running untrusted Lua 5.1 code from within PHP. 13 | 14 | 15 | Differences compared to the Lua extension: 16 | 17 | 18 | 19 | LuaSandbox has support for time and memory limits. 20 | 21 | 22 | 23 | 24 | LuaSandbox provides a default-safe environment for running untrusted code. 25 | Stock Lua functions were reviewed for security, and several were patched 26 | accordingly. 27 | 28 | 29 | 30 | 31 | LuaSandbox has a PHP interface which is more complex, precise and powerful, 32 | but it is less convenient for developers. 33 | 34 | 35 | 36 | 37 | LuaSandbox supports only Lua 5.1. It is difficult to change this, because 38 | LuaSandbox uses heavily modified Lua standard libraries, and 39 | due to the lack of backwards compatibility between major Lua versions. 40 | LuaSandbox aims to maximise backwards compatibility with user-supplied 41 | scripts. 42 | 43 | 44 | 45 | 46 | 47 | 48 | &reference.luasandbox.setup; 49 | 50 | &reference.luasandbox.differences; 51 | &reference.luasandbox.examples; 52 | 53 | 54 | 55 | &reference.luasandbox.luasandbox; 56 | &reference.luasandbox.luasandboxfunction; 57 | &reference.luasandbox.luasandboxerror; 58 | &reference.luasandbox.luasandboxerrorerror; 59 | &reference.luasandbox.luasandboxfatalerror; 60 | &reference.luasandbox.luasandboxmemoryerror; 61 | &reference.luasandbox.luasandboxruntimeerror; 62 | &reference.luasandbox.luasandboxsyntaxerror; 63 | &reference.luasandbox.luasandboxtimeouterror; 64 | 65 | 66 | 67 | 87 | -------------------------------------------------------------------------------- /docbook/configure.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | &reftitle.install; 6 | 7 | 8 | &pecl.info; 9 | &url.pecl.package;luasandbox 10 | 11 | 12 |
13 | 14 | 15 | 35 | -------------------------------------------------------------------------------- /docbook/constants.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | &reftitle.constants; 6 | &no.constants; 7 | 8 | 9 | 29 | -------------------------------------------------------------------------------- /docbook/differences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Differences from Standard Lua 5 | 6 | 7 | LuaSandbox provides a sandboxed environment which differs in some ways from standard Lua 5.1. 8 | 9 | 10 | 11 | Features that are not available 12 | 13 | 14 | 15 | 16 | dofile(), loadfile(), and the io package, as they allow direct filesystem access. If needed, filesystem access should be done via PHP callbacks. 17 | 18 | 19 | 20 | 21 | The package package, including require() and module(), as it depends heavily on direct filesystem access. A pure-Lua rewrite such as that used in the MediaWiki Scribunto extension may be used instead. 22 | 23 | 24 | 25 | 26 | load() and loadstring(), to allow for static analysis of Lua code. 27 | 28 | 29 | 30 | 31 | print(), since it outputs to standard output. If needed, output should be done via PHP callbacks. 32 | 33 | 34 | 35 | 36 | Most of the os package, as it allows manipulation of the process and executing of other processes. 37 | 38 | 39 | 40 | 41 | 42 | os.clock(), os.date(), os.difftime(), and os.time() remain available. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Most of the debug package, as it allows manipulation of Lua state and metadata in ways that can break sandboxing. 51 | 52 | 53 | 54 | 55 | 56 | debug.traceback() remains available. 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | string.dump(), as it may expose internal data. 65 | 66 | 67 | 68 | 69 | collectgarbage(), gcinfo(), and the coroutine package have not been reviewed for security. 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Features that have been modified 78 | 79 | 80 | 81 | 82 | pcall() and xpcall() cannot catch certain errors, particularly timeout errors. 83 | 84 | 85 | 86 | 87 | tostring() does not include pointer addresses. 88 | 89 | 90 | 91 | 92 | string.match() has been patched to limit the recursion depth and to periodically check for a timeout. 93 | 94 | 95 | 96 | 97 | math.random() and math.randomseed() are replaced with versions that don't share state with PHP's rand(). 98 | 99 | 100 | 101 | 102 | The Lua 5.2 __pairs and __ipairs metamethods are supported by pairs() and ipairs(). 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 131 | -------------------------------------------------------------------------------- /docbook/examples.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | &reftitle.examples; 6 | 7 | 8 | 9 |
10 | Basic usage for LuaSandbox 11 | 12 | Once you've compiled PHP with LuaSandbox support, you can begin using LuaSandbox to safely run user-provided Lua code. 13 | 14 | 15 | 16 | Execute some Lua code 17 | 18 | setMemoryLimit( 50 * 1024 * 1024 ); 23 | $sandbox->setCPULimit( 10 ); 24 | 25 | // Register some functions in the Lua environment 26 | 27 | function frobnosticate( $v ) { 28 | return [ $v + 42 ]; 29 | } 30 | 31 | $sandbox->registerLibrary( 'php', [ 32 | 'frobnosticate' => 'frobnosticate', 33 | 'output' => function ( $string ) { 34 | echo "$string\n"; 35 | }, 36 | 'error' => function () { 37 | throw new LuaSandboxRuntimeError( "Something is wrong" ); 38 | } 39 | ] ); 40 | 41 | // Execute some Lua code, including callbacks into PHP and into Lua 42 | 43 | $luaCode = <<loadString( $luaCode )->call(); 52 | assert( $frob->call( 4000 ) === [ 4242 ] ); 53 | 54 | // PHP-thrown LuaSandboxRuntimeError exceptions can be caught inside Lua 55 | 56 | list( $ok, $message ) = $sandbox->loadString( 'return pcall( php.error )' )->call(); 57 | assert( !$ok ); 58 | assert( $message === 'Something is wrong' ); 59 | 60 | ?> 61 | ]]> 62 | 63 | 64 | 65 |
66 | 67 |
68 | 69 | 89 | 90 | -------------------------------------------------------------------------------- /docbook/luasandbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandbox class 7 | LuaSandbox 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | The LuaSandbox class creates a Lua environment and allows for execution of 16 | Lua code. 17 | 18 |
19 | 20 | 21 |
22 | &reftitle.classsynopsis; 23 | 24 | 25 | 26 | LuaSandbox 27 | 28 | 29 | 30 | 31 | LuaSandbox 32 | 33 | 34 | 35 | Constants 36 | 37 | const 38 | int 39 | LuaSandbox::SAMPLES 40 | 0 41 | 42 | 43 | const 44 | int 45 | LuaSandbox::SECONDS 46 | 1 47 | 48 | 49 | const 50 | int 51 | LuaSandbox::PERCENT 52 | 2 53 | 54 | 55 | &Methods; 56 | 57 | 58 | 59 | 60 |
61 | 62 | 63 |
64 | &reftitle.constants; 65 | 66 | 67 | 68 | LuaSandbox::SAMPLES 69 | 70 | 71 | Used with LuaSandbox::getProfilerFunctionReport to return timings in samples. 72 | 73 | 74 | 75 | 76 | 77 | LuaSandbox::SECONDS 78 | 79 | 80 | Used with LuaSandbox::getProfilerFunctionReport to return timings in seconds. 81 | 82 | 83 | 84 | 85 | 86 | LuaSandbox::PERCENT 87 | 88 | 89 | Used with LuaSandbox::getProfilerFunctionReport to return timings in percentages of the total. 90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | 98 | 99 |
100 | 101 | &reference.luasandbox.entities.luasandbox; 102 | 103 |
104 | 105 | 125 | -------------------------------------------------------------------------------- /docbook/luasandbox/callfunction.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::callFunction 7 | Call a function in a Lua global variable 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public arrayboolLuaSandbox::callFunction 14 | stringname 15 | mixedargs 16 | 17 | 18 | Calls a function in a Lua global variable. 19 | 20 | 21 | If the name contains "." characters, the function is located via 22 | recursive table accesses, as if the name were a Lua expression. 23 | 24 | 25 | If the variable does not exist, or is not a function, false will be 26 | returned and a warning issued. 27 | 28 | 29 | For more information about calling Lua functions and the return values, 30 | see LuaSandboxFunction::call. 31 | 32 | 33 | 34 | 35 | &reftitle.parameters; 36 | 37 | 38 | name 39 | 40 | 41 | Lua variable name. 42 | 43 | 44 | 45 | 46 | args 47 | 48 | 49 | Arguments to the function. 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | &reftitle.returnvalues; 58 | 59 | Returns an array of values returned by the Lua function, which may be empty, or false in case of failure. 60 | 61 | 62 | 63 | 64 | &reftitle.examples; 65 | 66 | 67 | Calling a Lua function 68 | 69 | callFunction( 'string.match', $string, $pattern ); 77 | 78 | ?> 79 | ]]> 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 107 | -------------------------------------------------------------------------------- /docbook/luasandbox/disableprofiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::disableProfiler 7 | Disable the profiler 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public voidLuaSandbox::disableProfiler 14 | 15 | 16 | 17 | Disables the profiler. 18 | 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | &no.function.parameters; 25 | 26 | 27 | 28 | &reftitle.returnvalues; 29 | 30 | &return.void; 31 | 32 | 33 | 34 | 35 | &reftitle.seealso; 36 | 37 | 38 | LuaSandbox::enableProfiler 39 | LuaSandbox::getProfilerFunctionReport 40 | 41 | 42 | 43 | 44 | 45 | 46 | 66 | -------------------------------------------------------------------------------- /docbook/luasandbox/enableprofiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::enableProfiler 7 | Enable the profiler. 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public boolLuaSandbox::enableProfiler 14 | floatperiod0.02 15 | 16 | 17 | Enables the profiler. Profiling will begin when Lua code is entered. 18 | 19 | 20 | The profiler periodically samples the Lua environment to record the 21 | running function. Testing indicates that at least on Linux, setting a 22 | period less than 1ms will lead to a high overrun count but no 23 | performance problems. 24 | 25 | 26 | 27 | 28 | 29 | &reftitle.parameters; 30 | 31 | 32 | period 33 | 34 | 35 | Sampling period in seconds. 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | &reftitle.returnvalues; 44 | 45 | Returns a boolean indicating whether the profiler is enabled. 46 | 47 | 48 | 49 | 50 | &reftitle.seealso; 51 | 52 | 53 | LuaSandbox::disableProfiler 54 | LuaSandbox::getProfilerFunctionReport 55 | 56 | 57 | 58 | 59 | 60 | 61 | 81 | -------------------------------------------------------------------------------- /docbook/luasandbox/getcpuusage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::getCPUUsage 7 | Fetch the current CPU time usage of the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public floatLuaSandbox::getCPUUsage 14 | 15 | 16 | 17 | Fetches the current CPU time usage of the Lua environment. 18 | 19 | 20 | This includes time spent in PHP callbacks. 21 | 22 | 23 | 24 | 25 | 26 | &reftitle.parameters; 27 | &no.function.parameters; 28 | 29 | 30 | 31 | &reftitle.returnvalues; 32 | 33 | Returns the current CPU time usage in seconds. 34 | 35 | 36 | 37 | On Windows, this function always returns zero. On operating systems that do 38 | not support CLOCK_THREAD_CPUTIME_ID, such as FreeBSD 39 | and Mac OS X, this function will return the elapsed wall-clock time, not 40 | CPU time. 41 | 42 | 43 | 44 | 45 | 46 | &reftitle.seealso; 47 | 48 | 49 | LuaSandbox::getMemoryUsage 50 | LuaSandbox::getPeakMemoryUsage 51 | LuaSandbox::setCPULimit 52 | 53 | 54 | 55 | 56 | 57 | 58 | 78 | -------------------------------------------------------------------------------- /docbook/luasandbox/getmemoryusage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::getMemoryUsage 7 | Fetch the current memory usage of the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public intLuaSandbox::getMemoryUsage 14 | 15 | 16 | 17 | Fetches the current memory usage of the Lua environment. 18 | 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | &no.function.parameters; 25 | 26 | 27 | 28 | &reftitle.returnvalues; 29 | 30 | Returns the current memory usage in bytes. 31 | 32 | 33 | 34 | 35 | &reftitle.seealso; 36 | 37 | 38 | LuaSandbox::getPeakMemoryUsage 39 | LuaSandbox::getCPUUsage 40 | LuaSandbox::setMemoryLimit 41 | 42 | 43 | 44 | 45 | 46 | 47 | 67 | -------------------------------------------------------------------------------- /docbook/luasandbox/getpeakmemoryusage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::getPeakMemoryUsage 7 | Fetch the peak memory usage of the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public intLuaSandbox::getPeakMemoryUsage 14 | 15 | 16 | 17 | Fetches the peak memory usage of the Lua environment. 18 | 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | &no.function.parameters; 25 | 26 | 27 | 28 | &reftitle.returnvalues; 29 | 30 | Returns the peak memory usage in bytes. 31 | 32 | 33 | 34 | 35 | &reftitle.seealso; 36 | 37 | 38 | LuaSandbox::getMemoryUsage 39 | LuaSandbox::getCPUUsage 40 | LuaSandbox::setMemoryLimit 41 | 42 | 43 | 44 | 45 | 46 | 47 | 67 | -------------------------------------------------------------------------------- /docbook/luasandbox/getprofilerfunctionreport.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::getProfilerFunctionReport 7 | Fetch profiler data 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public arrayLuaSandbox::getProfilerFunctionReport 14 | intunitsLuaSandbox::SECONDS 15 | 16 | 17 | For a profiling instance previously started by 18 | LuaSandbox::enableProfiler, get a 19 | report of the cost of each function. 20 | 21 | 22 | The measurement unit used for the cost is determined by the 23 | $units parameter: 24 | 25 | 26 | 27 | 28 | LuaSandbox::SAMPLES 29 | Measure in number of samples. 30 | 31 | 32 | LuaSandbox::SECONDS 33 | Measure in seconds of CPU time. 34 | 35 | 36 | LuaSandbox::PERCENT 37 | Measure percentage of CPU time. 38 | 39 | 40 | 41 | 42 | 43 | 44 | &reftitle.parameters; 45 | 46 | 47 | units 48 | 49 | 50 | Measurement unit constant. 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | &reftitle.returnvalues; 59 | 60 | Returns profiler measurements, sorted in descending order, as an associative 61 | array. Keys are the Lua function names (with source file and line 62 | defined in angle brackets), values are the measurements as int 63 | or float. 64 | 65 | 66 | 67 | On Windows, this function always returns an empty array. On operating systems that do 68 | not support CLOCK_THREAD_CPUTIME_ID, such as FreeBSD 69 | and Mac OS X, this function will report the elapsed wall-clock time, not 70 | CPU time. 71 | 72 | 73 | 74 | 75 | 76 | &reftitle.examples; 77 | 78 | 79 | Profiling Lua code 80 | 81 | enableProfiler( 0.01 ); 89 | 90 | // ... Execute some Lua code here ... 91 | 92 | // Fetch the profiler data 93 | $data = $sandbox->getProfilerFunctionReport(); 94 | 95 | ?> 96 | ]]> 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 124 | -------------------------------------------------------------------------------- /docbook/luasandbox/getversioninfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::getVersionInfo 7 | Return the versions of LuaSandbox and Lua 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public static arrayLuaSandbox::getVersionInfo 14 | 15 | 16 | 17 | Returns the versions of LuaSandbox and Lua. 18 | 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | &no.function.parameters; 25 | 26 | 27 | 28 | &reftitle.returnvalues; 29 | 30 | Returns an array with two keys: 31 | 32 | 33 | 34 | 35 | 36 | elementtypedescription 37 | 38 | 39 | 40 | LuaSandbox 41 | string 42 | The version of the LuaSandbox extension. 43 | 44 | 45 | Lua 46 | string 47 | The library name and version as defined by the LUA_RELEASE macro, for example, "Lua 5.1.5". 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 77 | -------------------------------------------------------------------------------- /docbook/luasandbox/loadbinary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::loadBinary 7 | Load a precompiled binary chunk into the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public LuaSandboxFunctionLuaSandbox::loadBinary 14 | stringcode 15 | stringchunkName'' 16 | 17 | 18 | Loads data generated by LuaSandboxFunction::dump. 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | 25 | 26 | code 27 | 28 | 29 | Data from LuaSandboxFunction::dump. 30 | 31 | 32 | 33 | 34 | chunkName 35 | 36 | 37 | Name for the loaded function. 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | &reftitle.returnvalues; 46 | 47 | Returns a LuaSandboxFunction. 48 | 49 | 50 | 51 | 52 | &reftitle.seealso; 53 | 54 | 55 | LuaSandbox::loadString 56 | 57 | 58 | 59 | 60 | 61 | 62 | 82 | -------------------------------------------------------------------------------- /docbook/luasandbox/loadstring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::loadString 7 | Load Lua code into the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public LuaSandboxFunctionLuaSandbox::loadString 14 | stringcode 15 | stringchunkName'' 16 | 17 | 18 | Loads Lua code into the Lua environment. 19 | 20 | 21 | This is the equivalent of standard Lua's loadstring() function. 22 | 23 | 24 | 25 | 26 | 27 | &reftitle.parameters; 28 | 29 | 30 | code 31 | 32 | 33 | Lua code. 34 | 35 | 36 | 37 | 38 | chunkName 39 | 40 | 41 | Name for the loaded chunk, for use in error traces. 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | &reftitle.returnvalues; 50 | 51 | Returns a LuaSandboxFunction which, when executed, will execute the passed $code. 52 | 53 | 54 | 55 | 56 | &reftitle.examples; 57 | 58 | 59 | Loading code into Lua 60 | 61 | loadString( 69 | <<call() ); 76 | 77 | ?> 78 | ]]> 79 | 80 | &example.outputs; 81 | 82 | 85 | string(12) "Hello, world" 86 | } 87 | ]]> 88 | 89 | 90 | 91 | 92 | 93 | 94 | &reftitle.seealso; 95 | 96 | 97 | LuaSandbox::registerLibrary 98 | LuaSandbox::wrapPhpFunction 99 | 100 | 101 | 102 | 103 | 104 | 105 | 125 | -------------------------------------------------------------------------------- /docbook/luasandbox/pauseusagetimer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::pauseUsageTimer 7 | Pause the CPU usage timer 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public boolLuaSandbox::pauseUsageTimer 14 | 15 | 16 | 17 | Pauses the CPU usage timer. 18 | 19 | 20 | This only has effect when called from within a callback from Lua. When 21 | execution returns to Lua, the timer will be automatically unpaused. If 22 | a new call into Lua is made, the timer will be unpaused for the 23 | duration of that call. 24 | 25 | 26 | If a PHP callback calls into Lua again with timer not paused, and then 27 | that Lua function calls into PHP again, the second PHP call will not be 28 | able to pause the timer. The logic is that even though the second PHP 29 | call would avoid counting the CPU usage against the limit, the first 30 | call still counts it. 31 | 32 | 33 | 34 | 35 | &reftitle.parameters; 36 | &no.function.parameters; 37 | 38 | 39 | 40 | &reftitle.returnvalues; 41 | 42 | Returns a bool indicating whether the timer is now paused. 43 | 44 | 45 | 46 | 47 | &reftitle.examples; 48 | 49 | 50 | Manipulating the usage timer 51 | 52 | setCPULimit( 1 ); 58 | 59 | function doWait( $t ) { 60 | $end = microtime( true ) + $t; 61 | while ( microtime( true ) < $end ) { 62 | // waste CPU cycles 63 | } 64 | } 65 | 66 | // Register a PHP callback 67 | $sandbox->registerLibrary( 'php', [ 68 | 'test' => function () use ( $sandbox ) { 69 | $sandbox->pauseUsageTimer(); 70 | doWait( 5 ); 71 | 72 | $sandbox->unpauseUsageTimer(); 73 | doWait( 0.1 ); 74 | }, 75 | 'test2' => function () use ( $sandbox ) { 76 | $sandbox->pauseUsageTimer(); 77 | $sandbox->unpauseUsageTimer(); 78 | doWait( 1.1 ); 79 | } 80 | ] ); 81 | 82 | echo "This should not time out...\n"; 83 | $sandbox->loadString( 'php.test()' )->call(); 84 | 85 | echo "This should time out.\n"; 86 | try { 87 | $sandbox->loadString( 'php.test2()' )->call(); 88 | echo "It did not?\n"; 89 | } catch ( LuaSandboxTimeoutError $ex ) { 90 | echo "It did! " . $ex->getMessage() . "\n"; 91 | } 92 | 93 | ?> 94 | ]]> 95 | 96 | &example.outputs; 97 | 98 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | &reftitle.seealso; 110 | 111 | 112 | LuaSandbox::setCPULimit 113 | LuaSandbox::unpauseUsageTimer 114 | 115 | 116 | 117 | 118 | 119 | 120 | 140 | -------------------------------------------------------------------------------- /docbook/luasandbox/registerlibrary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::registerLibrary 7 | Register a set of PHP functions as a Lua library 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public voidLuaSandbox::registerLibrary 14 | stringlibname 15 | arrayfunctions 16 | 17 | 18 | Registers a set of PHP functions as a Lua library, so that Lua can call 19 | the relevant PHP code. 20 | 21 | 22 | For more information about calling Lua functions and the return values, 23 | see LuaSandboxFunction::call. 24 | 25 | 26 | 27 | 28 | 29 | &reftitle.parameters; 30 | 31 | 32 | libname 33 | 34 | 35 | The name of the library. In the Lua state, the global variable of this 36 | name will be set to the table of functions. If the table already exists, 37 | the new functions will be added to it. 38 | 39 | 40 | 41 | 42 | functions 43 | 44 | 45 | An array, where each key is a function name, and each value is a 46 | corresponding PHP callable. 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | &reftitle.returnvalues; 55 | 56 | &return.void; 57 | 58 | 59 | 60 | 61 | &reftitle.examples; 62 | 63 | 64 | Registering PHP functions to call from Lua 65 | 66 | registerLibrary( 'php', [ 79 | 'frobnosticate' => 'frobnosticate', 80 | 'output' => function ( $string ) { 81 | echo "$string\n"; 82 | }, 83 | 'error' => function () { 84 | throw new LuaSandboxRuntimeError( "Something is wrong" ); 85 | } 86 | ] ); 87 | 88 | ?> 89 | ]]> 90 | 91 | 92 | 93 | 94 | 95 | 96 | &reftitle.seealso; 97 | 98 | 99 | LuaSandbox::loadString 100 | LuaSandbox::wrapPhpFunction 101 | 102 | 103 | 104 | 105 | 106 | 107 | 127 | -------------------------------------------------------------------------------- /docbook/luasandbox/setcpulimit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::setCPULimit 7 | Set the CPU time limit for the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public voidLuaSandbox::setCPULimit 14 | floatboollimit 15 | 16 | 17 | Sets the CPU time limit for the Lua environment. 18 | 19 | 20 | If the total user and system time used by the environment after the call 21 | to this method exceeds this limit, a LuaSandboxTimeoutError 22 | exception is thrown. 23 | 24 | 25 | Time used in PHP callbacks is included in the limit. 26 | 27 | 28 | Setting the time limit from a callback while Lua is running causes the 29 | timer to be reset, or started if it was not already running. 30 | 31 | 32 | 33 | On Windows, the CPU limit will be ignored. On operating systems that do 34 | not support CLOCK_THREAD_CPUTIME_ID, such as FreeBSD 35 | and Mac OS X, wall-clock time rather than CPU time will be limited. 36 | 37 | 38 | 39 | 40 | 41 | &reftitle.parameters; 42 | 43 | 44 | limit 45 | 46 | 47 | Limit as a float in seconds, or false for no limit. 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | &reftitle.returnvalues; 56 | 57 | &return.void; 58 | 59 | 60 | 61 | 62 | &reftitle.examples; 63 | 64 | 65 | Calling a Lua function 66 | 67 | setCPULimit( 2 ); 75 | 76 | // Run Lua code 77 | $sandbox->loadString( 'while true do end' )->call(); 78 | 79 | ?> 80 | ]]> 81 | 82 | &example.outputs.similar; 83 | 84 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | &reftitle.seealso; 94 | 95 | 96 | LuaSandbox::getCPUUsage 97 | LuaSandbox::setMemoryLimit 98 | 99 | 100 | 101 | 102 | 103 | 104 | 124 | -------------------------------------------------------------------------------- /docbook/luasandbox/setmemorylimit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::setMemoryLimit 7 | Set the memory limit for the Lua environment 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public voidLuaSandbox::setMemoryLimit 14 | intlimit 15 | 16 | 17 | Sets the memory limit for the Lua environment. 18 | 19 | 20 | If this limit is exceeded, a LuaSandboxMemoryError 21 | exception is thrown. 22 | 23 | 24 | 25 | 26 | 27 | &reftitle.parameters; 28 | 29 | 30 | limit 31 | 32 | 33 | Memory limit in bytes. 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | &reftitle.returnvalues; 42 | 43 | &return.void; 44 | 45 | 46 | 47 | 48 | &reftitle.examples; 49 | 50 | 51 | Calling a Lua function 52 | 53 | setMemoryLimit( 50 * 1024 * 1024 ); 61 | 62 | // Run Lua code 63 | $sandbox->loadString( 'local x = "x"; while true do x = x .. x; end' )->call(); 64 | 65 | ?> 66 | ]]> 67 | 68 | &example.outputs.similar; 69 | 70 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | &reftitle.seealso; 80 | 81 | 82 | LuaSandbox::getMemoryUsage 83 | LuaSandbox::getPeakMemoryUsage 84 | LuaSandbox::setCPULimit 85 | 86 | 87 | 88 | 89 | 90 | 91 | 111 | -------------------------------------------------------------------------------- /docbook/luasandbox/unpauseusagetimer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::unpauseUsageTimer 7 | Unpause the timer paused by LuaSandbox::pauseUsageTimer 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public voidLuaSandbox::unpauseUsageTimer 14 | 15 | 16 | 17 | Unpauses the timer paused by LuaSandbox::pauseUsageTimer. 18 | 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | &no.function.parameters; 25 | 26 | 27 | 28 | &reftitle.returnvalues; 29 | 30 | &return.void; 31 | 32 | 33 | 34 | 35 | &reftitle.seealso; 36 | 37 | 38 | LuaSandbox::pauseUsageTimer 39 | LuaSandbox::setCPULimit 40 | 41 | 42 | 43 | 44 | 45 | 46 | 66 | -------------------------------------------------------------------------------- /docbook/luasandbox/wrapphpfunction.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandbox::wrapPhpFunction 7 | Wrap a PHP callable in a LuaSandboxFunction 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public LuaSandboxFunctionLuaSandbox::wrapPhpFunction 14 | callablefunction 15 | 16 | 17 | Wraps a PHP callable in a LuaSandboxFunction, so it 18 | can be passed into Lua as an anonymous function. 19 | 20 | 21 | The function must return either an array of values (which may be empty), 22 | or &null; which is equivalent to returning the empty array. 23 | 24 | 25 | Exceptions will be raised as errors in Lua, however only 26 | LuaSandboxRuntimeError exceptions may be caught 27 | inside Lua with pcall() or xpcall(). 28 | 29 | 30 | For more information about calling Lua functions and the return values, 31 | see LuaSandboxFunction::call. 32 | 33 | 34 | 35 | 36 | &reftitle.parameters; 37 | 38 | 39 | function 40 | 41 | 42 | Callable to wrap. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | &reftitle.returnvalues; 51 | 52 | Returns a LuaSandboxFunction. 53 | 54 | 55 | 56 | 57 | &reftitle.seealso; 58 | 59 | 60 | LuaSandbox::loadString 61 | LuaSandbox::registerLibrary 62 | 63 | 64 | 65 | 66 | 67 | 68 | 88 | -------------------------------------------------------------------------------- /docbook/luasandboxerror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxError class 7 | LuaSandboxError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Base class for LuaSandbox exceptions 16 | 17 |
18 | 19 | 20 |
21 | &reftitle.classsynopsis; 22 | 23 | 24 | 25 | LuaSandboxError 26 | 27 | 28 | 29 | 30 | LuaSandboxError 31 | 32 | 33 | 34 | extends 35 | Exception 36 | 37 | 38 | 39 | Constants 40 | 41 | const 42 | int 43 | LuaSandboxError::RUN 44 | 2 45 | 46 | 47 | const 48 | int 49 | LuaSandboxError::SYNTAX 50 | 3 51 | 52 | 53 | const 54 | int 55 | LuaSandboxError::MEM 56 | 4 57 | 58 | 59 | const 60 | int 61 | LuaSandboxError::ERR 62 | 5 63 | 64 | 65 | &InheritedProperties; 66 | 67 | 68 | 69 | 73 | 74 | &InheritedMethods; 75 | 76 | 77 | 78 | 79 | 80 |
81 | 82 | 83 |
84 | &reftitle.constants; 85 | 86 | 87 | 88 | LuaSandboxError::RUN 89 | 90 | 91 | 92 | 93 | 94 | 95 | LuaSandboxError::SYNTAX 96 | 97 | 98 | 99 | 100 | 101 | 102 | LuaSandboxError::MEM 103 | 104 | 105 | 106 | 107 | 108 | 109 | LuaSandboxError::ERR 110 | 111 | 112 | 113 | 114 | 115 | 116 |
117 | 118 | 119 | 120 |
121 | 122 | 123 | 124 |
125 | 126 | 146 | -------------------------------------------------------------------------------- /docbook/luasandboxerrorerror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxErrorError class 7 | LuaSandboxErrorError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Exception thrown when Lua encounters an error inside an error handler. 16 | 17 |
18 | 19 | 20 |
21 | &reftitle.classsynopsis; 22 | 23 | 24 | 25 | LuaSandboxErrorError 26 | 27 | 28 | 29 | 30 | LuaSandboxErrorError 31 | 32 | 33 | 34 | extends 35 | LuaSandboxFatalError 36 | 37 | 38 | 39 | 40 | &InheritedProperties; 41 | 42 | 43 | 44 | 48 | 49 | &InheritedMethods; 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 83 | -------------------------------------------------------------------------------- /docbook/luasandboxfatalerror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxFatalError class 7 | LuaSandboxFatalError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Uncatchable LuaSandbox exceptions. 16 | 17 | 18 | These may not be caught inside Lua using 19 | pcall() or xpcall(). 20 | 21 |
22 | 23 | 24 |
25 | &reftitle.classsynopsis; 26 | 27 | 28 | 29 | LuaSandboxFatalError 30 | 31 | 32 | 33 | 34 | LuaSandboxFatalError 35 | 36 | 37 | 38 | extends 39 | LuaSandboxError 40 | 41 | 42 | 43 | 44 | &InheritedProperties; 45 | 46 | 47 | 48 | 52 | 53 | &InheritedMethods; 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 |
62 | 63 | 64 | 65 |
66 | 67 | 87 | -------------------------------------------------------------------------------- /docbook/luasandboxfunction.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxFunction class 7 | LuaSandboxFunction 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Represents a Lua function, allowing it to be called from PHP. 16 | 17 | 18 | A LuaSandboxFunction may be obtained as a return value from Lua, as a parameter 19 | passed to a callback from Lua, or by using 20 | LuaSandbox::wrapPhpFunction, 21 | LuaSandbox::loadString, or 22 | LuaSandbox::loadBinary. 23 | 24 |
25 | 26 | 27 |
28 | &reftitle.classsynopsis; 29 | 30 | 31 | 32 | LuaSandboxFunction 33 | 34 | 35 | 36 | 37 | LuaSandboxFunction 38 | 39 | 40 | 41 | 42 | &Methods; 43 | 44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | &reference.luasandbox.entities.luasandboxfunction; 52 | 53 |
54 | 55 | 75 | -------------------------------------------------------------------------------- /docbook/luasandboxfunction/call.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandboxFunction::call 7 | Call a Lua function 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public arrayboolLuaSandboxFunction::call 14 | stringargs 15 | 16 | 17 | Calls a Lua function. 18 | 19 | 20 | Errors considered to be the fault of the PHP code will result in the 21 | function returning false and E_WARNING 22 | being raised, for example, a resource type being used as an 23 | argument. Lua errors will result in a LuaSandboxRuntimeError 24 | exception being thrown. 25 | 26 | 27 | PHP and Lua types are converted as follows: 28 | 29 | 30 | 31 | 32 | PHP &null; is Lua nil, and vice versa. 33 | 34 | 35 | 36 | PHP ints and floats are converted to Lua 37 | numbers. Infinity and NAN are supported. 38 | 39 | 40 | 41 | 42 | Lua numbers without a fractional part between approximately -2**53 43 | and 2**53 are converted to PHP ints, with others 44 | being converted to PHP floats. 45 | 46 | 47 | 48 | PHP bools are Lua booleans, and vice versa. 49 | 50 | 51 | PHP strings are Lua strings, and vice versa. 52 | 53 | 54 | 55 | Lua functions are PHP LuaSandboxFunction objects, and vice versa. 56 | General PHP callables are not supported. 57 | 58 | 59 | 60 | 61 | PHP arrays are converted to Lua tables, and vice versa. 62 | 63 | 64 | 65 | 66 | 67 | Note that Lua typically indexes arrays from 1, while PHP indexes 68 | arrays from 0. No adjustment is made for these differing 69 | conventions. 70 | 71 | 72 | 73 | Self-referential arrays are not supported in either direction. 74 | 75 | 76 | PHP references are dereferenced. 77 | 78 | 79 | 80 | Lua __pairs and __ipairs are processed. 81 | __index is ignored. 82 | 83 | 84 | 85 | 86 | When converting from PHP to Lua, integer keys between 87 | -2**53 and 2**53 are represented 88 | as Lua numbers. All other keys are represented as Lua strings. 89 | 90 | 91 | 92 | 93 | When converting from Lua to PHP, keys other than strings and 94 | numbers will result in an error, as will collisions when converting 95 | numbers to strings or vice versa (since PHP considers things like 96 | $a[0] and $a["0"] as being equivalent). 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | All other types are unsupported and will raise an error/exception, 105 | including general PHP objects and Lua userdata and thread types. 106 | 107 | 108 | 109 | 110 | 111 | Lua functions inherently return a list of results. So on success, this 112 | method returns an array containing all of the values returned by Lua, 113 | with int keys starting from zero. Lua may return no results, in 114 | which case an empty array is returned. 115 | 116 | 117 | 118 | 119 | &reftitle.parameters; 120 | 121 | 122 | args 123 | 124 | 125 | Arguments passed to the function. 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | &reftitle.returnvalues; 134 | 135 | Returns an array of values returned by the function, which may be empty, 136 | or false on error. 137 | 138 | 139 | 140 | 141 | 142 | 162 | -------------------------------------------------------------------------------- /docbook/luasandboxfunction/construct.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandboxFunction::__construct 7 | Unused 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | final private LuaSandboxFunction::__construct 14 | 15 | 16 | 17 | LuaSandboxFunction are obtained as a return value from Lua, 18 | as a parameter passed to a callback from Lua, or by using 19 | LuaSandbox::wrapPhpFunction, 20 | LuaSandbox::loadString, or 21 | LuaSandbox::loadBinary. They cannot be constructed directly. 22 | 23 | 24 | 25 | 26 | 27 | &reftitle.parameters; 28 | &no.function.parameters; 29 | 30 | 31 | 32 | 33 | 53 | -------------------------------------------------------------------------------- /docbook/luasandboxfunction/dump.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LuaSandboxFunction::dump 7 | Dump the function as a binary blob 8 | 9 | 10 | 11 | &reftitle.description; 12 | 13 | public stringLuaSandboxFunction::dump 14 | 15 | 16 | 17 | Dumps the function as a binary blob. 18 | 19 | 20 | 21 | 22 | 23 | &reftitle.parameters; 24 | &no.function.parameters; 25 | 26 | 27 | 28 | &reftitle.returnvalues; 29 | 30 | Returns a string that may be passed to LuaSandbox::loadBinary. 31 | 32 | 33 | 34 | 35 | 36 | 56 | -------------------------------------------------------------------------------- /docbook/luasandboxmemoryerror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxMemoryError class 7 | LuaSandboxMemoryError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Exception thrown when Lua cannot allocate memory. 16 | 17 |
18 | 19 | 20 |
21 | &reftitle.classsynopsis; 22 | 23 | 24 | 25 | LuaSandboxMemoryError 26 | 27 | 28 | 29 | 30 | LuaSandboxMemoryError 31 | 32 | 33 | 34 | extends 35 | LuaSandboxFatalError 36 | 37 | 38 | 39 | 40 | &InheritedProperties; 41 | 42 | 43 | 44 | 48 | 49 | &InheritedMethods; 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 |
58 | &reftitle.seealso; 59 | 60 | 61 | LuaSandbox::setMemoryLimit 62 | 63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 |
71 | 72 | 92 | -------------------------------------------------------------------------------- /docbook/luasandboxruntimeerror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxRuntimeError class 7 | LuaSandboxRuntimeError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Catchable LuaSandbox runtime exceptions. 16 | 17 | 18 | These may be caught inside Lua using 19 | pcall() or xpcall(). 20 | 21 |
22 | 23 | 24 |
25 | &reftitle.classsynopsis; 26 | 27 | 28 | 29 | LuaSandboxRuntimeError 30 | 31 | 32 | 33 | 34 | LuaSandboxRuntimeError 35 | 36 | 37 | 38 | extends 39 | LuaSandboxError 40 | 41 | 42 | 43 | 44 | &InheritedProperties; 45 | 46 | 47 | 48 | 52 | 53 | &InheritedMethods; 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 |
62 | 63 | 64 | 65 |
66 | 67 | 87 | -------------------------------------------------------------------------------- /docbook/luasandboxsyntaxerror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxSyntaxError class 7 | LuaSandboxSyntaxError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Exception thrown when Lua code cannot be parsed. 16 | 17 |
18 | 19 | 20 |
21 | &reftitle.classsynopsis; 22 | 23 | 24 | 25 | LuaSandboxSyntaxError 26 | 27 | 28 | 29 | 30 | LuaSandboxSyntaxError 31 | 32 | 33 | 34 | extends 35 | LuaSandboxFatalError 36 | 37 | 38 | 39 | 40 | &InheritedProperties; 41 | 42 | 43 | 44 | 48 | 49 | &InheritedMethods; 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 83 | -------------------------------------------------------------------------------- /docbook/luasandboxtimeouterror.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The LuaSandboxTimeoutError class 7 | LuaSandboxTimeoutError 8 | 9 | 10 | 11 | 12 |
13 | &reftitle.intro; 14 | 15 | Exception thrown when the configured CPU time limit is exceeded. 16 | 17 |
18 | 19 | 20 |
21 | &reftitle.classsynopsis; 22 | 23 | 24 | 25 | LuaSandboxTimeoutError 26 | 27 | 28 | 29 | 30 | LuaSandboxTimeoutError 31 | 32 | 33 | 34 | extends 35 | LuaSandboxFatalError 36 | 37 | 38 | 39 | 40 | &InheritedProperties; 41 | 42 | 43 | 44 | 48 | 49 | &InheritedMethods; 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 |
58 | &reftitle.seealso; 59 | 60 | 61 | LuaSandbox::setCPULimit 62 | 63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 |
71 | 72 | 92 | -------------------------------------------------------------------------------- /docbook/reference.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LuaSandbox &Functions; 6 | 7 | &reference.luasandbox.entities.functions; 8 | 9 | 10 | 11 | 31 | -------------------------------------------------------------------------------- /docbook/setup.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | &reftitle.setup; 6 | 7 |
8 | &reftitle.required; 9 | 10 | To use this extension, Lua 5.1 will need to be installed, available on the Lua homepage. 11 | 12 | 13 | To make full use of the timer features, LuaSandbox should be installed on 14 | Linux. 15 | 16 | 17 | If FreeBSD or Mac OS X is used, only real (wall-clock) time is 18 | supported, the functions purporting to return CPU time will actually return 19 | wall clock time. 20 | 21 | 22 | If Windows is used, no timer functions will be supported. The time limits 23 | will be inoperable. 24 | 25 |
26 | 27 |
28 | &reftitle.install; 29 | 30 | &pecl.moved; 31 | 32 | 33 | &pecl.info; 34 | &url.pecl.package;luasandbox. 35 | 36 | 37 | If the operating system is Debian 10 or later, or Ubuntu 18.04 or later, then 38 | LuaSandbox should typically be installed from the package 39 | php-luasandbox: 40 | 41 | 44 | 45 | 46 |
47 | 48 |
49 | &reftitle.runtime; 50 | &no.config; 51 |
52 | 53 |
54 | &reftitle.resources; 55 | &no.resource; 56 |
57 | 58 |
59 | 60 | 80 | -------------------------------------------------------------------------------- /docbook/versions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 68 | -------------------------------------------------------------------------------- /library.c: -------------------------------------------------------------------------------- 1 | /** 2 | * This file holds the library of functions which are written in C and exposed 3 | * to Lua code, and the code which manages registration of both the custom 4 | * library and the parts of the standard Lua library which we allow. 5 | */ 6 | 7 | #ifdef HAVE_CONFIG_H 8 | #include "config.h" 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "php.h" 17 | #include "php_luasandbox.h" 18 | 19 | #ifdef LUASANDBOX_NO_CLOCK 20 | #include 21 | #endif 22 | 23 | static void luasandbox_lib_filter_table(lua_State * L, char ** member_names); 24 | static HashTable * luasandbox_lib_get_allowed_globals(); 25 | 26 | static int luasandbox_base_tostring(lua_State * L); 27 | static int luasandbox_math_random(lua_State * L); 28 | static int luasandbox_math_randomseed(lua_State * L); 29 | static int luasandbox_base_pcall(lua_State * L); 30 | static int luasandbox_base_xpcall(lua_State *L); 31 | static int luasandbox_os_clock(lua_State * L); 32 | 33 | #if LUA_VERSION_NUM < 502 34 | static int luasandbox_base_pairs(lua_State *L); 35 | static int luasandbox_base_ipairs(lua_State *L); 36 | #endif 37 | 38 | /** 39 | * Allowed global variables. Omissions are: 40 | * * pcall, xpcall: We have our own versions which don't allow interception of 41 | * timeout etc. errors. 42 | * * loadfile: insecure. 43 | * * load, loadstring: Probably creates a protected environment so has 44 | * the same problem as pcall. Also omitting these makes analysis of the 45 | * code for runtime etc. feasible. 46 | * * print: Not compatible with a sandbox environment 47 | * * tostring: Provides addresses of tables and functions, which provides an 48 | * easy ASLR workaround or heap address discovery mechanism for a memory 49 | * corruption exploit. We have our own version. 50 | * * Any new or undocumented functions like newproxy. 51 | * * package: cpath, loadlib etc. are insecure. 52 | * * coroutine: Not useful for our application so unreviewed at present. 53 | * * io, file, os: insecure 54 | * * debug: Provides various ways to break the sandbox, such as setupvalue() 55 | * and getregistry(). 56 | */ 57 | char * luasandbox_allowed_globals[] = { 58 | // base 59 | "assert", 60 | "error", 61 | "getfenv", 62 | "getmetatable", 63 | "ipairs", 64 | "next", 65 | "pairs", 66 | "rawequal", 67 | "rawget", 68 | "rawset", 69 | "select", 70 | "setfenv", 71 | "setmetatable", 72 | "tonumber", 73 | "type", 74 | "unpack", 75 | "_G", 76 | "_VERSION", 77 | // libs 78 | "string", 79 | "table", 80 | "math", 81 | "os", 82 | "debug", 83 | NULL 84 | }; 85 | 86 | char * luasandbox_allowed_os_members[] = { 87 | "date", 88 | "difftime", 89 | "time", 90 | NULL 91 | }; 92 | 93 | char * luasandbox_allowed_debug_members[] = { 94 | "traceback", 95 | NULL 96 | }; 97 | 98 | 99 | 100 | ZEND_EXTERN_MODULE_GLOBALS(luasandbox); 101 | 102 | /** {{{ luasandbox_lib_register 103 | */ 104 | void luasandbox_lib_register(lua_State * L) 105 | { 106 | // Load the standard libraries that we need 107 | lua_pushcfunction(L, luaopen_base); 108 | lua_call(L, 0, 0); 109 | lua_pushcfunction(L, luaopen_table); 110 | lua_call(L, 0, 0); 111 | lua_pushcfunction(L, luaopen_math); 112 | lua_call(L, 0, 0); 113 | lua_pushcfunction(L, luaopen_debug); 114 | lua_call(L, 0, 0); 115 | lua_pushcfunction(L, luaopen_os); 116 | lua_call(L, 0, 0); 117 | 118 | // Install our own string library 119 | lua_pushcfunction(L, luasandbox_open_string); 120 | lua_call(L, 0, 0); 121 | 122 | // Filter the os library 123 | lua_getglobal(L, "os"); 124 | luasandbox_lib_filter_table(L, luasandbox_allowed_os_members); 125 | lua_setglobal(L, "os"); 126 | 127 | // Filter the debug library 128 | lua_getglobal(L, "debug"); 129 | luasandbox_lib_filter_table(L, luasandbox_allowed_debug_members); 130 | lua_setglobal(L, "debug"); 131 | 132 | // Remove any globals that aren't in a whitelist. This is mostly to remove 133 | // unsafe functions from the base library. 134 | lua_pushnil(L); 135 | while (lua_next(L, LUA_GLOBALSINDEX) != 0) { 136 | const char * key; 137 | size_t key_len; 138 | lua_pop(L, 1); 139 | if (lua_type(L, -1) != LUA_TSTRING) { 140 | continue; 141 | } 142 | key = lua_tolstring(L, -1, &key_len); 143 | if (!zend_hash_str_exists(luasandbox_lib_get_allowed_globals(), key, key_len)) { 144 | // Not allowed, delete it 145 | lua_pushnil(L); 146 | lua_setglobal(L, key); 147 | } 148 | } 149 | 150 | // Install our own versions of tostring, pcall, xpcall 151 | lua_pushcfunction(L, luasandbox_base_tostring); 152 | lua_setglobal(L, "tostring"); 153 | lua_pushcfunction(L, luasandbox_base_pcall); 154 | lua_setglobal(L, "pcall"); 155 | lua_pushcfunction(L, luasandbox_base_xpcall); 156 | lua_setglobal(L, "xpcall"); 157 | 158 | // Remove string.dump: may expose private data 159 | lua_getglobal(L, "string"); 160 | lua_pushnil(L); 161 | lua_setfield(L, -2, "dump"); 162 | lua_pop(L, 1); 163 | 164 | // Install our own versions of math.random and math.randomseed 165 | lua_getglobal(L, "math"); 166 | lua_pushcfunction(L, luasandbox_math_random); 167 | lua_setfield(L, -2, "random"); 168 | lua_pushcfunction(L, luasandbox_math_randomseed); 169 | lua_setfield(L, -2, "randomseed"); 170 | lua_pop(L, 1); 171 | 172 | // Install our own version of os.clock(), which uses our high-resolution 173 | // usage timer 174 | lua_getglobal(L, "os"); 175 | lua_pushcfunction(L, luasandbox_os_clock); 176 | lua_setfield(L, -2, "clock"); 177 | lua_pop(L, 1); 178 | 179 | // Install our own versions of pairs and ipairs, if necessary 180 | #if LUA_VERSION_NUM < 502 181 | lua_getfield(L, LUA_GLOBALSINDEX, "pairs"); 182 | lua_setfield(L, LUA_REGISTRYINDEX, "luasandbox_old_pairs"); 183 | lua_getfield(L, LUA_GLOBALSINDEX, "ipairs"); 184 | lua_setfield(L, LUA_REGISTRYINDEX, "luasandbox_old_ipairs"); 185 | lua_pushcfunction(L, luasandbox_base_pairs); 186 | lua_setglobal(L, "pairs"); 187 | lua_pushcfunction(L, luasandbox_base_ipairs); 188 | lua_setglobal(L, "ipairs"); 189 | #endif 190 | } 191 | /* }}} */ 192 | 193 | /** {{{ luasandbox_lib_filter_table 194 | * 195 | * Make a copy of the table at the top of the stack, and remove any members 196 | * from it that aren't in the given whitelist. 197 | */ 198 | static void luasandbox_lib_filter_table(lua_State * L, char ** member_names) 199 | { 200 | int i, n; 201 | int si = lua_gettop(L); 202 | for (n = 0; member_names[n]; n++); 203 | lua_createtable(L, 0, n); 204 | for (i = 0; member_names[i]; i++) { 205 | lua_getfield(L, si, member_names[i]); 206 | lua_setfield(L, si+1, member_names[i]); 207 | } 208 | lua_replace(L, si); 209 | } 210 | /* }}} */ 211 | 212 | /** {{{ luasandbox_lib_destroy_globals */ 213 | void luasandbox_lib_destroy_globals() 214 | { 215 | if (LUASANDBOX_G(allowed_globals)) { 216 | zend_hash_destroy(LUASANDBOX_G(allowed_globals)); 217 | FREE_HASHTABLE(LUASANDBOX_G(allowed_globals)); 218 | LUASANDBOX_G(allowed_globals) = NULL; 219 | } 220 | } 221 | /* }}} */ 222 | 223 | /** {{{ luasandbox_lib_get_allowed_globals 224 | * 225 | * Get a HashTable of allowed global variables 226 | */ 227 | static HashTable * luasandbox_lib_get_allowed_globals() 228 | { 229 | int i, n; 230 | if (LUASANDBOX_G(allowed_globals)) { 231 | return LUASANDBOX_G(allowed_globals); 232 | } 233 | 234 | for (n = 0; luasandbox_allowed_globals[n]; n++); 235 | 236 | ALLOC_HASHTABLE(LUASANDBOX_G(allowed_globals)); 237 | zend_hash_init(LUASANDBOX_G(allowed_globals), n, NULL, NULL, 0); 238 | 239 | zval zv; 240 | ZVAL_TRUE(&zv); 241 | 242 | for (i = 0; luasandbox_allowed_globals[i]; i++) { 243 | zend_hash_str_update(LUASANDBOX_G(allowed_globals), 244 | luasandbox_allowed_globals[i], strlen(luasandbox_allowed_globals[i]), &zv); 245 | } 246 | 247 | return LUASANDBOX_G(allowed_globals); 248 | } 249 | /* }}} */ 250 | 251 | /** {{{ luasandbox_base_tostring 252 | * 253 | * This is identical to luaB_tostring except that it does not call lua_topointer(). 254 | */ 255 | static int luasandbox_base_tostring(lua_State * L) 256 | { 257 | luaL_checkany(L, 1); 258 | if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ 259 | return 1; /* use its value */ 260 | switch (lua_type(L, 1)) { 261 | case LUA_TNUMBER: 262 | lua_pushstring(L, lua_tostring(L, 1)); 263 | break; 264 | case LUA_TSTRING: 265 | lua_pushvalue(L, 1); 266 | break; 267 | case LUA_TBOOLEAN: 268 | lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); 269 | break; 270 | case LUA_TNIL: 271 | lua_pushliteral(L, "nil"); 272 | break; 273 | default: 274 | lua_pushfstring(L, "%s", luaL_typename(L, 1)); 275 | break; 276 | } 277 | return 1; 278 | } 279 | /* }}} */ 280 | 281 | /** {{{ luasandbox_math_random 282 | * 283 | * A math.random implementation that doesn't share state with PHP's rand() 284 | */ 285 | static int luasandbox_math_random(lua_State * L) 286 | { 287 | php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); 288 | 289 | #ifdef PHP_WIN32 290 | // MSVC does not provide rand_r(). Note that srand/rand still does not 291 | // share state with PHP's rand() because PHP's rand() is an alias for mt_rand() 292 | // (and does not use C srand/rand) as of PHP 7.1. 293 | int i = rand(); 294 | #else 295 | int i = rand_r(&sandbox->random_seed); 296 | #endif 297 | 298 | if (i >= RAND_MAX) { 299 | i -= RAND_MAX; 300 | } 301 | lua_Number r = (lua_Number)i / (lua_Number)RAND_MAX; 302 | switch (lua_gettop(L)) { /* check number of arguments */ 303 | case 0: { /* no arguments */ 304 | lua_pushnumber(L, r); /* Number between 0 and 1 */ 305 | break; 306 | } 307 | case 1: { /* only upper limit */ 308 | int u = luaL_checkint(L, 1); 309 | luaL_argcheck(L, 1<=u, 1, "interval is empty"); 310 | lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ 311 | break; 312 | } 313 | case 2: { /* lower and upper limits */ 314 | int l = luaL_checkint(L, 1); 315 | int u = luaL_checkint(L, 2); 316 | luaL_argcheck(L, l<=u, 2, "interval is empty"); 317 | lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ 318 | break; 319 | } 320 | default: return luaL_error(L, "wrong number of arguments"); 321 | } 322 | return 1; 323 | } 324 | /* }}} */ 325 | 326 | /** {{{ luasandbox_math_randomseed 327 | * 328 | * Set the seed for the custom math.random. 329 | */ 330 | static int luasandbox_math_randomseed(lua_State * L) 331 | { 332 | php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); 333 | sandbox->random_seed = (unsigned int)luaL_checkint(L, 1); 334 | #ifdef PHP_WIN32 335 | srand(sandbox->random_seed); 336 | #endif 337 | return 0; 338 | } 339 | /* }}} */ 340 | 341 | /** {{{ luasandbox_lib_rethrow_fatal 342 | * 343 | * If the error on the top of the stack with the error return code given as a 344 | * parameter is a fatal, rethrow the error. If the error is rethrown, the 345 | * function does not return. 346 | */ 347 | static void luasandbox_lib_rethrow_fatal(lua_State * L, int status) 348 | { 349 | switch (status) { 350 | case 0: 351 | // No error 352 | return; 353 | case LUA_ERRRUN: 354 | // A runtime error which we can rethrow in a normal way 355 | if (luasandbox_is_fatal(L, -1)) { 356 | lua_error(L); 357 | } 358 | break; 359 | case LUA_ERRMEM: 360 | case LUA_ERRERR: 361 | // Lua doesn't provide a public API for rethrowing these, so we 362 | // have to convert them to our own fatal error type 363 | if (!luasandbox_is_fatal(L, -1)) { 364 | luasandbox_wrap_fatal(L); 365 | } 366 | lua_error(L); 367 | break; // not reached 368 | } 369 | } 370 | /* }}} */ 371 | 372 | /** {{{ luasandbox_lib_error_wrapper 373 | * 374 | * Wrapper for the xpcall error function 375 | */ 376 | static int luasandbox_lib_error_wrapper(lua_State * L) 377 | { 378 | int status; 379 | luaL_checkany(L, 1); 380 | 381 | // This function is only called from luaG_errormsg(), which will later 382 | // unconditionally set the status code to LUA_ERRRUN, so we can assume 383 | // that the error type is equivalent to LUA_ERRRUN. 384 | if (luasandbox_is_fatal(L, 1)) { 385 | // Just return to whatever called lua_pcall(), don't call the user 386 | // function 387 | return lua_gettop(L); 388 | } 389 | 390 | // Put the user error function at the bottom of the stack 391 | lua_pushvalue(L, lua_upvalueindex(1)); 392 | lua_insert(L, 1); 393 | // Call it, passing through the arguments to this function 394 | status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); 395 | if (status != 0) { 396 | luasandbox_lib_rethrow_fatal(L, LUA_ERRERR); 397 | } 398 | return lua_gettop(L); 399 | } 400 | /* }}} */ 401 | 402 | /** {{{ luasandbox_base_pcall 403 | * 404 | * This is our implementation of the Lua function pcall(). It allows Lua code 405 | * to handle its own errors, but forces internal errors to be rethrown. 406 | */ 407 | static int luasandbox_base_pcall(lua_State * L) 408 | { 409 | int status; 410 | luaL_checkany(L, 1); 411 | status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); 412 | luasandbox_lib_rethrow_fatal(L, status); 413 | lua_pushboolean(L, (status == 0)); 414 | lua_insert(L, 1); 415 | return lua_gettop(L); // return status + all results 416 | } 417 | /* }}} */ 418 | 419 | /** {{{ luasandbox_base_xpcall 420 | * 421 | * This is our implementation of the Lua function xpcall(). It allows Lua code 422 | * to handle its own errors, but forces internal errors to be rethrown. 423 | */ 424 | static int luasandbox_base_xpcall (lua_State *L) 425 | { 426 | int status; 427 | luaL_checkany(L, 2); 428 | lua_settop(L, 2); 429 | 430 | // We wrap the error function in a C closure. The error function already 431 | // happens to be at the top of the stack, so we don't need to push it before 432 | // calling lua_pushcfunction to make it an upvalue 433 | lua_pushcclosure(L, luasandbox_lib_error_wrapper, 1); 434 | lua_insert(L, 1); // put error function under function to be called 435 | 436 | status = lua_pcall(L, 0, LUA_MULTRET, 1); 437 | luasandbox_lib_rethrow_fatal(L, status); 438 | lua_pushboolean(L, (status == 0)); 439 | lua_replace(L, 1); 440 | return lua_gettop(L); // return status + all results 441 | } 442 | /* }}} */ 443 | 444 | /** {{{ luasandbox_os_clock 445 | * 446 | * Implementation of os.clock() which uses our high-resolution usage timer, 447 | * if available, to provide an accurate measure of Lua CPU usage since a 448 | * particular LuaSandbox object was created. 449 | */ 450 | static int luasandbox_os_clock(lua_State * L) 451 | { 452 | double clk; 453 | 454 | #ifdef LUASANDBOX_NO_CLOCK 455 | clk = ((double)clock())/(double)CLOCKS_PER_SEC; 456 | #else 457 | struct timespec ts; 458 | php_luasandbox_obj * sandbox = luasandbox_get_php_obj(L); 459 | luasandbox_timer_get_usage(&sandbox->timer, &ts); 460 | clk = ts.tv_sec + 1e-9 * ts.tv_nsec; 461 | #endif 462 | 463 | // Reduce precision to 20μs to mitigate timing attacks 464 | clk = round(clk * 50000) / 50000; 465 | 466 | lua_pushnumber(L, (lua_Number)clk); 467 | return 1; 468 | } 469 | 470 | /* }}} */ 471 | 472 | #if LUA_VERSION_NUM < 502 473 | /** {{{ luasandbox_base_pairs 474 | * 475 | * This is our implementation of the Lua function pairs(). It allows the Lua 476 | * 5.2 __pairs metamethod to override the standard behavior. 477 | */ 478 | static int luasandbox_base_pairs (lua_State *L) 479 | { 480 | if (!luaL_getmetafield(L, 1, "__pairs")) { 481 | luaL_checktype(L, 1, LUA_TTABLE); 482 | lua_getfield(L, LUA_REGISTRYINDEX, "luasandbox_old_pairs"); 483 | } 484 | lua_pushvalue(L, 1); 485 | lua_call(L, 1, 3); 486 | return 3; 487 | } 488 | /* }}} */ 489 | 490 | /** {{{ luasandbox_base_ipairs 491 | * 492 | * This is our implementation of the Lua function ipairs(). It allows the Lua 493 | * 5.2 __ipairs metamethod to override the standard behavior. 494 | */ 495 | static int luasandbox_base_ipairs (lua_State *L) 496 | { 497 | if (!luaL_getmetafield(L, 1, "__ipairs")) { 498 | luaL_checktype(L, 1, LUA_TTABLE); 499 | lua_getfield(L, LUA_REGISTRYINDEX, "luasandbox_old_ipairs"); 500 | } 501 | lua_pushvalue(L, 1); 502 | lua_call(L, 1, 3); 503 | return 3; 504 | } 505 | /* }}} */ 506 | #endif 507 | -------------------------------------------------------------------------------- /luasandbox_compat.h: -------------------------------------------------------------------------------- 1 | // Macros to hide the differences between PHP versions 2 | // Implementation details, include only from .c files 3 | #ifndef LUASANDBOX_COMPAT_H 4 | #define LUASANDBOX_COMPAT_H 5 | 6 | #if PHP_VERSION_ID < 80000 7 | 8 | #define luasandbox_update_property(scope, object, name, name_length, value) \ 9 | zend_update_property(scope, object, name, name_length, value) 10 | 11 | #define luasandbox_update_property_string(scope, object, name, name_length, value) \ 12 | zend_update_property_string(scope, object, name, name_length, value) 13 | 14 | #define luasandbox_update_property_long(scope, object, name, name_length, value) \ 15 | zend_update_property_long(scope, object, name, name_length, value) 16 | 17 | #define luasandbox_read_property(scope, object, name, name_length, silent, rv) \ 18 | zend_read_property(scope, object, name, name_length, silent, rv) 19 | 20 | #else 21 | 22 | #define luasandbox_update_property(scope, object, name, name_length, value) \ 23 | zend_update_property(scope, Z_OBJ_P(object), name, name_length, value) 24 | 25 | #define luasandbox_update_property_string(scope, object, name, name_length, value) \ 26 | zend_update_property_string(scope, Z_OBJ_P(object), name, name_length, value) 27 | 28 | #define luasandbox_update_property_long(scope, object, name, name_length, value) \ 29 | zend_update_property_long(scope, Z_OBJ_P(object), name, name_length, value) 30 | 31 | #define luasandbox_read_property(scope, object, name, name_length, silent, rv) \ 32 | zend_read_property(scope, Z_OBJ_P(object), name, name_length, silent, rv) 33 | 34 | #endif 35 | 36 | #endif // LUASANDBOX_COMPAT_H 37 | -------------------------------------------------------------------------------- /luasandbox_lstrlib.patch: -------------------------------------------------------------------------------- 1 | --- lstrlib.c 2013-02-27 17:27:45.973598391 -0800 2 | +++ luasandbox_lstrlib.c 2013-02-27 17:58:33.809497263 -0800 3 | @@ -19,6 +19,12 @@ 4 | #include "lauxlib.h" 5 | #include "lualib.h" 6 | 7 | +#ifdef LUAI_MAXCALLS 8 | +#define LUASANDBOX_MAX_MATCH_DEPTH LUAI_MAXCALLS 9 | +#else 10 | +#define LUASANDBOX_MAX_MATCH_DEPTH 20000 11 | +#endif 12 | + 13 | 14 | /* macro to `unsign' a character */ 15 | #define uchar(c) ((unsigned char)(c)) 16 | @@ -176,6 +182,7 @@ 17 | const char *init; 18 | ptrdiff_t len; 19 | } capture[LUA_MAXCAPTURES]; 20 | + int depth; /* the current recursion depth of match() */ 21 | } MatchState; 22 | 23 | 24 | @@ -361,24 +368,44 @@ 25 | else return NULL; 26 | } 27 | 28 | +static int do_nothing(lua_State * L) { 29 | + return 0; 30 | +} 31 | + 32 | +#define MATCH_RETURN(r) { \ 33 | + const char * result = (r); \ 34 | + --ms->depth; \ 35 | + return result; \ 36 | +} 37 | 38 | static const char *match (MatchState *ms, const char *s, const char *p) { 39 | + /* If there is a call hook, trigger it now so that it's possible to 40 | + * interrupt long-running recursive match operations */ 41 | + if (lua_gethookmask(ms->L) & LUA_MASKCALL) { 42 | + lua_pushcfunction(ms->L, do_nothing); 43 | + lua_call(ms->L, 0, 0); 44 | + } 45 | + 46 | + if (++ms->depth > LUASANDBOX_MAX_MATCH_DEPTH) { 47 | + luaL_error(ms->L, "recursion depth limit exceeded"); 48 | + } 49 | init: /* using goto's to optimize tail recursion */ 50 | switch (*p) { 51 | case '(': { /* start capture */ 52 | - if (*(p+1) == ')') /* position capture? */ 53 | - return start_capture(ms, s, p+2, CAP_POSITION); 54 | - else 55 | - return start_capture(ms, s, p+1, CAP_UNFINISHED); 56 | - } 57 | + if (*(p+1) == ')') { /* position capture? */ 58 | + MATCH_RETURN(start_capture(ms, s, p+2, CAP_POSITION)); 59 | + } else { 60 | + MATCH_RETURN(start_capture(ms, s, p+1, CAP_UNFINISHED)); 61 | + } 62 | + } 63 | case ')': { /* end capture */ 64 | - return end_capture(ms, s, p+1); 65 | + MATCH_RETURN(end_capture(ms, s, p+1)); 66 | } 67 | case L_ESC: { 68 | switch (*(p+1)) { 69 | case 'b': { /* balanced string? */ 70 | s = matchbalance(ms, s, p+2); 71 | - if (s == NULL) return NULL; 72 | + if (s == NULL) MATCH_RETURN(NULL); 73 | p+=4; goto init; /* else return match(ms, s, p+4); */ 74 | } 75 | case 'f': { /* frontier? */ 76 | @@ -390,13 +417,13 @@ 77 | ep = classend(ms, p); /* points to what is next */ 78 | previous = (s == ms->src_init) ? '\0' : *(s-1); 79 | if (matchbracketclass(uchar(previous), p, ep-1) || 80 | - !matchbracketclass(uchar(*s), p, ep-1)) return NULL; 81 | + !matchbracketclass(uchar(*s), p, ep-1)) MATCH_RETURN(NULL); 82 | p=ep; goto init; /* else return match(ms, s, ep); */ 83 | } 84 | default: { 85 | if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ 86 | s = match_capture(ms, s, uchar(*(p+1))); 87 | - if (s == NULL) return NULL; 88 | + if (s == NULL) MATCH_RETURN(NULL); 89 | p+=2; goto init; /* else return match(ms, s, p+2) */ 90 | } 91 | goto dflt; /* case default */ 92 | @@ -404,12 +431,12 @@ 93 | } 94 | } 95 | case '\0': { /* end of pattern */ 96 | - return s; /* match succeeded */ 97 | + MATCH_RETURN(s); /* match succeeded */ 98 | } 99 | case '$': { 100 | - if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ 101 | - return (s == ms->src_end) ? s : NULL; /* check end of string */ 102 | - else goto dflt; 103 | + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ 104 | + MATCH_RETURN((s == ms->src_end) ? s : NULL); /* check end of string */ 105 | + goto dflt; 106 | } 107 | default: dflt: { /* it is a pattern item */ 108 | const char *ep = classend(ms, p); /* points to what is next */ 109 | @@ -418,20 +445,20 @@ 110 | case '?': { /* optional */ 111 | const char *res; 112 | if (m && ((res=match(ms, s+1, ep+1)) != NULL)) 113 | - return res; 114 | + MATCH_RETURN(res); 115 | p=ep+1; goto init; /* else return match(ms, s, ep+1); */ 116 | } 117 | case '*': { /* 0 or more repetitions */ 118 | - return max_expand(ms, s, p, ep); 119 | + MATCH_RETURN(max_expand(ms, s, p, ep)); 120 | } 121 | case '+': { /* 1 or more repetitions */ 122 | - return (m ? max_expand(ms, s+1, p, ep) : NULL); 123 | + MATCH_RETURN(m ? max_expand(ms, s+1, p, ep) : NULL); 124 | } 125 | case '-': { /* 0 or more repetitions (minimum) */ 126 | - return min_expand(ms, s, p, ep); 127 | + MATCH_RETURN(min_expand(ms, s, p, ep)); 128 | } 129 | default: { 130 | - if (!m) return NULL; 131 | + if (!m) MATCH_RETURN(NULL); 132 | s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ 133 | } 134 | } 135 | @@ -439,7 +466,7 @@ 136 | } 137 | } 138 | 139 | - 140 | +#undef MATCH_RETURN 141 | 142 | static const char *lmemfind (const char *s1, size_t l1, 143 | const char *s2, size_t l2) { 144 | @@ -516,6 +543,7 @@ 145 | ms.L = L; 146 | ms.src_init = s; 147 | ms.src_end = s+l1; 148 | + ms.depth = 0; 149 | do { 150 | const char *res; 151 | ms.level = 0; 152 | @@ -554,6 +582,7 @@ 153 | ms.L = L; 154 | ms.src_init = s; 155 | ms.src_end = s+ls; 156 | + ms.depth = 0; 157 | for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); 158 | src <= ms.src_end; 159 | src++) { 160 | @@ -658,6 +687,7 @@ 161 | ms.L = L; 162 | ms.src_init = src; 163 | ms.src_end = src+srcl; 164 | + ms.depth = 0; 165 | while (n < max_s) { 166 | const char *e; 167 | ms.level = 0; 168 | @@ -859,7 +889,7 @@ 169 | /* 170 | ** Open string library 171 | */ 172 | -LUALIB_API int luaopen_string (lua_State *L) { 173 | +LUALIB_API int luasandbox_open_string (lua_State *L) { 174 | luaL_register(L, LUA_STRLIBNAME, strlib); 175 | #if defined(LUA_COMPAT_GFIND) 176 | lua_getfield(L, -1, "gmatch"); 177 | -------------------------------------------------------------------------------- /luasandbox_timer.h: -------------------------------------------------------------------------------- 1 | #ifndef LUASANDBOX_TIMER_H 2 | #define LUASANDBOX_TIMER_H 3 | 4 | #include "luasandbox_types.h" 5 | 6 | void luasandbox_timer_minit(); 7 | void luasandbox_timer_mshutdown(); 8 | 9 | void luasandbox_timer_create(luasandbox_timer_set * lts, struct _php_luasandbox_obj * sandbox); 10 | void luasandbox_timer_set_limit(luasandbox_timer_set * lts, 11 | struct timespec * timeout); 12 | int luasandbox_timer_enable_profiler(luasandbox_timer_set * lts, struct timespec * period); 13 | int luasandbox_timer_start(luasandbox_timer_set * lts); 14 | void luasandbox_timer_stop(luasandbox_timer_set * lts); 15 | void luasandbox_timer_destroy(luasandbox_timer_set * lts); 16 | void luasandbox_timer_get_usage(luasandbox_timer_set * lts, struct timespec * ts); 17 | void luasandbox_timer_pause(luasandbox_timer_set * lts); 18 | void luasandbox_timer_unpause(luasandbox_timer_set * lts); 19 | int luasandbox_timer_is_paused(luasandbox_timer_set * lts); 20 | void luasandbox_timer_timeout_error(lua_State *L); 21 | int luasandbox_timer_is_expired(luasandbox_timer_set * lts); 22 | 23 | #endif /*LUASANDBOX_TIMER_H*/ 24 | -------------------------------------------------------------------------------- /luasandbox_types.h: -------------------------------------------------------------------------------- 1 | #ifndef LUASANDBOX_TYPES_H 2 | #define LUASANDBOX_TYPES_H 3 | 4 | #include "php.h" 5 | 6 | #ifndef LUASANDBOX_NO_CLOCK 7 | #include 8 | #endif 9 | 10 | #ifdef LUASANDBOX_NO_CLOCK 11 | 12 | typedef struct { 13 | // structs must have at least one member 14 | int unused; 15 | } luasandbox_timer; 16 | 17 | typedef struct { 18 | struct timespec profiler_period; 19 | HashTable * function_counts; 20 | long total_count; 21 | int is_paused; 22 | } luasandbox_timer_set; 23 | 24 | #else /*LUASANDBOX_NO_CLOCK*/ 25 | 26 | struct _php_luasandbox_obj; 27 | 28 | typedef struct _luasandbox_timer { 29 | struct _php_luasandbox_obj * sandbox; 30 | timer_t timer; 31 | clockid_t clock_id; 32 | int type; 33 | sem_t semaphore; 34 | int id; 35 | } luasandbox_timer; 36 | 37 | typedef struct { 38 | luasandbox_timer *limiter_timer; 39 | luasandbox_timer *profiler_timer; 40 | struct timespec limiter_limit, limiter_remaining; 41 | struct timespec usage_start, usage; 42 | struct timespec pause_start, pause_delta; 43 | struct timespec limiter_expired_at; 44 | struct timespec profiler_period; 45 | struct _php_luasandbox_obj * sandbox; 46 | int is_running; 47 | int limiter_running; 48 | int profiler_running; 49 | 50 | // A HashTable storing the number of times each function was hit by the 51 | // profiler. The data is a size_t because that hits a special case in 52 | // zend_hash which avoids the need to allocate separate space for the data 53 | // on the heap. 54 | HashTable * function_counts; 55 | 56 | // The total number of samples recorded in function_counts 57 | long total_count; 58 | 59 | // The number of timer expirations that have occurred since the profiler hook 60 | // was last run 61 | volatile long profiler_signal_count; 62 | 63 | volatile long overrun_count; 64 | } luasandbox_timer_set; 65 | 66 | #endif /*LUASANDBOX_NO_CLOCK*/ 67 | 68 | ZEND_BEGIN_MODULE_GLOBALS(luasandbox) 69 | HashTable * allowed_globals; 70 | long active_count; 71 | ZEND_END_MODULE_GLOBALS(luasandbox) 72 | 73 | typedef struct { 74 | lua_Alloc old_alloc; 75 | void * old_alloc_ud; 76 | size_t memory_limit; 77 | size_t memory_usage; 78 | size_t peak_memory_usage; 79 | } php_luasandbox_alloc; 80 | 81 | struct _php_luasandbox_obj { 82 | lua_State * state; 83 | php_luasandbox_alloc alloc; 84 | int in_php; 85 | int in_lua; 86 | zval current_zval; /* The zval for the LuaSandbox which is currently executing Lua code */ 87 | volatile int timed_out; 88 | int is_cpu_limited; 89 | luasandbox_timer_set timer; 90 | int function_index; 91 | unsigned int random_seed; 92 | int allow_pause; 93 | zend_object std; 94 | }; 95 | typedef struct _php_luasandbox_obj php_luasandbox_obj; 96 | 97 | struct _php_luasandboxfunction_obj { 98 | zval sandbox; 99 | int index; 100 | zend_object std; 101 | }; 102 | typedef struct _php_luasandboxfunction_obj php_luasandboxfunction_obj; 103 | 104 | // Accessor macros 105 | static inline php_luasandbox_obj *php_luasandbox_fetch_object(zend_object *obj) { 106 | return (php_luasandbox_obj *)((char*)(obj) - XtOffsetOf(php_luasandbox_obj, std)); 107 | } 108 | 109 | static inline php_luasandboxfunction_obj *php_luasandboxfunction_fetch_object(zend_object *obj) { 110 | return (php_luasandboxfunction_obj *)((char*)(obj) - XtOffsetOf(php_luasandboxfunction_obj, std)); 111 | } 112 | 113 | #define GET_LUASANDBOX_OBJ(z) php_luasandbox_fetch_object(Z_OBJ_P(z)) 114 | #define GET_LUASANDBOXFUNCTION_OBJ(z) php_luasandboxfunction_fetch_object(Z_OBJ_P(z)) 115 | #define LUASANDBOXFUNCTION_SANDBOX_IS_OK(pfunc) !Z_ISUNDEF((pfunc)->sandbox) 116 | #define LUASANDBOXFUNCTION_GET_SANDBOX_ZVALPTR(pfunc) &((pfunc)->sandbox) 117 | #define LUASANDBOX_GET_CURRENT_ZVAL_PTR(psandbox) &((psandbox)->current_zval) 118 | 119 | #endif /*LUASANDBOX_TYPES_H*/ 120 | 121 | -------------------------------------------------------------------------------- /luasandbox_version.h: -------------------------------------------------------------------------------- 1 | #define LUASANDBOX_VERSION "4.1.2" 2 | -------------------------------------------------------------------------------- /m4/pkg.m4: -------------------------------------------------------------------------------- 1 | # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- 2 | # serial 1 (pkg-config-0.24) 3 | # 4 | # Copyright © 2004 Scott James Remnant . 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 | # 20 | # As a special exception to the GNU General Public License, if you 21 | # distribute this file as part of a program that contains a 22 | # configuration script generated by Autoconf, you may include it under 23 | # the same distribution terms that you use for the rest of that program. 24 | # 25 | # 26 | # CHANGES: 27 | # 2013-06-18: Print $2 instead of $1 in the "checking 28 | # for XXX" line. 29 | 30 | # PKG_PROG_PKG_CONFIG([MIN-VERSION]) 31 | # ---------------------------------- 32 | AC_DEFUN([PKG_PROG_PKG_CONFIG], 33 | [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) 34 | m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) 35 | m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) 36 | AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) 37 | AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) 38 | AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) 39 | 40 | if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then 41 | AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) 42 | fi 43 | if test -n "$PKG_CONFIG"; then 44 | _pkg_min_version=m4_default([$1], [0.9.0]) 45 | AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) 46 | if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then 47 | AC_MSG_RESULT([yes]) 48 | else 49 | AC_MSG_RESULT([no]) 50 | PKG_CONFIG="" 51 | fi 52 | fi[]dnl 53 | ])# PKG_PROG_PKG_CONFIG 54 | 55 | # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 56 | # 57 | # Check to see whether a particular set of modules exists. Similar 58 | # to PKG_CHECK_MODULES(), but does not set variables or print errors. 59 | # 60 | # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) 61 | # only at the first occurence in configure.ac, so if the first place 62 | # it's called might be skipped (such as if it is within an "if", you 63 | # have to call PKG_CHECK_EXISTS manually 64 | # -------------------------------------------------------------- 65 | AC_DEFUN([PKG_CHECK_EXISTS], 66 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 67 | if test -n "$PKG_CONFIG" && \ 68 | AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then 69 | m4_default([$2], [:]) 70 | m4_ifvaln([$3], [else 71 | $3])dnl 72 | fi]) 73 | 74 | # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) 75 | # --------------------------------------------- 76 | m4_define([_PKG_CONFIG], 77 | [if test -n "$$1"; then 78 | pkg_cv_[]$1="$$1" 79 | elif test -n "$PKG_CONFIG"; then 80 | PKG_CHECK_EXISTS([$3], 81 | [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` 82 | test "x$?" != "x0" && pkg_failed=yes ], 83 | [pkg_failed=yes]) 84 | else 85 | pkg_failed=untried 86 | fi[]dnl 87 | ])# _PKG_CONFIG 88 | 89 | # _PKG_SHORT_ERRORS_SUPPORTED 90 | # ----------------------------- 91 | AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], 92 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) 93 | if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then 94 | _pkg_short_errors_supported=yes 95 | else 96 | _pkg_short_errors_supported=no 97 | fi[]dnl 98 | ])# _PKG_SHORT_ERRORS_SUPPORTED 99 | 100 | 101 | # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], 102 | # [ACTION-IF-NOT-FOUND]) 103 | # 104 | # 105 | # Note that if there is a possibility the first call to 106 | # PKG_CHECK_MODULES might not happen, you should be sure to include an 107 | # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac 108 | # 109 | # 110 | # -------------------------------------------------------------- 111 | AC_DEFUN([PKG_CHECK_MODULES], 112 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 113 | AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl 114 | AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl 115 | 116 | pkg_failed=no 117 | AC_MSG_CHECKING([for $2]) 118 | 119 | _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) 120 | _PKG_CONFIG([$1][_LIBS], [libs], [$2]) 121 | 122 | m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS 123 | and $1[]_LIBS to avoid the need to call pkg-config. 124 | See the pkg-config man page for more details.]) 125 | 126 | if test $pkg_failed = yes; then 127 | AC_MSG_RESULT([no]) 128 | _PKG_SHORT_ERRORS_SUPPORTED 129 | if test $_pkg_short_errors_supported = yes; then 130 | $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` 131 | else 132 | $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` 133 | fi 134 | # Put the nasty error message in config.log where it belongs 135 | echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD 136 | 137 | m4_default([$4], [AC_MSG_ERROR( 138 | [Package requirements ($2) were not met: 139 | 140 | $$1_PKG_ERRORS 141 | 142 | Consider adjusting the PKG_CONFIG_PATH environment variable if you 143 | installed software in a non-standard prefix. 144 | 145 | _PKG_TEXT])[]dnl 146 | ]) 147 | elif test $pkg_failed = untried; then 148 | AC_MSG_RESULT([no]) 149 | m4_default([$4], [AC_MSG_FAILURE( 150 | [The pkg-config script could not be found or is too old. Make sure it 151 | is in your PATH or set the PKG_CONFIG environment variable to the full 152 | path to pkg-config. 153 | 154 | _PKG_TEXT 155 | 156 | To get pkg-config, see .])[]dnl 157 | ]) 158 | else 159 | $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS 160 | $1[]_LIBS=$pkg_cv_[]$1[]_LIBS 161 | AC_MSG_RESULT([yes]) 162 | $3 163 | fi[]dnl 164 | ])# PKG_CHECK_MODULES 165 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | LuaSandbox 4 | pecl.php.net 5 | Lua interpreter with limits and safe environment 6 | LuaSandbox is an extension for running untrusted Lua code within a PHP web request. Code is run in a stripped-down, safe environment. Time and memory limits can be set. 7 | 8 | Tim Starling 9 | tstarling 10 | tstarling@wikimedia.org 11 | yes 12 | 13 | 14 | Kunal Mehta 15 | legoktm 16 | legoktm@debian.org 17 | yes 18 | 19 | 20 | Timo Tijhof 21 | krinkle 22 | krinklemail@gmail.com 23 | yes 24 | 25 | 2023-12-13 26 | 27 | 4.1.2 28 | 4.1.2 29 | 30 | 31 | stable 32 | stable 33 | 34 | MIT 35 | 36 | - Run the GC more aggressively, especially as usage approaches the limit (T349462) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 7.2.0 137 | 138 | 139 | 1.10.0 140 | 141 | 142 | 143 | luasandbox 144 | 145 | 146 | 147 | 2023-07-31 148 | 149 | 4.1.1 150 | 4.1.1 151 | 152 | 153 | - Fix segmentation fault when memory limit is exceeded in LuaSandbox init 154 | - Fix incorrect version reported by phpversion('luasandbox') 155 | 156 | 157 | 158 | 2022-09-23 159 | 160 | 4.1.0 161 | 4.1.0 162 | 163 | 164 | - Add PHP 8.2 support 165 | 166 | 167 | 168 | 2021-05-19 169 | 170 | 4.0.2 171 | 4.0.2 172 | 173 | 174 | - Add config.w32 package.xml tarball (Closes #80850) 175 | 176 | 177 | 178 | 2021-03-10 179 | 180 | 4.0.1 181 | 4.0.1 182 | 183 | 184 | - Add missing file to package.xml tarball 185 | 186 | 187 | 188 | 2021-03-04 189 | 190 | 4.0.0 191 | 4.0.0 192 | 193 | 194 | - Add docbook documentation (for php.net) 195 | - Flag optional and variadic parameters properly for PHP reflection 196 | - Remove memory leaks in data_conversion.c 197 | - Drop PHP5 and HHVM compatibility 198 | - Add PHP 8 support 199 | - Windows compilation fixes 200 | 201 | 202 | 203 | 2018-10-11 204 | 205 | 3.0.3 206 | 3.0.3 207 | 208 | 209 | - Fix ZTS build on PHP 7+ (Patch by Remi Collet) 210 | 211 | 212 | 213 | 2018-10-09 214 | 215 | 3.0.2 216 | 3.0.2 217 | 218 | 219 | - Fix PHP 7 object layout 220 | - Initial PECL release 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /php_luasandbox.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PHP_LUASANDBOX_H 3 | #define PHP_LUASANDBOX_H 4 | 5 | #ifdef CLOCK_REALTIME 6 | # ifdef CLOCK_THREAD_CPUTIME_ID 7 | # define LUASANDBOX_CLOCK_ID CLOCK_THREAD_CPUTIME_ID 8 | # else 9 | # define LUASANDBOX_CLOCK_ID CLOCK_REALTIME 10 | # endif 11 | #else /*CLOCK_REALTIME*/ 12 | # define LUASANDBOX_NO_CLOCK 13 | #endif /*CLOCK_REALTIME*/ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "luasandbox_types.h" 20 | #include "luasandbox_timer.h" 21 | 22 | /* alloc.c */ 23 | 24 | 25 | lua_State * luasandbox_alloc_new_state(php_luasandbox_alloc * alloc, php_luasandbox_obj * sandbox); 26 | void luasandbox_alloc_delete_state(php_luasandbox_alloc * alloc, lua_State * L); 27 | 28 | /* luasandbox.c */ 29 | 30 | extern zend_module_entry luasandbox_module_entry; 31 | 32 | #define phpext_luasandbox_ptr &luasandbox_module_entry 33 | 34 | #ifdef PHP_WIN32 35 | # define PHP_LUASANDBOX_API __declspec(dllexport) 36 | #elif defined(__GNUC__) && __GNUC__ >= 4 37 | # define PHP_LUASANDBOX_API __attribute__ ((visibility("default"))) 38 | #else 39 | # define PHP_LUASANDBOX_API 40 | #endif 41 | 42 | #ifdef ZTS 43 | #include "TSRM.h" 44 | #endif 45 | 46 | int luasandbox_call_php(lua_State * L); 47 | int luasandbox_call_lua(php_luasandbox_obj * sandbox, zval * sandbox_zval, 48 | int nargs, int nresults, int errfunc); 49 | 50 | PHP_MINIT_FUNCTION(luasandbox); 51 | PHP_MSHUTDOWN_FUNCTION(luasandbox); 52 | PHP_RSHUTDOWN_FUNCTION(luasandbox); 53 | PHP_MINFO_FUNCTION(luasandbox); 54 | 55 | PHP_METHOD(LuaSandbox, getVersionInfo); 56 | PHP_METHOD(LuaSandbox, loadString); 57 | PHP_METHOD(LuaSandbox, loadBinary); 58 | PHP_METHOD(LuaSandbox, setMemoryLimit); 59 | PHP_METHOD(LuaSandbox, getMemoryUsage); 60 | PHP_METHOD(LuaSandbox, getPeakMemoryUsage); 61 | PHP_METHOD(LuaSandbox, setCPULimit); 62 | PHP_METHOD(LuaSandbox, getCPUUsage); 63 | PHP_METHOD(LuaSandbox, pauseUsageTimer); 64 | PHP_METHOD(LuaSandbox, unpauseUsageTimer); 65 | PHP_METHOD(LuaSandbox, enableProfiler); 66 | PHP_METHOD(LuaSandbox, disableProfiler); 67 | PHP_METHOD(LuaSandbox, getProfilerFunctionReport); 68 | PHP_METHOD(LuaSandbox, callFunction); 69 | PHP_METHOD(LuaSandbox, wrapPhpFunction); 70 | PHP_METHOD(LuaSandbox, registerLibrary); 71 | 72 | PHP_METHOD(LuaSandboxFunction, __construct); 73 | PHP_METHOD(LuaSandboxFunction, call); 74 | PHP_METHOD(LuaSandboxFunction, dump); 75 | 76 | #ifdef ZTS 77 | #define LUASANDBOX_G(v) TSRMG(luasandbox_globals_id, zend_luasandbox_globals *, v) 78 | #else 79 | #define LUASANDBOX_G(v) (luasandbox_globals.v) 80 | #endif 81 | 82 | 83 | php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L); 84 | 85 | /** {{{ luasandbox_enter_php 86 | * 87 | * This function must be called each time a C function is entered from Lua 88 | * and the PHP state needs to be accessed in any way. Before exiting the 89 | * function, luasandbox_leave_php() must be called. 90 | * 91 | * This sets a flag which indicates to the timeout signal handler that it is 92 | * unsafe to call longjmp() to return control to PHP. If the flag is not 93 | * correctly set, memory may be corrupted and security compromised. 94 | */ 95 | static inline void luasandbox_enter_php(lua_State * L, php_luasandbox_obj * intern) 96 | { 97 | intern->in_php ++; 98 | if (intern->timed_out) { 99 | intern->in_php --; 100 | luasandbox_timer_timeout_error(L); 101 | } 102 | } 103 | /* }}} */ 104 | 105 | /** 106 | * {{ luasandbox_enter_php_ignore_timeouts 107 | * 108 | * Like luasandbox_enter_php except that no error is raised if a timeout has occurred 109 | */ 110 | static inline void luasandbox_enter_php_ignore_timeouts(lua_State * L, php_luasandbox_obj * intern) 111 | { 112 | intern->in_php ++; 113 | } 114 | 115 | /** {{{ luasandbox_leave_php 116 | * 117 | * This function must be called after luasandbox_enter_php, before the callback 118 | * from Lua returns. 119 | */ 120 | static inline void luasandbox_leave_php(lua_State * L, php_luasandbox_obj * intern) 121 | { 122 | intern->in_php --; 123 | } 124 | /* }}} */ 125 | 126 | /* library.c */ 127 | 128 | void luasandbox_lib_register(lua_State * L); 129 | void luasandbox_lib_destroy_globals(); 130 | 131 | /* luasandbox_lstrlib.c */ 132 | 133 | int luasandbox_open_string(lua_State * L); 134 | 135 | /* data_conversion.c */ 136 | 137 | void luasandbox_data_conversion_init(lua_State * L); 138 | 139 | int luasandbox_push_zval(lua_State * L, zval * z, HashTable *recursionGuard); 140 | void luasandbox_push_zval_userdata(lua_State * L, zval * z); 141 | int luasandbox_lua_to_zval(zval * z, lua_State * L, int index, 142 | zval * sandbox_zval, HashTable * recursionGuard); 143 | void luasandbox_wrap_fatal(lua_State * L); 144 | int luasandbox_is_fatal(lua_State * L, int index); 145 | int luasandbox_is_trace_error(lua_State * L, int index); 146 | const char * luasandbox_error_to_string(lua_State * L, int index); 147 | int luasandbox_attach_trace(lua_State * L); 148 | void luasandbox_push_structured_trace(lua_State * L, int level); 149 | 150 | #endif /* PHP_LUASANDBOX_H */ 151 | 152 | -------------------------------------------------------------------------------- /stubs/Exceptions.php: -------------------------------------------------------------------------------- 1 | 5 | --FILE-- 6 | 9 | --EXPECTF-- 10 | %AFatal error:%sCall to private%S LuaSandboxFunction::__construct%S from %a 11 | -------------------------------------------------------------------------------- /tests/array-key-conversion.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Array key conversion 3 | --FILE-- 4 | setMemoryLimit( 100000 ); 11 | $sandbox->setCPULimit( 0.1 ); 12 | try { 13 | $ret = $sandbox 14 | ->loadString( 'local t, r = ..., {}; for k, v in pairs( t ) do r[v] = type(k) end return r' ) 15 | ->call( $array ); 16 | if ( is_array( $ret[0] ) ) { 17 | ksort( $ret[0], SORT_STRING ); 18 | } 19 | printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret[0], 1 ) ) ); 20 | } catch ( LuaSandboxError $e ) { 21 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 22 | } 23 | } 24 | 25 | function testLuaToPhp( $test, $lua ) { 26 | printf( "Lua→PHP %-30s ", "$test:" ); 27 | 28 | $sandbox = new LuaSandbox; 29 | $sandbox->setMemoryLimit( 100000 ); 30 | $sandbox->setCPULimit( 0.1 ); 31 | try { 32 | $ret = $sandbox->loadString( "return { $lua }" )->call(); 33 | if ( is_array( $ret[0] ) ) { 34 | ksort( $ret[0], SORT_STRING ); 35 | } 36 | printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret[0], 1 ) ) ); 37 | } catch ( LuaSandboxError $e ) { 38 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 39 | } 40 | } 41 | 42 | if ( PHP_INT_MAX > 9007199254740992 ) { 43 | $a = [ 44 | '9007199254740992' => 'max', '9007199254740993' => 'max+1', 45 | '-9007199254740992' => 'min', '-9007199254740993' => 'min-1', 46 | ]; 47 | $max = '9223372036854775807'; 48 | $max2 = '9223372036854775808'; 49 | $min = '-9223372036854775808'; 50 | $min2 = '-9223372036854775809'; 51 | } else { 52 | $a = [ 53 | '2147483647' => 'max', '2147483648' => 'max+1', 54 | '-2147483648' => 'min', '-2147483649' => 'min-1', 55 | ]; 56 | $max = '2147483647'; 57 | $max2 = '2147483648'; 58 | $min = '-2147483648'; 59 | $min2 = '-2147483649'; 60 | } 61 | 62 | testPhpToLua( 'simple integers', [ -10 => 'minus ten', 0 => 'zero', 10 => 'ten' ] ); 63 | testPhpToLua( 'maximal values', $a ); 64 | 65 | testLuaToPhp( 'simple integers', '[-10] = "minus ten", [0] = "zero", [10] = "ten"' ); 66 | testLuaToPhp( 'stringified integers', '["-10"] = "minus ten", ["0"] = "zero", ["10"] = "ten"' ); 67 | testLuaToPhp( 'maximal integers', "['$max'] = 'max', ['$max2'] = 'max+1', ['$min'] = 'min', ['$min2'] = 'min-1'" ); 68 | testLuaToPhp( 'collision (0)', '[0] = "number zero", ["0"] = "string zero"' ); 69 | testLuaToPhp( 'collision (float)', '[1.5] = "number 1.5", ["1.5"] = "string 1.5"' ); 70 | testLuaToPhp( 'collision (inf)', '[1/0] = "number inf", ["inf"] = "string inf"' ); 71 | 72 | --EXPECTF-- 73 | PHP→Lua simple integers: array ( 'minus ten' => 'number', 'ten' => 'number', 'zero' => 'number', ) 74 | PHP→Lua maximal values: array ( 'max' => 'number', 'max+1' => 'string', 'min' => 'number', 'min-1' => 'string', ) 75 | Lua→PHP simple integers: array ( -10 => 'minus ten', 0 => 'zero', 10 => 'ten', ) 76 | Lua→PHP stringified integers: array ( -10 => 'minus ten', 0 => 'zero', 10 => 'ten', ) 77 | Lua→PHP maximal integers: array ( -%d => 'min', '-%d' => 'min-1', %d => 'max', '%d' => 'max+1', ) 78 | Lua→PHP collision (0): EXCEPTION: Collision for array key 0 when passing data from Lua to PHP 79 | Lua→PHP collision (float): EXCEPTION: Collision for array key 1.5 when passing data from Lua to PHP 80 | Lua→PHP collision (inf): EXCEPTION: Collision for array key inf when passing data from Lua to PHP 81 | -------------------------------------------------------------------------------- /tests/call.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | LuaSandboxFunction::call 3 | --FILE-- 4 | loadString( 'return 1' )->call() ); 7 | 8 | echo "Proper handling of circular tables returned by Lua: "; 9 | $sandbox = new LuaSandbox; 10 | try { 11 | $ret = $sandbox->loadString( 'local t = {}; t.t = t; return t' )->call(); 12 | echo var_export( $ret, 1 ) . "\n"; 13 | } catch ( Exception $ex ) { 14 | echo "Exception: " . $ex->getMessage() . "\n"; 15 | } 16 | 17 | echo "Proper handling of circular tables in Lua→PHP call: "; 18 | $sandbox = new LuaSandbox; 19 | $f = $sandbox->wrapPhpFunction( function () { 20 | echo func_num_args() . " args ok\n"; 21 | } ); 22 | try { 23 | $sandbox->loadString( 'local f = ...; local t = {}; t.t = t; f( t )' )->call( $f ); 24 | } catch ( Exception $ex ) { 25 | echo "Exception: " . $ex->getMessage() . "\n"; 26 | } 27 | 28 | echo "Passing lots of arguments PHP->Lua doesn't cause a crash: "; 29 | $sandbox = new LuaSandbox; 30 | $ret = call_user_func_array( 31 | array( $sandbox->loadString( 'return select( "#", ... )' ), 'call' ), 32 | array_fill( 0, 500, '' ) 33 | ); 34 | echo "$ret[0] args ok\n"; 35 | 36 | echo "Passing lots of arguments Lua->PHP doesn't cause a crash: "; 37 | $sandbox = new LuaSandbox; 38 | $f = $sandbox->wrapPhpFunction( function () { 39 | echo func_num_args() . " args ok\n"; 40 | } ); 41 | $sandbox->loadString( 'local f = ...; f( string.byte( string.rep( "x", 500 ), 1, -1 ) )' )->call( $f ); 42 | 43 | echo "Returning lots of values PHP->Lua doesn't cause a crash: "; 44 | $sandbox = new LuaSandbox; 45 | $f = $sandbox->wrapPhpFunction( function () { 46 | return array_fill( 0, 500, '' ); 47 | } ); 48 | $ret = $sandbox->loadString( 'local f = ...; return select( "#", f() )' )->call( $f ); 49 | echo "$ret[0] values ok\n"; 50 | 51 | echo "Returning lots of values Lua->PHP doesn't cause a crash: "; 52 | $sandbox = new LuaSandbox; 53 | $ret = $sandbox->loadString( 'return string.byte( string.rep( "x", 500 ), 1, -1 )' )->call(); 54 | echo count( $ret ) . " values ok\n"; 55 | 56 | echo "Passing deeply-nested arrays PHP->Lua doesn't cause a crash: "; 57 | $sandbox = new LuaSandbox; 58 | $v = 1; 59 | for ( $i = 0; $i < 500; $i++ ) { 60 | $v = array( $v ); 61 | } 62 | $lua = <<loadString( $lua )->call( $v ); 71 | echo "$ret[0] levels ok\n"; 72 | 73 | echo "Passing deeply-nested tables Lua->PHP doesn't cause a crash: "; 74 | $sandbox = new LuaSandbox; 75 | $ret = $sandbox->loadString( 'local t = 1; for i = 1, 500 do t = { t } end; return t' )->call(); 76 | $ct = 0; 77 | $v = $ret[0]; 78 | while ( is_array( $v ) ) { 79 | $v = reset( $v ); 80 | $ct++; 81 | } 82 | echo "$ct levels ok\n"; 83 | 84 | echo "Proper handling of invalid keys in Lua→PHP conversion (table): "; 85 | $sandbox = new LuaSandbox; 86 | try { 87 | $ret = $sandbox->loadString( 'return { [{}] = 1 }' )->call(); 88 | echo var_export( $ret[0], 1 ) . "\n"; 89 | } catch ( Exception $ex ) { 90 | echo "Exception: " . $ex->getMessage() . "\n"; 91 | } 92 | 93 | echo "Proper handling of invalid keys in Lua→PHP conversion (bool): "; 94 | $sandbox = new LuaSandbox; 95 | try { 96 | $ret = $sandbox->loadString( 'return { [true] = 1 }' )->call(); 97 | echo var_export( $ret[0], 1 ) . "\n"; 98 | } catch ( Exception $ex ) { 99 | echo "Exception: " . $ex->getMessage() . "\n"; 100 | } 101 | 102 | echo "Proper handling of invalid keys in Lua→PHP conversion (function): "; 103 | $sandbox = new LuaSandbox; 104 | try { 105 | $ret = $sandbox->loadString( 'return { [tostring] = 1 }' )->call(); 106 | echo var_export( $ret[0], 1 ) . "\n"; 107 | } catch ( Exception $ex ) { 108 | echo "Exception: " . $ex->getMessage() . "\n"; 109 | } 110 | 111 | echo "Proper handling of unusual keys in Lua→PHP conversion (float): "; 112 | $sandbox = new LuaSandbox; 113 | try { 114 | $ret = $sandbox->loadString( 'return { [1.5] = 1 }' )->call(); 115 | echo var_export( $ret[0], 1 ) . "\n"; 116 | } catch ( Exception $ex ) { 117 | echo "Exception: " . $ex->getMessage() . "\n"; 118 | } 119 | 120 | echo "Proper handling of unusual keys in Lua→PHP conversion (inf): "; 121 | $sandbox = new LuaSandbox; 122 | try { 123 | $ret = $sandbox->loadString( 'return { [math.huge] = 1 }' )->call(); 124 | echo var_export( $ret[0], 1 ) . "\n"; 125 | } catch ( Exception $ex ) { 126 | echo "Exception: " . $ex->getMessage() . "\n"; 127 | } 128 | 129 | --EXPECT-- 130 | array(1) { 131 | [0]=> 132 | int(1) 133 | } 134 | Proper handling of circular tables returned by Lua: Exception: Cannot pass circular reference to PHP 135 | Proper handling of circular tables in Lua→PHP call: Exception: Cannot pass circular reference to PHP 136 | Passing lots of arguments PHP->Lua doesn't cause a crash: 500 args ok 137 | Passing lots of arguments Lua->PHP doesn't cause a crash: 500 args ok 138 | Returning lots of values PHP->Lua doesn't cause a crash: 500 values ok 139 | Returning lots of values Lua->PHP doesn't cause a crash: 500 values ok 140 | Passing deeply-nested arrays PHP->Lua doesn't cause a crash: 500 levels ok 141 | Passing deeply-nested tables Lua->PHP doesn't cause a crash: 500 levels ok 142 | Proper handling of invalid keys in Lua→PHP conversion (table): Exception: Cannot use table as an array key when passing data from Lua to PHP 143 | Proper handling of invalid keys in Lua→PHP conversion (bool): Exception: Cannot use boolean as an array key when passing data from Lua to PHP 144 | Proper handling of invalid keys in Lua→PHP conversion (function): Exception: Cannot use function as an array key when passing data from Lua to PHP 145 | Proper handling of unusual keys in Lua→PHP conversion (float): array ( 146 | '1.5' => 1, 147 | ) 148 | Proper handling of unusual keys in Lua→PHP conversion (inf): array ( 149 | 'inf' => 1, 150 | ) 151 | -------------------------------------------------------------------------------- /tests/callback_exception.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Exception in a PHP function called from Lua 3 | --FILE-- 4 | registerLibrary( 'test', array( 'throw_exception' => 'throw_exception' ) ); 11 | $f = $sandbox->loadString('test.throw_exception()'); 12 | try { 13 | $f->call(); 14 | } catch ( Exception $e ) { 15 | print $e->getMessage(); 16 | } 17 | 18 | --EXPECT-- 19 | message 20 | -------------------------------------------------------------------------------- /tests/datatypes-unsupported.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Handling of unsupported datatypes 3 | --FILE-- 4 | Lua):" ); 8 | $sandbox = new LuaSandbox; 9 | $sandbox->setMemoryLimit( 100000 ); 10 | $sandbox->setCPULimit( 0.1 ); 11 | try { 12 | $ret = $sandbox->loadString( 'return 1' )->call( $data ); 13 | printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret, 1 ) ) ); 14 | } catch ( LuaSandboxError $e ) { 15 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 16 | } 17 | 18 | printf( "%s ", "$test (return PHP->Lua):" ); 19 | $sandbox = new LuaSandbox; 20 | $sandbox->setMemoryLimit( 100000 ); 21 | $sandbox->setCPULimit( 0.1 ); 22 | $f = $sandbox->wrapPhpFunction( function () use ( $data ) { 23 | return [ $data ]; 24 | } ); 25 | try { 26 | $sandbox->loadString( 'local f = ...; f()' )->call( $f ); 27 | printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret, 1 ) ) ); 28 | } catch ( LuaSandboxError $e ) { 29 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 30 | } 31 | } 32 | 33 | function doTest2( $test, $lua ) { 34 | printf( "%s ", "$test (call Lua->PHP):" ); 35 | $sandbox = new LuaSandbox; 36 | $sandbox->setMemoryLimit( 100000 ); 37 | $sandbox->setCPULimit( 0.1 ); 38 | $f = $sandbox->wrapPhpFunction( function ( $val ) { 39 | echo "PHP received " . preg_replace( '/\s+/', ' ', var_export( $val, 1 ) ) . "\n"; 40 | } ); 41 | try { 42 | $sandbox->loadString( "local f = ...\n$lua\nf(v)" )->call( $f ); 43 | } catch ( LuaSandboxError $e ) { 44 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 45 | } 46 | 47 | printf( "%s ", "$test (return Lua->PHP):" ); 48 | $sandbox = new LuaSandbox; 49 | $sandbox->setMemoryLimit( 100000 ); 50 | $sandbox->setCPULimit( 0.1 ); 51 | try { 52 | $ret = $sandbox->loadString( "$lua\nreturn v" )->call(); 53 | printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret, 1 ) ) ); 54 | } catch ( LuaSandboxError $e ) { 55 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 56 | } 57 | } 58 | 59 | $test = array(); 60 | $test['foo'] = &$test; 61 | doTest( 'recursive array', $test ); 62 | 63 | $test = new stdClass; 64 | doTest( 'object', $test ); 65 | 66 | doTest2( 'recursive table', 'v = {}; v.v = v' ); 67 | 68 | --EXPECTF-- 69 | recursive array (call PHP->Lua): %AWarning: LuaSandboxFunction::call(): Cannot pass circular reference to Lua in %s on line %d 70 | %AWarning: LuaSandboxFunction::call(): unable to convert argument 1 to a lua value in %s on line %d 71 | false 72 | recursive array (return PHP->Lua): %AWarning: LuaSandboxFunction::call(): Cannot pass circular reference to Lua in %s on line %d 73 | false 74 | object (call PHP->Lua): %AWarning: LuaSandboxFunction::call(): Unable to convert object of type stdClass in %s on line %d 75 | %AWarning: LuaSandboxFunction::call(): unable to convert argument 1 to a lua value in %s on line %d 76 | false 77 | object (return PHP->Lua): %AWarning: LuaSandboxFunction::call(): Unable to convert object of type stdClass in %s on line %d 78 | false 79 | recursive table (call Lua->PHP): EXCEPTION: Cannot pass circular reference to PHP 80 | recursive table (return Lua->PHP): EXCEPTION: Cannot pass circular reference to PHP 81 | -------------------------------------------------------------------------------- /tests/datatypes.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Data type round-tripping 3 | --FILE-- 4 | setMemoryLimit( 100000 ); 11 | $sandbox->setCPULimit( 0.1 ); 12 | try { 13 | $ret = $sandbox->loadString( 'return ...' )->call( $data ); 14 | if ( is_array( $ret[0] ) ) { 15 | ksort( $ret[0], SORT_STRING ); 16 | } 17 | printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret[0], 1 ) ) ); 18 | } catch ( LuaSandboxError $e ) { 19 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 20 | } 21 | } 22 | 23 | doTest( 'null', null ); 24 | doTest( 'int', 123 ); 25 | if ( is_int( 17179869184 ) ) { 26 | doTest( 'long', 17179869184 ); 27 | } else { 28 | // Fake it for 32-bit systems 29 | printf( "%-25s %s\n", "long:", "17179869184" ); 30 | } 31 | doTest( 'double', 3.125 ); 32 | doTest( 'NAN', NAN ); 33 | doTest( 'INF', INF ); 34 | doTest( 'true', true ); 35 | doTest( 'false', false ); 36 | doTest( 'string', 'foobar' ); 37 | doTest( 'empty string', '' ); 38 | doTest( 'string containing NULs', "foo\0bar" ); 39 | doTest( 'array', array( 'foo', 'bar' ) ); 40 | doTest( 'associative array', array( 'foo', 'bar' => 'baz' ) ); 41 | 42 | $var = 42; 43 | doTest( 'array with reference', [ &$var ] ); 44 | 45 | $sandbox = new LuaSandbox; 46 | $sandbox->setMemoryLimit( 100000 ); 47 | $sandbox->setCPULimit( 0.1 ); 48 | $func = $sandbox->wrapPhpFunction( function ( $x ) { return [ "FUNC: $x" ]; } ); 49 | try { 50 | $ret = $sandbox->loadString( 'return ...' )->call( $func ); 51 | $ret2 = $ret[0]->call( "ok" ); 52 | printf( "%-25s %s\n", "function, pass-through:", $ret2[0] ); 53 | 54 | $ret = $sandbox->loadString( 'f = ...; return f( "ok" )' )->call( $func ); 55 | printf( "%-25s %s\n", "function, called:", $ret[0] ); 56 | 57 | $ret = $sandbox->loadString( 'return function ( x ) return "FUNC: " .. x end' )->call(); 58 | $ret2 = $ret[0]->call( "ok" ); 59 | printf( "%-25s %s\n", "function, returned:", $ret2[0] ); 60 | } catch ( LuaSandboxError $e ) { 61 | printf( "EXCEPTION: %s\n", $e->getMessage() ); 62 | } 63 | 64 | --EXPECT-- 65 | null: NULL 66 | int: 123 67 | long: 17179869184 68 | double: 3.125 69 | NAN: NAN 70 | INF: INF 71 | true: true 72 | false: false 73 | string: 'foobar' 74 | empty string: '' 75 | string containing NULs: 'foo' . "\0" . 'bar' 76 | array: array ( 0 => 'foo', 1 => 'bar', ) 77 | associative array: array ( 0 => 'foo', 'bar' => 'baz', ) 78 | array with reference: array ( 0 => 42, ) 79 | function, pass-through: FUNC: ok 80 | function, called: FUNC: ok 81 | function, returned: FUNC: ok 82 | 83 | -------------------------------------------------------------------------------- /tests/dump_loadBinary_call.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | dump -> loadBinary -> call 3 | --FILE-- 4 | loadString( 'return 1' ) ); 8 | $dump = $f->dump(); 9 | var_dump( $restore = $sandbox->loadBinary( $dump ) ); 10 | var_dump( $restore->call() ); 11 | 12 | --EXPECT-- 13 | object(LuaSandbox)#1 (0) { 14 | } 15 | object(LuaSandboxFunction)#2 (0) { 16 | } 17 | object(LuaSandboxFunction)#3 (0) { 18 | } 19 | array(1) { 20 | [0]=> 21 | int(1) 22 | } 23 | -------------------------------------------------------------------------------- /tests/errors-at-call-boundaries.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Errors at PHP→Lua call boundaries 3 | --FILE-- 4 | setMemoryLimit( 100000 ); 14 | try { 15 | $ret = $sandbox->loadString( 'local f = ...; return f()' ) 16 | ->call( $sandbox->wrapPhpFunction( $c ) ); 17 | var_dump( $ret ); 18 | } catch ( Exception $ex ) { 19 | echo "Exception: " . $ex->getMessage() . "\n"; 20 | } 21 | } 22 | 23 | doTest( 'LuaSandbox::callFunction', function () { 24 | global $sandbox; 25 | 26 | $sandbox->loadString( 'function foo() return "no error" end' )->call(); 27 | return $sandbox->callFunction( 'foo', 28 | str_repeat( 'a', 33334 ), 29 | str_repeat( 'b', 33334 ), 30 | str_repeat( 'c', 33334 ), 31 | str_repeat( 'd', 33334 ) 32 | ); 33 | } ); 34 | 35 | doTest( 'LuaSandbox::registerLibrary 1', function () { 36 | global $sandbox; 37 | 38 | $sandbox->registerLibrary( str_repeat( 'a', 33334 ), [ 'foo' => function () {} ] ); 39 | $sandbox->registerLibrary( str_repeat( 'b', 33334 ), [ 'foo' => function () {} ] ); 40 | $sandbox->registerLibrary( str_repeat( 'c', 33334 ), [ 'foo' => function () {} ] ); 41 | $sandbox->registerLibrary( str_repeat( 'd', 33334 ), [ 'foo' => function () {} ] ); 42 | 43 | return [ 'no error' ]; 44 | } ); 45 | 46 | doTest( 'LuaSandbox::registerLibrary 2', function () { 47 | global $sandbox; 48 | 49 | $sandbox->registerLibrary( 'foo', [ 50 | str_repeat( 'a', 33334 ) => function () {}, 51 | str_repeat( 'b', 33334 ) => function () {}, 52 | str_repeat( 'c', 33334 ) => function () {}, 53 | str_repeat( 'd', 33334 ) => function () {}, 54 | ] ); 55 | 56 | return [ 'no error' ]; 57 | } ); 58 | 59 | doTest( 'LuaSandboxFunction::call', function () { 60 | global $sandbox; 61 | 62 | return $sandbox->loadString( 'return "no error"' )->call( 63 | str_repeat( 'a', 33334 ), 64 | str_repeat( 'b', 33334 ), 65 | str_repeat( 'c', 33334 ), 66 | str_repeat( 'd', 33334 ) 67 | ); 68 | } ); 69 | 70 | --EXPECT-- 71 | LuaSandbox::callFunction: Exception: not enough memory 72 | LuaSandbox::registerLibrary 1: Exception: not enough memory 73 | LuaSandbox::registerLibrary 2: Exception: not enough memory 74 | LuaSandboxFunction::call: Exception: not enough memory 75 | -------------------------------------------------------------------------------- /tests/extending-LuaSandbox.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Extending LuaSandbox 3 | --FILE-- 4 | {"var$i"} = $i; 18 | } 19 | var_dump( $sandbox ); 20 | 21 | for($i=6; $i<=10; $i++){ 22 | $sandbox->{"var$i"} = $i; 23 | } 24 | var_dump( $sandbox ); 25 | 26 | echo "ok\n"; 27 | 28 | --EXPECT-- 29 | object(ExtendedLuaSandbox)#1 (5) { 30 | ["var1"]=> 31 | int(1) 32 | ["var2"]=> 33 | int(2) 34 | ["var3"]=> 35 | int(3) 36 | ["var4"]=> 37 | int(4) 38 | ["var5"]=> 39 | int(5) 40 | } 41 | object(ExtendedLuaSandbox)#1 (10) { 42 | ["var1"]=> 43 | int(1) 44 | ["var2"]=> 45 | int(2) 46 | ["var3"]=> 47 | int(3) 48 | ["var4"]=> 49 | int(4) 50 | ["var5"]=> 51 | int(5) 52 | ["var6"]=> 53 | int(6) 54 | ["var7"]=> 55 | int(7) 56 | ["var8"]=> 57 | int(8) 58 | ["var9"]=> 59 | int(9) 60 | ["var10"]=> 61 | int(10) 62 | } 63 | ok 64 | -------------------------------------------------------------------------------- /tests/ipairs.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ipairs() and __ipairs 3 | --FILE-- 4 | 'ipairs_test1', 44 | 'With __ipairs' => 'ipairs_test2', 45 | 'No argument' => 'ipairs_test3', 46 | ); 47 | 48 | foreach ( $tests as $desc => $func ) { 49 | echo "$desc: "; 50 | $sandbox = new LuaSandbox; 51 | $sandbox->loadString( $lua )->call(); 52 | $sandbox->setCPULimit( 0.25 ); 53 | $sandbox->setMemoryLimit( 100000 ); 54 | try { 55 | print implode("\n", $sandbox->callFunction( $func ) ) . "\n"; 56 | } catch ( LuaSandboxError $e ) { 57 | echo "LuaSandboxError: " . $e->getMessage() . "\n"; 58 | } 59 | } 60 | 61 | --EXPECT-- 62 | Normal: Ok 63 | With __ipairs: Ok 64 | No argument: LuaSandboxError: [string ""]:32: bad argument #1 to 'ipairs' (table expected, got no value) 65 | -------------------------------------------------------------------------------- /tests/loadString.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | loadString 1 3 | --FILE-- 4 | loadString('foo()')); 7 | try { 8 | $f = $sandbox->loadString('foo'); 9 | } catch( Exception $e ) { 10 | print $e->getMessage(); 11 | } 12 | 13 | --EXPECTF-- 14 | object(LuaSandbox)#1 (0) { 15 | } 16 | object(LuaSandboxFunction)#2 (0) { 17 | } 18 | [string ""]:1: '=' expected near '' 19 | -------------------------------------------------------------------------------- /tests/lua_catches_php_exception.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | PHP throwing exceptions to be caught by pcall() 3 | --FILE-- 4 | array( 'pcall_test', 'runtime_error' ), 34 | 'Fatal error' => array( 'hang_test', 'fatal_error' ), 35 | 'Plain Exception' => array( 'hang_test', 'plain_exception' ), 36 | ); 37 | 38 | foreach ( $tests as $desc => $info ) { 39 | list( $wrapper, $funcName ) = $info; 40 | echo "$desc: "; 41 | try { 42 | $sandbox = new LuaSandbox; 43 | $sandbox->loadString( $lua )->call(); 44 | $sandbox->setCPULimit( 0.25 ); 45 | $sandbox->registerLibrary( 'test', array( 'test' => $funcName ) ); 46 | $res = $sandbox->loadString( 'return test.test' )->call(); 47 | print implode("\n", 48 | $sandbox->callFunction( $wrapper, $res[0] ) ) . "\n"; 49 | } catch ( Exception $e ) { 50 | echo get_class( $e ) . ': ' . $e->getMessage() . "\n"; 51 | } 52 | } 53 | 54 | --EXPECT-- 55 | Runtime error: Caught: runtime error 56 | Fatal error: LuaSandboxFatalError: fatal error 57 | Plain Exception: Exception: exception 58 | -------------------------------------------------------------------------------- /tests/oom-init.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Memory limit exceeded during sandbox init 3 | --INI-- 4 | memory_limit=2M 5 | --FILE-- 6 | 13 | --EXPECTF-- 14 | Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d 15 | -------------------------------------------------------------------------------- /tests/pairs.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | pairs() and __pairs 3 | --FILE-- 4 | 'pairs_test1', 85 | 'With __pairs' => 'pairs_test2', 86 | 'No argument' => 'pairs_test3', 87 | 'With __pairs throwing an error' => 'pairs_error', 88 | 'With next func throwing an error' => 'pairs_next_error', 89 | 'Table with __pairs returned to PHP' => 'pairs_return', 90 | 'Table with __pairs throwing an error returned to PHP' => 'pairs_return_error', 91 | 'Table with next func throwing an error returned to PHP' => 'pairs_return_next_error', 92 | ); 93 | 94 | foreach ( $tests as $desc => $func ) { 95 | echo "$desc: "; 96 | $sandbox = new LuaSandbox; 97 | $sandbox->loadString( $lua )->call(); 98 | $sandbox->setCPULimit( 0.25 ); 99 | $sandbox->setMemoryLimit( 100000 ); 100 | try { 101 | print var_export( $sandbox->callFunction( $func ), 1 ) . "\n"; 102 | } catch ( LuaSandboxError $e ) { 103 | echo "LuaSandboxError: " . $e->getMessage() . "\n"; 104 | } 105 | } 106 | 107 | --EXPECT-- 108 | Normal: array ( 109 | 0 => 'Ok', 110 | ) 111 | With __pairs: array ( 112 | 0 => 'Ok', 113 | ) 114 | No argument: LuaSandboxError: [string ""]:32: bad argument #1 to 'pairs' (table expected, got no value) 115 | With __pairs throwing an error: LuaSandboxError: [string ""]:45: Error from __pairs function 116 | With next func throwing an error: LuaSandboxError: [string ""]:60: Error from next function 117 | Table with __pairs returned to PHP: array ( 118 | 0 => 119 | array ( 120 | 'a' => 1, 121 | 'b' => 2, 122 | ), 123 | ) 124 | Table with __pairs throwing an error returned to PHP: LuaSandboxError: [string ""]:52: Error from __pairs function 125 | Table with next func throwing an error returned to PHP: LuaSandboxError: [string ""]:71: Error from next function 126 | -------------------------------------------------------------------------------- /tests/pcall.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | pcall() catching various errors 3 | --FILE-- 4 | 'return 1', 19 | 'User error' => 'error("runtime error")', 20 | 'Argument check error' => 'string.byte()', 21 | 'Infinite recursion' => 'function foo() foo() end foo()', 22 | 'Infinite loop (timeout)' => 'while true do end', 23 | 'Out of memory' => 'string.rep("x", 1000000)' 24 | ); 25 | 26 | foreach ( $tests as $desc => $code ) { 27 | echo "$desc: "; 28 | $sandbox = new LuaSandbox; 29 | $sandbox->loadString( $lua )->call(); 30 | $sandbox->setCPULimit( 0.25 ); 31 | $sandbox->setMemoryLimit( 100000 ); 32 | try { 33 | print implode("\n", 34 | $sandbox->callFunction( 'pcall_test', $sandbox->loadString( $code ) ) ) . "\n"; 35 | } catch ( LuaSandboxError $e ) { 36 | echo "LuaSandboxError: " . $e->getMessage() . "\n"; 37 | } 38 | } 39 | 40 | --EXPECT-- 41 | Normal: success 42 | User error: Caught: [string ""]:1: runtime error 43 | Argument check error: Caught: [string ""]:1: bad argument #1 to 'byte' (string expected, got no value) 44 | Infinite recursion: LuaSandboxError: not enough memory 45 | Infinite loop (timeout): LuaSandboxError: The maximum execution time for this script was exceeded 46 | Out of memory: LuaSandboxError: not enough memory 47 | -------------------------------------------------------------------------------- /tests/profiler-sorting.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | profiler sorting 3 | --FILE-- 4 | loadString( $lua )->call(); 35 | $sandbox->enableProfiler( 0.01 ); 36 | 37 | $sandbox->callFunction( 'test' ); 38 | 39 | foreach( [ 40 | 'samples' => LuaSandbox::SAMPLES, 41 | 'seconds' => LuaSandbox::SECONDS, 42 | 'percent' => LuaSandbox::PERCENT 43 | ] as $name => $stat ) { 44 | $result = $sandbox->getProfilerFunctionReport( $stat ); 45 | // "clone" and sort 46 | $sorted = array_combine( array_keys( $result ), array_values( $result ) ); 47 | arsort( $sorted ); 48 | if ( $result === $sorted ) { 49 | echo "$name: OK\n"; 50 | } else { 51 | echo "$name: FAIL\n"; 52 | var_export( [ 53 | 'result' => $result, 54 | 'sorted' => $sorted, 55 | ] ); 56 | } 57 | } 58 | 59 | --EXPECTF-- 60 | samples: OK 61 | seconds: OK 62 | percent: OK 63 | -------------------------------------------------------------------------------- /tests/profiler.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | profiler 3 | --FILE-- 4 | loadString( $lua )->call(); 20 | $sandbox->enableProfiler( 0.02 ); 21 | 22 | $sandbox->callFunction( 'lua.test' ); 23 | 24 | echo "Samples: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SAMPLES )['clock'] . "\n"; 25 | echo "Seconds: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] . "\n"; 26 | echo "Seconds > 0: " 27 | . ( $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] > 0 ? 'yes' : 'no' ) 28 | . "\n"; 29 | echo "Percent: " . $sandbox->getProfilerFunctionReport( LuaSandbox::PERCENT )['clock'] . "\n"; 30 | 31 | // Test that re-enabling the profiler doesn't explode 32 | $sandbox->enableProfiler( 0.03 ); 33 | 34 | $sandbox->callFunction( 'lua.test' ); 35 | 36 | echo "Samples: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SAMPLES )['clock'] . "\n"; 37 | echo "Seconds: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] . "\n"; 38 | echo "Seconds > 0: " 39 | . ( $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] > 0 ? 'yes' : 'no' ) 40 | . "\n"; 41 | echo "Percent: " . $sandbox->getProfilerFunctionReport( LuaSandbox::PERCENT )['clock'] . "\n"; 42 | 43 | // Test that disabling the profiler doesn't explode 44 | $sandbox->disableProfiler(); 45 | 46 | --EXPECTF-- 47 | Samples: %d 48 | Seconds: %f 49 | Seconds > 0: yes 50 | Percent: %f 51 | Samples: %d 52 | Seconds: %f 53 | Seconds > 0: yes 54 | Percent: %f 55 | -------------------------------------------------------------------------------- /tests/reentrant.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Re-entering Lua during a callback to PHP 3 | --FILE-- 4 | loadString(' 7 | function factorial(n) 8 | if n <= 1 then 9 | return 1 10 | else 11 | return n * test.factorial(n - 1) 12 | end 13 | end 14 | 15 | return factorial 16 | '); 17 | 18 | $ret = $chunk->call(); 19 | $luaFactorial = $ret[0]; 20 | 21 | $sandbox->registerLibrary( 'test', array( 'factorial' => 'factorial' ) ); 22 | 23 | function factorial($n) { 24 | global $luaFactorial; 25 | if ($n <= 1) { 26 | return array(1); 27 | } else { 28 | $ret = $luaFactorial->call($n - 1); 29 | return array($n * $ret[0]); 30 | } 31 | } 32 | 33 | print implode('', factorial(10)) . "\n"; 34 | var_dump( $luaFactorial->call(10) ); 35 | 36 | try { 37 | $luaFactorial->call(1000000000); 38 | } catch ( LuaSandboxError $e ) { 39 | print $e->getMessage() . "\n"; 40 | } 41 | try { 42 | factorial(1000000000); 43 | } catch ( LuaSandboxError $e ) { 44 | print $e->getMessage() . "\n"; 45 | } 46 | 47 | --EXPECTF-- 48 | 3628800 49 | array(1) { 50 | [0]=> 51 | int(3628800) 52 | } 53 | %AWarning: LuaSandboxFunction::call(): Failed to generate Lua trace (C stack overflow) in %s on line %d 54 | C stack overflow 55 | %AWarning: LuaSandboxFunction::call(): Failed to generate Lua trace (C stack overflow) in %s on line %d 56 | C stack overflow 57 | -------------------------------------------------------------------------------- /tests/timer.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | timer pausing and unpausing 3 | --FILE-- 4 | pauseUsageTimer(); 42 | $t = microtime( 1 ) + 0.2; 43 | while ( microtime( 1 ) < $t ) { 44 | } 45 | } 46 | 47 | function unpaused() { 48 | global $sandbox; 49 | $sandbox->pauseUsageTimer(); 50 | $sandbox->unpauseUsageTimer(); 51 | $t = microtime( 1 ) + 0.2; 52 | while ( microtime( 1 ) < $t ) { 53 | } 54 | } 55 | 56 | function overrun() { 57 | global $sandbox; 58 | $sandbox->pauseUsageTimer(); 59 | $t = microtime( 1 ) + 0.45; 60 | while ( microtime( 1 ) < $t ) { 61 | } 62 | } 63 | 64 | function resetLimit() { 65 | global $sandbox; 66 | $sandbox->pauseUsageTimer(); 67 | $sandbox->setCPULimit( 0.1 ); 68 | $t = microtime( 1 ) + 0.2; 69 | while ( microtime( 1 ) < $t ) { 70 | } 71 | } 72 | 73 | function call() { 74 | global $sandbox; 75 | $args = func_get_args(); 76 | call_user_func_array( array( $sandbox, 'callFunction' ), $args ); 77 | } 78 | 79 | function pauseCall() { 80 | global $sandbox; 81 | $sandbox->pauseUsageTimer(); 82 | $args = func_get_args(); 83 | call_user_func_array( array( $sandbox, 'callFunction' ), $args ); 84 | } 85 | 86 | function doTest( $name ) { 87 | global $sandbox, $lua; 88 | 89 | $args = func_get_args(); 90 | array_shift( $args ); 91 | 92 | printf( "%-47s ", "$name:" ); 93 | 94 | $sandbox = new LuaSandbox; 95 | $sandbox->registerLibrary( 'php', array( 96 | 'expensive' => 'expensive', 97 | 'paused' => 'paused', 98 | 'unpaused' => 'unpaused', 99 | 'overrun' => 'overrun', 100 | 'resetLimit' => 'resetLimit', 101 | 'call' => 'call', 102 | 'pauseCall' => 'pauseCall', 103 | ) ); 104 | $sandbox->loadString( $lua )->call(); 105 | $sandbox->setMemoryLimit( 100000 ); 106 | $sandbox->setCPULimit( 0.1 ); 107 | 108 | try { 109 | $timeout='no'; 110 | $t0 = microtime( 1 ); 111 | $u0 = $sandbox->getCPUUsage(); 112 | call_user_func_array( array( $sandbox, 'callFunction' ), $args ); 113 | } catch ( LuaSandboxTimeoutError $err ) { 114 | $timeout='yes'; 115 | } 116 | $t1 = microtime( 1 ) - $t0; 117 | $u1 = $sandbox->getCPUUsage() - $u0; 118 | printf( "%3s (%.1fs of %.1fs)\n", $timeout, $u1, $t1 ); 119 | } 120 | 121 | doTest( 'Lua usage counted', 'lua.expensive' ); 122 | doTest( 'PHP usage counted', 'php.expensive' ); 123 | doTest( 'Paused PHP usage counted', 'php.paused' ); 124 | doTest( 'Unpause works', 'php.unpaused' ); 125 | doTest( 'Auto-unpause works', 'test_auto_unpause' ); 126 | doTest( 'Reset limit unpauses', 'php.resetLimit' ); 127 | doTest( 'Pause overrun prevented', 'test_pause_overrun' ); 128 | 129 | doTest( 'PHP to Lua counted', 'php.call', 'lua.expensive' ); 130 | doTest( 'PHP to paused-PHP counted', 'php.call', 'php.paused' ); 131 | doTest( 'PHP to paused-PHP to paused-PHP counted', 'php.call', 'php.pauseCall', 'php.paused' ); 132 | doTest( 'paused-PHP to Lua counted', 'php.pauseCall', 'lua.expensive' ); 133 | doTest( 'paused-PHP to PHP counted', 'php.pauseCall', 'php.expensive' ); 134 | doTest( 'paused-PHP to paused-PHP counted', 'php.pauseCall', 'php.paused' ); 135 | doTest( 'paused-PHP to paused-PHP to paused-PHP counted', 'php.pauseCall', 'php.pauseCall', 'php.paused' ); 136 | doTest( 'paused-PHP to PHP to paused-PHP counted', 'php.pauseCall', 'php.call', 'php.paused' ); 137 | 138 | --EXPECTF-- 139 | Lua usage counted: yes (0.1s of %fs) 140 | PHP usage counted: yes (0.2s of %fs) 141 | Paused PHP usage counted: no (0.0s of %fs) 142 | Unpause works: yes (0.2s of %fs) 143 | Auto-unpause works: yes (0.1s of %fs) 144 | Reset limit unpauses: no (0.0s of %fs) 145 | Pause overrun prevented: yes (0.1s of %fs) 146 | PHP to Lua counted: yes (0.1s of %fs) 147 | PHP to paused-PHP counted: yes (0.2s of %fs) 148 | PHP to paused-PHP to paused-PHP counted: yes (0.2s of %fs) 149 | paused-PHP to Lua counted: yes (0.1s of %fs) 150 | paused-PHP to PHP counted: yes (0.2s of %fs) 151 | paused-PHP to paused-PHP counted: no (0.0s of %fs) 152 | paused-PHP to paused-PHP to paused-PHP counted: no (0.0s of %fs) 153 | paused-PHP to PHP to paused-PHP counted: yes (0.2s of %fs) 154 | -------------------------------------------------------------------------------- /tests/xpcall.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | xpcall() basic behaviour 3 | --FILE-- 4 | array( 22 | 'return 1', 23 | $xperr 24 | ), 25 | 'User error' => array( 26 | 'error("runtime error")', 27 | $xperr 28 | ), 29 | 'Error in error handler' => array( 30 | 'error("original error")', 31 | 'error("error in handler")' 32 | ), 33 | 'Unconvertible error in error handler' => array( 34 | 'error("original error")', 35 | 'error({})' 36 | ), 37 | 'Numeric error in error handler' => array( 38 | 'error("original error")', 39 | 'error(2)', 40 | ), 41 | 'Argument check error' => array( 42 | 'string.byte()', 43 | $xperr 44 | ), 45 | 'Protected infinite recursion' => array( 46 | 'function foo() foo() end foo()', 47 | $xperr 48 | ), 49 | 'Infinite recursion in handler' => array( 50 | 'error("x")', 51 | 'function foo() foo() end foo()' 52 | ), 53 | 'Protected infinite loop' => array( 54 | 'while true do end', 55 | $xperr, 56 | ), 57 | 'Infinite loop in handler' => array( 58 | 'error("x")', 59 | 'while true do end', 60 | ), 61 | 'Out of memory in handler' => array( 62 | 'error("x")', 63 | 'string.rep("x", 1000000)' 64 | ), 65 | ); 66 | 67 | $sandbox = new LuaSandbox; 68 | $sandbox->loadString( $lua )->call(); 69 | $sandbox->setCPULimit( 0.25 ); 70 | $sandbox->setMemoryLimit( 100000 ); 71 | 72 | foreach ( $tests as $desc => $info ) { 73 | $sandbox = new LuaSandbox; 74 | $sandbox->loadString( $lua )->call(); 75 | $sandbox->setCPULimit( 0.25 ); 76 | $sandbox->setMemoryLimit( 100000 ); 77 | echo "$desc: "; 78 | list( $code, $errorCode ) = $info; 79 | $func = $sandbox->loadString( $code ); 80 | $errorCode = "return function(msg) $errorCode end"; 81 | $ret = $sandbox->loadString( $errorCode )->call(); 82 | $errorFunc = $ret[0]; 83 | 84 | try { 85 | print implode("\n", 86 | $sandbox->callFunction( 'xpcall_test', $func, $errorFunc ) ) . "\n"; 87 | } catch ( LuaSandboxError $e ) { 88 | echo "LuaSandboxError: " . $e->getMessage() . "\n"; 89 | } 90 | } 91 | 92 | --EXPECT-- 93 | Normal: success 94 | User error: xp: [string ""]:1: runtime error 95 | Error in error handler: LuaSandboxError: [string ""]:1: error in handler 96 | Unconvertible error in error handler: LuaSandboxError: unknown error 97 | Numeric error in error handler: LuaSandboxError: [string ""]:1: 2 98 | Argument check error: xp: [string ""]:1: bad argument #1 to 'byte' (string expected, got no value) 99 | Protected infinite recursion: LuaSandboxError: not enough memory 100 | Infinite recursion in handler: LuaSandboxError: not enough memory 101 | Protected infinite loop: LuaSandboxError: The maximum execution time for this script was exceeded 102 | Infinite loop in handler: LuaSandboxError: The maximum execution time for this script was exceeded 103 | Out of memory in handler: LuaSandboxError: not enough memory 104 | --------------------------------------------------------------------------------