1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* Helpers shared by the binfmt_misc selftests. */ 3 #ifndef __SELFTESTS_EXEC_BINFMT_MISC_COMMON_H 4 #define __SELFTESTS_EXEC_BINFMT_MISC_COMMON_H 5 6 #include <elf.h> 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <libgen.h> 10 #include <limits.h> 11 #include <link.h> 12 #include <stdbool.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <sys/mount.h> 17 #include <sys/types.h> 18 #include <sys/wait.h> 19 #include <unistd.h> 20 21 #define BINFMT_DIR "/proc/sys/fs/binfmt_misc" 22 #define BINFMT_REG BINFMT_DIR "/register" 23 24 /* comm holds 15 usable chars; a read of /proc/self/comm appends a newline. */ 25 #define TASK_COMM_LEN 16 26 27 /* The canonical payload argv: run_payload() passes it, the payloads assert it. */ 28 #define PAYLOAD_ARGV0 "payload-argv0" 29 #define PAYLOAD_ARG1 "argone" 30 #define PAYLOAD_ARG2 "argtwo" 31 32 /* Marker the loader tests poke into the payload's e_ident padding. */ 33 #define LOADER_MARKER "LDRTST" 34 35 /* Exit status run_payload() reports when the exec was refused as unhandled. */ 36 #define RUN_ENOEXEC 42 37 38 static inline int copy_file(const char *src, const char *dst) 39 { 40 char buf[4096]; 41 int in, out; 42 ssize_t n; 43 44 in = open(src, O_RDONLY); 45 if (in < 0) 46 return -1; 47 /* The tests share /tmp, so never write through a name they don't own. */ 48 unlink(dst); 49 out = open(dst, O_WRONLY | O_CREAT | O_EXCL, 0755); 50 if (out < 0) { 51 close(in); 52 return -1; 53 } 54 while ((n = read(in, buf, sizeof(buf))) > 0) { 55 if (write(out, buf, n) != n) { 56 close(in); 57 close(out); 58 return -1; 59 } 60 } 61 close(in); 62 close(out); 63 return n < 0 ? -1 : 0; 64 } 65 66 /* Write @rule to the register file, preserving the write's errno. */ 67 static inline int write_reg(const char *rule) 68 { 69 int fd, saved; 70 ssize_t n; 71 72 fd = open(BINFMT_REG, O_WRONLY); 73 if (fd < 0) 74 return -1; 75 n = write(fd, rule, strlen(rule)); 76 saved = errno; 77 close(fd); 78 errno = saved; 79 return n < 0 ? -1 : 0; 80 } 81 82 static inline void unregister(const char *name) 83 { 84 char path[PATH_MAX]; 85 int fd; 86 87 snprintf(path, sizeof(path), BINFMT_DIR "/%s", name); 88 fd = open(path, O_WRONLY); 89 if (fd >= 0) { 90 if (write(fd, "-1", 2) < 0) 91 ; /* best effort */ 92 close(fd); 93 } 94 } 95 96 /* Mount binfmt_misc unless it already is, and report whether it is usable. */ 97 static inline bool binfmt_misc_available(void) 98 { 99 if (access(BINFMT_REG, F_OK) < 0) 100 mount("binfmt_misc", BINFMT_DIR, "binfmt_misc", 0, NULL); 101 return access(BINFMT_REG, F_OK) == 0; 102 } 103 104 /* Absolute path of @name in the directory this test was built into. */ 105 static inline int artifact_path(char *out, size_t sz, const char *name) 106 { 107 char exe[PATH_MAX]; 108 ssize_t n; 109 110 n = readlink("/proc/self/exe", exe, sizeof(exe) - 1); 111 if (n < 0) 112 return -1; 113 exe[n] = '\0'; 114 if ((size_t)snprintf(out, sz, "%s/%s", dirname(exe), name) >= sz) 115 return -1; 116 return 0; 117 } 118 119 /* Probe kernel support for a registration flag with a throwaway entry. */ 120 static inline bool binfmt_flag_supported(char flag) 121 { 122 char rule[64]; 123 124 snprintf(rule, sizeof(rule), ":bm_flag_probe:E::bmprobe::/bin/true:%c", 125 flag); 126 if (write_reg(rule)) 127 return false; 128 unregister("bm_flag_probe"); 129 return true; 130 } 131 132 /* 133 * Run @path with the canonical payload argv and return its exit status, or 134 * RUN_ENOEXEC when the exec itself was refused as unhandled. 135 */ 136 static inline int run_payload(const char *path) 137 { 138 int status; 139 pid_t pid; 140 141 pid = fork(); 142 if (pid == 0) { 143 execl(path, PAYLOAD_ARGV0, PAYLOAD_ARG1, PAYLOAD_ARG2, 144 (char *)NULL); 145 _exit(errno == ENOEXEC ? RUN_ENOEXEC : 126); 146 } 147 if (pid < 0 || waitpid(pid, &status, 0) != pid || !WIFEXITED(status)) 148 return -1; 149 return WEXITSTATUS(status); 150 } 151 152 /* Does the exe link name @path? */ 153 static inline bool exe_is(const char *path) 154 { 155 char exe[PATH_MAX], real[PATH_MAX]; 156 ssize_t n; 157 158 n = readlink("/proc/self/exe", exe, sizeof(exe) - 1); 159 if (n <= 0 || !realpath(path, real)) 160 return false; 161 exe[n] = '\0'; 162 return !strcmp(exe, real); 163 } 164 165 /* Is comm @name truncated to what a comm can hold? */ 166 static inline bool comm_is(const char *name) 167 { 168 char comm[TASK_COMM_LEN + 2], expect[TASK_COMM_LEN]; 169 ssize_t n; 170 int fd; 171 172 fd = open("/proc/self/comm", O_RDONLY); 173 if (fd < 0) 174 return false; 175 n = read(fd, comm, sizeof(comm) - 1); 176 close(fd); 177 if (n <= 0) 178 return false; 179 if (comm[n - 1] == '\n') 180 n--; 181 comm[n] = '\0'; 182 snprintf(expect, sizeof(expect), "%s", name); 183 return !strcmp(comm, expect); 184 } 185 186 /* Opening @path for writing has to fail with ETXTBSY. */ 187 static inline bool write_denied(const char *path) 188 { 189 int fd = open(path, O_WRONLY); 190 191 if (fd >= 0) { 192 close(fd); 193 return false; 194 } 195 return errno == ETXTBSY; 196 } 197 198 static inline int patch_file(const char *path, off_t off, const void *data, size_t len) 199 { 200 ssize_t n; 201 int fd; 202 203 fd = open(path, O_WRONLY); 204 if (fd < 0) 205 return -1; 206 n = pwrite(fd, data, len, off); 207 close(fd); 208 return n == (ssize_t)len ? 0 : -1; 209 } 210 211 /* start_code and end_code are the 26th and 27th fields of /proc/pid/stat. */ 212 static inline int stat_codes(pid_t pid, unsigned long *start_code, 213 unsigned long *end_code) 214 { 215 char buf[4096], path[64], *p; 216 ssize_t n; 217 int fd, i; 218 219 snprintf(path, sizeof(path), "/proc/%d/stat", pid); 220 fd = open(path, O_RDONLY); 221 if (fd < 0) 222 return -1; 223 n = read(fd, buf, sizeof(buf) - 1); 224 close(fd); 225 if (n <= 0) 226 return -1; 227 buf[n] = '\0'; 228 229 /* Skip "pid (comm)", then start_code is the 24th field after it. */ 230 p = strrchr(buf, ')'); 231 if (!p) 232 return -1; 233 p++; 234 for (i = 0; i < 23; i++) { 235 p = strchr(p + 1, ' '); 236 if (!p) 237 return -1; 238 } 239 if (sscanf(p, " %lu %lu", start_code, end_code) != 2) 240 return -1; 241 return 0; 242 } 243 244 /* Find the system loader through our own PT_INTERP. */ 245 static inline int find_loader(char *out, size_t sz) 246 { 247 ElfW(Ehdr) eh; 248 ElfW(Phdr) ph; 249 int fd, i, ret = -1; 250 251 fd = open("/proc/self/exe", O_RDONLY); 252 if (fd < 0) 253 return -1; 254 if (pread(fd, &eh, sizeof(eh), 0) != sizeof(eh)) 255 goto out; 256 for (i = 0; i < eh.e_phnum; i++) { 257 if (pread(fd, &ph, sizeof(ph), 258 eh.e_phoff + i * eh.e_phentsize) != sizeof(ph)) 259 goto out; 260 if (ph.p_type != PT_INTERP) 261 continue; 262 if (!ph.p_filesz || ph.p_filesz > sz) 263 goto out; 264 if (pread(fd, out, ph.p_filesz, ph.p_offset) != 265 (ssize_t)ph.p_filesz) 266 goto out; 267 out[ph.p_filesz - 1] = '\0'; 268 ret = 0; 269 break; 270 } 271 out: 272 close(fd); 273 return ret; 274 } 275 276 #endif /* __SELFTESTS_EXEC_BINFMT_MISC_COMMON_H */ 277