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
pr_target_hook(void)41 pr_target_hook(void)
42 {
43 }
44
45 static bool
pr_target_check_fd(const char * desc,int fd,int fflag,int fdflags,const struct stat * st)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
main(void)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