racket/sandbox: use `gui-available?' at sandbox creation

Previously, sandbox creation used `gui?', which is the result of
`gui-available?' at the time that `racket/sandbox' is instanited.
This change makes sandbox behavior less sensitive tothe order in
which modules `require'd into a program are intiantiated.

The change depends on a new `sandbox-make-namespace' default
function for `sandbox-namespace-specs'. The new function uses
either  `make-base-namespace' or `make-gui-namespace', depending
on whether the GUI library is available at that point.

A new `sandbox-gui-enabled' parameter can disable use of the
GUI library even if it is available.

The `gui?' binding is still exported for backward compatibility,
but it shouldn't be used anymore.
This commit is contained in:
Matthew Flatt 2012-03-02 07:47:16 -07:00
parent a976c56cb9
commit 5630a3a1ca
4 changed files with 110 additions and 27 deletions

View File

@ -10,6 +10,7 @@
setup/dirs) setup/dirs)
(provide gui? (provide gui?
sandbox-gui-available
sandbox-init-hook sandbox-init-hook
sandbox-reader sandbox-reader
sandbox-input sandbox-input
@ -23,6 +24,7 @@
sandbox-security-guard sandbox-security-guard
sandbox-network-guard sandbox-network-guard
sandbox-exit-handler sandbox-exit-handler
sandbox-make-namespace
sandbox-make-inspector sandbox-make-inspector
sandbox-make-code-inspector sandbox-make-code-inspector
sandbox-make-logger sandbox-make-logger
@ -53,12 +55,16 @@
exn:fail:resource? exn:fail:resource?
exn:fail:resource-resource) exn:fail:resource-resource)
; for backward compatibility; maybe it should be removed:
(define gui? (gui-available?)) (define gui? (gui-available?))
;; When this parameter is #t, it is adjusted when creating a sandbox:
(define sandbox-gui-available (make-parameter #t (lambda (v) (and v #t))))
(define-syntax mz/mr ; use a value for plain racket, or pull a gui binding (define-syntax mz/mr ; use a value for plain racket, or pull a gui binding
(syntax-rules () (syntax-rules ()
[(mz/mr mzval mrsym) [(mz/mr mzval mrsym)
(if gui? (gui-dynamic-require 'mrsym) mzval)])) (if (sandbox-gui-available) (gui-dynamic-require 'mrsym) mzval)]))
;; Configuration ------------------------------------------------------------ ;; Configuration ------------------------------------------------------------
@ -85,8 +91,11 @@
[sandbox-eval-handlers '(#f #f)]) [sandbox-eval-handlers '(#f #f)])
(thunk))) (thunk)))
(define (sandbox-make-namespace)
((mz/mr make-base-namespace make-gui-namespace)))
(define sandbox-namespace-specs (define sandbox-namespace-specs
(make-parameter `(,(mz/mr make-base-namespace make-gui-namespace) (make-parameter `(,sandbox-make-namespace
#| no modules here by default |#))) #| no modules here by default |#)))
(define (default-sandbox-reader source) (define (default-sandbox-reader source)
@ -613,15 +622,7 @@
(uncovered! (list (get) get))) (uncovered! (list (get) get)))
(when (namespace? ns) (current-namespace ns))) (when (namespace? ns) (current-namespace ns)))
(define current-eventspace (mz/mr (make-parameter #f) current-eventspace))
(define make-eventspace (mz/mr void make-eventspace))
(define run-in-bg (mz/mr thread queue-callback))
(define null-input (open-input-bytes #"")) (define null-input (open-input-bytes #""))
(define bg-run->thread
(if gui?
(lambda (ignored)
((mz/mr void eventspace-handler-thread) (current-eventspace)))
values))
;; special message values for the evaluator procedure, also inside the user ;; special message values for the evaluator procedure, also inside the user
;; context they're used for function applications. ;; context they're used for function applications.
@ -879,6 +880,8 @@
memory-cust)) memory-cust))
(parameterize* ; the order in these matters (parameterize* ; the order in these matters
(;; create a sandbox context first (;; create a sandbox context first
[sandbox-gui-available (and (sandbox-gui-available)
(gui-available?))]
[current-custodian user-cust] [current-custodian user-cust]
[current-thread-group (make-thread-group)] [current-thread-group (make-thread-group)]
;; paths ;; paths
@ -944,17 +947,24 @@
[current-command-line-arguments '#()] [current-command-line-arguments '#()]
;; Finally, create the namespace in the restricted environment (in ;; Finally, create the namespace in the restricted environment (in
;; particular, it must be created under the new code inspector) ;; particular, it must be created under the new code inspector)
[current-namespace (make-evaluation-namespace)] [current-namespace (make-evaluation-namespace)])
(define current-eventspace (mz/mr (make-parameter #f) current-eventspace))
(parameterize*
;; Note the above definition of `current-eventspace': in Racket, it ;; Note the above definition of `current-eventspace': in Racket, it
;; is an unused parameter. Also note that creating an eventspace ;; is an unused parameter. Also note that creating an eventspace
;; starts a thread that will eventually run the callback code (which ;; starts a thread that will eventually run the callback code (which
;; evaluates the program in `run-in-bg') -- so this parameterization ;; evaluates the program in `run-in-bg') -- so this parameterization
;; must be nested in the above (which is what paramaterize* does), or ;; must be nested in the above (which is what paramaterize* does), or
;; it will not use the new namespace. ;; it will not use the new namespace.
[current-eventspace (parameterize-break #f (make-eventspace))]) ([current-eventspace (parameterize-break #f ((mz/mr void make-eventspace)))])
(define run-in-bg (mz/mr thread queue-callback))
(define bg-run->thread (if (sandbox-gui-available)
(lambda (ignored)
((mz/mr void eventspace-handler-thread) (current-eventspace)))
values))
(define t (bg-run->thread (run-in-bg user-process))) (define t (bg-run->thread (run-in-bg user-process)))
(set! user-done-evt (handle-evt t (lambda (_) (terminate+kill! #t #t)))) (set! user-done-evt (handle-evt t (lambda (_) (terminate+kill! #t #t))))
(set! user-thread t)) (set! user-thread t)))
(define r (get-user-result)) (define r (get-user-result))
(if (eq? r 'ok) (if (eq? r 'ok)
;; initial program executed ok, so return an evaluator ;; initial program executed ok, so return an evaluator

View File

@ -128,8 +128,10 @@ argument:
In this case, a new namespace is created using In this case, a new namespace is created using
@racket[sandbox-namespace-specs], which by default creates a @racket[sandbox-namespace-specs], which by default creates a
new namespace using @racket[make-base-namespace] or new namespace using @racket[sandbox-make-namespace] (which, in
@racket[make-gui-namespace] (depending on @racket[gui?]). turn, uses @racket[make-base-namespace] or
@racket[make-gui-namespace] depending on
@racket[sandbox-gui-available] and @racket[gui-available?]).
In the new namespace, @racket[language] is evaluated as an In the new namespace, @racket[language] is evaluated as an
expression to further initialize the namespace.} expression to further initialize the namespace.}
@ -188,7 +190,8 @@ In all cases, the evaluator operates in an isolated and limited
environment: environment:
@itemize[ @itemize[
@item{It uses a new custodian and namespace. When @racket[gui?] is @item{It uses a new custodian and namespace. When
@racket[gui-available?] and @racket[sandbox-gui-available] produce
true, it is also runs in its own eventspace.} true, it is also runs in its own eventspace.}
@item{The evaluator works under the @racket[sandbox-security-guard], @item{The evaluator works under the @racket[sandbox-security-guard],
@ -463,9 +466,7 @@ that creates the namespace, and the rest are module paths for modules
to be attached to the created namespace using to be attached to the created namespace using
@racket[namespace-attach-module]. @racket[namespace-attach-module].
The default is @racket[(list make-base-namespace)] if @racket[gui?] is The default is @racket[(list sandbox-make-namespace)].
@racket[#f], @racket[(list make-gui-namespace)] if @racket[gui?] is
@racket[#t].
The module paths are needed for sharing module instantiations between The module paths are needed for sharing module instantiations between
the sandbox and the caller. For example, sandbox code that returns the sandbox and the caller. For example, sandbox code that returns
@ -484,10 +485,28 @@ of code can be helpful:
`(,(car specs) `(,(car specs)
,@(cdr specs) ,@(cdr specs)
lang/posn lang/posn
,@(if gui? '(mrlib/cache-image-snip) '())))) ,@(if (gui-available?) '(mrlib/cache-image-snip) '()))))
]} ]}
@defproc[(sandbox-make-namespace) namespace?]{
Calls @racket[make-gui-namespace] when @racket[(sandbox-gui-available)]
produces true, @racket[make-base-namespace] otherwise.}
@defboolparam[sandbox-gui-available avail?]{
Determines whether the @racketmodname[racket/gui] module can be used
when a sandbox evaluator is created. If @racket[gui-available?]
produces @racket[#f] during the creation of a sandbox evaluator, this
parameter is forced to @racket[#f] during initialization of the
sandbox. The default value of the parameter is @racket[#t].
Various aspects of the library change when the GUI library is
available, such as using a new eventspace for each evaluator.}
@defparam[sandbox-override-collection-paths paths (listof path-string?)]{ @defparam[sandbox-override-collection-paths paths (listof path-string?)]{
A parameter that determines a list of collection directories to prefix A parameter that determines a list of collection directories to prefix
@ -922,12 +941,13 @@ your own permissions, for example,
@defthing[gui? boolean?]{ @defthing[gui? boolean?]{
True if the @racketmodname[racket/gui] module can be used, @racket[#f] For backward compatibility, only: the result of @racket[gui-available?]
otherwise; see @racket[gui-available?]. at the time that @racketmodname[racket/sandbox] was instantiated.
Various aspects of the @racketmodname[racket/sandbox] library change The value of @racket[gui?] is no longer used by
when the GUI library is available, such as using a new eventspace for @racketmodname[racket/sandbox] itself. Instead,
each evaluator.} @racket[gui-available?] and @racket[sandbox-gui-available] are
checked at the time that a sandbox evaluator is created.}
@defproc[(call-with-limits [secs (or/c exact-nonnegative-integer? #f)] @defproc[(call-with-limits [secs (or/c exact-nonnegative-integer? #f)]

View File

@ -0,0 +1,48 @@
#lang racket
(require racket/sandbox
racket/gui/dynamic)
(define-syntax-rule (test expect expr)
(do-test 'expr expect expr))
(define (do-test which expect got)
(unless (equal? expect got)
(error 'test "failed: ~s expect: ~e got: ~e" which expect got)))
;; GUI is initialled allowed by sandbox, but not initially available:
(test #t (sandbox-gui-available))
(test #f (gui-available?))
;; Create a pre-GUI evaluator:
(define e (call-with-trusted-sandbox-configuration
(lambda ()
(make-evaluator 'racket))))
(test (void) (e '(require racket/gui/dynamic)))
(test #f (e '(gui-available?)))
;; Load GUI library
(test (void) (dynamic-require 'racket/gui #f))
;; Now the GUI is available:
(test #t (gui-available?))
;; Create a post-GUI evaluator:
(define ge (call-with-trusted-sandbox-configuration
(lambda ()
(make-evaluator 'racket))))
(test (void) (ge '(require racket/gui/dynamic)))
(test #t (ge '(gui-available?)))
;; A post-GUI evaluator, but with GUI disabled:
(define pe (parameterize ([sandbox-gui-available #f])
(call-with-trusted-sandbox-configuration
(lambda ()
(make-evaluator 'racket)))))
(test (void) (pe '(require racket/gui/dynamic)))
(test #f (pe '(gui-available?)))

View File

@ -1,3 +1,8 @@
Version 5.2.1.7
racket/sandbox: added sandbox-gui-enabled and sandbox-make-namespace;
deprecated gui?, which is no long used internally; changed the default
for sandbox-namespace-specs to sandbox-make-namespsace
Version 5.2.1.6 Version 5.2.1.6
Added prop:cpointer Added prop:cpointer
Fixed handle-evt to disallow a handle-evt Fixed handle-evt to disallow a handle-evt