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