diff --git a/NEWS b/NEWS index 50eccca47..3163f395c 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,26 @@ See the end for copying conditions. Please send Guile bug reports to bug-guile@gnu.org. + +Changes in 2.2.1 (since 2.2.0): + +* Notable changes + +** Syntax objects are now a distinct type + +It used to be that syntax objects were represented as a tagged vector. +These values could be forged by users to break scoping abstractions, +preventing the implementation of sandboxing facilities in Guile. We are +as embarrassed about the previous situation as we pleased are about the +fact that we've fixed it. + +Unfortunately, during the 2.2 stable series (or at least during part of +it), we need to support files compiled with Guile 2.2.0. These files +may contain macros that contain legacy syntax object constants. See the +discussion of "allow-legacy-syntax-objects?" in "Syntax Transformer +Helpers" in the manual for full details. + + Changes in 2.2.0 (changes since the 2.0.x stable release series): diff --git a/doc/ref/api-macros.texi b/doc/ref/api-macros.texi index ef0621415..7fa62e3d6 100644 --- a/doc/ref/api-macros.texi +++ b/doc/ref/api-macros.texi @@ -791,6 +791,44 @@ Return the source properties that correspond to the syntax object @var{x}. @xref{Source Properties}, for more information. @end deffn +And now, a bit of confession time. Guile's syntax expander originates +in code from Chez Scheme: a version of the expander in Chez Scheme that +was made portable to other Scheme systems. Way back in the mid-1990s, +some Scheme systems didn't even have the ability to define new abstract +data types. For this reason, the portable expander from Chez Scheme +that Guile inherited used tagged vectors as syntax objects: vectors +whose first element was the symbol, @code{syntax-object}. + +At the time of this writing it is 2017 and Guile still has support for +this strategy. It worked for this long because no one ever puts a +literal vector in the operator position: + +@example +(#(syntax-object ...) 1 2 3) +@end example + +But this state of affairs was an error. Because syntax objects are just +vectors, this makes it possible for any Scheme code to forge a syntax +object which might cause it to violate abstraction boundaries. You +can't build a sandboxing facility that limits the set of bindings in +scope when one can always escape that limit just by evaluating a special +vector. To fix this problem, Guile 2.2.1 finally migrated to represent +syntax objects as a distinct type with a distinct constructor that is +unavailable to user code. + +However, Guile still has to support ``legacy'' syntax objects, because +it could be that a file compiled with Guile 2.2.0 embeds syntax objects +of the vector kind. Whether the expander treats the special tagged +vectors as syntax objects is now controllable by the +@code{allow-legacy-syntax-objects?} parameter: + +@deffn {Scheme Procedure} allow-legacy-syntax-objects? +A parameter that indicates whether the expander should support legacy +syntax objects, as described above. For ABI stability reasons, the +default is @code{#t}. Use @code{parameterize} to bind it to @code{#f}. +@xref{Parameters}. +@end deffn + Guile also offers some more experimental interfaces in a separate module. As was the case with the Large Hadron Collider, it is unclear to our senior macrologists whether adding these interfaces will result diff --git a/module/ice-9/boot-9.scm b/module/ice-9/boot-9.scm index be890fa45..a70cd11ef 100644 --- a/module/ice-9/boot-9.scm +++ b/module/ice-9/boot-9.scm @@ -299,6 +299,9 @@ This is handy for tracing function calls, e.g.: (define (absolute-file-name? file-name) #t) (define (open-input-file str) (open-file str "r")) +;; Temporary definition; replaced by a parameter later. +(define (allow-legacy-syntax-objects?) #f) + ;;; {and-map and or-map} ;;; ;;; (and-map fn lst) is like (and (fn (car lst)) (fn (cadr lst)) (fn...) ...) @@ -1423,11 +1426,15 @@ CONV is not applied to the initial value." ;;; Once parameters have booted, define the default prompt tag as being -;;; a parameter. +;;; a parameter, and make allow-legacy-syntax-objects? a parameter. ;;; (set! default-prompt-tag (make-parameter (default-prompt-tag))) +;; Because code compiled with Guile 2.2.0 embeds legacy syntax objects +;; into its compiled macros, we have to default to true, sadly. +(set! allow-legacy-syntax-objects? (make-parameter #t)) + ;;; {Languages} diff --git a/module/ice-9/psyntax.scm b/module/ice-9/psyntax.scm index a45e2a6cc..5696c4642 100644 --- a/module/ice-9/psyntax.scm +++ b/module/ice-9/psyntax.scm @@ -473,7 +473,8 @@ (define (syntax-object? x) (or (syntax? x) - (and (vector? x) + (and (allow-legacy-syntax-objects?) + (vector? x) (= (vector-length x) 4) (eqv? (vector-ref x 0) 'syntax-object)))) (define (make-syntax-object expression wrap module)