stage-2-init: fix false positives for RO Nix store mounts (#375257)

authored by

Jörg Thalheim and committed by
GitHub
b29c6f5a b97dde9a

+76 -1
+2 -1
nixos/modules/system/boot/stage-2-init.sh
··· 69 69 chown -f 0:30000 /nix/store 70 70 chmod -f 1775 /nix/store 71 71 if [ -n "@readOnlyNixStore@" ]; then 72 - if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then 72 + # #375257: Ensure that we pick the "top" (i.e. last) mount so we don't get a false positive for a lower mount. 73 + if ! [[ "$(findmnt --direction backward --first-only --noheadings --output OPTIONS /nix/store)" =~ (^|,)ro(,|$) ]]; then 73 74 if [ -z "$container" ]; then 74 75 mount --bind /nix/store /nix/store 75 76 else
+1
nixos/tests/all-tests.nix
··· 165 165 boot = handleTestOn ["x86_64-linux" "aarch64-linux"] ./boot.nix {}; 166 166 bootspec = handleTestOn ["x86_64-linux"] ./bootspec.nix {}; 167 167 boot-stage1 = handleTest ./boot-stage1.nix {}; 168 + boot-stage2 = handleTest ./boot-stage2.nix {}; 168 169 borgbackup = handleTest ./borgbackup.nix {}; 169 170 borgmatic = handleTest ./borgmatic.nix {}; 170 171 botamusique = handleTest ./botamusique.nix {};
+73
nixos/tests/boot-stage2.nix
··· 1 + import ./make-test-python.nix ( 2 + { pkgs, ... }: 3 + { 4 + name = "boot-stage2"; 5 + 6 + nodes.machine = 7 + { 8 + config, 9 + pkgs, 10 + lib, 11 + ... 12 + }: 13 + { 14 + virtualisation = { 15 + emptyDiskImages = [ 256 ]; 16 + 17 + # Mount an ext4 as the upper layer of the Nix store. 18 + fileSystems = { 19 + "/nix/store" = lib.mkForce { 20 + device = "/dev/vdb"; # the above disk image 21 + fsType = "ext4"; 22 + 23 + # data=journal always displays after errors=remount-ro; this is only needed because of the overlay 24 + # and #375257 will trigger with `errors=remount-ro` on a non-overlaid store: 25 + # see ordering in https://github.com/torvalds/linux/blob/v6.12/fs/ext4/super.c#L2974 26 + options = [ 27 + "defaults" 28 + "errors=remount-ro" 29 + "data=journal" 30 + ]; 31 + }; 32 + }; 33 + }; 34 + 35 + boot = { 36 + initrd = { 37 + # Format the upper Nix store. 38 + postDeviceCommands = '' 39 + ${pkgs.e2fsprogs}/bin/mkfs.ext4 /dev/vdb 40 + ''; 41 + 42 + # Overlay the RO store onto it. 43 + # Note that bug #375257 can be triggered without an overlay, 44 + # using the errors=remount-ro option (or similar) or with an overlay where any of the 45 + # paths ends in 'ro'. The offending mountpoint also has to be the last (top) one 46 + # if an option ending in 'ro' is the last in the list, so test both cases here. 47 + postMountCommands = '' 48 + mkdir -p /mnt-root/nix/store/ro /mnt-root/nix/store/rw /mnt-root/nix/store/work 49 + mount --bind /mnt-root/nix/.ro-store /mnt-root/nix/store/ro 50 + mount -t overlay overlay \ 51 + -o lowerdir=/mnt-root/nix/store/ro,upperdir=/mnt-root/nix/store/rw,workdir=/mnt-root/nix/store/work \ 52 + /mnt-root/nix/store 53 + ''; 54 + 55 + kernelModules = [ "overlay" ]; 56 + }; 57 + 58 + postBootCommands = '' 59 + touch /etc/post-boot-ran 60 + mount 61 + ''; 62 + }; 63 + }; 64 + 65 + testScript = '' 66 + machine.wait_for_unit("multi-user.target") 67 + machine.succeed("test /etc/post-boot-ran") 68 + machine.fail("touch /nix/store/should-not-work"); 69 + ''; 70 + 71 + meta.maintainers = with pkgs.lib.maintainers; [ numinit ]; 72 + } 73 + )