LuaSandbox

From MediaWiki.org
Jump to navigation Jump to search

LuaSandbox is an extension for PHP 5, PHP 7, and HHVM to allow running Lua 5.1 code from within PHP, which will generally be faster than shelling out to a Lua binary and using inter-process communication.

Installation[edit]

Prepackaged[edit]

LuaSandbox is available in Debian 9 and Ubuntu 18.04 LTS. Install with the following command:

sudo apt-get install php-luasandbox

Note Debian 9 users may need to add deb http://ftp.debian.org/debian stretch-backports main to their sources.list.

Manual installation[edit]

Requirements[edit]

Install the headers and library files for PHP and Lua 5.1.

  • For Debian-derived Linux distributions, such as Ubuntu:
    sudo apt-get install php-dev liblua5.1-0-dev
    
  • For Centos/Redhat-derived Linux distributions:
    sudo yum install php-devel lua lua-devel
    
  • For Mac OS X:
    brew install lua
    

Download[edit]

Download the source code into an appropriate directory from git:

git clone https://gerrit.wikimedia.org/r/p/mediawiki/php/luasandbox.git

Or download a snapshot and unpack.

Build[edit]

luasandbox here is the directory that luasandbox git repository was cloned to.

cd luasandbox
phpize && ./configure && make && sudo make install

Then add extension=luasandbox.so to the PHP configuration in an appropriate place. For example, in modern Debian-derived distributions you'd add a file to /etc/php/$version/mods-available (where $version is the version of PHP for which you complied LuaSandbox) and use the phpenmod command to enable it.

If you're using HHVM instead, build with

cd luasandbox
hphpize && cmake . -DCMAKE_BUILD_TYPE=None -DLUA_USE_CPP=1 && sudo make install

Then add

hhvm.enable_zend_compat = true
hhvm.dynamic_extension_path=/usr/lib/x86_64-linux-gnu/hhvm/extensions/20150212
hhvm.dynamic_extensions[luasandbox]=luasandbox.so

to /etc/hhvm/php.ini. You will need to replace the path to luasandbox.so (/usr/lib/x86_64-linux-gnu/hhvm/extensions/20150212) with whatever install path was reported by make install.

In both PHP and HHVM, if you're using LuaSandbox with a web application such as MediaWiki, you'll need to restart your webserver or php-fpm for PHP to load the extension. After such reload, you should see LuaSandbox in the output of phpinfo() and get_loaded_extensions() (and, for MediaWiki with Scribunto installed, Special:Version).

Examples[edit]

$sandbox = new LuaSandbox;
$sandbox->setMemoryLimit( 50 * 1024 * 1024 );
$sandbox->setCPULimit( 10 );

// Register some functions in the Lua environment

function frobnosticate( $v ) {
    return [ $v + 42 ];
}

$sandbox->registerLibrary( 'php', [
    'frobnosticate' => 'frobnosticate',
    'output' => function ( $string ) {
        echo "$string\n";
    },
    'error' => function () {
        throw new LuaSandboxRuntimeError( "Something is wrong" );
    }
] );

// Execute some Lua code, including callbacks into PHP and into Lua

$luaCode = <<<EOF
php.output( "Hello, world" );

return "Hi", function ( v )
    return php.frobnosticate( v + 200 )
end
EOF;

list( $hi, $frob ) = $sandbox->loadString( $luaCode )->call();
assert( $frob->call( 4000 ) === 4242 );

// PHP-thrown LuaSandboxRuntimeError exceptions can be caught inside Lua

list( $ok, $message ) = $sandbox->loadString( 'return pcall( php.error )' )->call();
assert( !$ok );
assert( $message === 'Something is wrong' );

Documentation[edit]

Generated documentation for classes provided by the extension is available at https://doc.wikimedia.org/mediawiki-php-luasandbox/master/.

Stub files with documentation in PHPDoc format are available in the LuaSandbox repository.

Differences from standard Lua[edit]

LuaSandbox provides a sandboxed environment which differs in some ways from standard Lua 5.1.

The following functions and packages are not available:

  • dofile(), loadfile(), and the io package, as they allow direct filesystem access. If needed, filesystem access should be done via PHP callbacks.
  • The package package, including require() and module(), as it depends heavily on direct filesystem access. A pure-Lua rewrite such as that used in Scribunto may be used instead.
  • load() and loadstring(), to allow for static analysis of Lua code.
  • print(), since it outputs to standard output. If needed, output should be done via PHP callbacks.
  • Most of the os package, as it allows manipulation of the process and executing of other processes.
    • os.clock(), os.date(), os.difftime(), and os.time() remain available.
  • Most of the debug package, as it allows manipulation of Lua state and metadata in ways that can break sandboxing.
    • debug.traceback() remains available.
  • string.dump(), as it may expose internal data.
  • collectgarbage(), gcinfo(), and the coroutine package have not been reviewed for security.

The following features have been modified:

History[edit]

Over the years, MediaWiki's wikitext template "language" gained more features and grew more complicated. As early as 2009, MediaWiki developers began discussing the idea of embedding a real scripting language instead of continuing to make wikitext more complex.

Requirements for such a project included a strong sandbox and strict limitations on memory and CPU time usage, since it would be executing untrusted user code on production servers. It would need to be usable by shelling out to a standalone binary, with the ability to be run in-process via a PHP extension for better performance being a major benefit.

When development started in earnest circa 2011, four candidate languages were identified: Lua, JavaScript, PHP, or a hypothetical "WikiScript" language to be developed. Lua had several advantages:

  • Small (170K standalone) and fast. The existence of LuaJIT was also considered a benefit.
  • Designed for embedding, including easy hooks for CPU and memory limiting.
  • Easy sandboxing, no internal globals.
  • Detailed reference manual, including instructions on embedding.

The main disadvantage was that it wasn't known as widely as JavaScript.

JavaScript, in the form of the V8 engine at the time, had several disadvantages:

  • Minimal documentation on embedding.
  • Continued support for embedding unclear.
  • No allocation hook.
  • Huge standalone binary.

The Rhino engine was worse, as being written in Java it couldn't sanely be embedded in PHP at all. PHP itself was rejected since proper embedding and sandboxing would have been extremely difficult and pre-parsing would have been slow, and "WikiScript" would have been a much larger project in that it would have required developing an interpreter (or two) from scratch.

Thus, Lua was chosen, specifically version 5.1 that was available at the time, and this PHP extension was developed. The changes made to function environment handling in 5.2 have prevented a simple upgrade since, see phab:T178146 for details.