| 
									
										
										
										
											2019-11-05 13:48:02 +01:00
										 |  |  | import ./make-test-python.nix ({ pkgs, ... }: { | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  |   name = "boot-stage1"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   machine = { config, pkgs, lib, ... }: { | 
					
						
							|  |  |  |     boot.extraModulePackages = let | 
					
						
							| 
									
										
										
										
											2016-10-09 14:12:45 +02:00
										 |  |  |       compileKernelModule = name: source: pkgs.runCommandCC name rec { | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  |         inherit source; | 
					
						
							|  |  |  |         kdev = config.boot.kernelPackages.kernel.dev; | 
					
						
							|  |  |  |         kver = config.boot.kernelPackages.kernel.modDirVersion; | 
					
						
							|  |  |  |         ksrc = "${kdev}/lib/modules/${kver}/build"; | 
					
						
							| 
									
										
										
										
											2016-08-13 09:45:48 +00:00
										 |  |  |         hardeningDisable = [ "pic" ]; | 
					
						
							| 
									
										
										
										
											2018-02-22 04:04:49 +01:00
										 |  |  |         nativeBuildInputs = kdev.moduleBuildDependencies; | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  |       } ''
 | 
					
						
							|  |  |  |         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" ''
 | 
					
						
							| 
									
										
										
										
											2017-11-14 04:30:08 +01:00
										 |  |  |         #include <linux/version.h> | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  |         #include <linux/init.h> | 
					
						
							|  |  |  |         #include <linux/module.h> | 
					
						
							|  |  |  |         #include <linux/kernel.h> | 
					
						
							|  |  |  |         #include <linux/kthread.h> | 
					
						
							|  |  |  |         #include <linux/sched.h> | 
					
						
							| 
									
										
										
										
											2017-11-14 04:30:08 +01:00
										 |  |  |         #include <linux/signal.h> | 
					
						
							|  |  |  |         #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) | 
					
						
							|  |  |  |         #include <linux/sched/signal.h> | 
					
						
							|  |  |  |         #endif | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         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 | 
					
						
							| 
									
										
										
										
											2016-09-27 16:36:16 +02:00
										 |  |  |       compile = name: source: pkgs.runCommandCC name { inherit source; } ''
 | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  |         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 = ''
 | 
					
						
							| 
									
										
										
										
											2019-11-05 13:48:02 +01:00
										 |  |  |     machine.wait_for_unit("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$"') | 
					
						
							| 
									
										
										
										
											2016-05-06 16:06:22 +02:00
										 |  |  |   '';
 | 
					
						
							| 
									
										
										
										
											2016-05-06 21:32:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   meta.maintainers = with pkgs.stdenv.lib.maintainers; [ aszlig ]; | 
					
						
							|  |  |  | }) |