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