我什么时候应该在球拍中使用“保护”功能?

时间:2018-08-08 17:28:59

标签: module macros racket eval

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设计的特定工作流程?

1 个答案:

答案 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的安全防护机制。该过程以另一种方式是不安全的,因为fopenfwrite可能会阻塞,而Racket代码不应阻塞。

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

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

目标是:如果命名空间仅包含具有保护其不安全出口的属性的模块,则使用较弱的代码检查器评估的任何其他代码都不应违反Racket VM的不变式(例如,使其崩溃,规避安全检查等)。