diff options
Diffstat (limited to 'nixpkgs/nixos/tests/boot-stage1.nix')
-rw-r--r-- | nixpkgs/nixos/tests/boot-stage1.nix | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/nixpkgs/nixos/tests/boot-stage1.nix b/nixpkgs/nixos/tests/boot-stage1.nix new file mode 100644 index 000000000000..b2e74bff6fcd --- /dev/null +++ b/nixpkgs/nixos/tests/boot-stage1.nix @@ -0,0 +1,162 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "boot-stage1"; + + machine = { config, pkgs, lib, ... }: { + boot.extraModulePackages = let + compileKernelModule = name: source: pkgs.runCommandCC name rec { + inherit source; + kdev = config.boot.kernelPackages.kernel.dev; + kver = config.boot.kernelPackages.kernel.modDirVersion; + ksrc = "${kdev}/lib/modules/${kver}/build"; + hardeningDisable = [ "pic" ]; + nativeBuildInputs = kdev.moduleBuildDependencies; + } '' + echo "obj-m += $name.o" > Makefile + echo "$source" > "$name.c" + make -C "$ksrc" M=$(pwd) modules + install -vD "$name.ko" "$out/lib/modules/$kver/$name.ko" + ''; + + # This spawns a kthread which just waits until it gets a signal and + # terminates if that is the case. We want to make sure that nothing during + # the boot process kills any kthread by accident, like what happened in + # issue #15226. + kcanary = compileKernelModule "kcanary" '' + #include <linux/version.h> + #include <linux/init.h> + #include <linux/module.h> + #include <linux/kernel.h> + #include <linux/kthread.h> + #include <linux/sched.h> + #include <linux/signal.h> + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + #include <linux/sched/signal.h> + #endif + + struct task_struct *canaryTask; + + static int kcanary(void *nothing) + { + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout_interruptible(msecs_to_jiffies(100)); + if (signal_pending(current)) break; + } + return 0; + } + + static int kcanaryInit(void) + { + kthread_run(&kcanary, NULL, "kcanary"); + return 0; + } + + static void kcanaryExit(void) + { + kthread_stop(canaryTask); + } + + module_init(kcanaryInit); + module_exit(kcanaryExit); + ''; + + in lib.singleton kcanary; + + boot.initrd.kernelModules = [ "kcanary" ]; + + boot.initrd.extraUtilsCommands = let + compile = name: source: pkgs.runCommandCC name { inherit source; } '' + mkdir -p "$out/bin" + echo "$source" | gcc -Wall -o "$out/bin/$name" -xc - + ''; + + daemonize = name: source: compile name '' + #include <stdio.h> + #include <unistd.h> + + void runSource(void) { + ${source} + } + + int main(void) { + if (fork() > 0) return 0; + setsid(); + runSource(); + return 1; + } + ''; + + mkCmdlineCanary = { name, cmdline ? "", source ? "" }: (daemonize name '' + char *argv[] = {"${cmdline}", NULL}; + execvp("${name}-child", argv); + '') // { + child = compile "${name}-child" '' + #include <stdio.h> + #include <unistd.h> + + int main(void) { + ${source} + while (1) sleep(1); + return 1; + } + ''; + }; + + copyCanaries = with lib; concatMapStrings (canary: '' + ${optionalString (canary ? child) '' + copy_bin_and_libs "${canary.child}/bin/${canary.child.name}" + ''} + copy_bin_and_libs "${canary}/bin/${canary.name}" + ''); + + in copyCanaries [ + # Simple canary process which just sleeps forever and should be killed by + # stage 2. + (daemonize "canary1" "while (1) sleep(1);") + + # We want this canary process to try mimicking a kthread using a cmdline + # with a zero length so we can make sure that the process is properly + # killed in stage 1. + (mkCmdlineCanary { + name = "canary2"; + source = '' + FILE *f; + f = fopen("/run/canary2.pid", "w"); + fprintf(f, "%d\n", getpid()); + fclose(f); + ''; + }) + + # This canary process mimicks a storage daemon, which we do NOT want to be + # killed before going into stage 2. For more on root storage daemons, see: + # https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/ + (mkCmdlineCanary { + name = "canary3"; + cmdline = "@canary3"; + }) + ]; + + boot.initrd.postMountCommands = '' + canary1 + canary2 + canary3 + # Make sure the pidfile of canary 2 is created so that we still can get + # its former pid after the killing spree starts next within stage 1. + while [ ! -s /run/canary2.pid ]; do sleep 0.1; done + ''; + }; + + testScript = '' + $machine->waitForUnit("multi-user.target"); + $machine->succeed('test -s /run/canary2.pid'); + $machine->fail('pgrep -a canary1'); + $machine->fail('kill -0 $(< /run/canary2.pid)'); + $machine->succeed('pgrep -a -f \'^@canary3$\'''); + $machine->succeed('pgrep -a -f \'^kcanary$\'''); + ''; + + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ aszlig ]; +}) |