18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Test triggering of loading of firmware from different mount 38c2ecf20Sopenharmony_ci * namespaces. Expect firmware to be always loaded from the mount 48c2ecf20Sopenharmony_ci * namespace of PID 1. */ 58c2ecf20Sopenharmony_ci#define _GNU_SOURCE 68c2ecf20Sopenharmony_ci#include <errno.h> 78c2ecf20Sopenharmony_ci#include <fcntl.h> 88c2ecf20Sopenharmony_ci#include <sched.h> 98c2ecf20Sopenharmony_ci#include <stdarg.h> 108c2ecf20Sopenharmony_ci#include <stdbool.h> 118c2ecf20Sopenharmony_ci#include <stdio.h> 128c2ecf20Sopenharmony_ci#include <stdlib.h> 138c2ecf20Sopenharmony_ci#include <string.h> 148c2ecf20Sopenharmony_ci#include <sys/mount.h> 158c2ecf20Sopenharmony_ci#include <sys/stat.h> 168c2ecf20Sopenharmony_ci#include <sys/types.h> 178c2ecf20Sopenharmony_ci#include <sys/wait.h> 188c2ecf20Sopenharmony_ci#include <unistd.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#ifndef CLONE_NEWNS 218c2ecf20Sopenharmony_ci# define CLONE_NEWNS 0x00020000 228c2ecf20Sopenharmony_ci#endif 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic char *fw_path = NULL; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void die(char *fmt, ...) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci va_list ap; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci va_start(ap, fmt); 318c2ecf20Sopenharmony_ci vfprintf(stderr, fmt, ap); 328c2ecf20Sopenharmony_ci va_end(ap); 338c2ecf20Sopenharmony_ci if (fw_path) 348c2ecf20Sopenharmony_ci unlink(fw_path); 358c2ecf20Sopenharmony_ci umount("/lib/firmware"); 368c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void trigger_fw(const char *fw_name, const char *sys_path) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int fd; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci fd = open(sys_path, O_WRONLY); 448c2ecf20Sopenharmony_ci if (fd < 0) 458c2ecf20Sopenharmony_ci die("open failed: %s\n", 468c2ecf20Sopenharmony_ci strerror(errno)); 478c2ecf20Sopenharmony_ci if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name)) 488c2ecf20Sopenharmony_ci exit(EXIT_FAILURE); 498c2ecf20Sopenharmony_ci close(fd); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void setup_fw(const char *fw_path) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int fd; 558c2ecf20Sopenharmony_ci const char fw[] = "ABCD0123"; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci fd = open(fw_path, O_WRONLY | O_CREAT, 0600); 588c2ecf20Sopenharmony_ci if (fd < 0) 598c2ecf20Sopenharmony_ci die("open failed: %s\n", 608c2ecf20Sopenharmony_ci strerror(errno)); 618c2ecf20Sopenharmony_ci if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1) 628c2ecf20Sopenharmony_ci die("write failed: %s\n", 638c2ecf20Sopenharmony_ci strerror(errno)); 648c2ecf20Sopenharmony_ci close(fd); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci pid_t child; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (block_fw_in_parent_ns) 728c2ecf20Sopenharmony_ci if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) 738c2ecf20Sopenharmony_ci die("blocking firmware in parent ns failed\n"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci child = fork(); 768c2ecf20Sopenharmony_ci if (child == -1) { 778c2ecf20Sopenharmony_ci die("fork failed: %s\n", 788c2ecf20Sopenharmony_ci strerror(errno)); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci if (child != 0) { /* parent */ 818c2ecf20Sopenharmony_ci pid_t pid; 828c2ecf20Sopenharmony_ci int status; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci pid = waitpid(child, &status, 0); 858c2ecf20Sopenharmony_ci if (pid == -1) { 868c2ecf20Sopenharmony_ci die("waitpid failed: %s\n", 878c2ecf20Sopenharmony_ci strerror(errno)); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci if (pid != child) { 908c2ecf20Sopenharmony_ci die("waited for %d got %d\n", 918c2ecf20Sopenharmony_ci child, pid); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci if (!WIFEXITED(status)) { 948c2ecf20Sopenharmony_ci die("child did not terminate cleanly\n"); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci if (block_fw_in_parent_ns) 978c2ecf20Sopenharmony_ci umount("/lib/firmware"); 988c2ecf20Sopenharmony_ci return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (unshare(CLONE_NEWNS) != 0) { 1028c2ecf20Sopenharmony_ci die("unshare(CLONE_NEWNS) failed: %s\n", 1038c2ecf20Sopenharmony_ci strerror(errno)); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1) 1068c2ecf20Sopenharmony_ci die("remount root in child ns failed\n"); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!block_fw_in_parent_ns) { 1098c2ecf20Sopenharmony_ci if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) 1108c2ecf20Sopenharmony_ci die("blocking firmware in child ns failed\n"); 1118c2ecf20Sopenharmony_ci } else 1128c2ecf20Sopenharmony_ci umount("/lib/firmware"); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci trigger_fw(fw_name, sys_path); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci exit(EXIT_SUCCESS); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci const char *fw_name = "test-firmware.bin"; 1228c2ecf20Sopenharmony_ci char *sys_path; 1238c2ecf20Sopenharmony_ci if (argc != 2) 1248c2ecf20Sopenharmony_ci die("usage: %s sys_path\n", argv[0]); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Mount tmpfs to /lib/firmware so we don't have to assume 1278c2ecf20Sopenharmony_ci that it is writable for us.*/ 1288c2ecf20Sopenharmony_ci if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1) 1298c2ecf20Sopenharmony_ci die("mounting tmpfs to /lib/firmware failed\n"); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci sys_path = argv[1]; 1328c2ecf20Sopenharmony_ci if (asprintf(&fw_path, "/lib/firmware/%s", fw_name) < 0) 1338c2ecf20Sopenharmony_ci die("error: failed to build full fw_path\n"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci setup_fw(fw_path); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci setvbuf(stdout, NULL, _IONBF, 0); 1388c2ecf20Sopenharmony_ci /* Positive case: firmware in PID1 mount namespace */ 1398c2ecf20Sopenharmony_ci printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n"); 1408c2ecf20Sopenharmony_ci if (!test_fw_in_ns(fw_name, sys_path, false)) 1418c2ecf20Sopenharmony_ci die("error: failed to access firmware\n"); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Negative case: firmware in child mount namespace, expected to fail */ 1448c2ecf20Sopenharmony_ci printf("Testing with firmware in child namespace\n"); 1458c2ecf20Sopenharmony_ci if (test_fw_in_ns(fw_name, sys_path, true)) 1468c2ecf20Sopenharmony_ci die("error: firmware access did not fail\n"); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci unlink(fw_path); 1498c2ecf20Sopenharmony_ci free(fw_path); 1508c2ecf20Sopenharmony_ci umount("/lib/firmware"); 1518c2ecf20Sopenharmony_ci exit(EXIT_SUCCESS); 1528c2ecf20Sopenharmony_ci} 153