diff options
| -rw-r--r-- | guix/ui.scm | 32 | ||||
| -rw-r--r-- | tests/ui.scm | 90 |
2 files changed, 110 insertions, 12 deletions
diff --git a/guix/ui.scm b/guix/ui.scm index e6829bd4d60..e953ef23ca1 100644 --- a/guix/ui.scm +++ b/guix/ui.scm | |||
| @@ -250,11 +250,11 @@ arbitrary code execution." | |||
| 250 | (error "can't sever module?")) | 250 | (error "can't sever module?")) |
| 251 | (hashq-remove! (module-submodules parent) tail))))) | 251 | (hashq-remove! (module-submodules parent) tail))))) |
| 252 | 252 | ||
| 253 | (define* (load/isolated file bindings | 253 | (define* (load/isolated port bindings |
| 254 | #:key | 254 | #:key |
| 255 | (time-limit 30) | 255 | (time-limit 30) |
| 256 | (allocation-limit #e1e6)) | 256 | (allocation-limit #e1e6)) |
| 257 | "Read and evaluate code from FILE in a isolated evaluation environment that | 257 | "Read and evaluate code from PORT in a isolated evaluation environment that |
| 258 | only contains the given BINDINGS. Evaluation may not take more than | 258 | only contains the given BINDINGS. Evaluation may not take more than |
| 259 | TIME-LIMIT seconds and may not allocate more than ALLOCATION-LIMIT bytes." | 259 | TIME-LIMIT seconds and may not allocate more than ALLOCATION-LIMIT bytes." |
| 260 | ;; This is similar to 'eval-in-sandbox' except that the time and allocation | 260 | ;; This is similar to 'eval-in-sandbox' except that the time and allocation |
| @@ -265,7 +265,7 @@ TIME-LIMIT seconds and may not allocate more than ALLOCATION-LIMIT bytes." | |||
| 265 | (lambda () | 265 | (lambda () |
| 266 | (call-with-time-and-allocation-limits time-limit allocation-limit | 266 | (call-with-time-and-allocation-limits time-limit allocation-limit |
| 267 | (lambda () | 267 | (lambda () |
| 268 | (eval (call-with-input-file file read/safe) module)))) | 268 | (eval (read/safe port) module)))) |
| 269 | (lambda () | 269 | (lambda () |
| 270 | (sever-module! module))))) | 270 | (sever-module! module))))) |
| 271 | 271 | ||
| @@ -300,9 +300,10 @@ TIME-LIMIT seconds and may not allocate more than ALLOCATION-LIMIT bytes." | |||
| 300 | #:key | 300 | #:key |
| 301 | (on-error 'nothing-special) | 301 | (on-error 'nothing-special) |
| 302 | (isolated? #f)) | 302 | (isolated? #f)) |
| 303 | "Load the user provided Scheme source code FILE. When ISOLATED? is true, | 303 | "Load the user provided Scheme source code FILE-OR-PORT. When ISOLATED? is true, |
| 304 | load FILE in an isolated \"sandbox\" where only IMPORTS are available--see the | 304 | load FILE-OR-PORT in an isolated \"sandbox\" where only IMPORTS are |
| 305 | documentation for (ice-9 sandbox) for what goes into IMPORTS." | 305 | available--see the documentation for (ice-9 sandbox) for what goes into |
| 306 | IMPORTS." | ||
| 306 | (define (error-string frame args) | 307 | (define (error-string frame args) |
| 307 | (call-with-output-string | 308 | (call-with-output-string |
| 308 | (lambda (port) | 309 | (lambda (port) |
| @@ -324,14 +325,23 @@ documentation for (ice-9 sandbox) for what goes into IMPORTS." | |||
| 324 | module)) | 325 | module)) |
| 325 | imports))))) | 326 | imports))))) |
| 326 | 327 | ||
| 328 | (when (and (port? file-or-port) (not isolated?)) | ||
| 329 | ;; XXX: This case is not implemented because it's hard to defend and hard | ||
| 330 | ;; to implement due to the use of the compiler instead of the interpreter. | ||
| 331 | (leave (G_ "code coming from a port must be isolated~%"))) | ||
| 332 | |||
| 327 | (catch #t | 333 | (catch #t |
| 328 | (lambda () | 334 | (lambda () |
| 329 | (if isolated? | 335 | (if isolated? |
| 330 | (call-with-prompt tag | 336 | (call-with-prompt tag |
| 331 | (lambda () | 337 | (lambda () |
| 332 | (load/isolated (try-canonicalize-path file) | 338 | (let loop ((port file-or-port)) |
| 333 | (append imports | 339 | (if (port? port) |
| 334 | (force pure-bindings-sans-allocators)))) | 340 | (load/isolated port |
| 341 | (append | ||
| 342 | imports | ||
| 343 | (force pure-bindings-sans-allocators))) | ||
| 344 | (call-with-input-file file-or-port loop)))) | ||
| 335 | (const #f)) | 345 | (const #f)) |
| 336 | (save-module-excursion | 346 | (save-module-excursion |
| 337 | (lambda () | 347 | (lambda () |
| @@ -351,7 +361,7 @@ documentation for (ice-9 sandbox) for what goes into IMPORTS." | |||
| 351 | ;; compiled, which then allows us to provide better error | 361 | ;; compiled, which then allows us to provide better error |
| 352 | ;; reporting with source line numbers. | 362 | ;; reporting with source line numbers. |
| 353 | (without-compiler-optimizations | 363 | (without-compiler-optimizations |
| 354 | (load (try-canonicalize-path file)))) | 364 | (load (try-canonicalize-path file-or-port)))) |
| 355 | (const #f))))))) | 365 | (const #f))))))) |
| 356 | (lambda _ | 366 | (lambda _ |
| 357 | ;; XXX: Errors are reported from the pre-unwind handler below, but | 367 | ;; XXX: Errors are reported from the pre-unwind handler below, but |
| @@ -363,7 +373,7 @@ documentation for (ice-9 sandbox) for what goes into IMPORTS." | |||
| 363 | (let* ((stack (make-stack #t handle-error tag)) | 373 | (let* ((stack (make-stack #t handle-error tag)) |
| 364 | (frame (last-frame-with-source stack))) | 374 | (frame (last-frame-with-source stack))) |
| 365 | 375 | ||
| 366 | (report-load-error file args frame) | 376 | (report-load-error file-or-port args frame) |
| 367 | 377 | ||
| 368 | (case on-error | 378 | (case on-error |
| 369 | ((debug) | 379 | ((debug) |
diff --git a/tests/ui.scm b/tests/ui.scm index 438acae5252..048f8500722 100644 --- a/tests/ui.scm +++ b/tests/ui.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2013-2017, 2019-2020, 2022 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2013-2017, 2019-2020, 2022, 2026 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info> | 3 | ;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info> |
| 4 | ;;; | 4 | ;;; |
| 5 | ;;; This file is part of GNU Guix. | 5 | ;;; This file is part of GNU Guix. |
| @@ -26,11 +26,19 @@ | |||
| 26 | #:use-module ((gnu packages) #:select (specification->package)) | 26 | #:use-module ((gnu packages) #:select (specification->package)) |
| 27 | #:use-module (guix tests) | 27 | #:use-module (guix tests) |
| 28 | #:use-module (guix utils) | 28 | #:use-module (guix utils) |
| 29 | #:use-module ((guix channels) | ||
| 30 | #:select (channel->code | ||
| 31 | %default-guix-channel | ||
| 32 | guix-channel? | ||
| 33 | channel-introduction)) | ||
| 29 | #:use-module (srfi srfi-1) | 34 | #:use-module (srfi srfi-1) |
| 30 | #:use-module (srfi srfi-11) | 35 | #:use-module (srfi srfi-11) |
| 31 | #:use-module (srfi srfi-19) | 36 | #:use-module (srfi srfi-19) |
| 32 | #:use-module (srfi srfi-26) | 37 | #:use-module (srfi srfi-26) |
| 33 | #:use-module (srfi srfi-64) | 38 | #:use-module (srfi srfi-64) |
| 39 | #:use-module (rnrs bytevectors) | ||
| 40 | #:use-module (ice-9 binary-ports) | ||
| 41 | #:use-module (ice-9 match) | ||
| 34 | #:use-module (ice-9 regex)) | 42 | #:use-module (ice-9 regex)) |
| 35 | 43 | ||
| 36 | ;; Test the (guix ui) module. | 44 | ;; Test the (guix ui) module. |
| @@ -369,4 +377,84 @@ Second line" 24)) | |||
| 369 | ("PAGER" #false)) | 377 | ("PAGER" #false)) |
| 370 | (assert-equals-find-available-pager ""))))) | 378 | (assert-equals-find-available-pager ""))))) |
| 371 | 379 | ||
| 380 | (test-equal "load/isolated, reading exceeds limits" | ||
| 381 | 'quit ;'limit-exceeded is raised, caught, and then 'quit is raised | ||
| 382 | (let ((port (make-custom-binary-input-port | ||
| 383 | "infinite-paren-stream" | ||
| 384 | (lambda (bv start count) | ||
| 385 | (bytevector-u8-set! bv start (char->integer #\()) | ||
| 386 | 1) | ||
| 387 | #f #f #f))) | ||
| 388 | (catch #t | ||
| 389 | (lambda () | ||
| 390 | (load* port '() #:isolated? #t) | ||
| 391 | #f) | ||
| 392 | (lambda (key . args) | ||
| 393 | key)))) | ||
| 394 | |||
| 395 | (test-equal "load/isolated, attempt to import module" | ||
| 396 | 'quit | ||
| 397 | (call-with-input-string (object->string | ||
| 398 | '(begin | ||
| 399 | (use-modules (system foreign)) | ||
| 400 | (dereference-pointer (make-pointer 123)))) | ||
| 401 | (lambda (port) | ||
| 402 | (catch #t | ||
| 403 | (lambda () | ||
| 404 | (load* port '() #:isolated? #t) | ||
| 405 | #f) | ||
| 406 | (lambda (key . args) | ||
| 407 | key))))) | ||
| 408 | |||
| 409 | (test-equal "load/isolated, attempt to allocate with 'cons'" | ||
| 410 | 'quit | ||
| 411 | ;; 'make-list' is not available in the environment so try to allocate memory | ||
| 412 | ;; via macro expansion or repeated calls to 'cons'. | ||
| 413 | (call-with-input-string | ||
| 414 | (object->string | ||
| 415 | '(letrec-syntax ((make-list | ||
| 416 | (lambda (s) | ||
| 417 | (syntax-case s () | ||
| 418 | ((_ 0) #''()) | ||
| 419 | ((_ n) | ||
| 420 | #`(cons #f | ||
| 421 | (make-list | ||
| 422 | #,(- (syntax->datum #'n) 1)))))))) | ||
| 423 | (make-list 100000))) | ||
| 424 | (lambda (port) | ||
| 425 | (catch #t | ||
| 426 | (lambda () | ||
| 427 | (load* port '() #:isolated? #t) | ||
| 428 | #f) | ||
| 429 | (lambda (key . args) | ||
| 430 | key))))) | ||
| 431 | |||
| 432 | (test-equal "load/isolated, attempt to allocate with 'make-vector'" | ||
| 433 | 'quit | ||
| 434 | ;; 'make-vector' is not available in the environment. | ||
| 435 | (call-with-input-string (object->string '(make-vector 123123123)) | ||
| 436 | (lambda (port) | ||
| 437 | (catch #t | ||
| 438 | (lambda () | ||
| 439 | (load* port '() #:isolated? #t) | ||
| 440 | #f) | ||
| 441 | (lambda (key . args) | ||
| 442 | key))))) | ||
| 443 | |||
| 444 | (test-assert "load/isolated, use of allowed bindings" | ||
| 445 | (call-with-input-string | ||
| 446 | (object->string | ||
| 447 | `(list ,(channel->code %default-guix-channel))) | ||
| 448 | (lambda (port) | ||
| 449 | (match (load* port | ||
| 450 | '(((guix channels) | ||
| 451 | channel make-channel-introduction openpgp-fingerprint)) | ||
| 452 | #:isolated? #t) | ||
| 453 | ((channel) | ||
| 454 | ;; The channels have a different 'location' field value hence this | ||
| 455 | ;; limited comparison. | ||
| 456 | (and (guix-channel? channel) | ||
| 457 | (equal? (channel-introduction channel) | ||
| 458 | (channel-introduction %default-guix-channel)))))))) | ||
| 459 | |||
| 372 | (test-end "ui") | 460 | (test-end "ui") |
