xref: /illumos-gate/usr/src/test/libproc-tests/tests/syscall/pr_target.c (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
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