at 23.11-pre 5.0 kB view raw
1import ./make-test-python.nix ({ pkgs, ... }: { 2 name = "boot-stage1"; 3 4 nodes.machine = { config, pkgs, lib, ... }: { 5 boot.extraModulePackages = let 6 compileKernelModule = name: source: pkgs.runCommandCC name rec { 7 inherit source; 8 kdev = config.boot.kernelPackages.kernel.dev; 9 kver = config.boot.kernelPackages.kernel.modDirVersion; 10 ksrc = "${kdev}/lib/modules/${kver}/build"; 11 hardeningDisable = [ "pic" ]; 12 nativeBuildInputs = kdev.moduleBuildDependencies; 13 } '' 14 echo "obj-m += $name.o" > Makefile 15 echo "$source" > "$name.c" 16 make -C "$ksrc" M=$(pwd) modules 17 install -vD "$name.ko" "$out/lib/modules/$kver/$name.ko" 18 ''; 19 20 # This spawns a kthread which just waits until it gets a signal and 21 # terminates if that is the case. We want to make sure that nothing during 22 # the boot process kills any kthread by accident, like what happened in 23 # issue #15226. 24 kcanary = compileKernelModule "kcanary" '' 25 #include <linux/version.h> 26 #include <linux/init.h> 27 #include <linux/module.h> 28 #include <linux/kernel.h> 29 #include <linux/kthread.h> 30 #include <linux/sched.h> 31 #include <linux/signal.h> 32 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) 33 #include <linux/sched/signal.h> 34 #endif 35 36 MODULE_LICENSE("GPL"); 37 38 struct task_struct *canaryTask; 39 40 static int kcanary(void *nothing) 41 { 42 allow_signal(SIGINT); 43 allow_signal(SIGTERM); 44 allow_signal(SIGKILL); 45 while (!kthread_should_stop()) { 46 set_current_state(TASK_INTERRUPTIBLE); 47 schedule_timeout_interruptible(msecs_to_jiffies(100)); 48 if (signal_pending(current)) break; 49 } 50 return 0; 51 } 52 53 static int kcanaryInit(void) 54 { 55 kthread_run(&kcanary, NULL, "kcanary"); 56 return 0; 57 } 58 59 static void kcanaryExit(void) 60 { 61 kthread_stop(canaryTask); 62 } 63 64 module_init(kcanaryInit); 65 module_exit(kcanaryExit); 66 ''; 67 68 in lib.singleton kcanary; 69 70 boot.initrd.kernelModules = [ "kcanary" ]; 71 72 boot.initrd.extraUtilsCommands = let 73 compile = name: source: pkgs.runCommandCC name { inherit source; } '' 74 mkdir -p "$out/bin" 75 echo "$source" | gcc -Wall -o "$out/bin/$name" -xc - 76 ''; 77 78 daemonize = name: source: compile name '' 79 #include <stdio.h> 80 #include <unistd.h> 81 82 void runSource(void) { 83 ${source} 84 } 85 86 int main(void) { 87 if (fork() > 0) return 0; 88 setsid(); 89 runSource(); 90 return 1; 91 } 92 ''; 93 94 mkCmdlineCanary = { name, cmdline ? "", source ? "" }: (daemonize name '' 95 char *argv[] = {"${cmdline}", NULL}; 96 execvp("${name}-child", argv); 97 '') // { 98 child = compile "${name}-child" '' 99 #include <stdio.h> 100 #include <unistd.h> 101 102 int main(void) { 103 ${source} 104 while (1) sleep(1); 105 return 1; 106 } 107 ''; 108 }; 109 110 copyCanaries = lib.concatMapStrings (canary: '' 111 ${lib.optionalString (canary ? child) '' 112 copy_bin_and_libs "${canary.child}/bin/${canary.child.name}" 113 ''} 114 copy_bin_and_libs "${canary}/bin/${canary.name}" 115 ''); 116 117 in copyCanaries [ 118 # Simple canary process which just sleeps forever and should be killed by 119 # stage 2. 120 (daemonize "canary1" "while (1) sleep(1);") 121 122 # We want this canary process to try mimicking a kthread using a cmdline 123 # with a zero length so we can make sure that the process is properly 124 # killed in stage 1. 125 (mkCmdlineCanary { 126 name = "canary2"; 127 source = '' 128 FILE *f; 129 f = fopen("/run/canary2.pid", "w"); 130 fprintf(f, "%d\n", getpid()); 131 fclose(f); 132 ''; 133 }) 134 135 # This canary process mimics a storage daemon, which we do NOT want to be 136 # killed before going into stage 2. For more on root storage daemons, see: 137 # https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/ 138 (mkCmdlineCanary { 139 name = "canary3"; 140 cmdline = "@canary3"; 141 }) 142 ]; 143 144 boot.initrd.postMountCommands = '' 145 canary1 146 canary2 147 canary3 148 # Make sure the pidfile of canary 2 is created so that we still can get 149 # its former pid after the killing spree starts next within stage 1. 150 while [ ! -s /run/canary2.pid ]; do sleep 0.1; done 151 ''; 152 }; 153 154 testScript = '' 155 machine.wait_for_unit("multi-user.target") 156 machine.succeed("test -s /run/canary2.pid") 157 machine.fail("pgrep -a canary1") 158 machine.fail("kill -0 $(< /run/canary2.pid)") 159 machine.succeed('pgrep -a -f "^@canary3$"') 160 machine.succeed('pgrep -a -f "^kcanary$"') 161 ''; 162 163 meta.maintainers = with pkgs.lib.maintainers; [ aszlig ]; 164})