--- "R. Kent Dybvig" <dyb_at_cs.indiana.edu> wrote:
> I'm responding as a user/implementor, not speaking for the editors.
>
> > Assuming this comes to pass, do you have a precise semantics in mind
>
> Yes. Instead of expanding a library body into the equivalent of:
>
> (letrec* ((var rhs-expr) ...) body-expr ... (unspecified))
>
> we would expand it instead into the equivalent of:
>
> (letrec* ((var (once-only rhs-expr)) ...) body-expr ... (unspecified))
>
> where (once-only e) raises an exception if e returns more than once.
> once-only might be defined as follows:
>
> (define-syntax once-only
> (syntax-rules ()
> [(_ e)
> (let ([done #f])
> (let ([t e])
> (when done
> (error #f "library definition RHS expr returned twice"))
> (set! done #t)
> t))]))
>
> > (perhaps with some illustrative examples)?
>
> Okay, here's one example:
>
> (library (L1)
> (export y get-y set-y!)
> (import (r6rs))
> (define x (call/cc (lambda (k) (list 0 k values))))
> (define y (car x))
> (define z ((caddr x)))
> (define get-y (lambda () y))
> (define set-y!
> (lambda (v)
> (call/cc (lambda (k) ((cadr x) (list v (cadr x) k)))))))
>
> By my reading of the current library description, the program:
>
> (import (r6rs) (L1))
> (write (list y (get-y))) (newline)
> (set-y! 3)
> (write (list y (get-y))) (newline)
>
> prints
>
> (0 0)
> (0 3)
>
> With the proposed semantics, it would print
>
> (0 0)
>
> then raise an exception (if we decide it "must" or if an implementation
> does what it "should"). If the exception were caught and the program
> subsequently referenced y or called (get-y), the values of those
> expressions would be 0.
>
> Now that I've demonstrated how the loophole can be exploited on a public
> mailing list, we have an even more urgent need to close it. ;-)
>
> Kent
Typically, a scheme programmer imports a library (or, if we're only considering pure R5RS, "load"s
a file) and risks something ranging from "minor annoyance" to "the fate of the free world" that
library will perform as advertised. If the library foolishly exports accessible continuations,
then the library risks undefined/unsecure behavior. I'm certain there are other things that a
library can do that risk undefined behavior. Here is a modification to library L1 that gets around
the above "loophole fix":
(library (L1)
(export y get-y set-y!)
(import (r6rs))
(define x #f)
(define y (car x))
(define z ((caddr x)))
(define get-y (lambda () y))
(define set-y! #f)
(set! x (call/cc (lambda (k) (list 0 k values))))
(set! set-y!
(lambda (v)
(call/cc (lambda (k) ((cadr x) (list v (cadr x) k)))))))
I see this as applying to two issues: user-friendliness and security.
For the first issue, the question gets raised to the higher-level question of what does R6RS
mandate versus recommend. Certainly it may be useful for a library developer or library user to
catch such a bug, and the future R6RS-Wonder-Scheme should catch it. But if the future
mini-tiny-r6rs-scheme doesn't catch it, so be it.
I personally think that R6RS should have lots of "should"s and few "must"s. The future
R6RS-Wonder-Scheme which heeds all "should"s will raise an exception/warning when given the above
lib & program, but mini-tiny-r6rs-scheme will behave in an unspecified, possibly unpredictable,
manner.
I can't speak with any authority or experience about security. If a fragment of untrusted code is
executed in a trusted environment, I'm sure there numerous issues that must be considered -
continuations from within libraries being just one of them.
____________________________________________________________________________________
No need to miss a message. Get email on-the-go
with Yahoo! Mail for Mobile. Get started.
http://mobile.yahoo.com/mail
Received on Mon Mar 12 2007 - 16:06:33 UTC