Racket提供protect-out
来防止模块导出与eval(或解构的语法对象)一起使用,除非模块具有足够的特权(又名strong enough code inspector)。这些文档还为做什么提供了一个很好的例子:
> (module nest racket
(provide num-eggs (protect-out num-chicks))
(define num-eggs 2)
(define num-chicks 3))
> (define weak-inspector (make-inspector (current-code-inspector)))
> (define (weak-eval x)
(parameterize ([current-code-inspector weak-inspector])
(define weak-ns (make-base-namespace))
(namespace-attach-module (current-namespace)
''nest
weak-ns)
(parameterize ([current-namespace weak-ns])
(namespace-require ''nest)
(eval x))))
> (require 'nest)
> (list num-eggs num-chicks)
'(2 3)
> (weak-eval 'num-eggs)
2
> (weak-eval 'num-chicks)
?: access disallowed by code inspector to protected variable
from module: 'nest
at: num-chicks
也就是说,eval
具有足够强大的代码检查器(因为在最初需要该模块的同一作用域中调用了它),因此能够导出。但是,weak-eval
并没有,因为它具有相同的模块实例,但是检查器的功能较弱,无法进行eval
处理。
我的问题是,我们什么时候应该使用protect-out
?是否应始终使用它(或至少在可能的情况下)?还是有protect-out
设计的特定工作流程?
答案 0 :(得分:6)
对{em> unsafe 导出使用protect-out
,其中unsafe表示具有违反Racket语言或虚拟机规则的功能。特别是,经常有可能通过滥用不安全的功能而使Racket VM崩溃。
不安全功能的示例:
unsafe-vector-set!
或多或少使您可以写入任意内存位置,从而有可能违反GC的不变式,内存中Racket值表示的不变式等。unsafe-vector-ref
似乎没有那么危险,但是,即使Racket语言不允许检查过程的实现,您也可以使用它来从闭包中提取自由变量的值。ffi/unsafe
中的大多数显然不安全以下是不安全过程的一个不太明显的示例:
#lang racket
(require ffi/unsafe ffi/unsafe/define)
(define-ffi-definer define-c #f) ;; searches libc, etc
(define-c fopen (_fun _path (_bytes = #"a") -> _pointer))
(define-c fclose (_fun _pointer -> _void))
(define-c fwrite (_fun _bytes _size _size _pointer -> _size))
(define (append-to-file path buf)
(define fp (fopen path))
(unless fp (error "couldn't open file"))
(fwrite buf (bytes-length buf) 1 fp)
(fclose fp))
(provide append-to-file)
考虑append-to-file
过程。它是使用不安全的功能(FFI)定义的,但是FFI类型_path
和_bytes
会拒绝错误的Racket值,因此使用append-to-file
不可能使Racket崩溃。 append-to-file
过程仍然不安全(尽管可能不像unsafe-vector-set!
那样灾难性地不安全),因为它规避了Racket的安全防护机制。该过程以另一种方式是不安全的,因为fopen
和fwrite
可能会阻塞,而Racket代码不应阻塞。
protect-out
提供了Racket核心库中的不安全操作。如果使用它们来定义自己的不安全操作,则还应该使用protect-out
提供派生的不安全操作。如果您使用不安全的功能定义安全功能,则可以正常提供该功能(即不使用protect-out
),但请先仔细考虑!
如果捕获特权代码检查器(直接或通过#%variable-reference
间接捕获),则也应加以保护,因为它可用于动态获取对不安全功能的访问。
目标是:如果命名空间仅包含具有保护其不安全出口的属性的模块,则使用较弱的代码检查器评估的任何其他代码都不应违反Racket VM的不变式(例如,使其崩溃,规避安全检查等)。