1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015 Garrett D'Amore <garrett@damore.org> 14 * Copyright 2024 Oxide Computer Company 15 */ 16 17 /* 18 * This program tests that fexecve works properly. 19 */ 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <fcntl.h> 25 #include <err.h> 26 #include <errno.h> 27 #include <unistd.h> 28 #include <note.h> 29 #include <sys/execx.h> 30 #include <sys/utsname.h> 31 #include <sys/wait.h> 32 #include "test_common.h" 33 34 int extra_debug = 0; 35 36 struct utsname un; 37 38 static void 39 forkit(char *msg, const char *expect, void (*postfn)(void)) 40 { 41 int fd; 42 FILE *f; 43 pid_t pid; 44 char *ptr = NULL, *p; 45 size_t cap = 0; 46 int wstat; 47 int rv; 48 test_t t; 49 char fname[32]; 50 51 (void) strcpy(fname, "/tmp/testXXXXXX"); 52 t = test_start(msg); 53 54 fd = mkstemp(fname); 55 if (fd < 0) { 56 test_failed(t, "mkstemp failed: %s", strerror(errno)); 57 return; 58 } 59 60 /* don't leave it in the filesystem */ 61 (void) unlink(fname); 62 63 pid = fork(); 64 switch (pid) { 65 case -1: 66 test_failed(t, "fork failed: %s", strerror(errno)); 67 return; 68 case 0: 69 if (dup2(fd, 1) < 0) { 70 test_failed(t, "dup2 failed: %s", strerror(errno)); 71 exit(9); 72 } 73 postfn(); 74 exit(0); 75 default: 76 break; 77 } 78 79 /* parent */ 80 f = fdopen(fd, "r"); 81 if (f == NULL) { 82 (void) close(fd); 83 test_failed(t, "fdopen failed: %s", strerror(errno)); 84 (void) wait(NULL); 85 return; 86 } 87 if (waitpid(pid, &wstat, 0) < 0) { 88 test_failed(t, "wait failed: %s", strerror(errno)); 89 (void) fclose(f); 90 return; 91 } 92 if (!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) { 93 test_failed(t, "child failed: %#x", wstat); 94 (void) fclose(f); 95 return; 96 } 97 (void) lseek(fd, 0, SEEK_SET); 98 if ((rv = getline(&ptr, &cap, f)) < 1) { 99 test_failed(t, "child gave no data: %d", rv); 100 (void) fclose(f); 101 return; 102 } 103 (void) fclose(f); 104 105 if ((p = strchr(ptr, '\n')) != NULL) 106 *p = '\0'; 107 if (extra_debug) 108 printf("Child output: [%s]\n", ptr); 109 if (strcmp(ptr, expect) != 0) { 110 test_failed(t, "[%s] != [%s]", ptr, expect); 111 return; 112 } 113 114 (void) free(ptr); 115 test_passed(t); 116 } 117 118 static void 119 case_badf(void) 120 { 121 int fd = -1; 122 int rv; 123 char *args[] = { "uname", NULL }; 124 char *env[] = { NULL }; 125 126 rv = fexecve(fd, args, env); 127 if (rv != -1) { 128 (void) printf("rv is not -1\n"); 129 (void) exit(0); 130 } 131 if (errno != EBADF) { 132 (void) printf("err %d(%s) != EBADF\n", errno, strerror(errno)); 133 (void) exit(0); 134 } 135 (void) printf("GOOD\n"); 136 (void) exit(0); 137 } 138 139 static void 140 case_bad_highf(void) 141 { 142 int fd = 55; 143 int rv; 144 char *args[] = { "uname", NULL }; 145 char *env[] = { NULL }; 146 147 closefrom(3); 148 rv = fexecve(fd, args, env); 149 if (rv != -1) { 150 (void) printf("rv is not -1\n"); 151 (void) exit(0); 152 } 153 if (errno != EBADF) { 154 (void) printf("err %d(%s) != EBADF\n", errno, strerror(errno)); 155 (void) exit(0); 156 } 157 (void) printf("GOOD\n"); 158 (void) exit(0); 159 } 160 161 static void 162 case_notexec(void) 163 { 164 int fd; 165 int rv; 166 char *args[] = { "uname", NULL }; 167 char *env[] = { NULL }; 168 169 fd = open("/usr/bin/uname", O_RDONLY); 170 171 rv = fexecve(fd, args, env); 172 if (rv != -1) { 173 (void) printf("rv is not -1\n"); 174 (void) exit(0); 175 } 176 (void) printf("FAILURE\n"); 177 (void) exit(0); 178 } 179 180 static void 181 case_cloexec(void) 182 { 183 int fd; 184 int rv; 185 char *args[] = { "ls", "-C", "/proc/self/fd", NULL }; 186 char *env[] = { NULL }; 187 188 /* 189 * We set things up so that this process has only stdin, stdout and 190 * stderr, then `ls` will open a file descriptor for the directory 191 * being listed. If we have more than that then the descriptor we're 192 * about to open has leaked through to the new process despite the 193 * O_CLOEXEC. 194 */ 195 closefrom(3); 196 fd = open("/usr/bin/ls", O_RDONLY | O_EXEC | O_CLOEXEC); 197 198 rv = fexecve(fd, args, env); 199 if (rv != -1) { 200 (void) printf("rv is not -1\n"); 201 (void) exit(0); 202 } 203 (void) printf("FAILURE\n"); 204 (void) exit(0); 205 } 206 207 static void 208 case_uname(void) 209 { 210 int fd; 211 char *args[] = { "uname", NULL }; 212 char *env[] = { NULL }; 213 214 fd = open("/usr/bin/uname", O_EXEC); 215 if (fd < 0) { 216 (void) printf("failed to open /usr/bin/uname: %s", 217 strerror(errno)); 218 (void) exit(0); 219 } 220 221 (void) fexecve(fd, args, env); 222 (void) printf("EXEC FAILED: %s\n", strerror(errno)); 223 (void) exit(0); 224 } 225 226 static void 227 case_uname_r(void) 228 { 229 int fd; 230 char *args[] = { "uname", "-r", NULL }; 231 char *env[] = { NULL }; 232 233 fd = open("/usr/bin/uname", O_EXEC); 234 if (fd < 0) { 235 (void) printf("failed to open /usr/bin/uname: %s", 236 strerror(errno)); 237 (void) exit(0); 238 } 239 240 (void) fexecve(fd, args, env); 241 (void) printf("EXEC FAILED: %s\n", strerror(errno)); 242 (void) exit(0); 243 } 244 245 static void 246 case_execvex_bad_flags(void) 247 { 248 int fd = -1; 249 int rv; 250 char *args[] = { "uname", NULL }; 251 char *env[] = { NULL }; 252 253 rv = execvex(fd, args, env, 0x1005); 254 if (rv != -1) { 255 (void) printf("rv is not -1\n"); 256 (void) exit(0); 257 } 258 if (errno != EINVAL) { 259 (void) printf("err %d(%s) != EINVAL\n", errno, strerror(errno)); 260 (void) exit(0); 261 } 262 (void) printf("GOOD\n"); 263 (void) exit(0); 264 } 265 266 static void 267 test_fexecve_badf(void) 268 { 269 forkit("fexecve (bad FD)", "GOOD", case_badf); 270 } 271 272 static void 273 test_fexecve_bad_highf(void) 274 { 275 forkit("fexecve (bad FD)", "GOOD", case_bad_highf); 276 } 277 278 static void 279 test_fexecve_notexec(void) 280 { 281 forkit("fexecve (not O_EXEC)", un.sysname, case_notexec); 282 } 283 284 static void 285 test_fexecve_cloexec(void) 286 { 287 forkit("fexecve (O_CLOEXEC)", "0 1 2 3", case_cloexec); 288 } 289 290 static void 291 test_fexecve_uname(void) 292 { 293 forkit("fexecve (uname)", un.sysname, case_uname); 294 } 295 296 static void 297 test_fexecve_uname_r(void) 298 { 299 forkit("fexecve (uname)", un.release, case_uname_r); 300 } 301 302 static void 303 test_execvex_bad_flags(void) 304 { 305 forkit("execvex (bad flags)", "GOOD", case_execvex_bad_flags); 306 } 307 308 int 309 main(int argc, char **argv) 310 { 311 int optc; 312 313 (void) uname(&un); 314 315 while ((optc = getopt(argc, argv, "dfD")) != EOF) { 316 switch (optc) { 317 case 'd': 318 test_set_debug(); 319 break; 320 case 'f': 321 test_set_force(); 322 break; 323 case 'D': 324 test_set_debug(); 325 extra_debug++; 326 break; 327 default: 328 (void) fprintf(stderr, "Usage: %s [-dfD]\n", argv[0]); 329 exit(1); 330 } 331 } 332 333 test_fexecve_badf(); 334 test_fexecve_bad_highf(); 335 test_fexecve_notexec(); 336 test_fexecve_cloexec(); 337 test_fexecve_uname(); 338 test_fexecve_uname_r(); 339 test_execvex_bad_flags(); 340 341 exit(0); 342 } 343