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 2024 Oxide Computer Company 14 */ 15 16 /* 17 * This program serves as a target for the pr_inject program to run. 18 * pr_target_hook() is used as a place to set a breakpoint and the before and 19 * after point for the test. pr_inject will modify a bunch of our state 20 * (currently file descriptors) and then we will validate it after we run again. 21 */ 22 23 #include <stdlib.h> 24 #include <err.h> 25 #include <libproc.h> 26 #include <sys/stat.h> 27 #include <fcntl.h> 28 #include <stdbool.h> 29 #include <errno.h> 30 #include <string.h> 31 #include <sys/debug.h> 32 33 #include "pr_target.h" 34 35 /* 36 * This is used as a place to set a breakpoint for the target to find us. It is 37 * a weak symbol to help avoid compiler optimization. 38 */ 39 #pragma weak pr_target_hook 40 void 41 pr_target_hook(void) 42 { 43 } 44 45 static bool 46 pr_target_check_fd(const char *desc, int fd, int fflag, int fdflags, 47 const struct stat *st) 48 { 49 int val; 50 bool ret = true; 51 struct stat targ; 52 53 val = fcntl(fd, F_GETFL, NULL); 54 if (val < 0) { 55 warn("TEST FAILED: %s F_GETFL failed", desc); 56 ret = false; 57 } else if ((val & O_ACCMODE) != fflag) { 58 warnx("TEST FAILED: %s: open flags mismatch: found 0x%x, " 59 "expected 0x%x", desc, val & O_ACCMODE, fflag); 60 ret = false; 61 } else { 62 (void) printf("TEST PASSED: %s: injected open flags match " 63 "expected value\n", desc); 64 } 65 66 val = fcntl(fd, F_GETFD, NULL); 67 if (val < 0) { 68 warn("TEST FAILED: %s F_GETFD failed", desc); 69 ret = false; 70 } else if (val != fdflags) { 71 warnx("TEST FAILED: %s: fd flags mismatch: found 0x%x, " 72 "expected 0x%x", desc, val, fdflags); 73 ret = false; 74 } else { 75 (void) printf("TEST PASSED: %s: injected fd flags match " 76 "expected value\n", desc); 77 } 78 79 if (fstat(fd, &targ) != 0) { 80 warn("TEST FAILED: %s: failed to stat fd", desc); 81 ret = false; 82 } else if (st->st_ino != targ.st_ino || st->st_dev != targ.st_dev || 83 st->st_rdev != targ.st_rdev) { 84 warnx("TEST FAILED: %s: fstat data does not match " 85 "expectations", desc); 86 ret = false; 87 } else { 88 (void) printf("TEST PASSED: %s: fstat information matched\n", 89 desc); 90 } 91 92 return (ret); 93 } 94 95 int 96 main(void) 97 { 98 int ret = EXIT_SUCCESS; 99 struct stat nstat, zstat; 100 int fd; 101 102 (void) closefrom(STDERR_FILENO + 1); 103 104 fd = open("/dev/null", PRT_NULL_OFLAG); 105 if (fd < 0) { 106 errx(EXIT_FAILURE, "TEST FAILED: failed to open /dev/null"); 107 } 108 VERIFY3S(fd, ==, PRT_NULL_FD); 109 110 if (fstat(fd, &nstat) != 0) { 111 err(EXIT_FAILURE, "failed to fstat /dev/null"); 112 } 113 114 fd = open("/dev/zero", O_RDONLY); 115 if (fd < 0) { 116 errx(EXIT_FAILURE, "TEST FAILED: failed to open /dev/zero"); 117 } 118 VERIFY3S(fd, ==, PRT_CLOSE_FD); 119 120 if (fstat(fd, &zstat) != 0) { 121 err(EXIT_FAILURE, "failed to fstat /dev/zero"); 122 } 123 124 pr_target_hook(); 125 126 if (!pr_target_check_fd("normal open", PRT_NULL_FD, PRT_NULL_OFLAG, 127 PRT_NULL_GETFD, &nstat)) { 128 ret = EXIT_FAILURE; 129 } 130 131 if (!pr_target_check_fd("injected open", PRT_ZERO_FD, PRT_ZERO_OFLAG, 132 PRT_ZERO_GETFD, &zstat)) { 133 ret = EXIT_FAILURE; 134 } 135 136 if (!pr_target_check_fd("injected F_DUPFD", PRT_DUP_FD, PRT_DUP_OFLAG, 137 PRT_DUP_GETFD, &nstat)) { 138 ret = EXIT_FAILURE; 139 } 140 141 if (!pr_target_check_fd("injected F_DUP2FD_CLOFORK", PRT_CLOFORK_FD, 142 PRT_CLOFORK_OFLAG, PRT_CLOFORK_GETFD, &nstat)) { 143 ret = EXIT_FAILURE; 144 } 145 146 if (!pr_target_check_fd("injected F_DUP3FD", PRT_DUP3_FD, 147 PRT_DUP3_OFLAG, PRT_DUP3_GETFD, &zstat)) { 148 ret = EXIT_FAILURE; 149 } 150 151 /* 152 * The close fd we expect to have been closed already. 153 */ 154 if (fcntl(PRT_CLOSE_FD, F_GETFD, NULL) != -1) { 155 warnx("TEST FAILED: fstat on supposedly closed fd worked"); 156 ret = false; 157 } else if (errno != EBADF) { 158 warnx("TEST FAILED: expected EBADF on closed fd, but found %s", 159 strerrorname_np(errno)); 160 ret = false; 161 } else { 162 (void) printf("TEST PASSED: injected close successfully closed " 163 "fd\n"); 164 } 165 166 return (ret); 167 } 168