1 /*
2 * Check decoding of getsockopt and setsockopt for SOL_SOCKET level.
3 *
4 * Copyright (c) 2017-2023 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2022 Eugene Syromyatnikov <evgsyr@gmail.com>
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12 #include <stdio.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15
16 #define XLAT_MACROS_ONLY
17 # include "xlat/sock_options.h"
18 #undef XLAT_MACROS_ONLY
19
20 #ifdef INJECT_RETVAL
21 # define INJSTR " (INJECTED)"
22 #else
23 # define INJSTR ""
24 #endif
25
26 static int rc;
27 static const char *errstr;
28
29 struct intstr {
30 int val;
31 const char *str;
32 };
33
34 static int
35 get_sockopt(int fd, int name, void *val, socklen_t *len)
36 {
37 rc = getsockopt(fd, SOL_SOCKET, name, val, len);
38 errstr = sprintrc(rc);
39 return rc;
40 }
41
42 static int
43 set_sockopt(int fd, int name, void *val, socklen_t len)
44 {
45 rc = setsockopt(fd, SOL_SOCKET, name, val, len);
46 errstr = sprintrc(rc);
47 return rc;
48 }
49
50 static void
51 print_optval(int val, const struct intstr *vecs, size_t vecs_sz)
52 {
53 for (size_t k = 0; k < vecs_sz; k++) {
54 if (vecs[k].val == val) {
55 printf("[%s]", vecs[k].str);
56 return;
57 }
58 }
59
60 printf("[%d]", val);
61 }
62
63 int
64 main(void)
65 {
66 static const struct intstr int_vecs[] = {
67 { ARG_STR(0) },
68 { ARG_STR(1) },
69 { ARG_STR(1234567890) },
70 { ARG_STR(-1234567890) },
71 };
72 static const struct intstr txrehash_vecs[] = {
73 { ARG_XLAT_KNOWN(0, "SOCK_TXREHASH_DISABLED") },
74 { ARG_XLAT_KNOWN(1, "SOCK_TXREHASH_ENABLED") },
75 { ARG_XLAT_UNKNOWN(2, "SOCK_TXREHASH_???") },
76 { ARG_XLAT_UNKNOWN(254, "SOCK_TXREHASH_???") },
77 { ARG_XLAT_KNOWN(255, "SOCK_TXREHASH_DEFAULT") },
78 { ARG_XLAT_UNKNOWN(256, "SOCK_TXREHASH_???") },
79 { ARG_XLAT_UNKNOWN(511, "SOCK_TXREHASH_???") },
80 { ARG_XLAT_UNKNOWN(-1, "SOCK_TXREHASH_???") },
81 };
82 static const struct {
83 int val;
84 const char *str;
85 const struct intstr *const vecs;
86 size_t vecs_sz;
87 size_t optsz;
88 } names[] = {
89 { ARG_STR(SO_DEBUG), .optsz = sizeof(int) },
90 { ARG_STR(SO_REUSEADDR), .optsz = sizeof(int) },
91 { ARG_STR(SO_TYPE), /* TODO */ },
92 /* SO_ERROR - see so_error test */
93 { ARG_STR(SO_DONTROUTE), .optsz = sizeof(int) },
94 { ARG_STR(SO_BROADCAST), .optsz = sizeof(int) },
95 { ARG_STR(SO_SNDBUF), .optsz = sizeof(int) },
96 { ARG_STR(SO_RCVBUF), .optsz = sizeof(int) },
97 { ARG_STR(SO_KEEPALIVE), .optsz = sizeof(int) },
98 { ARG_STR(SO_OOBINLINE), .optsz = sizeof(int) },
99 { ARG_STR(SO_NO_CHECK), .optsz = sizeof(int) },
100 { ARG_STR(SO_PRIORITY), .optsz = sizeof(int) },
101 /* SO_LINGER - see so_linger test */
102 { ARG_STR(SO_BSDCOMPAT), .optsz = sizeof(int) },
103 { ARG_STR(SO_REUSEPORT), .optsz = sizeof(int) },
104 { ARG_STR(SO_PASSCRED), .optsz = sizeof(int) },
105 /* SO_PEERCRED - see so_peercred test */
106 { ARG_STR(SO_RCVLOWAT), .optsz = sizeof(int) },
107 { ARG_STR(SO_SNDLOWAT), .optsz = sizeof(int) },
108 { ARG_STR(SO_RCVTIMEO_OLD), /* TODO */ },
109 { ARG_STR(SO_SNDTIMEO_OLD), /* TODO */ },
110 { ARG_STR(SO_SECURITY_AUTHENTICATION) },
111 { ARG_STR(SO_SECURITY_ENCRYPTION_TRANSPORT) },
112 { ARG_STR(SO_SECURITY_ENCRYPTION_NETWORK) },
113 /* TODO: SO_BINDTODEVICE */
114 { ARG_STR(SO_DETACH_FILTER), .optsz = sizeof(int) },
115 { ARG_STR(SO_PEERNAME), /* TODO */ },
116 { ARG_STR(SO_TIMESTAMP_OLD), .optsz = sizeof(int) },
117 { ARG_STR(SO_ACCEPTCONN), .optsz = sizeof(int) },
118 { ARG_STR(SO_PEERSEC), /* TODO */ },
119 { ARG_STR(SO_SNDBUFFORCE), .optsz = sizeof(int) },
120 { ARG_STR(SO_RCVBUFFORCE), .optsz = sizeof(int) },
121 { ARG_STR(SO_PASSSEC), .optsz = sizeof(int) },
122 { ARG_STR(SO_TIMESTAMPNS_OLD), .optsz = sizeof(int) },
123 { ARG_STR(SO_MARK), .optsz = sizeof(int) },
124 { ARG_STR(SO_TIMESTAMPING_OLD), .optsz = sizeof(int) },
125 { ARG_STR(SO_PROTOCOL), /* TODO */ },
126 { ARG_STR(SO_DOMAIN), /* TODO */ },
127 { ARG_STR(SO_RXQ_OVFL), .optsz = sizeof(int) },
128 { ARG_STR(SO_WIFI_STATUS), .optsz = sizeof(int) },
129 { ARG_STR(SO_PEEK_OFF), .optsz = sizeof(int) },
130 { ARG_STR(SO_NOFCS), .optsz = sizeof(int) },
131 { ARG_STR(SO_LOCK_FILTER), .optsz = sizeof(int) },
132 { ARG_STR(SO_SELECT_ERR_QUEUE), .optsz = sizeof(int) },
133 { ARG_STR(SO_BUSY_POLL), .optsz = sizeof(int) },
134 { ARG_STR(SO_MAX_PACING_RATE), /* TODO */ },
135 { ARG_STR(SO_BPF_EXTENSIONS), /* TODO */ },
136 { ARG_STR(SO_INCOMING_CPU), .optsz = sizeof(int) },
137 { ARG_STR(SO_ATTACH_BPF), /* TODO */ },
138 /* SO_ATTACH_REUSEPORT_CBPF - see sock_filter-v test */
139 { ARG_STR(SO_ATTACH_REUSEPORT_EBPF), /* TODO */ },
140 { ARG_STR(SO_CNX_ADVICE), .optsz = sizeof(int) },
141 { ARG_STR(SO_MEMINFO), /* TODO */ },
142 { ARG_STR(SO_INCOMING_NAPI_ID), .optsz = sizeof(int) },
143 { ARG_STR(SO_COOKIE), /* TODO */ },
144 { ARG_STR(SO_PEERGROUPS), /* TODO */ },
145 { ARG_STR(SO_ZEROCOPY), .optsz = sizeof(int) },
146 { ARG_STR(SO_TXTIME), /* TODO */ },
147 { ARG_STR(SO_BINDTOIFINDEX), /* TODO */ },
148 { ARG_STR(SO_TIMESTAMP_NEW), .optsz = sizeof(int) },
149 { ARG_STR(SO_TIMESTAMPNS_NEW), .optsz = sizeof(int) },
150 { ARG_STR(SO_TIMESTAMPING_NEW), .optsz = sizeof(int) },
151 { ARG_STR(SO_RCVTIMEO_NEW), /* TODO */ },
152 { ARG_STR(SO_SNDTIMEO_NEW), /* TODO */ },
153 { ARG_STR(SO_DETACH_REUSEPORT_BPF), .optsz = sizeof(int) },
154 { ARG_STR(SO_PREFER_BUSY_POLL), .optsz = sizeof(int) },
155 { ARG_STR(SO_BUSY_POLL_BUDGET), .optsz = sizeof(int) },
156 { ARG_STR(SO_NETNS_COOKIE), /* TODO */ },
157 { ARG_STR(SO_BUF_LOCK), /* TODO */ },
158 { ARG_STR(SO_RESERVE_MEM), .optsz = sizeof(int) },
159 { ARG_STR(SO_TXREHASH), ARRSZ_PAIR(txrehash_vecs), sizeof(int) },
160 { ARG_STR(SO_RCVMARK), .optsz = sizeof(int) },
161 { ARG_STR(SO_PASSPIDFD), .optsz = sizeof(int) },
162 /* SO_PEERPIDFD - see so_peerpidfd test */
163 { 78, NULL },
164 { -1, NULL },
165 };
166
167 char pfx_str[256];
168 char sol_str[64];
169 TAIL_ALLOC_OBJECT_CONST_PTR(int, val);
170 TAIL_ALLOC_OBJECT_CONST_ARR(int, bigval, 2);
171 TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
172 void *const efault = val + 1;
173 int fd = socket(AF_UNIX, SOCK_RAW, 0);
174 if (fd < 0)
175 perror_msg_and_skip("socket AF_UNIX SOCK_RAW");
176
177 snprintf(sol_str, sizeof(sol_str), XLAT_FMT, XLAT_ARGS(SOL_SOCKET));
178
179 for (size_t i = 0; i < ARRAY_SIZE(names); ++i) {
180 static char so_str[64];
181 const struct intstr *const vecs = names[i].vecs ?: int_vecs;
182 size_t vecs_sz = names[i].vecs_sz ?: ARRAY_SIZE(int_vecs);
183
184 if (names[i].str) {
185 snprintf(so_str, sizeof(so_str), XLAT_FMT,
186 XLAT_SEL(names[i].val, names[i].str));
187 } else {
188 snprintf(so_str, sizeof(so_str),
189 "%#x" NRAW(" /* SO_??? */"), names[i].val);
190 }
191
192 snprintf(pfx_str, sizeof(pfx_str), "etsockopt(%d, %s, %s, ",
193 fd, sol_str, so_str);
194
195 /* getsockopt */
196
197 for (size_t j = 0; j < vecs_sz; j++) {
198 /* classic */
199 *len = sizeof(*val);
200 *val = vecs[j].val;
201 get_sockopt(fd, names[i].val, val, len);
202 printf("g%s", pfx_str);
203 if (rc < 0)
204 printf("%p", val);
205 else
206 print_optval(*val, vecs, vecs_sz);
207 printf(", [%d]) = %s" INJSTR "\n", *len, errstr);
208
209 /* optlen larger than accessible memory */
210 *len = sizeof(*val) + 1;
211 get_sockopt(fd, names[i].val, val, len);
212 printf("g%s", pfx_str);
213 if (rc < 0 || (!names[i].optsz && *len > sizeof(*val)))
214 printf("%p", val);
215 else
216 print_optval(*val, vecs, vecs_sz);
217 printf(", [%d", (int) sizeof(*val) + 1);
218 if ((int) sizeof(*val) + 1 != *len)
219 printf(" => %d", *len);
220 printf("]) = %s" INJSTR "\n", errstr);
221
222 /* optlen larger than necessary */
223 *len = sizeof(*val) + 1;
224 *bigval = vecs[j].val;
225 get_sockopt(fd, names[i].val, bigval, len);
226 printf("g%s", pfx_str);
227 if (rc < 0) {
228 printf("%p", bigval);
229 } else {
230 if (*len == sizeof(*val) || names[i].optsz)
231 print_optval(*val, vecs, vecs_sz);
232 else
233 print_quoted_memory(bigval, *len);
234 }
235 printf(", [%d", (int) sizeof(*val) + 1);
236 if ((int) sizeof(*val) + 1 != *len)
237 printf(" => %d", *len);
238 printf("]) = %s" INJSTR "\n", errstr);
239 }
240
241 /* zero optlen - print returned optlen */
242 *len = 0;
243 #ifdef INJECT_RETVAL
244 *val = vecs[0].val;
245 #endif
246 get_sockopt(fd, names[i].val, NULL, len);
247 printf("g%sNULL, [0", pfx_str);
248 if (*len)
249 printf(" => %d", *len);
250 printf("]) = %s" INJSTR "\n", errstr);
251
252 /* optlen shorter than necessary */
253 *len = sizeof(*val) - 1;
254 get_sockopt(fd, names[i].val, val, len);
255 printf("g%s", pfx_str);
256 if (rc < 0)
257 printf("%p", val);
258 else if (names[i].optsz)
259 print_quoted_hex(val, sizeof(*val) - 1);
260 else
261 print_quoted_memory(val, sizeof(*val) - 1);
262 printf(", [%d", (int) sizeof(*val) - 1);
263 if ((int) sizeof(*val) - 1 != *len)
264 printf(" => %d", *len);
265 printf("]) = %s" INJSTR "\n", errstr);
266
267 /* optval EFAULT - print address */
268 *len = sizeof(*val);
269 get_sockopt(fd, names[i].val, efault, len);
270 printf("g%s%p, [%d]) = %s" INJSTR "\n",
271 pfx_str, efault, *len, errstr);
272
273 /* optlen EFAULT - print address */
274 get_sockopt(fd, names[i].val, val, len + 1);
275 printf("g%s%p, %p) = %s" INJSTR "\n",
276 pfx_str, val, len + 1, errstr);
277
278
279 /* setsockopt */
280
281 for (size_t j = 0; j < vecs_sz; j++) {
282 /* classic */
283 *val = vecs[j].val;
284 set_sockopt(fd, names[i].val, val, sizeof(*val));
285 printf("s%s[%s], %d) = %s" INJSTR "\n",
286 pfx_str, vecs[j].str, (int) sizeof(*val), errstr);
287
288 /* optlen larger than necessary */
289 set_sockopt(fd, names[i].val, val, sizeof(*val) + 1);
290 printf("s%s", pfx_str);
291 if (names[i].optsz && names[i].optsz <= sizeof(*val))
292 printf("[%s]", vecs[j].str);
293 else
294 printf("%p", val);
295 printf(", %d) = %s" INJSTR "\n",
296 (int) sizeof(*val) + 1, errstr);
297 }
298
299 /* optlen < 0 - print address */
300 *val = vecs[0].val;
301 set_sockopt(fd, names[i].val, val, -1U);
302 printf("s%s%p, -1) = %s" INJSTR "\n", pfx_str, val, errstr);
303
304 /* optlen smaller than necessary */
305 set_sockopt(fd, names[i].val, val, sizeof(*val) - 1);
306 printf("s%s", pfx_str);
307 if (names[i].optsz)
308 printf("%p", val);
309 else
310 print_quoted_memory(val, sizeof(*val) - 1);
311 printf(", %d) = %s" INJSTR "\n", (int) sizeof(*val) - 1, errstr);
312
313 /* optval EFAULT - print address */
314 set_sockopt(fd, names[i].val, efault, sizeof(*val));
315 printf("s%s%p, %d) = %s" INJSTR "\n",
316 pfx_str, efault, (int) sizeof(*val), errstr);
317 }
318
319 puts("+++ exited with 0 +++");
320 return 0;
321 }