0001
0002
0003
0004
0005 #define _GNU_SOURCE
0006 #include <errno.h>
0007 #include <fcntl.h>
0008 #include <sched.h>
0009 #include <stdarg.h>
0010 #include <stdbool.h>
0011 #include <stdio.h>
0012 #include <stdlib.h>
0013 #include <string.h>
0014 #include <sys/mount.h>
0015 #include <sys/stat.h>
0016 #include <sys/types.h>
0017 #include <sys/wait.h>
0018 #include <unistd.h>
0019
0020 #ifndef CLONE_NEWNS
0021 # define CLONE_NEWNS 0x00020000
0022 #endif
0023
0024 static char *fw_path = NULL;
0025
0026 static void die(char *fmt, ...)
0027 {
0028 va_list ap;
0029
0030 va_start(ap, fmt);
0031 vfprintf(stderr, fmt, ap);
0032 va_end(ap);
0033 if (fw_path)
0034 unlink(fw_path);
0035 umount("/lib/firmware");
0036 exit(EXIT_FAILURE);
0037 }
0038
0039 static void trigger_fw(const char *fw_name, const char *sys_path)
0040 {
0041 int fd;
0042
0043 fd = open(sys_path, O_WRONLY);
0044 if (fd < 0)
0045 die("open failed: %s\n",
0046 strerror(errno));
0047 if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name))
0048 exit(EXIT_FAILURE);
0049 close(fd);
0050 }
0051
0052 static void setup_fw(const char *fw_path)
0053 {
0054 int fd;
0055 const char fw[] = "ABCD0123";
0056
0057 fd = open(fw_path, O_WRONLY | O_CREAT, 0600);
0058 if (fd < 0)
0059 die("open failed: %s\n",
0060 strerror(errno));
0061 if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1)
0062 die("write failed: %s\n",
0063 strerror(errno));
0064 close(fd);
0065 }
0066
0067 static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns)
0068 {
0069 pid_t child;
0070
0071 if (block_fw_in_parent_ns)
0072 if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1)
0073 die("blocking firmware in parent ns failed\n");
0074
0075 child = fork();
0076 if (child == -1) {
0077 die("fork failed: %s\n",
0078 strerror(errno));
0079 }
0080 if (child != 0) {
0081 pid_t pid;
0082 int status;
0083
0084 pid = waitpid(child, &status, 0);
0085 if (pid == -1) {
0086 die("waitpid failed: %s\n",
0087 strerror(errno));
0088 }
0089 if (pid != child) {
0090 die("waited for %d got %d\n",
0091 child, pid);
0092 }
0093 if (!WIFEXITED(status)) {
0094 die("child did not terminate cleanly\n");
0095 }
0096 if (block_fw_in_parent_ns)
0097 umount("/lib/firmware");
0098 return WEXITSTATUS(status) == EXIT_SUCCESS;
0099 }
0100
0101 if (unshare(CLONE_NEWNS) != 0) {
0102 die("unshare(CLONE_NEWNS) failed: %s\n",
0103 strerror(errno));
0104 }
0105 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1)
0106 die("remount root in child ns failed\n");
0107
0108 if (!block_fw_in_parent_ns) {
0109 if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1)
0110 die("blocking firmware in child ns failed\n");
0111 } else
0112 umount("/lib/firmware");
0113
0114 trigger_fw(fw_name, sys_path);
0115
0116 exit(EXIT_SUCCESS);
0117 }
0118
0119 int main(int argc, char **argv)
0120 {
0121 const char *fw_name = "test-firmware.bin";
0122 char *sys_path;
0123 if (argc != 2)
0124 die("usage: %s sys_path\n", argv[0]);
0125
0126
0127
0128 if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1)
0129 die("mounting tmpfs to /lib/firmware failed\n");
0130
0131 sys_path = argv[1];
0132 if (asprintf(&fw_path, "/lib/firmware/%s", fw_name) < 0)
0133 die("error: failed to build full fw_path\n");
0134
0135 setup_fw(fw_path);
0136
0137 setvbuf(stdout, NULL, _IONBF, 0);
0138
0139 printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n");
0140 if (!test_fw_in_ns(fw_name, sys_path, false))
0141 die("error: failed to access firmware\n");
0142
0143
0144 printf("Testing with firmware in child namespace\n");
0145 if (test_fw_in_ns(fw_name, sys_path, true))
0146 die("error: firmware access did not fail\n");
0147
0148 unlink(fw_path);
0149 free(fw_path);
0150 umount("/lib/firmware");
0151 exit(EXIT_SUCCESS);
0152 }