1 // SPDX-License-Identifier: GPL-2.0 2 /* Test triggering of loading of firmware from different mount 3 * namespaces. Expect firmware to be always loaded from the mount 4 * namespace of PID 1. */ 5 #define _GNU_SOURCE 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <sched.h> 9 #include <stdarg.h> 10 #include <stdbool.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/mount.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 #include <unistd.h> 19 20 #ifndef CLONE_NEWNS 21 # define CLONE_NEWNS 0x00020000 22 #endif 23 24 static char *fw_path = NULL; 25 26 static void die(char *fmt, ...) 27 { 28 va_list ap; 29 30 va_start(ap, fmt); 31 vfprintf(stderr, fmt, ap); 32 va_end(ap); 33 if (fw_path) 34 unlink(fw_path); 35 umount("/lib/firmware"); 36 exit(EXIT_FAILURE); 37 } 38 39 static void trigger_fw(const char *fw_name, const char *sys_path) 40 { 41 int fd; 42 43 fd = open(sys_path, O_WRONLY); 44 if (fd < 0) 45 die("open failed: %s\n", 46 strerror(errno)); 47 if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name)) 48 exit(EXIT_FAILURE); 49 close(fd); 50 } 51 52 static void setup_fw(const char *fw_path) 53 { 54 int fd; 55 const char fw[] = "ABCD0123"; 56 57 fd = open(fw_path, O_WRONLY | O_CREAT, 0600); 58 if (fd < 0) 59 die("open failed: %s\n", 60 strerror(errno)); 61 if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1) 62 die("write failed: %s\n", 63 strerror(errno)); 64 close(fd); 65 } 66 67 static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns) 68 { 69 pid_t child; 70 71 if (block_fw_in_parent_ns) 72 if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) 73 die("blocking firmware in parent ns failed\n"); 74 75 child = fork(); 76 if (child == -1) { 77 die("fork failed: %s\n", 78 strerror(errno)); 79 } 80 if (child != 0) { /* parent */ 81 pid_t pid; 82 int status; 83 84 pid = waitpid(child, &status, 0); 85 if (pid == -1) { 86 die("waitpid failed: %s\n", 87 strerror(errno)); 88 } 89 if (pid != child) { 90 die("waited for %d got %d\n", 91 child, pid); 92 } 93 if (!WIFEXITED(status)) { 94 die("child did not terminate cleanly\n"); 95 } 96 if (block_fw_in_parent_ns) 97 umount("/lib/firmware"); 98 return WEXITSTATUS(status) == EXIT_SUCCESS; 99 } 100 101 if (unshare(CLONE_NEWNS) != 0) { 102 die("unshare(CLONE_NEWNS) failed: %s\n", 103 strerror(errno)); 104 } 105 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1) 106 die("remount root in child ns failed\n"); 107 108 if (!block_fw_in_parent_ns) { 109 if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) 110 die("blocking firmware in child ns failed\n"); 111 } else 112 umount("/lib/firmware"); 113 114 trigger_fw(fw_name, sys_path); 115 116 exit(EXIT_SUCCESS); 117 } 118 119 int main(int argc, char **argv) 120 { 121 const char *fw_name = "test-firmware.bin"; 122 char *sys_path; 123 if (argc != 2) 124 die("usage: %s sys_path\n", argv[0]); 125 126 /* Mount tmpfs to /lib/firmware so we don't have to assume 127 that it is writable for us.*/ 128 if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1) 129 die("mounting tmpfs to /lib/firmware failed\n"); 130 131 sys_path = argv[1]; 132 asprintf(&fw_path, "/lib/firmware/%s", fw_name); 133 134 setup_fw(fw_path); 135 136 setvbuf(stdout, NULL, _IONBF, 0); 137 /* Positive case: firmware in PID1 mount namespace */ 138 printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n"); 139 if (!test_fw_in_ns(fw_name, sys_path, false)) 140 die("error: failed to access firmware\n"); 141 142 /* Negative case: firmware in child mount namespace, expected to fail */ 143 printf("Testing with firmware in child namespace\n"); 144 if (test_fw_in_ns(fw_name, sys_path, true)) 145 die("error: firmware access did not fail\n"); 146 147 unlink(fw_path); 148 free(fw_path); 149 umount("/lib/firmware"); 150 exit(EXIT_SUCCESS); 151 } 152