Sunday, 27 January 2019

Boxes of Sand

Fig 1. A Sandbox

Sandboxing is a technique used in testing and security to execute unsafe, or untrusted code in a safe environment. There are different levels of sandboxing: In security a sandboxed environment may refer to a (virtual) machine dedicated to the execution of unsafe code. In testing, a sandbox may refer to a thread or process dedicated to the same purpose.

For most of the history of PHP, sandboxing has been provided by Runkit, and the first thing I'd like to do is clear up some confusion that is present in the manual:
Instantiating the Runkit_Sandbox class creates a new thread with its own scope and program stack.
This has never actually been true. Runkit never created a thread for the sandbox: What it created was a PHP thread context provided by a much misunderstood layer of software called TSRM. It is TSRM that provides the "share nothing" architecture that PHP requires when running in a threaded SAPI, such as Apache on windows. Builds of PHP that use TSRM are colloquially known as ZTS - Zend Thread Safe(ty).

Runkit "switched" between the parent and the sandbox context to execute code as if it were in another thread, but there was never actually another thread, so this extremely old documentation is very misleading. For the sake of nostalgia, I don't intend to fix the documentation, it has been that way for 13 years, and the feature is all but dead, so it's not affecting anyone anymore.

There is a version of runkit available for PHP7, however, sandboxing was removed because it looks like the new maintainer couldn't figure out how to make it work, maybe mislead by current documentation.

The only people who care about TSRM/ZTS in PHP are the windows people, and myself. The windows people need it for Apache, and I need it for pthreads, what's more I recognise the value in the abilities that TSRM provides. So when PHP 7 quietly improved the performance of ZTS with improvements to TSRM, nobody really noticed, I've even heard people say TSRM is going to be removed. It isn't.

Those improvements broke the ability to abuse TSRM as Runkit did, for many boring reasons, runkits sandbox cannot work as it did before, it's not possible.

I've bumped up against runkit before; uopz exists because 5 years ago (roughly) I started a new job and was presented with test suites that would crash all the time because runkit was doing bad things. This is the reason I didn't and won't work on runkit, but do think a sandbox is a useful thing.

So I wrote Sandbox, it's a PHP 7.1+ extension (because nobody is using 7.0, right !?) that requires a ZTS build of PHP and creates real sandboxes. A Sandbox is truly a separate thread, isolation is provided by TSRM as it always has been, this means a Sandbox thread is as isolated from the thread that created it as are two PHP threads inside apache, almost complete isolation.

While the Runkit implementation provided a lot of methods to affect the context it created, the new implementation provides just an entry point into the sandbox thread:

$sandbox = new \sandbox\Runtime();

    /* I will execute in the sandbox thread */

This doesn't give you multi-threading, the sandbox and parent threads are synchronized so that no user code executes in parallel, this is the safest (only) thing to do for a sandbox.

The code executed in the sandbox may do anything up to but excluding making PHP segfault, and not affect the thread (process) that created it. It may exhaust memory limits and time constraints and not only will the parent thread remain stable, so will the child (you may re-enter after such failures).

By passing an array of INI options to the constructor of the runtime you can gain great control over the sandbox.

I'm afraid I haven't created a PHP manual for Sandbox, so that job is up for grabs ... I'm quite happy to leave it quietly in a corner and have the kind of people who read my blog know about it, but not everyone who reads the manunal, although I won't object to anyone commiting manual entries.

Thanks to my awesome QA/RM team, Anatol and Remi, who I adopted from PHP for my own purposes; Sandbox is available on PECL, windows builds are available, and it's available from Remi's repositories.

So, now we have a sandbox for PHP7.1+ ... have fun using it ...