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

Lei*_*sen 7 macros module eval racket

Racket提供protect-out了防止模块导出与eval(或解构的语法对象)一起使用,除非模块具有足够的权限(也就是说,具有足够强大的代码检查器).该文档还提供了一个很好的例子是什么它:

> (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
Run Code Online (Sandbox Code Playgroud)

也就是说,eval有一个足够强大的代码检查器(因为它在最初需要模块的同一范围内调用),因此能够获得导出.但是,weak-eval没有,因为它有相同的模块实例,但有一个较弱的检查员用于evaling.

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

Rya*_*per 6

使用protect-out不安全的出口,在不安全的手段的东西,有违反球拍语言或虚拟机的规则的能力.特别是,通常可能会因滥用不安全功能而导致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)
Run Code Online (Sandbox Code Playgroud)

考虑一下append-to-file程序.它使用了不安全的功能(在FFI)定义,但FFI类型_path_bytes将拒绝坏球拍值,所以它不应该是可以通过使用崩溃球拍append-to-file.该append-to-file程序仍然不安全(尽管可能不像灾难性的那样不安全unsafe-vector-set!)因为它绕过了Racket的安全防范机制.该过程以另一种方式是不安全的,因为fopen并且fwrite可以阻止,并且不应该阻止Racket代码.

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

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

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