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