Hello! Andy Wingo <wi...@pobox.com> skribis:
> Any thoughts? I would like something like this for a web service that > has to evaluate untrusted code. Would be nice! > (define (call-with-allocation-limit limit thunk limit-reached) > "Call @var{thunk}, but cancel it if @var{limit} bytes have been > allocated. If the computation is cancelled, call @var{limit-reached} in > tail position. @var{thunk} must not disable interrupts or prevent an > abort via a @code{dynamic-wind} unwind handler. > > This limit applies to both stack and heap allocation. The computation > will not be aborted before @var{limit} bytes have been allocated, but > for the heap allocation limit, the check may be postponed until the next > garbage collection." > (define (bytes-allocated) (assq-ref (gc-stats) 'heap-total-allocated)) > (let ((zero (bytes-allocated)) > (tag (make-prompt-tag))) > (define (check-allocation) > (when (< limit (- (bytes-allocated) zero)) > (abort-to-prompt tag))) > (call-with-prompt tag > (lambda () > (dynamic-wind > (lambda () > (add-hook! after-gc-hook check-allocation)) > (lambda () > (call-with-stack-overflow-handler > ;; The limit is in "words", which used to be 4 or 8 but now > ;; is always 8 bytes. > (floor/ limit 8) > thunk > (lambda () (abort-to-prompt tag)))) > (lambda () > (remove-hook! after-gc-hook check-allocation)))) > (lambda (k) > (limit-reached))))) The allocations that trigger ‘after-gc-hook’ could be caused by a separate thread, right? That’s probably an acceptable limitation, but one to be aware of. Also, if the code does: (make-bytevector (expt 2 32)) then ‘after-gc-hook’ run too late, as the comment notes. > (define (make-sandbox-module bindings) > "Return a fresh module that only contains @var{bindings}. > > The @var{bindings} should be given as a list of import sets. One import > set is a list whose car names an interface, like @code{(ice-9 q)}, and > whose cdr is a list of imports. An import is either a bare symbol or a > pair of @code{(@var{out} . @var{in})}, where @var{out} and @var{in} are > both symbols and denote the name under which a binding is exported from > the module, and the name under which to make the binding available, > respectively." > (let ((m (make-fresh-user-module))) > (purify-module! m) > ;; FIXME: We want to have a module that will be collectable by GC. > ;; Currently in Guile all modules are part of a single tree, and > ;; once a module is part of that tree it will never be collected. > ;; So we want to sever the module off from that tree. However the > ;; psyntax syntax expander currently needs to be able to look up > ;; modules by name; being severed from the name tree prevents that > ;; from happening. So for now, each evaluation leaks memory :/ > ;; > ;; (sever-module! m) > (module-use-interfaces! m > (map (match-lambda > ((mod-name . bindings) > (resolve-interface mod-name > #:select bindings))) > bindings)) > m)) IIUC ‘@@’ in unavailable in the returned module, right? --8<---------------cut here---------------start------------->8--- scheme@(guile-user)> (eval '(@@ (guile) resolve-interface) (let ((m (make-fresh-user-module))) (purify-module! m) m)) ERROR: In procedure %resolve-variable: ERROR: Unbound variable: @@ --8<---------------cut here---------------end--------------->8--- Isn’t make-fresh-user-module + purify-module! equivalent to just (make-module)? > ;; These can only form part of a safe binding set if no mutable > ;; pair is exposed to the sandbox. > (define *mutating-pair-bindings* > '(((guile) > set-car! > set-cdr!))) When used on a literal pair (mapped read-only), these can cause a segfault. Now since the code is ‘eval’d, the only literal pairs it can see are those passed by the caller I suppose, so this may be safe? > (define *all-pure-and-impure-bindings* > (append *all-pure-bindings* Last but not least: why all the stars? :-) I’m used to ‘%something’. Thank you! Ludo’.