On this page:
make-evaluator
make-module-evaluator
13.10.1 Customizing Evaluators
sandbox-init-hook
sandbox-reader
sandbox-input
sandbox-output
sandbox-error-output
sandbox-coverage-enabled
sandbox-propagate-breaks
sandbox-namespace-specs
sandbox-override-collection-paths
sandbox-security-guard
sandbox-path-permissions
sandbox-network-guard
sandbox-eval-limits
sandbox-make-inspector
sandbox-make-logger
13.10.2 Interacting with Evaluators
kill-evaluator
break-evaluator
set-eval-limits
put-input
get-output
get-error-output
get-uncovered-expressions
13.10.3 Miscellaneous
gui?
call-with-limits
with-limits
exn: fail: resource?
exn: fail: resource-resource
Version: 4.1

13.10 Sandboxed Evaluation

 (require scheme/sandbox)

The bindings documented in this section are provided by the scheme/sandbox library, not scheme/base or scheme.

The scheme/sandbox module provides utilities for creating “sandboxed” evaluators, which are configured in a particular way and can have restricted resources (memory and time), filesystem access, and network access.

(make-evaluator

 

language

 

 

 

 

 

 

input-program ...

 

 

 

 

 

 

#:requires requires

 

 

 

 

 

 

#:allow-read allow)

 

 

(any/c . -> . any)

  

language

 

:

 

(or/c module-path?

      (list/c (one-of/c 'special) symbol?)

      (cons/c (one-of/c 'begin) list?))

  input-program : any/c

  requires : (listof (or/c module-path? path?))

  allow : (listof (or/c module-path? path?))

(make-module-evaluator

 

module-decl

 

 

 

 

 

 

#:allow-read allow)

 

 

(any/c . -> . any)

  module-decl : (or/c syntax? pair?)

  allow : (listof (or/c module-path? path?))

The make-evaluator function creates an evaluator with a language and requires specification, and starts evaluating the given input-programs. The make-module-evaluator function creates an evaluator that works in the context of a given module. The result in either case is a function for further evaluation.

The returned evaluator operates in an isolated and limited environment. In particular, filesystem access is restricted. The allow argument extends the set of files that are readable by the evaluator to include the specified modules and their imports (transitively). When language is a module path and when requires is provided, the indicated modules are implicitly included in the allow list.

Each input-program or module-decl argument provides a program in one of the following forms:

In the first three cases above, the program is read using sandbox-reader, with line-counting enabled for sensible error messages, and with 'program as the source (used for testing coverage). In the last case, the input is expected to be the complete program, and is converted to a syntax object (using 'program as the source), unless it already is a syntax object.

The returned evaluator function accepts additional expressions (each time it is called) in essentially the same form: a string or byte string holding a sequence of expressions, a path for a file holding expressions, an S-expression, or a syntax object. If the evaluator receives an eof value, it is terminated and raises errors thereafter. See also kill-evaluator, which terminates the evaluator without raising an exception.

For make-evaluator, multiple input-programs are effectively concatenated to form a single program. The way that the input-programs are evaluated depends on the language argument:

The requires list adds additional imports to the module or namespace for the input-programs, even in the case that require is not made available through the language.

The following examples illustrate the difference between an evaluator that puts the program in a module and one that merely initializes a top-level namespace:

  > (define base-module-eval

      ; a module cannot have free variables...

      (make-evaluator 'scheme/base '(define (f) later)))

  program:1:0: compile: unbound variable in module in: later

  > (define base-module-eval

      (make-evaluator 'scheme/base '(define (f) later)

                                   '(define later 5)))

  > (base-module-eval '(f))

  5

  > (define base-top-eval

      ; non-module code can have free variables:

      (make-evaluator '(begin) '(define (f) later)))

  > (base-top-eval '(+ 1 2))

  3

  > (base-top-eval '(define later 5))

  > (base-top-eval '(f))

  5

The make-module-evaluator function is essentially a restriction of make-evaluator, where the program must be a module, and all imports are part of the program:

  (define base-module-eval2

    ; equivalent to base-module-eval:

    (make-module-evaluator '(module m scheme/base

                              (define (f) later)

                              (define later 5))))

In all cases, the evaluator operates in an isolated and limited environment:

Evaluation can also be instrumented to track coverage information when sandbox-coverage-enabled is set. Exceptions (both syntax and run-time) are propagated as usual to the caller of the evaluation function (i.e., catch it with with-handlers). However, note that a sandboxed evaluator is convenient for testing, since all exceptions happen in the same way, so you don’t need special code to catch syntax errors.

Finally, the fact that a sandboxed evaluator accept syntax objects makes it usable as the value for "current-eval", which means that you can easily start a sandboxed read-eval-print-loop. For example, here is a quick implementation of a networked REPL:

  (define e

    (make-module-evaluator '(module m scheme/base)))

  (let-values ([(i o) (tcp-accept (tcp-listen 9999))])

    (parameterize ([current-input-port i]

                   [current-output-port o]

                   [current-error-port o]

                   [current-eval a])

      (read-eval-print-loop)

      (fprintf o "\nBye...\n")

      (close-output-port o)))

13.10.1 Customizing Evaluators

The evaluators that make-evaluator creates can be customized via several parameters. These parameters affect newly created evaluators; changing them has no effect on already-running evaluators.

(sandbox-init-hook)  (-> any)

(sandbox-init-hook thunk)  void?

  thunk : (-> any)

A parameter that determines a thunk to be called for initializing a new evaluator. The hook is called just before the program is evaluated in a newly-created evaluator context. It can be used to setup environment parameters related to reading, writing, evaluation, and so on. Certain languages ('(special r5rs) and the teaching languages) have initializations specific to the language; the hook is used after that initialization, so it can override settings.

(sandbox-reader)  (any/c . -> . any)

(sandbox-reader proc)  void?

  proc : (any/c . -> . any)

A parameter that determines a function to reads all expressions from (current-input-port). The function is used to read program source for an evaluator when a string. byte string, or port is supplies. The reader function receives a value to be used as input source (i.e., the first argument to read-syntax), and it should return a list of syntax objects. The default reader calls read-syntax, accumulating results in a list until it receives eof.

(sandbox-input)

 

 

(or/c false/c

      string? bytes?

      input-port?

      (one-of/c 'pipe)

      (-> input-port?))

(sandbox-input in)  void?

  

in

 

:

 

(or/c false/c

      string? bytes?

      input-port?

      (one-of/c 'pipe)

      (-> input-port?))

A parameter that determines the initial current-input-port setting for a newly created evaluator. It defaults to #f, which creates an empty port. The following other values are allowed:

(sandbox-output)

 

 

(or/c false/c

      output-port?

      (one-of/c 'pipe 'bytes 'string)

      (-> output-port?))

(sandbox-output in)  void?

  

in

 

:

 

(or/c false/c

      output-port?

      (one-of/c 'pipe 'bytes 'string)

      (-> output-port?))

A parameter that determines the initial current-output-port setting for a newly created evaluator. It defaults to #f, which creates a port that discrds all data. The following other values are allowed:

(sandbox-error-output)

 

 

(or/c false/c

      output-port?

      (one-of/c 'pipe 'bytes 'string)

      (-> output-port?))

(sandbox-error-output in)  void?

  

in

 

:

 

(or/c false/c

      output-port?

      (one-of/c 'pipe 'bytes 'string)

      (-> output-port?))

Like sandbox-output, but for the initial current-error-port value. An evaluator’s error output is set after its output, so using current-output-port (the parameter itself, not its value) for this parameter value means that the error port is the same as the evaluator’s initial output port.

The default is (lambda () (dup-output-port (current-error-port))), which means that the error output of the generated evaluator goes to the calling context’s error port.

(sandbox-coverage-enabled)  boolean?

(sandbox-coverage-enabled enabled?)  void?

  enabled? : any/c

A parameter that controls whether syntactic coverage information is collected by sandbox evaluators. Use get-uncovered-expressions to retrieve coverage information.

(sandbox-propagate-breaks)  boolean?

(sandbox-propagate-breaks propagate?)  void?

  propagate? : any/c

When this boolean parameter is true, breaking while an evaluator is running evaluator propagates the break signal to the sandboxed context. This makes the sandboxed evaluator break, typically, but beware that sandboxed evaluation can capture and avoid the breaks (so if safe execution of code is your goal, make sure you use it with a time limit). The default is #t.

(sandbox-namespace-specs)

 

 

(cons/c (-> namespace?)

        (listof module-path?))

(sandbox-namespace-specs spec)  void?

  

spec

 

:

 

(cons/c (-> namespace?)

        (listof module-path?))

A parameter that holds a list of values that specify how to create a namespace for evaluation in make-evaluator or make-module-evaluator. The first item in the list is a thunk that creates the namespace, and the rest are module paths for modules that to be attached to the created namespace using namespace-attach-module.

The default is (list make-base-namespace) if gui? is #f, (list make-gui-namespace) if gui? is #t.

The module paths are needed for sharing module instantiations between the sandbox and the caller. For example, sandbox code that returns posn values (from the lang/posn module) will not be recognized as such by your own code by default, since the sandbox will have its own instance of lang/posn and thus its own struct type for posns. To be able to use such values, include 'lang/posn in the list of module paths.

When testing code that uses a teaching language, the following piece of code can be helpful:

  (sandbox-namespace-specs

   (let ([specs (sandbox-namespace-specs)])

     `(,(car specs)

       ,@(cdr specs)

       lang/posn

       ,@(if gui? '(mrlib/cache-image-snip) '()))))

(sandbox-override-collection-paths)  (listof path-string?)

(sandbox-override-collection-paths paths)  void?

  paths : (listof path-string?)

A parameter that determines a list of collection directories to prefix current-library-collection-paths in an evaluator. This parameter is useful for cases when you want to test code using an alternate, test-friendly version of a collection, for example, testing code that uses a GUI (like the htdp/world teachpack) can be done using a fake library that provides the same interface but no actual interaction. The default is null.

(sandbox-security-guard)  security-guard?

(sandbox-security-guard guard)  void?

  guard : security-guard?

A parameter that determines the initial (current-security-guard) for sandboxed evaluations. The default forbids all filesystem I/O except for things in sandbox-path-permissions, and it uses sandbox-network-guard for network connections.

(sandbox-path-permissions)

 

 

(listof (list/c (one-of/c 'execute 'write 'delete

                          'read 'exists)

                (or/c byte-regexp? bytes? string? path?)))

(sandbox-path-permissions perms)  void?

  

perms

 

:

 

(listof (list/c (one-of/c 'execute 'write 'delete

                          'read 'exists)

                (or/c byte-regexp? bytes? string? path?)))

A parameter that configures the behavior of the default sandbox security guard by listing paths and access modes that are allowed for them. The contents of this parameter is a list of specifications, each is an access mode and a byte-regexp for paths that are granted this access.

The access mode symbol is one of: 'execute, 'write, 'delete, 'read, or 'exists. These symbols are in decreasing order: each implies access for the following modes too (e.g., 'read allows reading or checking for existence).

The path regexp is used to identify paths that are granted access. It can also be given as a path (or a string or a byte string), which is (made into a complete path, cleansed, simplified, and then) converted to a regexp that allows the path and sub-directories; e.g., "/foo/bar" applies to "/foo/bar/baz".

The default value is null, but when an evaluator is created, it is augmented by 'read permissions that make it possible to use collection libraries (including sandbox-override-collection-paths). See make-evalautor for more information.

(sandbox-network-guard)

 

 

(symbol?

 (or/c (and/c string? immutable?) false/c)

 (or/c (integer-in 1 65535) false/c)

 (one-of/c 'server 'client)

 . -> . any)

(sandbox-network-guard proc)  void?

  

proc

 

:

 

(symbol?

 (or/c (and/c string? immutable?) false/c)

 (or/c (integer-in 1 65535) false/c)

 (one-of/c 'server 'client)

 . -> . any)

A parameter that specifieds a procedure to be used (as is) by the default sandbox-security-guard. The default forbids all network connection.

(sandbox-eval-limits)

 

 

(or/c (list/c (or/c exact-nonnegative-integer? false/c)

              (or/c exact-nonnegative-integer? false/c))

      false/c)

(sandbox-eval-limits limits)  void?

  

limits

 

:

 

(or/c (list/c (or/c exact-nonnegative-integer? false/c)

              (or/c exact-nonnegative-integer? false/c))

      false/c)

A parameter that determines the default limits on each use of a make-evaluator function, including the initial evaluation of the input program. Its value should be a list of two numbers, the first is a timeout value in seconds, and the second is a memory limit in megabytes. Either one can be #f for disabling the corresponding limit; alternately, the parameter can be set to #f to disable all limits (in case more are available in future versions). The default is (list 30 20).

When limits are set, call-with-limits (see below) is wrapped around each use of the evaluator, so consuming too much time or memory results in an exception. Change the limits of a running evaluator using set-eval-limits.

(sandbox-make-inspector)  (-> inspector?)

(sandbox-make-inspector make)  void?

  make : (-> inspector?)

A parameter that determines the procedure used to create the inspector for sandboxed evaluation. The procedure is called when initializing an evaluator, and the default parameter value is make-inspector.

(sandbox-make-logger)  (-> logger?)

(sandbox-make-logger make)  void?

  make : (-> logger?)

A parameter that determines the procedure used to create the logger for sandboxed evaluation. The procedure is called when initializing an evaluator, and the default parameter value is current-logger.

13.10.2 Interacting with Evaluators

The following functions are used to interact with a sandboxed evaluator in addition to using it to evaluate code.

(kill-evaluator evaluator)  void?

  evaluator : (any/c . -> . any)

Releases the resources that are held by evaluator by shutting down the evaluator’s custodian. Attempting to use an evaluator after killing raises an exception, and attempts to kill a dead evaluator are ignored.

Killing an evaluator is similar to sending an eof value to the evaluator, except that an eof value will raise an error immediately.

(break-evaluator evaluator)  void?

  evaluator : (any/c . -> . any)

Sends a break to the running evaluator. The effect of this is as if Ctrl-C was typed when the evaluator is currently executing, which propagates the break to the evaluator’s context.

(set-eval-limits evaluator secs mb)  void?

  evaluator : (any/c . -> . any)

  secs : (or/c exact-nonnegative-integer? false/c)

  mb : (or/c exact-nonnegative-integer? false/c)

Changes the per-expression limits that evaluator uses to sec seconds and mb megabytes (either one can be #f, indicating no limit).

This procedure should be used to modify an existing evaluator limits, because changing the sandbox-eval-limits parameter does not affect existing evaluators. See also call-with-limits.

(put-input evaluator)  output-port?

  evaluator : (any/c . -> . any)

(put-input evaluator i/o)  void?

  evaluator : (any/c . -> . any)

  i/o : (or/c bytes? string? eof-object?)

If (sandbox-input) is 'pipe when an evaluator is created, then this procedure can be used to retrieve the output port end of the pipe (when used with no arguments), or to add a string or a byte string into the pipe. It can also be used with eof, which closes the pipe.

(get-output evaluator)  (or/c input-port? bytes? string?)

  evaluator : (any/c . -> . any)

(get-error-output evaluator)

  (or/c input-port? bytes? string?)

  evaluator : (any/c . -> . any)

Returns the output or error-output of the evaluator, in a way that depends on the setting of (sandbox-output) or (sandbox-error-output) when the evaluator was created:

(get-uncovered-expressions

 

evaluator

 

 

 

 

 

 [

prog?

 

 

 

 

 

 

src])

 

 

(listof syntax?)

  evaluator : (any/c . -> . any)

  prog? : any/c = #t

  src : any/c = 'program

Retrieves uncovered expression from an evaluator, as longs as the sandbox-coverage-enabled parameter had a true value when the evaluator was created. Otherwise, and exception is raised to indicate that no coverage information is available.

The prog? argument specifies whether to obtain expressions that were uncovered after only the original input program was evaluated (#t) or after all later uses of the evaluator (#f). Using #t retrieves a list that is saved after the input program is evaluated, and before the evaluator is used, so the result is always the same.

A #t value of prog? is useful for testing student programs to find out whether a submission has sufficient test coverage built in. A #f value is useful for writing test suites for a program to ensure that your tests cover the whole code.

The second optional argument, src, specifies that the result should be filtered to hold only syntax objects whose source matches src. The default, 'program, is the source associated with the input program by the default sandbox-reader – which provides only syntax objects from the input program (and not from required modules or expressions that were passed to the evaluator). A #f avoids filtering.

The resulting list of syntax objects has at most one expression for each position and span. Thus, the contents may be unreliable, but the position information is reliable (i.e., it always indicates source code that would be painted red in DrScheme when coverage information is used).

Note that if the input program is a sequence of syntax values, either make sure that they have 'program as the source field, or use the src argument. Using a sequence of S-expressions (not syntax objects) for an input program leads to unreliable coverage results, since each expression may be assigned a single source location.

13.10.3 Miscellaneous

gui? : boolean?

True if the scheme/gui module can be used, #f otherwise; see gui-available?.

Various aspects of the scheme/sandbox library change when the GUI library is available, such as using a new eventspace for each evaluator.

(call-with-limits secs mb thunk)  any

  secs : (or/c exact-nonnegative-integer? false/c)

  mb : (or/c exact-nonnegative-integer? false/c)

  thunk : (-> any)

Executes the given thunk with memory and time restrictions: if execution consumes more than mb megabytes or more than sec seconds, then the computation is aborted and the exn:fail:resource exception is raised. Otherwise the result of the thunk is returned as usual (a value, multiple values, or an exception). Each of the two limits can be #f to indicate the absence of a limit. See also custodian-limit-memory for information on memory limits.

Sandboxed evaluators use call-with-limits, according to the sandbox-eval-limits setting and uses of set-eval-limits: each expression evaluation is protected from timeouts and memory problems. Use call-with-limits directly only to limit a whole testing session, instead of each expression.

(with-limits mb-expr body-expr body ...)

A macro version of call-with-limits.

(exn:fail:resource? v)  boolean?

  v : any/c

(exn:fail:resource-resource exn)  (one-of/c 'time 'memory)

  exn : exn:fail:resource?

A predicate and accessor for exceptions that are raised by call-with-limits. The resource field holds a symbol, either 'time or 'memory.