xref: /illumos-gate/usr/src/test/os-tests/tests/oclo/oclo_errors.c (revision 0250c53ad267726f2438e3c6556199a0bbf588a2)
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  * Verify that unsupported flags will properly generate errors across the
18  * functions that we know perform strict error checking. This includes:
19  *
20  *  o fcntl(..., F_DUP3FD, ...)
21  *  o dup3()
22  *  o pipe2()
23  *  o socket()
24  *  o accept4()
25  */
26 
27 #include <stdlib.h>
28 #include <err.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <sys/stdbool.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <sys/socket.h>
37 
38 static bool
oclo_check(const char * desc,const char * act,int ret,int e)39 oclo_check(const char *desc, const char *act, int ret, int e)
40 {
41 	if (ret >= 0) {
42 		warnx("TEST FAILED: %s: fd was %s!", desc, act);
43 		return (false);
44 	} else if (errno != EINVAL) {
45 		int e = errno;
46 		warnx("TEST FAILED: %s: failed with %s, expected "
47 		    "EINVAL", desc, strerrorname_np(e));
48 		return (false);
49 	}
50 
51 	(void) printf("TEST PASSED: %s: correctly failed with EINVAL\n",
52 	    desc);
53 	return (true);
54 }
55 
56 static bool
oclo_dup3(const char * desc,int flags)57 oclo_dup3(const char *desc, int flags)
58 {
59 	int fd = dup3(STDERR_FILENO, 23, flags);
60 	return (oclo_check(desc, "duplicated", fd, errno));
61 }
62 
63 static bool
oclo_dup3fd(const char * desc,int flags)64 oclo_dup3fd(const char *desc, int flags)
65 {
66 	int fd = fcntl(STDERR_FILENO, F_DUP3FD, 23, flags);
67 	return (oclo_check(desc, "duplicated", fd, errno));
68 }
69 
70 
71 static bool
oclo_pipe2(const char * desc,int flags)72 oclo_pipe2(const char *desc, int flags)
73 {
74 	int fds[2], ret;
75 
76 	ret = pipe2(fds, flags);
77 	return (oclo_check(desc, "piped", ret, errno));
78 }
79 
80 static bool
oclo_socket(const char * desc,int type)81 oclo_socket(const char *desc, int type)
82 {
83 	int fd = socket(PF_UNIX, SOCK_STREAM | type, 0);
84 	return (oclo_check(desc, "created", fd, errno));
85 }
86 
87 static bool
oclo_accept(const char * desc,int flags)88 oclo_accept(const char *desc, int flags)
89 {
90 	int sock, fd, e;
91 	struct sockaddr_in in;
92 
93 	sock = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
94 	if (sock < 0) {
95 		warn("TEST FAILED: %s: failed to create listen socket", desc);
96 		return (false);
97 	}
98 
99 	(void) memset(&in, 0, sizeof (in));
100 	in.sin_family = AF_INET;
101 	in.sin_port = 0;
102 	in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
103 
104 	if (bind(sock, (struct sockaddr *)&in, sizeof (in)) != 0) {
105 		warn("TEST FAILED: %s: failed to bind socket", desc);
106 		(void) close(sock);
107 		return (false);
108 	}
109 
110 	if (listen(sock, 5) < 0) {
111 		warn("TEST FAILED: %s: failed to listen on socket", desc);
112 		(void) close(sock);
113 		return (false);
114 	}
115 
116 
117 	fd = accept4(sock, NULL, NULL, flags);
118 	e = errno;
119 	(void) close(sock);
120 	return (oclo_check(desc, "accepted", fd, e));
121 }
122 
123 int
main(void)124 main(void)
125 {
126 	int ret = EXIT_SUCCESS;
127 
128 	closefrom(STDERR_FILENO + 1);
129 
130 	if (!oclo_dup3("dup3(): O_RDWR", O_RDWR)) {
131 		ret = EXIT_FAILURE;
132 	}
133 
134 	if (!oclo_dup3("dup3(): O_NONBLOCK|O_CLOXEC", O_NONBLOCK | O_CLOEXEC)) {
135 		ret = EXIT_FAILURE;
136 	}
137 
138 	if (!oclo_dup3("dup3(): O_CLOFORK|O_WRONLY", O_CLOFORK | O_WRONLY)) {
139 		ret = EXIT_FAILURE;
140 	}
141 
142 	if (!oclo_dup3fd("fcntl(FDUP3FD): 0x7777", 0x7777)) {
143 		ret = EXIT_FAILURE;
144 	}
145 
146 	if (!oclo_dup3fd("fcntl(FDUP3FD): FD_CLOEXEC|FD_CLOFORK + 1",
147 	    (FD_CLOEXEC | FD_CLOFORK) + 1)) {
148 		ret = EXIT_FAILURE;
149 	}
150 
151 	if (!oclo_dup3fd("fcntl(FDUP3FD): INT_MAX", INT_MAX)) {
152 		ret = EXIT_FAILURE;
153 	}
154 
155 
156 	if (!oclo_pipe2("pipe2(): O_RDWR", O_RDWR)) {
157 		ret = EXIT_FAILURE;
158 	}
159 
160 	if (!oclo_pipe2("pipe2(): O_SYNC|O_CLOXEC", O_SYNC | O_CLOEXEC)) {
161 		ret = EXIT_FAILURE;
162 	}
163 
164 	if (!oclo_pipe2("pipe2(): O_CLOFORK|O_WRONLY", O_CLOFORK | O_WRONLY)) {
165 		ret = EXIT_FAILURE;
166 	}
167 
168 	if (!oclo_pipe2("pipe2(): INT32_MAX", INT32_MAX)) {
169 		ret = EXIT_FAILURE;
170 	}
171 
172 	if (!oclo_socket("socket(): INT32_MAX", INT32_MAX)) {
173 		ret = EXIT_FAILURE;
174 	}
175 
176 	if (!oclo_socket("socket(): 3 << 25", 3 << 25)) {
177 		ret = EXIT_FAILURE;
178 	}
179 
180 	if (!oclo_accept("accept4(): INT32_MAX", INT32_MAX)) {
181 		ret = EXIT_FAILURE;
182 	}
183 
184 	if (!oclo_accept("accept4(): 3 << 25", 3 << 25)) {
185 		ret = EXIT_FAILURE;
186 	}
187 
188 	if (ret == EXIT_SUCCESS) {
189 		(void) printf("All tests completed successfully\n");
190 	}
191 
192 	return (ret);
193 }
194