14.12 Sandboxed Evaluation

► |  | The Racket Reference

1 | Language Model
2 | Notation for Documentation
3 | Syntactic Forms
4 | Datatypes
5 | Structures
6 | Classes and Objects
7 | Units
8 | Contracts
9 | Pattern Matching
10 | Control Flow
11 | Concurrency and Parallelism
12 | Macros
13 | Input and Output
14 | Reflection and Security
15 | Operating System
16 | Memory Management
17 | Unsafe Operations
18 | Running Racket
| Bibliography
| Index

▼ | 14 | Reflection and Security

14.1 | Namespaces
14.2 | Evaluation and Compilation
14.3 | The racket/ load Language
14.4 | Module Names and Loading
14.5 | Impersonators and Chaperones
14.6 | Security Guards
14.7 | Custodians
14.8 | Thread Groups
14.9 | Structure Inspectors
14.10 | Code Inspectors
14.11 | Plumbers
14.12 | Sandboxed Evaluation
14.13 | The racket/ repl Library
14.14 | Linklets and the Core Compiler

► | 14.12 | Sandboxed Evaluation

14.12.1 | Security Considerations
14.12.2 | Customizing Evaluators
14.12.3 | Interacting with Evaluators
14.12.4 | Miscellaneous

On this page:

make-evaluator
make-module-evaluator
exn: fail: sandbox-terminated?
exn: fail: sandbox-terminated-reason
14.12.1`` Security Considerations
14.12.2`` Customizing Evaluators
call-with-trusted-sandbox-configuration
sandbox-init-hook
sandbox-reader
sandbox-input
sandbox-output
sandbox-error-output
sandbox-coverage-enabled
sandbox-propagate-breaks
sandbox-propagate-exceptions
sandbox-namespace-specs
sandbox-make-namespace
sandbox-gui-available
sandbox-override-collection-paths
sandbox-security-guard
sandbox-path-permissions
sandbox-network-guard
sandbox-exit-handler
sandbox-memory-limit
sandbox-eval-limits
sandbox-eval-handlers
sandbox-run-submodules
sandbox-make-inspector
sandbox-make-code-inspector
sandbox-make-logger
sandbox-make-plumber
sandbox-make-environment-variables
14.12.3`` Interacting with Evaluators
evaluator-alive?
kill-evaluator
break-evaluator
get-user-custodian
set-eval-limits
set-eval-handler
call-with-custodian-shutdown
call-with-killing-threads
put-input
get-output
get-error-output
get-uncovered-expressions
call-in-sandbox-context
14.12.4`` Miscellaneous
gui?
call-with-limits
with-limits
call-with-deep-time-limit
with-deep-time-limit
exn: fail: resource?
exn: fail: resource-resource

top ← prev up next →

#### 14.12`` Sandboxed Evaluation

(require racket/sandbox) | package: sandbox-lib

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

The racket/sandbox module provides utilities for creating “sandboxed” evaluators, which are configured in a particular way and can have restricted resources (memory and time), filesystem and network access, and much more. Sandboxed evaluators can be configured through numerous parameters — and the defaults are set for the common use case where sandboxes are very limited.

> > procedure
> > (make-evaluator |  | language |
> > |  | input-program ... |
> > | [ | #:requires requires |
> > |  | #:allow-for-require allow-for-require |
> > |  | #:allow-for-load allow-for-load |
> > |  | #:allow-read allow-read]) |
> → (any/c . -> . any)
> | language |  | : |  |
> (or/c module-path?
> (list/c 'special symbol?)
> (cons/c 'begin list?))
> input-program : any/c
> | requires |  | : |  |
> (listof (or/c module-path? path-string?
> (cons/c 'for-syntax (listof module-path?))))
> |  |  | = |  | null
> allow-for-require : (listof (or/c module-path? path?)) = null
> allow-for-load : (listof path-string?) = null
> allow-read : (listof (or/c module-path? path-string?)) = null
> (make-module-evaluator |  | module-decl |
> | [ | #:language lang |
> |  | #:allow-for-require allow-for-require |
> |  | #:allow-for-load allow-for-load |
> |  | #:allow-read allow-read]) |
> → (any/c . -> . any)
> module-decl : (or/c syntax? pair? path? input-port? string? bytes?)
> lang : (or/c #f module-path?) = #f
> allow-for-require : (listof (or/c module-path? path?)) = null
> allow-for-load : (listof path-string?) = null
> allow-read : (listof (or/c module-path? path-string?)) = null

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, which may interfere with using modules from the filesystem that are not in a collection. See below for information on the allow-for-require, allow-for-load, and allow-read arguments. When language is a module path or when requires is provided, the indicated modules are implicitly included in the allow-for-require list. (For backward compatibility, non-module-path? path strings are allowed in requires; they are implicitly converted to paths before addition to allow-for-require.)

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

- an input port used to read the program;

- a string or a byte string holding the complete input;

- a path that names a file holding the input; or

- an S-expression or a syntax object, which is evaluated as with eval (see also get-uncovered-expressions).

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 language argument can be a module path (i.e., a datum that matches the grammar for module-path of require).

  In this case, the input-programs are automatically wrapped in a module, and the resulting evaluator works within the resulting module’s namespace.

- The language argument can be a list starting with 'special, which indicates a built-in language with special input configuration. The possible values are '(special r5rs) or a value indicating a teaching language: '(special beginner), '(special beginner-abbr), '(special intermediate), '(special intermediate-lambda), or '(special advanced).

  In this case, the input-programs are automatically wrapped in a module, and the resulting evaluator works within the resulting module’s namespace. In addition, certain parameters (such as such as read-accept-infix-dot) are set to customize reading programs from strings and ports.

  This option is provided mainly for older test systems. Using make-module-evaluator with input starting with #lang is generally better.

- Finally, language can be a list whose first element is 'begin.

  In this case, a new namespace is created using sandbox-namespace-specs, which by default creates a new namespace using sandbox-make-namespace (which, in turn, uses make-base-namespace or make-gui-namespace depending on sandbox-gui-available and gui-available?).

  In the new namespace, language is evaluated as an expression to further initialize the namespace.

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 'racket/base '(define (f) later)))
> program:1:0: later: unbound identifier
> in: later
> > (define base-module-eval
> (make-evaluator 'racket/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. In some cases it is useful to restrict the program to be a module using a specific module in its language position — use the optional lang argument to specify such a restriction (the default, #f, means no restriction is enforced). When the program is specified as a path, then the path is implicitly added to the allow-for-load list.

> (define base-module-eval2
> ; equivalent to base-module-eval:
> (make-module-evaluator '(module m racket/base
> (define (f) later)
> (define later 5))))

The make-module-evaluator function can be convenient for testing module files: pass in a path value for the file name, and you get back an evaluator in the module’s context which you can use with your favorite test facility.

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

- It uses a new custodian and namespace. When gui-available? and sandbox-gui-available produce true, it is also runs in its own eventspace.

- The evaluator works under the sandbox-security-guard, which restricts file system and network access.

- The evaluator is contained in a memory-restricted environment, and each evaluation is wrapped in a call-with-limits (when memory accounting is available); see also sandbox-memory-limit, sandbox-eval-limits and set-eval-limits.

Note that these limits apply to the creation of the sandbox environment too — so, for example, if the memory that is required to create the sandbox is higher than the limit, then make-evaluator will fail with a memory limit exception.

The allow-for-require and allow-for-load arguments adjust filesystem permissions to extend the set of files that are usable by the evaluator. Modules that are in a collection are automatically accessible, but the allow-for-require argument lists additional modules that can be required along with their imports (transitively) through a filesystem path. The allow-for-load argument similarly lists files that can be loaded. (The precise permissions needed for require versus load can differ.) The allow-read argument is for backward compatibility, only; each module-path? element of allow-read is effectively moved to allow-for-require, while other elements are moved to allow-for-load.

The sandboxed environment is well isolated, and the evaluator function essentially sends it an expression and waits for a result. This form of communication makes it impossible to have nested (or concurrent) calls to a single evaluator. Usually this is not a problem, but in some cases you can get the evaluator function available inside the sandboxed code, for example:

> > (let ([e (make-evaluator 'racket/base)])
> (e `(,e 1)))
> evaluator: nested evaluator call with: 1

An error will be signaled in such cases.

If the value of sandbox-propagate-exceptions is true (the default) when the sandbox is created, then exceptions (both syntax and run-time) are propagated as usual to the caller of the evaluation function (i.e., catch them with with-handlers). If the value of sandbox-propagate-exceptions is #f when the sandbox is created, then uncaught exceptions in a sandbox evaluation cause the error to be printed to the sandbox’s error port, and the caller of the evaluation receives #<void>.

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:

> (define e (make-evaluator 'racket/base))
> (parameterize ([current-eval e])
> (read-eval-print-loop))

Note that in this code only the REPL interactions will be printed to the current output ports; using I/O operations inside the REPL will still use the usual sandbox parameters (defaulting to no I/O). In addition, the code works only from an existing toplevel REPL — specifically, read-eval-print-loop reads a syntax value and gives it the lexical context of the current namespace. Here is a variation that also allows I/O over the current input and output ports, and works when used from a module (by using a new namespace):

> (parameterize ([sandbox-input current-input-port]
> [sandbox-output current-output-port]
> [sandbox-error-output current-error-port]
> [current-namespace (make-empty-namespace)])
> (parameterize ([current-eval (make-evaluator 'racket/base)])
> (read-eval-print-loop)))

> procedure (exn:fail:sandbox-terminated? v) → boolean?
> v : any/c
> (exn:fail:sandbox-terminated-reason exn) → symbol?
> exn : exn:fail:sandbox-terminated?

A predicate and accessor for exceptions that are raised when a sandbox is terminated. Once a sandbox raises such an exception, it will continue to raise it on further evaluation attempts.

##### 14.12.1`` Security Considerations

Although the sandbox is designed to provide a safe environment for executing Racket programs with restricted access to system resources, executing untrusted programs in a sandbox still carries some risk. Because a malicious program can exercise arbitrary functionality from the Racket runtime and installed collections, an attacker who identifies a vulnerability in Racket or an installed collection may be able to escape the sandbox.

To mitigate this risk, programs that use the sandbox should employ additional precautions when possible. Suggested measures include:

- Supplying a custom module language to make-evaluator or make-module-evaluator that gives untrusted code access to only the language constructs it absolutely requires.

- If untrusted code needs access to installed collections, installing only the collections required by your program.

- Using operating-system-level security features to provide defense-in-depth in case the process running the sandbox is compromised.

- Making sure your Racket installation and installed packages are up-to-date with the latest release.

##### 14.12.2`` Customizing Evaluators

The sandboxed evaluators that make-evaluator creates can be customized via many parameters. Most of the configuration parameters affect newly created evaluators; changing them has no effect on already-running evaluators.

The default configuration options are set for a very restricted sandboxed environment — one that is safe to make publicly available. Further customizations might be needed in case more privileges are needed, or if you want tighter restrictions. Another useful approach for customizing an evaluator is to begin with a relatively unrestricted configuration and add the desired restrictions. This approach is made possible by the call-with-trusted-sandbox-configuration function.

The sandbox environment uses two notions of restricting the time that evaluations takes: shallow time and deep time. Shallow time refers to the immediate execution of an expression. For example, a shallow time limit of five seconds would restrict (sleep 6) and other computations that take longer than five seconds. Deep time refers to the total execution of the expression and all threads and sub-processes that the expression creates. For example, a deep time limit of five seconds would restrict (thread (λ () (sleep 6))), which shallow time would not, as well as all expressions that shallow time would restrict. By default, most sandboxes only restrict shallow time to facilitate expressions that use threads.

> procedure (call-with-trusted-sandbox-configuration thunk) → any
> thunk : (-> any)

Invokes the thunk in a context where sandbox configuration parameters are set for minimal restrictions. More specifically, there are no memory or time limits, and the existing existing inspectors, security guard, exit handler, logger, plumber, and environment variable set are used. (Note that the I/O ports settings are not included.)

> parameter (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.

> parameter (sandbox-reader) → (any/c . -> . any)
> (sandbox-reader proc) → void?
> proc : (any/c . -> . any)

A parameter that specifies a function that 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 supplied. 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.

Note that the reader function is usually called as is, but when it is used to read the program input for make-module-evaluator, read-accept-lang and read-accept-reader are set to #t.

> > parameter
> > (sandbox-input) |  | → |  |
> > (or/c #f
> > string? bytes?
> > input-port?
> > 'pipe
> > (-> input-port?))
> (sandbox-input in) → void?
> | in |  | : |  |
> (or/c #f
> string? bytes?
> input-port?
> '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:

- a string or byte string, which is converted to a port using open-input-string or open-input-bytes;

- an input port;

- the symbol 'pipe, which triggers the creation of a pipe, where put-input can return the output end of the pipe or write directly to it;

- a thunk, which is called to obtain a port (e.g., using current-input-port means that the evaluator input is the same as the calling context’s input).

> > parameter
> > (sandbox-output) |  | → |  |
> > (or/c #f
> > output-port?
> > 'pipe
> > 'bytes
> > 'string
> > (-> output-port?))
> (sandbox-output in) → void?
> | in |  | : |  |
> (or/c #f
> output-port?
> '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 discards all data. The following other values are allowed:

- an output port, which is used as-is;

- the symbol 'bytes, which causes get-output to return the complete output as a byte string as long as the evaluator has not yet terminated (so that the size of the bytes can be charged to the evaluator);

- the symbol 'string, which is similar to 'bytes, but makes get-output produce a string;

- the symbol 'pipe, which triggers the creation of a pipe, where get-output returns the input end of the pipe;

- a thunk, which is called to obtain a port (e.g., using current-output-port means that the evaluator output is not diverted).

> > parameter
> > (sandbox-error-output) |  | → |  |
> > (or/c #f
> > output-port?
> > 'pipe
> > 'bytes
> > 'string
> > (-> output-port?))
> (sandbox-error-output in) → void?
> | in |  | : |  |
> (or/c #f
> output-port?
> '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.

> parameter (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.

The default value is #f.

> parameter (sandbox-propagate-breaks) → boolean?
> (sandbox-propagate-breaks propagate?) → void?
> propagate? : any/c

When both this boolean parameter and (break-enabled) are true, breaking while an evaluator is running 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). Also, beware that a break may be received after the evaluator’s result, in which case the evaluation result is lost. Finally, beware that a break may be propagated after an evaluator has produced a result, so that the break is visible on the next interaction with the evaluator (or the break is lost if the evaluator is not used further). The default is #t.

> parameter (sandbox-propagate-exceptions) → boolean?
> (sandbox-propagate-exceptions propagate?) → void?
> propagate? : any/c

A parameter that controls how uncaught exceptions during a sandbox evaluation are treated. When the parameter value is #t, then the exception is propagated to the caller of sandbox. When the parameter value is #f, the exception message is printed to the sandbox’s error port, and the caller of the sandbox receives #<void> for the evaluation. The default is #t.

> > parameter
> > (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 to be attached to the created namespace using namespace-attach-module.

The default is (list sandbox-make-namespace).

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-available?) '(mrlib/cache-image-snip) '()))))

> procedure (sandbox-make-namespace) → namespace?

Calls make-gui-namespace when (sandbox-gui-available) produces true, make-base-namespace otherwise.

> parameter (sandbox-gui-available) → boolean?
> (sandbox-gui-available avail?) → void?
> avail? : any/c

Determines whether the racket/gui module can be used when a sandbox evaluator is created. If gui-available? produces #f during the creation of a sandbox evaluator, this parameter is forced to #f during initialization of the sandbox. The default value of the parameter is #t.

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

> parameter (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.

> parameter (sandbox-security-guard)
> → (or/c security-guard? (-> security-guard?))
> (sandbox-security-guard guard) → void?
> guard : (or/c security-guard? (-> security-guard?))

A parameter that determines the initial (current-security-guard) for sandboxed evaluations. It can be either a security guard, or a function to construct one. The default is a function that restricts the access of the current security guard by forbidding all filesystem I/O except for specifications in sandbox-path-permissions, and it uses sandbox-network-guard for network connections.

> parameter (sandbox-path-permissions)
> | → |  |
> (listof (list/c (or/c 'execute 'write 'delete
> 'read-bytecode 'read 'exists)
> (or/c byte-regexp? bytes? string? path?)))
> (sandbox-path-permissions perms) → void?
> | perms |  | : |  |
> (listof (list/c (or/c 'execute 'write 'delete
> 'read-bytecode '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".

An additional mode symbol, 'read-bytecode, is not part of the linear order of these modes. Specifying this mode is similar to specifying 'read, but it is not implied by any other mode. (For example, even if you specify 'write for a certain path, you need to also specify 'read-bytecode to grant this permission.) The sandbox usually works in the context of a lower code inspector (see sandbox-make-code-inspector) which prevents loading of untrusted bytecode files — the sandbox is set-up to allow loading bytecode from files that are specified with 'read-bytecode. This specification is given by default to the Racket collection hierarchy (including user-specific libraries) and to libraries that are explicitly specified in an #:allow-read argument. (Note that this applies for loading bytecode files only, under a lower code inspector it is still impossible to use protected module bindings (see Code Inspectors).)

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

> parameter (sandbox-network-guard)
> | → |  |
> (symbol?
> (or/c (and/c string? immutable?) #f)
> (or/c (integer-in 1 65535) #f)
> (or/c 'server 'client)
> . -> . any)
> (sandbox-network-guard proc) → void?
> | proc |  | : |  |
> (symbol?
> (or/c (and/c string? immutable?) #f)
> (or/c (integer-in 1 65535) #f)
> (or/c 'server 'client)
> . -> . any)

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

> parameter (sandbox-exit-handler) → (any/c . -> . any)
> (sandbox-exit-handler handler) → void?
> handler : (any/c . -> . any)

A parameter that determines the initial (exit-handler) for sandboxed evaluations. The default kills the evaluator with an appropriate error message (see exn:fail:sandbox-terminated-reason).

> parameter (sandbox-memory-limit) → (or/c (>=/c 0) #f)
> (sandbox-memory-limit limit) → void?
> limit : (or/c (>=/c 0) #f)

A parameter that determines the total memory limit on the sandbox in megabytes (it can hold a rational or a floating point number). When this limit is exceeded, the sandbox is terminated. This value is used when the sandbox is created and the limit cannot be changed afterwards. It defaults to 30mb. See sandbox-eval-limits for per-evaluation limits and a description of how the two limits work together.

Note that (when memory accounting is enabled) memory is attributed to the highest custodian that refers to it. This means that if you inspect a value that sandboxed evaluation returns outside of the sandbox, your own custodian will be charged for it. To ensure that it is charged back to the sandbox, you should remove references to such values when the code is done inspecting it.

This policy has an impact on how the sandbox memory limit interacts with the per-expression limit specified by sandbox-eval-limits: values that are reachable from the sandbox, as well as from the interaction will count against the sandbox limit. For example, in the last interaction of this code,

> (define e (make-evaluator 'racket/base))
> (e '(define a 1))
> (e '(for ([i (in-range 20)]) (set! a (cons (make-bytes 500000) a))))

the memory blocks are allocated within the interaction limit, but since they’re chained to the defined variable, they’re also reachable from the sandbox — so they will count against the sandbox memory limit but not against the interaction limit (more precisely, no more than one block counts against the interaction limit).

> > parameter
> > (sandbox-eval-limits) |  | → |  |
> > (or/c (list/c (or/c (>=/c 0) #f)
> > (or/c (>=/c 0) #f))
> > #f)
> (sandbox-eval-limits limits) → void?
> | limits |  | : |  |
> (or/c (list/c (or/c (>=/c 0) #f)
> (or/c (>=/c 0) #f))
> #f)

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; where the first is a shallow time value in seconds, and the second is a memory limit in megabytes (note that they don’t have to be integers). Either one can be #f for disabling the corresponding limit; alternately, the parameter can be set to #f to disable all per-evaluation limits (useful in case more limit kinds are available in future versions). The default is (list 30 20).

Note that these limits apply to the creation of the sandbox environment too — even (make-evaluator 'racket/base) can fail if the limits are strict enough. For example,

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

will throw an error instead of creating an evaluator. Therefore, to avoid surprises you need to catch errors that happen when the sandbox is created.

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.

> > > A custodian’s limit is checked only after a garbage collection, except that it may also be checked during certain large allocations that are individually larger than the custodian’s limit.

The memory limit that is specified by this parameter applies to each individual evaluation, but not to the whole sandbox — that limit is specified via sandbox-memory-limit. When the global limit is exceeded, the sandbox is terminated, but when the per-evaluation limit is exceeded, an exception recognizable by exn:fail:resource? is raised. For example, say that you evaluate an expression like

> (for ([i (in-range 1000)])
> (set! a (cons (make-bytes 1000000) a))
> (collect-garbage))

then, assuming sufficiently small limits,

- if a global limit is set but no per-evaluation limit, the sandbox will eventually be terminated and no further evaluations possible;

- if there is a per-evaluation limit, but no global limit, the evaluation will abort with an error and it can be used again — specifically, a will still hold a number of blocks, and you can evaluate the same expression again which will add more blocks to it;

- if both limits are set, with the global one larger than the per-evaluation limit, then the evaluation will abort and you will be able to repeat it, but doing so several times will eventually terminate the sandbox (this will be indicated by the error message, and by the evaluator-alive? predicate).

> parameter (sandbox-eval-handlers)
> | → |  |
> (list/c (or/c #f ((-> any) . -> . any))
> (or/c #f ((-> any) . -> . any)))
> (sandbox-eval-handlers handlers) → void?
> | handlers |  | : |  |
> (list/c (or/c #f ((-> any) . -> . any))
> (or/c #f ((-> any) . -> . any)))

A parameter that determines two (optional) handlers that wrap sandboxed evaluations. The first one is used when evaluating the initial program when the sandbox is being set-up, and the second is used for each interaction. Each of these handlers should expect a thunk as an argument, and they should execute these thunks — possibly imposing further restrictions. The default values are #f and call-with-custodian-shutdown, meaning no additional restrictions on initial sandbox code (e.g., it can start background threads), and a custodian-shutdown around each interaction that follows. Another useful function for this is call-with-killing-threads which kills all threads, but leaves other resources intact.

> parameter (sandbox-run-submodules) → (list/c symbol?)
> (sandbox-run-submodules submod-syms) → void?
> submod-syms : (list/c symbol?)

A parameter that determines submodules to run when a sandbox is created by make-module-evaluator. The parameter’s default value is the empty list.

> parameter (sandbox-make-inspector) → (-> inspector?)
> (sandbox-make-inspector make) → void?
> make : (-> inspector?)

A parameter that determines the (nullary) procedure that is used to create the inspector for sandboxed evaluation. The procedure is called when initializing an evaluator. The default parameter value is (lambda () (make-inspector (current-inspector))).

> parameter (sandbox-make-code-inspector) → (-> inspector?)
> (sandbox-make-code-inspector make) → void?
> make : (-> inspector?)

A parameter that determines the (nullary) procedure that is used to create the code inspector for sandboxed evaluation. The procedure is called when initializing an evaluator. The default parameter value is (lambda () (make-inspector (current-code-inspector))).

The current-load/use-compiled handler is setup to allow loading of bytecode files under the original code inspector when sandbox-path-permissions allows it through a 'read-bytecode mode symbol, which makes loading libraries possible.

> parameter (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. This means that it is not creating a new logger (this might change in the future).

> parameter (sandbox-make-plumber) → (or/c (-> plumber?) 'propagate)
> (sandbox-make-plumber make) → void?
> make : (or/c (-> plumber?) 'propagate)

A parameter that determines the procedure used to create the plumber for sandboxed evaluation. The procedure is called when initializing an evaluator.

If the value is 'propagate (the default), then a new plumber is created, and a flush callback is added to the current plumber to propagate the request to the new plumber within the created sandbox (if the sandbox has not already terminated).

Added in version 6.0.1.8 of package sandbox-lib.

> parameter (sandbox-make-environment-variables)
> → (-> environment-variables?)
> (sandbox-make-environment-variables make) → void?
> make : (-> environment-variables?)

A parameter that determines the procedure used to create the environment variable set for sandboxed evaluation. The procedure is called when initializing an evaluator, and the default parameter value constructs a new environment variable set using (environment-variables-copy (current-environment-variables)).

##### 14.12.3`` Interacting with Evaluators

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

> procedure (evaluator-alive? evaluator) → boolean?
> evaluator : (any/c . -> . any)

Determines whether the evaluator is still alive.

> procedure (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.

> procedure (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.

> procedure (get-user-custodian evaluator) → void?
> evaluator : (any/c . -> . any)

Retrieves the evaluator’s toplevel custodian. This returns a value that is different from (evaluator '(current-custodian)) or call-in-sandbox-context evaluator current-custodian — each sandbox interaction is wrapped in its own custodian, which is what these would return.

(One use for this custodian is with current-memory-use, where the per-interaction sub-custodians will not be charged with the memory for the whole sandbox.)

> procedure (set-eval-limits evaluator secs mb) → void?
> evaluator : (any/c . -> . any)
> secs : (or/c exact-nonnegative-integer? #f)
> mb : (or/c exact-nonnegative-integer? #f)

Changes the per-expression limits that evaluator uses to secs seconds of shallow time 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.

> procedure (set-eval-handler evaluator handler) → void?
> evaluator : (any/c . -> . any)
> handler : (or/c #f ((-> any) . -> . any))

Changes the per-expression handler that the evaluator uses around each interaction. A #f value means no handler is used.

This procedure should be used to modify an existing evaluator handler, because changing the sandbox-eval-handlers parameter does not affect existing evaluators. See also call-with-custodian-shutdown and call-with-killing-threads for two useful handlers that are provided.

> procedure (call-with-custodian-shutdown thunk) → any
> thunk : (-> any)
> (call-with-killing-threads thunk) → any
> thunk : (-> any)

These functions are useful for use as an evaluation handler. call-with-custodian-shutdown will execute the thunk in a fresh custodian, then shutdown that custodian, making sure that thunk could not have left behind any resources. call-with-killing-threads is similar, except that it kills threads that were left, but leaves other resources as is.

> procedure (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.

> procedure (get-output evaluator) → (or/c #f input-port? bytes? string?)
> evaluator : (any/c . -> . any)
> (get-error-output evaluator)
> → (or/c #f 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:

- if it was 'pipe, then get-output returns the input port end of the created pipe;

- if it was 'bytes or 'string, then the result is the accumulated output, and the output port is reset so each call returns a different piece of the evaluator’s output (note that results are available only until the evaluator has terminated, and any allocations of the output are subject to the sandbox memory limit);

- otherwise, it returns #f.

> > procedure
> > (get-uncovered-expressions |  | evaluator |  |  |  |
> > | [ | prog? |  |  |  |
> > |  | src]) |  | → |  | (listof syntax?)
> evaluator : (any/c . -> . any)
> prog? : any/c = #t
> src : any/c = default-src

Retrieves uncovered expression from an evaluator, as longs as the sandbox-coverage-enabled parameter had a true value when the evaluator was created. Otherwise, an 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 is the source that was used in the program code, if there was one. Note that 'program is used as the source value if the input program was given as S-expressions or as a string (and in these cases it will be the default for filtering). If given #f, the result is the unfiltered list of expressions.

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 DrRacket 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.

> > procedure
> > (call-in-sandbox-context |  | evaluator |  |  |  |
> > |  | thunk |  |  |  |
> > | [ | unrestricted?]) |  | → |  | any
> evaluator : (any/c . -> . any)
> thunk : (-> any)
> unrestricted? : boolean? = #f

Calls the given thunk in the context of a sandboxed evaluator. The call is performed under the resource limits and evaluation handler that are used for evaluating expressions, unless unrestricted? is specified as true.

This process is usually similar to (evaluator (list thunk)), except that it does not rely on the common meaning of a sexpr-based syntax with list expressions as function application (which is not true in all languages). Note that this is more useful for meta-level operations such as namespace manipulation, it is not intended to be used as a safe-evaluation replacement (i.e., using the sandbox evaluator as usual).

In addition, you can avoid some of the sandboxed restrictions by using your own permissions, for example,

> (let ([guard (current-security-guard)])
> (call-in-sandbox-context
> ev
> (lambda ()
> (parameterize ([current-security-guard guard])
> ; can access anything you want here
> (delete-file "/some/file")))))

##### 14.12.4`` Miscellaneous

> value gui? : boolean?

For backward compatibility, only: the result of gui-available? at the time that racket/sandbox was instantiated.

The value of gui? is no longer used by racket/sandbox itself. Instead, gui-available? and sandbox-gui-available are checked at the time that a sandbox evaluator is created.

> procedure (call-with-limits secs mb thunk) → any
> secs : (or/c exact-nonnegative-integer? #f)
> mb : (or/c exact-nonnegative-integer? #f)
> thunk : (-> any)

Executes the given thunk with memory and time restrictions: if execution consumes more than mb megabytes or more than secs shallow time seconds, then the computation is aborted and an exception recognizable by exn:fail:resource? 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.

> syntax (with-limits sec-expr mb-expr body ...)

A macro version of call-with-limits.

> procedure (call-with-deep-time-limit secs thunk) → any
> secs : exact-nonnegative-integer?
> thunk : (-> any)

Executes the given thunk with deep time restrictions, and returns the values produced by thunk.

The given thunk is run in a new thread. If it errors or if the thread terminates returning a value, then (values) is returned.

Changed in version 1.1 of package sandbox-lib: Changed to return thunk’s result if it completes normally.

> syntax (with-deep-time-limit secs-expr body ...)

A macro version of call-with-deep-time-limit.

> procedure (exn:fail:resource? v) → boolean?
> v : any/c
> (exn:fail:resource-resource exn)
> → (or/c 'time 'memory 'deep-time)
> exn : exn:fail:resource?

A predicate and accessor for exceptions that are raised by call-with-limits. The resource field holds a symbol, representing the resource that was expended. 'time is used for shallow time and 'deep-time is used for deep time.
top ← prev up next →