샌드박스 평가

샌드박스 평가 (Sandboxed Evaluation)

격리된 샌드박스(sandbox) 환경에서 코드를 평가하게 해 주는 racket/sandbox 라이브러리를 살펴볼게요. 파일시스템·네트워크 접근과 메모리·시간 자원을 제한해 신뢰할 수 없는 코드를 안전하게 실행하는 데 쓰여요.

출처: Racket Reference

본문

(require racket/sandbox)  |  package: sandbox-lib

이 절에 문서화된 바인딩들은 racket/sandbox 라이브러리가 제공하는 것이지, racket/baseracket이 제공하는 게 아니에요.

racket/sandbox 모듈은 "샌드박스" 평가자를 만드는 유틸리티를 제공해요. 평가자는 특정 방식으로 구성되며, 제한된 자원(메모리와 시간), 파일시스템·네트워크 접근, 그 밖의 많은 것을 가질 수 있어요. 샌드박스 평가자는 수많은 파라미터로 구성할 수 있고, 기본값은 샌드박스가 매우 제한적인 흔한 사용 사례에 맞춰 설정돼요.

14.12.0 주요 프로시저

procedure

(make-evaluator language
                input-program ...
                [#:requires requires
                 #:allow-for-require allow-for-require
                 #:allow-for-load allow-for-load
                 #:allow-read allow-read
                 #:allow-syntactic-requires allow-syntactic-requires])
 → (any/c . -> . any)
  language :
 (or/c module-path?
       (list/c 'special symbol?)
       (cons/c 'begin list?))
  input-program : any/c
  requires :
 (listof (or/c module-path? path-string?
               (cons/c 'for-syntax (listof module-path?))))
            = null
  allow-for-require : (listof (or/c module-path? path?)) = null
  allow-for-load : (listof path-string?) = null
  allow-read : (listof (or/c module-path? path-string?)) = null
  allow-syntactic-requires : (or/c #f (listof module-path?))
            = #f

(make-module-evaluator module-decl
                       [#:language lang
                        #:readers readers
                        #:allow-for-require allow-for-require
                        #:allow-for-load allow-for-load
                        #:allow-read allow-read
                        #:allow-syntactic-requires allow-syntactic-requires])
 → (any/c . -> . any)
  module-decl : (or/c syntax? pair? path? input-port? string? bytes?)
  lang : (or/c #f module-path?) = #f
  readers : (or/c #f (listof module-path?))
            = (and lang (default-language-readers lang))
  allow-for-require : (listof (or/c module-path? path?)) = null
  allow-for-load : (listof path-string?) = null
  allow-read : (listof (or/c module-path? path-string?)) = null
  allow-syntactic-requires : (or/c #f (listof module-path?))
            = #f

make-evaluator 함수는 언어와 require 지정으로 평가자를 만들고, 주어진 input-program들을 평가하기 시작해요. make-module-evaluator 함수는 주어진 모듈의 컨텍스트에서 동작하는 평가자를 만들어요. 어느 경우든 결과는 추가 평가를 위한 함수예요.

돌려받은 평가자는 격리되고 제한된 환경에서 동작해요. 특히 파일시스템 접근이 제한되는데, 이는 컬렉션에 없는 모듈을 파일시스템에서 사용하는 것을 방해할 수 있어요. allow-for-require, allow-for-load, allow-read 인자에 대한 정보는 아래를 참고하세요. 컬렉션 기반 모듈 파일은 보통 그 목록에 포함될 필요가 없어요. language가 모듈 경로이거나 requires가 제공되면, 그 표시된 모듈들은 allow-for-require 목록에 암시적으로 포함돼요. allow-syntactic-requires#f가 아니면, 모듈에서 직접 참조될 수 있는 모듈 집합을 제약해요. 자세한 내용은 아래를 참고하세요. (역호환성을 위해, requires 같은 인자에는 module-path?가 아닌 경로 문자열이 허용되는데, allow-for-require에 추가되기 전에 암시적으로 경로로 변환돼요.)

input-program 또는 module-decl 인자는 다음 형태 중 하나로 프로그램을 제공해요:

  • 프로그램을 읽는 데 쓰이는 입력 포트;
  • 완전한 입력을 담은 문자열 또는 바이트 문자열;
  • 입력을 담은 파일을 이름 짓는 경로; 또는
  • eval로 평가되는 것처럼 평가되는 S-표현식 또는 구문 객체(get-uncovered-expressions도 참고).

위 처음 세 경우에서 프로그램은 sandbox-reader로 읽히는데, 타당한 오류 메시지를 위해 줄 세기가 활성화되고, 소스로 'program이 사용돼요(커버리지 테스트에 쓰임). 마지막 경우에 입력은 완전한 프로그램일 것으로 기대되고, (이미 구문 객체가 아니라면) 'program을 소스로 사용해 구문 객체로 변환돼요.

돌려받은 평가자 함수는 (호출될 때마다) 본질적으로 같은 형태의 추가 표현식을 받아요. 문자열 또는 일련의 표현식을 담은 바이트 문자열, 표현식을 담은 파일의 경로, S-표현식, 또는 구문 객체. 평가자가 eof 값을 받으면 종료되고 이후 오류를 발생시켜요. 예외를 발생시키지 않고 평가자를 종료하는 kill-evaluator도 참고하세요.

make-evaluator의 경우, 여러 input-program은 효과적으로 연결되어 단일 프로그램을 이뤄요. input-program들의 평가 방식은 language 인자에 따라 달라져요:

  • language 인자는 모듈 경로일 수 있어요(즉 requiremodule-path 문법과 일치하는 datum).

이 경우 input-program들은 자동으로 모듈에 감싸지고, 결과 평가자는 결과 모듈의 네임스페이스 안에서 동작해요.

  • language 인자는 'special로 시작하는 리스트일 수 있는데, 이는 특별한 입력 구성을 가진 내장 언어를 나타내요. 가능한 값은 '(special r5rs) 또는 교수 언어(teaching language)를 나타내는 값인 '(special beginner), '(special beginner-abbr), '(special intermediate), '(special intermediate-lambda), '(special advanced)예요.

이 경우 input-program들은 자동으로 모듈에 감싸지고, 결과 평가자는 결과 모듈의 네임스페이스 안에서 동작해요. 게다가 read-accept-infix-dot 같은 특정 파라미터들이 문자열·포트에서 프로그램을 읽도록 맞춰 설정돼요.

이 옵션은 주로 더 오래된 테스트 시스템을 위해 제공돼요. #lang으로 시작하는 입력과 함께 make-module-evaluator를 쓰는 것이 일반적으로 더 좋아요.

  • 마지막으로 language는 첫 요소가 'begin인 리스트일 수 있어요.

이 경우 sandbox-namespace-specs를 사용해 새 네임스페이스가 만들어지는데, 기본적으로는 sandbox-make-namespace로 새 네임스페이스를 만든다(sandbox-make-namespacesandbox-gui-availablegui-available?에 따라 make-base-namespace 또는 make-gui-namespace를 사용).

새 네임스페이스에서 language는 네임스페이스를 더 초기화하기 위한 표현식으로 평가돼요.

requires 리스트는 input-program을 위한 모듈·네임스페이스에 추가 import를 더해요. require가 언어를 통해 이용 가능하지 않더라도요. allow-syntactic-requires 인자는, #f가 아니면, language 인자가 모듈 래퍼를 의미할 때 모듈에서 확장되는 require 참조를 제약해요. 더 정확히 말하면, 구문 객체가 모듈 이름 해석기에 제공될 때 해석될 수 있는 모듈 경로를 제약하는데, 여기에는 매크로 확장이 만든 require 폼도 포함돼요. submod 다음에 "." 또는 ".."가 오는 상대-서브모듈 경로는 항상 허용돼요.

다음 예시는 프로그램을 모듈에 넣는 평가자와 최상위 네임스페이스를 단지 초기화하는 평가자의 차이를 보여줘요:

> (define base-module-eval
    ; a module cannot have free variables...
    (make-evaluator 'racket/base '(define (f) later)))
program:1:0: later: unbound identifier

  in: later
> (define base-module-eval
    (make-evaluator 'racket/base '(define (f) later)
                                 '(define later 5)))
> (base-module-eval '(f))
5
> (define base-top-eval
    ; non-module code can have free variables:
    (make-evaluator '(begin) '(define (f) later)))
> (base-top-eval '(+ 1 2))
3
> (base-top-eval '(define later 5))
> (base-top-eval '(f))
5

make-module-evaluator 함수는 본질적으로 make-evaluator의 제한이에요. 프로그램이 모듈이어야 하고, 모든 import가 프로그램의 일부예요. 어떤 경우에는 프로그램을, 언어 위치에서 특정 모듈을 쓰는 모듈로 제한하는 것이 유용해요. 선택 인자 lang 인자로 그런 제한을 지정하세요. 여기서 #f는 제한이 강제되지 않음을 뜻해요. readers 인자는 비슷하게 #f가 아니면 #lang이나 #reader 뒤에 올 수 있는 경로를 제약하고, 기본값은 lang에 기반해요. allow-syntactic-requires 인자는 모듈-래퍼 경우에서 make-evaluator와 같게 취급돼요.

프로그램이 경로로 지정되면, 그 경로는 allow-for-load 목록에 암시적으로 추가돼요.

(define base-module-eval2
  ; equivalent to base-module-eval:
  (make-module-evaluator '(module m racket/base
                            (define (f) later)
                            (define later 5))))

make-module-evaluator 함수는 모듈 파일을 테스트하는 데 편리할 수 있어요. 파일 이름에 대한 경로 값을 넘기면, 좋아하는 테스트 시설과 함께 쓸 수 있는 모듈 컨텍스트의 평가자를 돌려받아요.

모든 경우에서 평가자는 격리되고 제한된 환경에서 동작해요:

  • 새 custodian과 네임스페이스를 사용해요. gui-available?sandbox-gui-available이 참이면, 자신의 이벤트스페이스에서도 동작해요.
  • 평가자는 sandbox-security-guard 아래에서 동작하며, 파일시스템·네트워크 접근을 제한해요.
  • 평가자는 메모리 제한 환경에 담기고, 각 평가는 (메모리 회계가 가능할 때) call-with-limits에 감싸져요. sandbox-memory-limit, sandbox-eval-limits, set-eval-limits도 참고하세요. 이 제한은 샌드박스 환경의 생성에도 적용된다는 점에 주의하세요. 예를 들어 샌드박스 생성에 필요한 메모리가 제한보다 크면 make-evaluator는 메모리 제한 예외로 실패해요.

allow-for-requireallow-for-load 인자는 파일시스템 권한을 조정해 평가자가 쓸 수 있는 파일 집합을 확장해요. 컬렉션에 있는 모듈은 자동으로 접근 가능하지만, allow-for-require 인자는 파일 시스템 경로를 통해 (전이적으로) 그 import와 함께 require될 수 있는 추가 모듈을 나열해요. allow-for-load 인자는 비슷하게 로드될 수 있는 파일을 나열해요. (requireload에 필요한 정확한 권한은 다를 수 있어요.) allow-read 인자는 역호환성만을 위한 것이에요. allow-read의 각 module-path? 요소는 효과적으로 allow-for-require로 옮겨지고, 다른 요소는 allow-for-load로 옮겨져요.

샌드박스 환경은 잘 격리되어 있고, 평가자 함수는 본질적으로 그 환경에 표현식을 보내고 결과를 기다려요. 이런 통신 형태는 단일 평가자에 대한 중첩(또는 동시) 호출을 불가능하게 해요. 보통은 문제가 되지 않지만, 어떤 경우에는 샌드박스 코드 안에서 평가자 함수를 쓸 수 있는 경우도 있어요. 예를 들어:

> (let ([e (make-evaluator 'racket/base)])
    (e `(,e 1)))
evaluator: nested evaluator call with: 1

그런 경우 오류가 신호돼요.

샌드박스가 생성될 때 sandbox-propagate-exceptions의 값이 참(기본값)이면, 예외(구문·런타임 모두)는 평소처럼 평가 함수의 호출자에게 전파돼요(즉 with-handlers로 잡으세요). 발생된 예외를 직접 사용하는 것에 대한 주의는 아래를 참고하세요. 샌드박스가 생성될 때 sandbox-propagate-exceptions의 값이 #f면, 샌드박스 평가에서 잡히지 않은 예외는 오류가 샌드박스의 오류 포트로 프린트되고, 평가의 호출자는 #<void>를 받아요.

샌드박스에서 돌려받거나 샌드박스가 예외로 발생시킨 값을 다룰 때 주의하세요. 그 값은 impersonator일 수도 있고, 같음 비교나 프린팅 연산을 리다이렉트하는 구조 타입을 가진 구조일 수도 있어요. 샌드박스가 만들어 낸 알 수 없는 값을 안전하게 다루려면, 가능하면 call-in-sandbox-context를 사용해 샌드박스 안에서 조작하세요.

평가자는 한 번에 한 스레드만 사용할 수 있고, 감지된 동시 사용은 예외를 촉발해요. 메인 스레드가 아닌 곳에서 평가자를 쓰는 데 주의하세요. sandbox-make-plumber의 기본값이 현재 plumber에 평가자의 plumber를 flush하는 콜백을 등록하기 때문에, (Racket 프로세스가 종료하려 할 때처럼) 현재 plumber의 flush는 평가자의 사용을 의미하기 때문이에요.

패키지 sandbox-lib의 버전 1.2에서 변경됨: #:readers#:allow-syntactic-require 인자를 추가했어요.

procedure

(exn:fail:sandbox-terminated? v) → boolean?
  v : any/c
(exn:fail:sandbox-terminated-reason exn) → symbol?
  exn : exn:fail:sandbox-terminated?

샌드박스가 종료될 때 발생하는 예외에 대한 술어와 접근자예요. 샌드박스가 그런 예외를 한 번 발생시키면, 이후 평가 시도에서도 계속 그것을 발생시켜요.

14.12.1 보안 고려 사항 (Security Considerations)

샌드박스는 시스템 자원에 대한 접근이 제한된 Racket 프로그램을 실행할 안전한 환경을 제공하도록 설계되었지만, 신뢰할 수 없는 프로그램을 샌드박스에서 실행하는 것은 여전히 어느 정도 위험을 수반해요. 악의적인 프로그램은 Racket 런타임과 설치된 컬렉션의 임의 기능을 실행할 수 있으므로, Racket이나 설치된 컬렉션의 취약점을 식별한 공격자는 샌드박스를 탈출할 수도 있어요.

이 위험을 완화하려면 샌드박스를 사용하는 프로그램은 가능할 때 추가 예방 조치를 써야 해요. 제안되는 조치는 다음과 같아요:

  • make-evaluatormake-module-evaluator에 커스텀 모듈 언어를 제공해, 신뢰할 수 없는 코드가 절대적으로 필요한 언어 구성에만 접근하게 하는 것.
  • 신뢰할 수 없는 코드가 설치된 컬렉션에 접근해야 한다면, 프로그램이 요구하는 컬렉션만 설치하는 것.
  • 샌드박스를 실행하는 프로세스가 손상되는 경우를 대비해 방어 심층(defense-in-depth)을 제공하는 운영체제 수준 보안 기능 사용.
  • Racket 설치와 설치된 패키지를 최신 릴리스로 유지하는 것.

14.12.2 평가자 커스터마이징 (Customizing Evaluators)

make-evaluator가 만드는 샌드박스 평가자는 많은 파라미터로 커스터마이즈할 수 있어요. 대부분의 구성 파라미터는 새로 만들어진 평가자에 영향을 줘요. 그것들을 바꿔도 이미 실행 중인 평가자에는 효과가 없어요.

기본 구성 옵션은 매우 제한된 샌드박스 환경, 즉 공개적으로 만들어도 안전한 환경을 위해 설정돼요. 더 많은 권한이 필요하거나 더 엄격한 제한을 원한다면 추가 커스터마이즈가 필요할 수 있어요. 평가자를 커스터마이즈하는 또 다른 유용한 접근은 비교적 제한이 없는 구성에서 시작해 원하는 제한을 추가하는 것이에요. 이 접근은 call-with-trusted-sandbox-configuration 함수로 가능해져요.

샌드박스 환경은 평가가 걸리는 시간을 제한하는 두 개념을 사용해요: 얕은 시간(shallow time)과 깊은 시간(deep time). 얕은 시간은 표현식의 즉시 실행을 가리켜요. 예를 들어 얕은 시간 제한 5초는 (sleep 6)과 5초보다 오래 걸리는 다른 계산을 제한해요. 깊은 시간은 표현식과 표현식이 만드는 모든 스레드·하위 프로세스의 총 실행을 가리켜요. 예를 들어 깊은 시간 제한 5초는 (thread (λ () (sleep 6)))을 제한하는데, 얕은 시간은 그것을 제한하지 못해요. 그리고 얕은 시간이 제한하는 모든 표현식도 제한해요. 기본적으로 대부분의 샌드박스는 스레드를 사용하는 표현식을 용이하게 하기 위해 얕은 시간만 제한해요.

procedure

(call-with-trusted-sandbox-configuration thunk) → any
  thunk : (-> any)

샌드박스 구성 파라미터가 최소 제한으로 설정된 컨텍스트에서 thunk를 호출해요. 더 정확히 말하면, 메모리·시간 제한이 없고, 기존 인스펙터, 보안 가드, exit 핸들러, 로거, plumber, 환경 변수 집합이 사용돼요. (I/O 포트 설정은 포함되지 않는다는 점에 주의하세요.)

parameter

(sandbox-init-hook) → (-> any)
(sandbox-init-hook thunk) → void?
  thunk : (-> any)

새 평가자를 초기화하기 위해 호출될 썽크를 결정하는 파라미터예요. 훅은 새로 만들어진 평가자 컨텍스트에서 프로그램이 평가되기 직전에 호출돼요. 읽기·쓰기·평가 등과 관련된 환경 파라미터를 설정하는 데 쓸 수 있어요. 특정 언어('(special r5rs)와 교수 언어)는 언어 특화된 초기화를 가지는데, 훅은 그 초기화 뒤에 사용되므로 설정을 덮어쓸 수 있어요.

parameter

(sandbox-reader) → (any/c . -> . any)
(sandbox-reader proc) → void?
  proc : (any/c . -> . any)

(current-input-port에서) 모든 표현식을 읽는 함수를 지정하는 파라미터예요. 이 함수는 문자열·바이트 문자열·포트가 공급될 때 평가자를 위한 프로그램 소스를 읽는 데 사용돼요. 리더 함수는 입력 소스로 사용될 값(즉 read-syntax의 첫 인자)을 받고, 구문 객체의 리스트를 돌려줘야 해요. 기본 리더는 read-syntax를 호출해 eof를 받을 때까지 결과를 리스트에 누적해요.

리더 함수는 보통 그대로 호출되지만, make-module-evaluator의 프로그램 입력을 읽는 데 사용될 때는 read-accept-langread-accept-reader#t로 설정돼요.

parameter

(sandbox-input) →
 (or/c #f
       string? bytes?
       input-port?
       'pipe
       (-> input-port?))
(sandbox-input in) → void?
  in :
 (or/c #f
       string? bytes?
       input-port?
       'pipe
       (-> input-port?))

새로 만들어진 평가자의 초기 current-input-port 설정을 결정하는 파라미터예요. 기본값은 #f로, 빈 포트를 만들어요. 다른 값은 다음이 허용돼요:

  • open-input-string이나 open-input-bytes로 포트로 변환되는 문자열 또는 바이트 문자열;
  • 입력 포트;
  • 심볼 'pipe는 파이프의 생성을 촉발하는데, put-input이 파이프의 출력 끝을 돌려주거나 직접 쓸 수 있어요;
  • 포트를 얻기 위해 호출되는 썽크(예: current-input-port를 사용하는 것은 평가자 입력이 호출 컨텍스트의 입력과 같다는 뜻).
parameter

(sandbox-output) →
 (or/c #f
       output-port?
       'pipe
       'bytes
       'string
       (-> output-port?))
(sandbox-output in) → void?
  in :
 (or/c #f
       output-port?
       'pipe
       'bytes
       'string
       (-> output-port?))

새로 만들어진 평가자의 초기 current-output-port 설정을 결정하는 파라미터예요. 기본값은 #f로, 모든 데이터를 버리는 포트를 만들어요. 다른 값은 다음이 허용돼요:

  • 그대로 사용되는 출력 포트;
  • 심볼 'bytes는 평가자가 아직 종료되지 않은 동안 get-output이 완전한 출력을 바이트 문자열로 돌려주게 해요(그래서 바이트 크기를 평가자에게 청구할 수 있음);
  • 'string 심볼은 'bytes와 비슷하지만 get-output이 문자열을 만들어 내요;
  • 심볼 'pipe는 파이프의 생성을 촉발하는데, get-output이 파이프의 입력 끝을 돌려줘요;
  • 포트를 얻기 위해 호출되는 썽크(예: current-output-port를 사용하는 것은 평가자 출력이 전환되지 않는다는 뜻).
parameter

(sandbox-error-output) →
 (or/c #f
       output-port?
       'pipe
       'bytes
       'string
       (-> output-port?))
(sandbox-error-output in) → void?
  in :
 (or/c #f
       output-port?
       'pipe
       'bytes
       'string
       (-> output-port?))

sandbox-output와 같지만, 초기 current-error-port 값을 위한 것이에요. 평가자의 오류 출력은 출력 다음에 설정되므로, 이 파라미터 값에 current-output-port(파라미터 자체지, 그 값이 아님)를 쓰는 것은 오류 포트가 평가자의 초기 출력 포트와 같다는 뜻이에요.

기본값은 (lambda () (dup-output-port (current-error-port)))로, 생성된 평가자의 오류 출력이 호출 컨텍스트의 오류 포트로 간다는 뜻이에요.

parameter

(sandbox-coverage-enabled) → boolean?
(sandbox-coverage-enabled enabled?) → void?
  enabled? : any/c

샌드박스 평가자가 구문 커버리지 정보를 수집할지 여부를 제어하는 파라미터예요. 커버리지 정보를 검색하려면 get-uncovered-expressions를 사용하세요.

기본값은 #f예요.

parameter

(sandbox-propagate-breaks) → boolean?
(sandbox-propagate-breaks propagate?) → void?
  propagate? : any/c

이 불리언 파라미터와 (break-enabled)가 둘 다 참일 때, 평가자가 실행되는 동안 break는 샌드박스 컨텍스트로 break 신호를 전파해요. 이는 샌드박스 평가자를 보통 break시켜요. 하지만 샌드박스 평가가 break를 잡거나 피할 수 있다는 점에 주의하세요(안전한 코드 실행이 목표라면 시간 제한과 함께 쓰도록 하세요). 또한 break가 평가자 결과 뒤에 수신될 수 있다는 점에 주의하세요. 그 경우 평가 결과는 손실돼요. 마지막으로 break가 평가자가 결과를 만든 후에 전파되어 다음 상호작용에서 보일 수 있다는 점에 주의하세요(또는 평가자를 더 이상 쓰지 않으면 break가 손실돼요). 기본값은 #t예요.

parameter

(sandbox-propagate-exceptions) → boolean?
(sandbox-propagate-exceptions propagate?) → void?
  propagate? : any/c

샌드박스 평가 중 잡히지 않은 예외를 어떻게 취급할지 제어하는 파라미터예요. 파라미터 값이 #t면 예외는 sandbox의 호출자에게 전파돼요. 파라미터 값이 #f면 예외 메시지가 샌드박스의 오류 포트로 프린트되고, sandbox의 호출자는 평가에 대해 #<void>를 받아요. 기본값은 #t예요.

parameter

(sandbox-namespace-specs) →
 (cons/c (-> namespace?)
         (listof module-path?))
(sandbox-namespace-specs spec) → void?
  spec :
 (cons/c (-> namespace?)
         (listof module-path?))

make-evaluatormake-module-evaluator에서 평가를 위한 네임스페이스를 만드는 방법을 지정하는 값들의 리스트를 담는 파라미터예요. 리스트의 첫 항목은 네임스페이스를 만드는 썽크이고, 나머지는 namespace-attach-module로 만들어진 네임스페이스에 붙일 모듈들의 모듈 경로예요.

기본값은 (list sandbox-make-namespace)예요.

모듈 경로는 샌드박스와 호출자 사이에 모듈 인스턴스화를 공유하는 데 필요해요. 예를 들어 lang/posn 모듈에서 온 posn 값을 돌려주는 샌드박스 코드는 기본적으로 자신의 코드가 그것을 그렇게 인식하지 못해요. 샌드박스가 자신의 lang/posn 인스턴스를 가지므로 posn에 대한 자신의 구조 타입을 갖기 때문이에요. 그런 값을 사용할 수 있게 하려면 'lang/posn을 모듈 경로 리스트에 포함하세요.

교수 언어를 사용하는 코드를 테스트할 때는 다음 코드 조각이 도움이 될 수 있어요:

(sandbox-namespace-specs
 (let ([specs (sandbox-namespace-specs)])
   `(,(car specs)
     ,@(cdr specs)
     lang/posn
     ,@(if (gui-available?) '(mrlib/cache-image-snip) '()))))
procedure

(sandbox-make-namespace) → namespace?

(sandbox-gui-available)이 참을 만들면 make-gui-namespace를 호출하고, 그렇지 않으면 make-base-namespace를 호출해요.

parameter

(sandbox-gui-available) → boolean?
(sandbox-gui-available avail?) → void?
  avail? : any/c

샌드박스 평가자가 생성될 때 racket/gui 모듈을 쓸 수 있는지 결정해요. 샌드박스 평가자 생성 중 gui-available?#f를 만들면, 이 파라미터는 샌드박스 초기화 중에 #f로 강제돼요. 파라미터의 기본값은 #t예요.

GUI 라이브러리를 쓸 수 있을 때 라이브러리의 여러 측면이 바뀌는데, 예를 들어 각 평가자에 새 이벤트스페이스를 사용하는 것이 그렇죠.

parameter

(sandbox-override-collection-paths) → (listof path-string?)
(sandbox-override-collection-paths paths) → void?
  paths : (listof path-string?)

평가자에서 current-library-collection-paths의 앞에 붙일 컬렉션 디렉터리 리스트를 결정하는 파라미터예요. 이 파라미터는 컬렉션의 대안적인 테스트 친화적 버전으로 코드를 테스트하고 싶을 때 유용해요. 예를 들어 GUI를 쓰는 코드(htdp/world teachpack 같은)를 실제 상호작용 없이 같은 인터페이스를 제공하는 가짜 라이브러리로 테스트할 수 있어요. 기본값은 null이에요.

parameter

(sandbox-security-guard)
 → (or/c security-guard? (-> security-guard?))
(sandbox-security-guard guard) → void?
  guard : (or/c security-guard? (-> security-guard?))

샌드박스 평가를 위한 초기 (current-security-guard)를 결정하는 파라미터예요. 보안 가드이거나 가드를 만드는 함수일 수 있어요. 기본값은 sandbox-path-permissions의 지정을 제외한 모든 파일시스템 I/O를 금지해 현재 보안 가드의 접근을 제한하고, 네트워크 연결에는 sandbox-network-guard를 사용하는 함수예요.

parameter

(sandbox-path-permissions)
 →
 (listof (list/c (or/c 'execute 'write 'delete
                       'read-bytecode 'read 'exists)
               (or/c byte-regexp? bytes? string? path?)))
(sandbox-path-permissions perms) → void?
  perms :
 (listof (list/c (or/c 'execute 'write 'delete
                       'read-bytecode 'read 'exists)
               (or/c byte-regexp? bytes? string? path?)))

기본 샌드박스 보안 가드의 동작을, 그것이 허용하는 경로와 접근 모드를 나열함으로써 구성하는 파라미터예요. 이 파라미터의 내용은 지정들의 리스트인데, 각 지정은 접근 모드와 그 접근이 부여되는 경로에 대한 byte-regexp예요.

접근 모드 심볼은 'execute, 'write, 'delete, 'read, 'exists 중 하나예요. 이 심볼들은 내림차순이에요. 각각은 다음 모드에 대한 접근도 함의해요(예: 'read는 읽기나 존재 확인을 허용).

경로 regexp는 접근이 부여된 경로를 식별하는 데 사용돼요. 경로(또는 문자열, 바이트 문자열)로도 줄 수 있는데, (완전한 경로로 만들고, 정화하고, 단순화한 뒤) 그 경로와 하위 디렉터리를 허용하는 regexp로 변환돼요. 예를 들어 /foo/bar/foo/bar/baz에 적용돼요.

추가 모드 심볼 'read-bytecode는 이 모드들의 선형 순서의 일부가 아니에요. 이 모드를 지정하는 것은 'read를 지정하는 것과 비슷하지만, 다른 어떤 모드에도 함의되지 않아요. (예를 들어 특정 경로에 'write를 지정해도 'read-bytecode도 지정해야 그 권한을 부여해요.) 샌드박스는 보통 더 낮은 코드 인스펙터(sandbox-make-code-inspector 참고)의 컨텍스트에서 동작해 신뢰할 수 없는 바이트코드 파일의 로딩을 막는데, 샌드박스는 'read-bytecode로 지정된 파일에서 바이트코드를 로드하도록 설정돼요. 이 지정은 기본적으로 Racket 컬렉션 계층(사용자 특화 라이브러리 포함)과 #:allow-read 인자에 명시적으로 지정된 라이브러리에 주어져요. (이것은 바이트코드 파일 로딩에만 적용되고, 더 낮은 코드 인스펙터 아래에서는 보호된 모듈 바인딩을 여전히 사용할 수 없다는 점에 주의하세요. Code Inspectors 참고.)

기본값은 null이지만, 평가자가 생성될 때 'read-bytecode 권한으로 보강되어 컬렉션 라이브러리(sandbox-override-collection-paths 포함)를 사용할 수 있게 해요. 자세한 내용은 make-evaluator를 참고하세요.

parameter

(sandbox-network-guard)
 →
 (symbol?
  (or/c (and/c string? immutable?) #f)
  (or/c (integer-in 1 65535) #f)
  (or/c 'server 'client)
  . -> . any)
(sandbox-network-guard proc) → void?
  proc :
 (symbol?
  (or/c (and/c string? immutable?) #f)
  (or/c (integer-in 1 65535) #f)
  (or/c 'server 'client)
  . -> . any)

기본 sandbox-security-guard가 (그대로) 사용할 프로시저를 지정하는 파라미터예요. 기본값은 모든 네트워크 연결을 금지해요.

parameter

(sandbox-exit-handler) → (any/c . -> . any)
(sandbox-exit-handler handler) → void?
  handler : (any/c . -> . any)

샌드박스 평가를 위한 초기 (exit-handler)를 결정하는 파라미터예요. 기본값은 적절한 오류 메시지로 평가자를 죽여요(exn:fail:sandbox-terminated-reason 참고).

parameter

(sandbox-memory-limit) → (or/c (>=/c 0) #f)
(sandbox-memory-limit limit) → void?
  limit : (or/c (>=/c 0) #f)

메가바이트 단위로 샌드박스의 총 메모리 제한을 결정하는 파라미터예요(유리수나 부동소수점 수를 담을 수 있어요). 이 제한을 초과하면 샌드박스가 종료돼요. 이 값은 샌드박스가 생성될 때 사용되고, 이후에는 제한을 바꿀 수 없어요. 기본값은 30mb예요. 평가별 제한과 두 제한이 어떻게 함께 동작하는지에 대한 설명은 sandbox-eval-limits를 참고하세요.

(메모리 회계가 활성화되어 있을 때) 메모리는 그것을 참조하는 가장 높은 custodian에 귀속된다는 점에 주의하세요. 이는 샌드박스 평가가 돌려준 값을 샌드박스 밖에서 검사하면, 자신의 custodian에 대해 청구된다는 뜻이에요. 그것이 샌드박스에 다시 청구되도록 하려면, 코드가 값을 검사할 때 그 값에 대한 참조를 제거해야 해요.

이 정책은 샌드박스 메모리 제한이 sandbox-eval-limits가 지정한 표현식별 제한과 상호작용하는 방식에 영향을 줘요. 샌드박스에서 도달할 수 있고 상호작용에서도 도달할 수 있는 값은 샌드박스 제한에 포함돼요. 예를 들어 이 코드의 마지막 상호작용에서,

(define e (make-evaluator 'racket/base))
(e '(define a 1))
(e '(for ([i (in-range 20)]) (set! a (cons (make-bytes 500000) a))))

메모리 블록은 상호작용 제한 안에서 할당되지만, 그것들이 정의된 변수에 연결되어 있으므로 샌드박스에서도 도달 가능해요. 그래서 그것들은 샌드박스 메모리 제한에는 포함되지만 상호작용 제한에는 포함되지 않아요(더 정확히는, 하나 이상의 블록이 상호작용 제한에 포함되지 않아요).

parameter

(sandbox-eval-limits) →
 (or/c (list/c (or/c (>=/c 0) #f)
               (or/c (>=/c 0) #f))
       #f)
(sandbox-eval-limits limits) → void?
  limits :
 (or/c (list/c (or/c (>=/c 0) #f)
               (or/c (>=/c 0) #f))
       #f)

make-evaluator 함수의 각 사용(입력 프로그램의 초기 평가 포함)에 대한 기본 제한을 결정하는 파라미터예요. 값은 두 숫자의 리스트여야 하는데, 첫 번째는 초 단위의 얕은 시간 값이고, 두 번째는 메가바이트 단위의 메모리 제한이에요(정수일 필요는 없다는 점에 주의하세요). 둘 중 하나는 #f일 수 있는데, 대응하는 제한을 비활성화해요. 또는 파라미터를 #f로 설정해 모든 평가별 제한을 비활성화할 수 있어요(미래 버전에서 더 많은 제한 종류가 가능할 경우에 유용). 기본값은 (list 30 20)이에요.

이 제한은 샌드박스 환경의 생성에도 적용된다는 점에 주의하세요. 제한이 충분히 엄격하면 (make-evaluator 'racket/base)조차 실패할 수 있어요. 예를 들어,

(parameterize ([sandbox-eval-limits '(0.25 5)])
  (make-evaluator 'racket/base '(sleep 2)))

는 평가자를 만드는 대신 오류를 던져요. 따라서 놀라움을 피하려면 샌드박스가 생성될 때 일어나는 오류를 잡아야 해요.

제한이 설정되면, call-with-limits(아래 참고)가 평가자의 각 사용에 감싸져서, 시간이나 메모리를 너무 많이 소모하면 예외가 돼요. 실행 중인 평가자의 제한을 바꾸려면 set-eval-limits를 사용하세요.

custodian의 제한은 garbage collection 후에만 검사되지만, custodian의 제한보다 개별적으로 큰 특정 대량 할당 중에도 검사될 수 있어요.

이 파라미터가 지정하는 메모리 제한은 각 개별 평가에 적용되지만, 전체 샌드박스에는 적용되지 않아요. 그 제한은 sandbox-memory-limit으로 지정돼요. 전역 제한을 초과하면 샌드박스가 종료되고, 평가별 제한을 초과하면 exn:fail:resource?로 알아볼 수 있는 예외가 발생해요. 예를 들어 다음과 같은 표현식을 평가한다고 해 봐요:

(for ([i (in-range 1000)])
  (set! a (cons (make-bytes 1000000) a))
  (collect-garbage))

충분히 작은 제한을 가정하면,

  • 전역 제한은 설정했지만 평가별 제한은 없으면, 샌드박스는 결국 종료되고 더 이상의 평가가 불가능해요;
  • 평가별 제한은 있지만 전역 제한은 없으면, 평가는 오류로 중단되고 다시 사용할 수 있어요. 특히 a는 여전히 여러 블록을 가지게 되고, 같은 표현식을 다시 평가하면 블록이 더 추가돼요;
  • 두 제한 모두 설정되었고 전역 제한이 평가별 제한보다 크면, 평가는 중단되고 반복할 수 있지만, 여러 번 그렇게 하면 결국 샌드박스가 종료돼요(이것은 오류 메시지와 evaluator-alive? 술어로 표시돼요).
parameter

(sandbox-eval-handlers)
 →
 (list/c (or/c #f ((-> any) . -> . any))
         (or/c #f ((-> any) . -> . any)))
(sandbox-eval-handlers handlers) → void?
  handlers :
 (list/c (or/c #f ((-> any) . -> . any))
         (or/c #f ((-> any) . -> . any)))

샌드박스 평가를 감싸는 두 (선택) 핸들러를 결정하는 파라미터예요. 첫 번째는 샌드박스가 설정될 때 초기 프로그램을 평가할 때 사용되고, 두 번째는 각 상호작용에 사용돼요. 이 핸들러 각각은 썽크를 인자로 기대하고, 그 썽크를 실행해야 해요. 추가 제한을 부과할 수도 있어요. 기본값은 #fcall-with-custodian-shutdown으로, 초기 샌드박스 코드에 추가 제한고 없고(예: 백그라운드 스레드를 시작할 수 있음), 그 뒤에 따르는 각 상호작용 주위에 custodian-shutdown이 있다는 뜻이에요. 이에 유용한 또 다른 함수는 call-with-killing-threads인데, 모든 스레드를 죽이지만 다른 자원은 그대로 둬요.

parameter

(sandbox-run-submodules) → (list/c symbol?)
(sandbox-run-submodules submod-syms) → void?
  submod-syms : (list/c symbol?)

make-module-evaluator가 샌드박스를 만들 때 실행할 서브모듈을 결정하는 파라미터예요. 파라미터의 기본값은 빈 리스트예요.

parameter

(sandbox-make-inspector) → (-> inspector?)
(sandbox-make-inspector make) → void?
  make : (-> inspector?)

샌드박스 평가를 위한 인스펙터를 만드는 데 사용되는 (nullary) 프로시저를 결정하는 파라미터예요. 프로시저는 평가자를 초기화할 때 호출돼요. 기본 파라미터 값은 (lambda () (make-inspector (current-inspector)))이에요.

parameter

(sandbox-make-code-inspector) → (-> inspector?)
(sandbox-make-code-inspector make) → void?
  make : (-> inspector?)

샌드박스 평가를 위한 코드 인스펙터를 만드는 데 사용되는 (nullary) 프로시저를 결정하는 파라미터예요. 프로시저는 평가자를 초기화할 때 호출돼요. 기본 파라미터 값은 (lambda () (make-inspector (current-code-inspector)))이에요.

current-load/use-compiled 핸들러는 sandbox-path-permissions'read-bytecode 모드 심볼로 허용할 때 원래 코드 인스펙터 아래에서 바이트코드 파일을 로드하도록 설정돼요. 이는 라이브러리 로딩을 가능하게 해요.

parameter

(sandbox-make-logger) → (-> logger?)
(sandbox-make-logger make) → void?
  make : (-> logger?)

샌드박스 평가를 위한 로거를 만드는 데 사용되는 프로시저를 결정하는 파라미터예요. 프로시저는 평가자를 초기화할 때 호출되고, 기본 파라미터 값은 current-logger예요. 이는 새 로거를 만들지 않는다는 뜻이에요(이것은 미래에 바뀔 수 있어요).

parameter

(sandbox-make-plumber) → (or/c (-> plumber?) 'propagate)
(sandbox-make-plumber make) → void?
  make : (or/c (-> plumber?) 'propagate)

샌드박스 평가를 위한 plumber를 만드는 데 사용되는 프로시저를 결정하는 파라미터예요. 프로시저는 평가자를 초기화할 때 호출돼요.

값이 'propagate(기본값)면, 새 plumber가 만들어지고, (샌드박스가 이미 종료되지 않았다면) 만들어진 샌드박스 안에서 새 plumber로 요청을 전파하기 위해 flush 콜백이 현재 plumber에 추가돼요.

패키지 sandbox-lib의 버전 6.0.1.8에서 추가됨.

parameter

(sandbox-make-environment-variables)
 → (-> environment-variables?)
(sandbox-make-environment-variables make) → void?
  make : (-> environment-variables?)

샌드박스 평가를 위한 환경 변수 집합을 만드는 데 사용되는 프로시저를 결정하는 파라미터예요. 프로시저는 평가자를 초기화할 때 호출되고, 기본 파라미터 값은 (environment-variables-copy (current-environment-variables))로 새 환경 변수 집합을 만들어요.

procedure

(default-language-readers lang) → (listof module-path?)
  lang : module-path?

lang을 언어로 사용하는 모듈을 만들도록 허용되어야 하는 기본 리더 리스트를 만들어요.

이 기본 리스트는 다음을 포함해요(그리고 미래에는 더 많은 경로가 추가될 수 있어요):

  • `(submod ,lang reader)
  • lang이 심볼이면 'lang/lang/reader
  • lang이 심볼이 아니면 lang에 상대 경로 "lang/reader.rkt"를 더해 만들어지는 모듈 경로
  • '(submod at-exp reader)
  • 'at-exp/lang/reader

패키지 sandbox-lib의 버전 1.2에서 추가됨.

14.12.3 평가자와 상호작용 (Interacting with Evaluators)

다음 함수들은 샌드박스 평가자와, 코드를 평가하는 데 쓰는 것 외에 상호작용하는 데 사용돼요.

procedure

(evaluator-alive? evaluator) → boolean?
  evaluator : (any/c . -> . any)

평가자가 아직 살아 있는지 결정해요.

procedure

(kill-evaluator evaluator) → void?
  evaluator : (any/c . -> . any)

평가자의 custodian을 종료해 평가자가 보유한 자원을 해제해요. 죽인 뒤 평가자를 사용하려 하면 예외가 발생하고, 죽은 평가자를 죽이려는 시도는 무시돼요.

평가자를 죽이는 것은 평가자에 eof 값을 보내는 것과 비슷한데, 다만 eof 값은 즉시 오류를 발생시킬 거예요.

procedure

(break-evaluator evaluator) → void?
  evaluator : (any/c . -> . any)

실행 중인 평가자에 break를 보내요. 그 효과는 평가자가 현재 실행 중일 때 Ctrl-C를 친 것과 같아서, 평가자의 컨텍스트로 break를 전파해요.

procedure

(get-user-custodian evaluator) → void?
  evaluator : (any/c . -> . any)

평가자의 최상위 custodian을 검색해요. 이것은 (evaluator '(current-custodian))이나 (call-in-sandbox-context evaluator current-custodian)과 다른 값을 돌려줘요. 각 샌드박스 상호작용은 자신의 custodian에 감싸져 있는데, 이것들이 돌려주는 값이에요.

(이 custodian의 한 용도는 current-memory-use와 함께 쓰는 것인데, 상호작용별 하위 custodian이 전체 샌드박스의 메모리로 청구되지 않을 거예요.)

procedure

(set-eval-limits evaluator secs mb) → void?
  evaluator : (any/c . -> . any)
  secs : (or/c exact-nonnegative-integer? #f)
  mb : (or/c exact-nonnegative-integer? #f)

평가자가 사용하는 표현식별 제한을 secs 초의 얕은 시간과 mb 메가바이트로 바꿔요(둘 중 하나는 #f일 수 있는데 무제한을 뜻해요).

이 프로시저는 기존 평가자 제한을 수정하는 데 사용해야 해요. sandbox-eval-limits 파라미터를 바꾸는 것은 기존 평가자에 영향을 주지 않기 때문이에요. call-with-limits도 참고하세요.

procedure

(set-eval-handler evaluator handler) → void?
  evaluator : (any/c . -> . any)
  handler : (or/c #f ((-> any) . -> . any))

평가자가 각 상호작용 주위에서 사용하는 표현식별 핸들러를 바꿔요. #f 값은 핸들러가 사용되지 않음을 뜻해요.

이 프로시저는 기존 평가자 핸들러를 수정하는 데 사용해야 해요. sandbox-eval-handlers 파라미터를 바꾸는 것은 기존 평가자에 영향을 주지 않기 때문이에요. 제공되는 두 유용한 핸들러인 call-with-custodian-shutdowncall-with-killing-threads도 참고하세요.

procedure

(call-with-custodian-shutdown thunk) → any
  thunk : (-> any)
(call-with-killing-threads thunk) → any
  thunk : (-> any)

이 함수들은 평가 핸들러로 쓰기에 유용해요. call-with-custodian-shutdown은 fresh custodian에서 thunk를 실행한 다음 그 custodian을 종료해, thunk가 어떤 자원도 남겨 두지 않았음을 보장해요. call-with-killing-threads는 비슷하지만, 남겨진 스레드를 죽이고 다른 자원은 그대로 둬요.

procedure

(put-input evaluator) → output-port?
  evaluator : (any/c . -> . any)
(put-input evaluator i/o) → void?
  evaluator : (any/c . -> . any)
  i/o : (or/c bytes? string? eof-object?)

평가자가 생성될 때 (sandbox-input)'pipe이면, 이 프로시저를 (인자 없이) 사용해 파이프의 출력 포트 끝을 검색하거나, 문자열·바이트 문자열을 파이프에 추가할 수 있어요. eof로도 쓸 수 있는데, 그러면 파이프를 닫아요.

procedure

(get-output evaluator) → (or/c #f input-port? bytes? string?)
  evaluator : (any/c . -> . any)
(get-error-output evaluator)
 → (or/c #f input-port? bytes? string?)
  evaluator : (any/c . -> . any)

평가자 생성 시 (sandbox-output) 또는 (sandbox-error-output)의 설정에 의존하는 방식으로 평가자의 출력 또는 오류 출력을 돌려줘요:

  • 'pipe였다면, get-output은 만들어진 파이프의 입력 포트 끝을 돌려줘요;
  • 'bytes 또는 'string이었다면, 결과는 누적된 출력이고, 출력 포트는 리셋되어 각 호출이 평가자 출력의 다른 조각을 돌려줘요(결과는 평가자가 종료될 때까지만 이용 가능하고, 출력의 모든 할당은 샌드박스 메모리 제한의 대상이라는 점에 주의하세요);
  • 그렇지 않으면 #f를 돌려줘요.
procedure

(get-uncovered-expressions evaluator
                           [prog?
                            src]) → (listof syntax?)
  evaluator : (any/c . -> . any)
  prog? : any/c = #t
  src : any/c = default-src

평가자가 생성될 때 sandbox-coverage-enabled 파라미터가 참 값을 가졌다면, 평가자에서 커버되지 않은(uncovered) 표현식을 검색해요. 그렇지 않으면 커버리지 정보가 없음을 나타내는 예외가 발생해요.

prog? 인자는 원래 입력 프로그램만 평가된 뒤 커버되지 않은 표현식을 얻을지(#t), 아니면 평가자에 대한 모든 이후 사용 뒤의 것을 얻을지(#f)를 지정해요. #t를 사용하면 입력 프로그램이 평가된 뒤, 평가자가 사용되기 전에 저장된 리스트를 검색하므로 결과가 항상 같아요.

prog?#t 값은 학생 프로그램을 테스트해 제출물에 충분한 테스트 커버리지가 내장되어 있는지 알아내는 데 유용해요. #f 값은 프로그램에 대한 테스트 스위트를 작성해 테스트가 전체 코드를 덮는지 보장하는 데 유용해요.

두 번째 선택 인자 src는 결과가 소스가 src와 일치하는 구문 객체만 담도록 필터링되어야 함을 지정해요. 기본값은 (있었다면) 프로그램 코드에 사용된 소스예요. 입력 프로그램이 S-표현식이나 문자열로 주어졌다면 'program이 소스 값으로 사용되고(그리고 이 경우 필터링의 기본값이 될 거예요). #f가 주어지면 결과는 필터링되지 않은 표현식 리스트예요.

결과 구문 객체 리스트는 각 위치·범위에 대해 최대 한 표현식을 가져요. 따라서 내용은 신뢰할 수 없을 수 있지만, 위치 정보는 신뢰할 수 있어요(즉 커버리지 정보를 사용할 때 DrRacket에서 빨갛게 칠해질 소스 코드를 항상 가리켜요).

입력 프로그램이 구문 값들의 시퀀스라면, 그것들이 'program을 소스 필드로 가지도록 하거나 src 인자를 사용하세요. 입력 프로그램에 (구문 객체가 아닌) S-표현식 시퀀스를 사용하면, 각 표현식이 단일 소스 위치를 할당받을 수 있으므로 커버리지 결과가 신뢰할 수 없게 돼요.

procedure

(call-in-sandbox-context evaluator
                          thunk
                          [unrestricted?]) → any
  evaluator : (any/c . -> . any)
  thunk : (-> any)
  unrestricted? : boolean? = #f

샌드박스 평가자의 컨텍스트에서 주어진 thunk를 호출해요. 호출은 unrestricted?가 참으로 지정되지 않는 한, 표현식을 평가할 때 사용되는 자원 제한과 평가 핸들러 아래에서 수행돼요.

이 과정은 보통 (evaluator (list thunk))과 비슷하지만, 리스트 표현식을 함수 적용으로 해석하는 sexpr 기반 구문의 흔한 의미(모든 언어에서 참이 아닌)에 의존하지 않아요. 이것은 네임스페이스 조작 같은 메타 레벨 연산에 더 유용하고, 안전한 평가 대체물(즉 샌드박스 평가자를 평소처럼 쓰는 것)로 쓰기 위한 것이 아니라는 점에 주의하세요.

게다가 자신의 권한을 사용해 샌드박스 제한 중 일부를 피할 수 있어요. 예를 들어:

(let ([guard (current-security-guard)])
  (call-in-sandbox-context
    ev
    (lambda ()
      (parameterize ([current-security-guard guard])
        ; can access anything you want here
        (delete-file "/some/file")))))

14.12.4 기타 (Miscellaneous)

value

gui? : boolean?

역호환성만을 위한 값이에요. racket/sandbox가 인스턴스화될 때의 gui-available?의 결과예요.

gui?의 값은 이제 racket/sandbox 자체가 사용하지 않아요. 대신 gui-available?sandbox-gui-available이 샌드박스 평가자가 생성될 때 검사돼요.

procedure

(call-with-limits secs mb thunk) → any
  secs : (or/c exact-nonnegative-integer? #f)
  mb : (or/c exact-nonnegative-integer? #f)
  thunk : (-> any)

메모리·시간 제한으로 주어진 thunk를 실행해요. 실행이 mb 메가바이트보다 많거나 secs 초의 얕은 시간보다 많이 소모하면, 계산이 중단되고 exn:fail:resource?로 알아볼 수 있는 예외가 발생해요. 그렇지 않으면 thunk의 결과가 평소처럼 돌려져요(값, 여러 값, 또는 예외). 두 제한 각각은 #f일 수 있는데 제한이 없음을 나타내요. 메모리 제한에 대한 정보는 custodian-limit-memory도 참고하세요.

제한을 강제하기 위해 thunk는 새 스레드에서 실행돼요. 평소처럼 새 스레드는 call-with-limits를 호출한 스레드와 같은 파라미터 값으로 시작해요. 평소와 다르게, thunk를 실행하는 데 사용된 스레드의 파라미터 값은 thunk가 완료되면 call-with-limits를 호출한 스레드로 복사돼요.

샌드박스 평가자는 sandbox-eval-limits 설정과 set-eval-limits 사용에 따라 call-with-limits를 사용해요. 각 표현식 평가는 타임아웃과 메모리 문제로부터 보호돼요. call-with-limits를 직접 사용하는 것은 각 표현식 대신 전체 테스트 세션을 제한할 때만 하세요.

syntax

(with-limits sec-expr mb-expr body ...)

call-with-limits의 매크로 버전이에요.

procedure

(call-with-deep-time-limit secs thunk) → any
  secs : exact-nonnegative-integer?
  thunk : (-> any)

깊은 시간 제한으로 주어진 thunk를 실행하고, thunk가 만든 값을 돌려줘요.

주어진 thunk는 새 스레드에서 실행돼요. 오류가 나거나 스레드가 값을 돌려주고 종료되면 (values)가 돌려져요.

패키지 sandbox-lib의 버전 1.1에서 변경됨: 정상적으로 완료되면 thunk의 결과를 돌려주도록 변경됐어요.

syntax

(with-deep-time-limit secs-expr body ...)

call-with-deep-time-limit의 매크로 버전이에요.

procedure

(exn:fail:resource? v) → boolean?
  v : any/c
(exn:fail:resource-resource exn)
 → (or/c 'time 'memory 'deep-time)
  exn : exn:fail:resource?

call-with-limits가 발생시키는 예외에 대한 술어와 접근자예요. resource 필드는 소모된 자원을 나타내는 심볼을 담아요. 'time은 얕은 시간에 쓰이고, 'deep-time은 깊은 시간에 쓰여요.

더 알아보기