首页 文章

什么时候应该在Racket中使用`protect-out`?

提问于
浏览
7

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 ing .

我的问题是,我们什么时候应该使用 protect-out ?它应该始终使用(或至少在可能的情况下使用)?或者 protect-out 是否有针对特定的工作流程?

1 回答

  • 6

    protect-out 用于不安全的导出,其中不安全意味着能够违反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的安全防护机制 . 该过程以另一种方式是不安全的,因为 fopenfwrite 可以阻止,并且不应该阻止Racket代码 .

    Racket核心库中的不安全操作随 protect-out 一起提供 . 如果您使用它们来定义自己的不安全操作,则应使用 protect-out 提供派生的不安全操作 . 如果您使用不安全的功能来定义安全功能,那么您可以正常提供(即没有 protect-out ),但请先仔细考虑!

    如果您捕获特权代码检查器(直接或间接通过 #%variable-reference ),您还应该保护它,因为它可以用于动态获取对不安全功能的访问 .

    目标是:如果命名空间仅包含具有属性的模块保护其不安全的导出,那么使用较弱的代码检查器评估的任何其他代码都不应该违反Racket VM的不变量(例如,使其崩溃,绕过安全保护检查等) .

相关问题