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

View File

@ -128,8 +128,10 @@ argument:
In this case, a new namespace is created using
@racket[sandbox-namespace-specs], which by default creates a
new namespace using @racket[make-base-namespace] or
@racket[make-gui-namespace] (depending on @racket[gui?]).
new namespace using @racket[sandbox-make-namespace] (which, in
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
expression to further initialize the namespace.}
@ -188,7 +190,8 @@ In all cases, the evaluator operates in an isolated and limited
environment:
@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.}
@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
@racket[namespace-attach-module].
The default is @racket[(list make-base-namespace)] if @racket[gui?] is
@racket[#f], @racket[(list make-gui-namespace)] if @racket[gui?] is
@racket[#t].
The default is @racket[(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
@ -484,10 +485,28 @@ of code can be helpful:
`(,(car specs)
,@(cdr specs)
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?)]{
A parameter that determines a list of collection directories to prefix
@ -922,12 +941,13 @@ your own permissions, for example,
@defthing[gui? boolean?]{
True if the @racketmodname[racket/gui] module can be used, @racket[#f]
otherwise; see @racket[gui-available?].
For backward compatibility, only: the result of @racket[gui-available?]
at the time that @racketmodname[racket/sandbox] was instantiated.
Various aspects of the @racketmodname[racket/sandbox] library change
when the GUI library is available, such as using a new eventspace for
each evaluator.}
The value of @racket[gui?] is no longer used by
@racketmodname[racket/sandbox] itself. Instead,
@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)]

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
Added prop:cpointer
Fixed handle-evt to disallow a handle-evt