summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-aux/test-env.in18
-rw-r--r--tests/processes.scm9
-rw-r--r--tests/store.scm247
3 files changed, 236 insertions, 38 deletions
diff --git a/build-aux/test-env.in b/build-aux/test-env.in
index 9caa29da581..86c2e585d73 100644
--- a/build-aux/test-env.in
+++ b/build-aux/test-env.in
@@ -1,7 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2 2
3# GNU Guix --- Functional package management for GNU 3# GNU Guix --- Functional package management for GNU
4# Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2021 Ludovic Courtès <ludo@gnu.org> 4# Copyright © 2012-2019, 2021, 2025 Ludovic Courtès <ludo@gnu.org>
5# 5#
6# This file is part of GNU Guix. 6# This file is part of GNU Guix.
7# 7#
@@ -102,10 +102,24 @@ then
102 rm -rf "$GUIX_STATE_DIRECTORY/daemon-socket" 102 rm -rf "$GUIX_STATE_DIRECTORY/daemon-socket"
103 mkdir -m 0700 "$GUIX_STATE_DIRECTORY/daemon-socket" 103 mkdir -m 0700 "$GUIX_STATE_DIRECTORY/daemon-socket"
104 104
105 # If unprivileged user namespaces are not supported, pass
106 # '--disable-chroot'.
107 if [ -f /proc/self/ns/user ] \
108 && { [ ! -f /proc/sys/kernel/unprivileged_userns_clone ] \
109 || [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -eq 1 ]; }
110 then
111 extra_options=""
112 else
113 extra_options="--disable-chroot"
114 echo "unprivileged user namespaces not supported; \
115running 'guix-daemon $extra_options'" >&2
116 fi
117
105 # Launch the daemon without chroot support because is may be 118 # Launch the daemon without chroot support because is may be
106 # unavailable, for instance if we're not running as root. 119 # unavailable, for instance if we're not running as root.
107 "@abs_top_builddir@/pre-inst-env" \ 120 "@abs_top_builddir@/pre-inst-env" \
108 "@abs_top_builddir@/guix-daemon" --disable-chroot \ 121 "@abs_top_builddir@/guix-daemon" \
122 $extra_options \
109 --substitute-urls="$GUIX_BINARY_SUBSTITUTE_URL" & 123 --substitute-urls="$GUIX_BINARY_SUBSTITUTE_URL" &
110 124
111 daemon_pid=$! 125 daemon_pid=$!
diff --git a/tests/processes.scm b/tests/processes.scm
index ba518f2d9e3..a72ba16f587 100644
--- a/tests/processes.scm
+++ b/tests/processes.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2018 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2018, 2025 Ludovic Courtès <ludo@gnu.org>
3;;; Copyright © 2019 Mathieu Othacehe <m.othacehe@gmail.com> 3;;; Copyright © 2019 Mathieu Othacehe <m.othacehe@gmail.com>
4;;; 4;;;
5;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
@@ -25,6 +25,8 @@
25 #:use-module (guix gexp) 25 #:use-module (guix gexp)
26 #:use-module ((guix utils) #:select (call-with-temporary-directory)) 26 #:use-module ((guix utils) #:select (call-with-temporary-directory))
27 #:use-module (gnu packages bootstrap) 27 #:use-module (gnu packages bootstrap)
28 #:use-module ((gnu build linux-container)
29 #:select (unprivileged-user-namespace-supported?))
28 #:use-module (guix tests) 30 #:use-module (guix tests)
29 #:use-module (srfi srfi-1) 31 #:use-module (srfi srfi-1)
30 #:use-module (srfi srfi-64) 32 #:use-module (srfi srfi-64)
@@ -84,6 +86,11 @@
84 (and (kill (process-id daemon) 0) 86 (and (kill (process-id daemon) 0)
85 (string-suffix? "guix-daemon" (first (process-command daemon))))))) 87 (string-suffix? "guix-daemon" (first (process-command daemon)))))))
86 88
89(when (unprivileged-user-namespace-supported?)
90 ;; The test below assumes the build process can communicate with the outside
91 ;; world via the TOKEN1 and TOKEN2 files, which is impossible when
92 ;; guix-daemon is set up to build in separate namespaces.
93 (test-skip 1))
87(test-assert* "client + lock" 94(test-assert* "client + lock"
88 (with-store store 95 (with-store store
89 (call-with-temporary-directory 96 (call-with-temporary-directory
diff --git a/tests/store.scm b/tests/store.scm
index 45948f4f433..b1ddff2082e 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012-2021, 2023 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2012-2021, 2023, 2025 Ludovic Courtès <ludo@gnu.org>
3;;; 3;;;
4;;; This file is part of GNU Guix. 4;;; This file is part of GNU Guix.
5;;; 5;;;
@@ -28,8 +28,12 @@
28 #:use-module (guix base32) 28 #:use-module (guix base32)
29 #:use-module (guix packages) 29 #:use-module (guix packages)
30 #:use-module (guix derivations) 30 #:use-module (guix derivations)
31 #:use-module ((guix modules)
32 #:select (source-module-closure))
31 #:use-module (guix serialization) 33 #:use-module (guix serialization)
32 #:use-module (guix build utils) 34 #:use-module (guix build utils)
35 #:use-module ((gnu build linux-container)
36 #:select (unprivileged-user-namespace-supported?))
33 #:use-module (guix gexp) 37 #:use-module (guix gexp)
34 #:use-module (gnu packages) 38 #:use-module (gnu packages)
35 #:use-module (gnu packages bootstrap) 39 #:use-module (gnu packages bootstrap)
@@ -391,6 +395,188 @@
391 (equal? (valid-derivers %store o) 395 (equal? (valid-derivers %store o)
392 (list (derivation-file-name d)))))) 396 (list (derivation-file-name d))))))
393 397
398(test-assert "symlink is symlink"
399 (let* ((a (add-text-to-store %store "hello.txt" (random-text)))
400 (b (build-expression->derivation
401 %store "symlink"
402 '(symlink (assoc-ref %build-inputs "a") %output)
403 #:inputs `(("a" ,a))))
404 (c (build-expression->derivation
405 %store "symlink-reference"
406 `(call-with-output-file %output
407 (lambda (port)
408 ;; Check that B is indeed visible as a symlink. This should
409 ;; always be the case, both in the '--disable-chroot' and in
410 ;; the user namespace setups.
411 (pk 'stat (lstat (assoc-ref %build-inputs "b")))
412 (display (readlink (assoc-ref %build-inputs "b"))
413 port)))
414 #:inputs `(("b" ,b)))))
415 (and (build-derivations %store (list c))
416 (string=? (call-with-input-file (derivation->output-path c)
417 get-string-all)
418 a))))
419
420(unless (unprivileged-user-namespace-supported?)
421 (test-skip 1))
422(test-equal "isolated environment"
423 (string-join (append
424 '("PID: 1" "UID: 30001")
425 (delete-duplicates
426 (sort (list "/dev" "/tmp" "/proc" "/etc"
427 (match (string-tokenize (%store-prefix)
428 (char-set-complement
429 (char-set #\/)))
430 ((top _ ...) (string-append "/" top))))
431 string<?))
432 '("/etc/group" "/etc/hosts" "/etc/passwd")))
433 (let* ((b (add-text-to-store %store "build.sh"
434 "echo -n PID: $$ UID: $UID /* /etc/* > $out"))
435 (s (add-to-store %store "bash" #t "sha256"
436 (search-bootstrap-binary "bash"
437 (%current-system))))
438 (d (derivation %store "the-thing"
439 s `("-e" ,b)
440 #:env-vars `(("foo" . ,(random-text)))
441 #:sources (list b s)))
442 (o (derivation->output-path d)))
443 (and (build-derivations %store (list d))
444 (call-with-input-file o get-string-all))))
445
446(unless (unprivileged-user-namespace-supported?)
447 (test-skip 1))
448(test-equal "inputs are read-only"
449 "All good!"
450 (let* ((input (plain-file (string-append "might-be-tampered-with-"
451 (number->string
452 (car (gettimeofday))
453 16))
454 "All good!"))
455 (drv
456 (run-with-store %store
457 (gexp->derivation
458 "attempt-to-write-to-input"
459 (with-imported-modules (source-module-closure
460 '((guix build syscalls)))
461 #~(begin
462 (use-modules (guix build syscalls))
463
464 (let ((input #$input))
465 (chmod input #o666)
466 (call-with-output-file input
467 (lambda (port)
468 (display "BAD!" port)))
469 (mkdir #$output))))))))
470 (and (guard (c ((store-protocol-error? c) #t))
471 (build-derivations %store (list drv)))
472 (call-with-input-file (run-with-store %store
473 (lower-object input))
474 get-string-all))))
475
476(unless (unprivileged-user-namespace-supported?)
477 (test-skip 1))
478(test-assert "inputs cannot be remounted read-write"
479 (let ((drv
480 (run-with-store %store
481 (gexp->derivation
482 "attempt-to-remount-input-read-write"
483 (with-imported-modules (source-module-closure
484 '((guix build syscalls)))
485 #~(begin
486 (use-modules (guix build syscalls))
487
488 (let ((input #$(plain-file "input-that-might-be-tampered-with"
489 "All good!")))
490 (mount "none" input "none" (logior MS_BIND MS_REMOUNT))
491 (call-with-output-file input
492 (lambda (port)
493 (display "BAD!" port)))
494 (mkdir #$output))))))))
495 (guard (c ((store-protocol-error? c) #t))
496 (build-derivations %store (list drv))
497 #f)))
498
499(unless (unprivileged-user-namespace-supported?)
500 (test-skip 1))
501(test-assert "build root cannot be made world-readable"
502 (let ((drv
503 (run-with-store %store
504 (gexp->derivation
505 "attempt-to-make-root-world-readable"
506 (with-imported-modules (source-module-closure
507 '((guix build syscalls)))
508 #~(begin
509 (use-modules (guix build syscalls))
510
511 (catch 'system-error
512 (lambda ()
513 (chmod "/" #o777))
514 (lambda args
515 (format #t "failed to make root writable: ~a~%"
516 (strerror (system-error-errno args)))
517 (format #t "attempting read-write remount~%")
518 (mount "none" "/" "/" (logior MS_BIND MS_REMOUNT))
519 (chmod "/" #o777)))
520
521 ;; At this point, the build process could create a
522 ;; world-readable setuid binary under its root (so in the
523 ;; store) that would remain visible until the build
524 ;; completes.
525 (mkdir #$output)))))))
526 (guard (c ((store-protocol-error? c) #t))
527 (build-derivations %store (list drv))
528 #f)))
529
530(unless (unprivileged-user-namespace-supported?)
531 (test-skip 1))
532(test-assert "/tmp, store, and /dev/{null,full} are writable"
533 ;; All of /tmp and all of the store must be writable (the store is writable
534 ;; so that derivation outputs can be written to it, but in practice it's
535 ;; always been wide open). Things like /dev/null must be writable too.
536 (let ((drv (run-with-store %store
537 (gexp->derivation
538 "check-tmp-and-store-are-writable"
539 #~(begin
540 (mkdir "/tmp/something")
541 (mkdir (in-vicinity (getenv "NIX_STORE")
542 "some-other-thing"))
543 (call-with-output-file "/dev/null"
544 (lambda (port)
545 (display "Welcome to the void." port)))
546 (catch 'system-error
547 (lambda ()
548 (call-with-output-file "/dev/full"
549 (lambda (port)
550 (display "No space left!" port)))
551 (error "Should have thrown!"))
552 (lambda args
553 (unless (= ENOSPC (system-error-errno args))
554 (apply throw args))))
555 (mkdir #$output))))))
556 (build-derivations %store (list drv))))
557
558(unless (unprivileged-user-namespace-supported?)
559 (test-skip 1))
560(test-assert "network is unreachable"
561 (let ((drv (run-with-store %store
562 (gexp->derivation
563 "check-network-unreachable"
564 #~(let ((check-connection-failure
565 (lambda (address expected-code)
566 (let ((s (socket AF_INET SOCK_STREAM 0)))
567 (catch 'system-error
568 (lambda ()
569 (connect s AF_INET (inet-pton AF_INET address) 80))
570 (lambda args
571 (let ((errno (system-error-errno args)))
572 (unless (= expected-code errno)
573 (error "wrong error code"
574 errno (strerror errno))))))))))
575 (check-connection-failure "127.0.0.1" ECONNREFUSED)
576 (check-connection-failure "9.9.9.9" ENETUNREACH)
577 (mkdir #$output))))))
578 (build-derivations %store (list drv))))
579
394(test-equal "with-build-handler" 580(test-equal "with-build-handler"
395 'success 581 'success
396 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '())) 582 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
@@ -1333,40 +1519,31 @@ System: x86_64-linux~%"
1333 1519
1334(test-assert "build-things, check mode" 1520(test-assert "build-things, check mode"
1335 (with-store store 1521 (with-store store
1336 (call-with-temporary-output-file 1522 (let* ((drv (build-expression->derivation
1337 (lambda (entropy entropy-port) 1523 store "non-deterministic"
1338 (write (random-text) entropy-port) 1524 `(begin
1339 (force-output entropy-port) 1525 (use-modules (rnrs io ports))
1340 (let* ((drv (build-expression->derivation 1526 (let ((out (assoc-ref %outputs "out")))
1341 store "non-deterministic" 1527 (call-with-output-file out
1342 `(begin 1528 (lambda (port)
1343 (use-modules (rnrs io ports)) 1529 (let ((now (gettimeofday)))
1344 (let ((out (assoc-ref %outputs "out"))) 1530 (display (+ (car now) (cdr now)) port))))
1345 (call-with-output-file out 1531 #t))
1346 (lambda (port) 1532 #:guile-for-build
1347 ;; Rely on the fact that tests do not use the 1533 (package-derivation store %bootstrap-guile (%current-system))))
1348 ;; chroot, and thus ENTROPY is readable. 1534 (file (derivation->output-path drv)))
1349 (display (call-with-input-file ,entropy 1535 (and (build-things store (list (derivation-file-name drv)))
1350 get-string-all) 1536 (begin
1351 port))) 1537 (guard (c ((store-protocol-error? c)
1352 #t)) 1538 (pk 'determinism-exception c)
1353 #:guile-for-build 1539 (and (not (zero? (store-protocol-error-status c)))
1354 (package-derivation store %bootstrap-guile (%current-system)))) 1540 (string-contains (store-protocol-error-message c)
1355 (file (derivation->output-path drv))) 1541 "deterministic"))))
1356 (and (build-things store (list (derivation-file-name drv))) 1542 ;; This one will produce a different result. Since we're in
1357 (begin 1543 ;; 'check' mode, this must fail.
1358 (write (random-text) entropy-port) 1544 (build-things store (list (derivation-file-name drv))
1359 (force-output entropy-port) 1545 (build-mode check))
1360 (guard (c ((store-protocol-error? c) 1546 #f))))))
1361 (pk 'determinism-exception c)
1362 (and (not (zero? (store-protocol-error-status c)))
1363 (string-contains (store-protocol-error-message c)
1364 "deterministic"))))
1365 ;; This one will produce a different result. Since we're in
1366 ;; 'check' mode, this must fail.
1367 (build-things store (list (derivation-file-name drv))
1368 (build-mode check))
1369 #f))))))))
1370 1547
1371(test-assert "build-succeeded trace in check mode" 1548(test-assert "build-succeeded trace in check mode"
1372 (string-contains 1549 (string-contains