[plt-scheme] Sandbox updates

From: Eli Barzilay (eli at barzilay.org)
Date: Thu Dec 18 22:28:38 EST 2008

The sandbox library has had some major work done, this is a
not-too-short summary of the important changes.

* Implemented a global sandbox memory limit and added a new
  `sandbox-memory-limit' parameter to control it.  Previously, each
  expression evaluation had a memory limit, but there was no global
  limit for the whole sandbox.  For example, if you construct a repl
  that evaluates each expression, then user code could allocate space
  that is smaller than the evaluation limit on each expression,
  therefore grabbing arbitrary amount of memory overall.

* Memory accounting in MzScheme changed in a way that can potentially
  affect sandbox users: an object is counted against the highest
  custodian that can reach it.  This means that if you're running a
  sandbox and pulling out a value from it (for example, to check that
  it satisfies some properties), then that object will be held against
  your own custodian's memory limit (if any).  To get it back under
  the sandboxed custodian, you can simply release references to it.

* When the sandbox commits suicide with (kill-thread (current-thread))
  or (custodian-shutdown-all (current-custodian)), then it is properly
  terminated.  This is possible through an improvement to
  call-with-limits': it now properly propagates such suicide actions
  to the caller.

* Added `sandbox-exit-handler' to control the exit handler.  The
  default will *not* throw an error -- instead, it kills the sandbox.
  (And the "terminated" error message indicates this as a reason.)

* When the sandbox is terminated, a new exn:fail:sandbox-terminated
  exception is thrown.  This exception has a `reason' field which is a
  symbol indicating the reason.  Currently, this can be 'eof (when you
  evaluate the eof value), 'evaluator-killed (when `kill-evaluator'
  was used to kill it), 'thread-killed, 'custodian-shutdown, 'exited
  (when `exit' is used with the default exit handler above),
  'out-of-memory (when `sandbox-memory-limit' is exceeded).

* A new `evaluator-alive?' predicate can be used to determine if the
  sandboxed evaluator is still alive.

* When using a 'string output, the output is created in the sandboxed
  environment, so it counts against its memory limit.

* When a sandbox is created with a given initial code, the constrcuted
  module expression was previously evaluated under the expression
  limits, but the module was invoked outside of these limits.  This is
  fixed now, for example, this code throws a proper error:

       (parameterize ([sandbox-eval-limits '(0.25 5)])
         (make-evaluator 'scheme/base '(sleep 2)))

* The sandbox uses now a new code-inspector to avoid using protected
  bindings (most notably, abusing `scheme/foreign' is impossible now,
  since `unsafe!' cannot be used in sandboxed code).  A new
  `sandbox-make-code-inspector' parameter can be used to control this.

* There is a new access mode for `sandbox-path-permissions':
  'read-bytecode.  This is generally like 'read access, but in
  addition it allows loading bytecode files from such paths.  (This is
  necessary because a lower code-inspector generally means that you
  will not be able to load bytecode files.)  This is used by default
  for the plt collects tree and additional #:allow-read arguments.

* `sandbox-security-guard' can now be a thunk that creates a security
  guard.  The default value uses this, so if you happen to create a
  sandbox in an already-restricted environment, the sandbox will be
  further restricted.

* `call-with-trusted-sandbox-configuration' is a function that invokes
  a thunk in a context where the sandbox customization parameters are
  set for minimum restrictions.  It is useful in cases where you want
  to choose restrictions to add to an unrestricted default rather than
  choose ones to remove from a restricted one.

* `sandbox-eval-handlers' is a new configuration that holds a list of
  two functions that are used to impose arbitrary restrictions on
  sandbox interactions.  The functions are called with a thunk, and
  they're expected to execute the thunk in some restricted way.  The
  first function is used for the initial evaluation when the sandbox
  is setup, and the second is used for further interactions.

  The default value is (list #f call-with-custodian-shutdown), which
  means that no additional restrictions (beyond the usual ones) are
  put on the setup stage, and later evaluations occur in a custodian
  that is shutdown at the end of the interaction.  This prevents
  interactions from leaving behind running threads, open ports, etc,
  but still allows doing so when setting up the sandbox.  Also
  provided are `call-with-killing-threads' (similar, but only kills
  threads), and `set-eval-handler' (makes it possible to change the
  handler in an existing sandbox).

* There is a new `call-in-sandbox-context' which invokes a thunk in
  the sandboxed context.  `get-namespace' is gone, since it's easy to
  do with: (call-in-sandbox-context evaluator current-namespace).
  Note that such code is restricted like any other evaluation, but you
  can use more privileges because your own code is not inside the
  sandbox.  For example:

    (let ([guard (current-security-guard)])
        (lambda ()
          (parameterize ([current-security-guard guard])
            ...can access anything you want here...))))

  An optional second argument can be used to avoid the memory
  `sandbox-eval-handlers' restrictions.

          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!

Posted on the users mailing list.