1 /*
2 * Check decoding of SIOC* ioctl commands.
3 *
4 * Copyright (c) 2020-2021 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11 #include <errno.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17 #include <net/if.h>
18 #include <arpa/inet.h>
19 #include <linux/sockios.h>
20
21 static const char *errstr;
22
23 static int
24 do_ioctl(int fd, kernel_ulong_t cmd, kernel_ulong_t arg)
25 {
26 int rc = ioctl(fd, cmd, arg);
27 errstr = sprintrc(rc);
28
29 return rc;
30 }
31
32 static int
33 do_ioctl_ptr(int fd, kernel_ulong_t cmd, const void *arg)
34 {
35 return do_ioctl(fd, cmd, (uintptr_t) arg);
36 }
37
38 static void
39 test_ptr(void)
40 {
41 const char *const efault = tail_alloc(1) + 1;
42
43 static const struct {
44 uint32_t cmd;
45 const char *str;
46 } cmd[] = {
47 { ARG_STR(FIOSETOWN) },
48 { ARG_STR(SIOCSPGRP) },
49 { ARG_STR(FIOGETOWN) },
50 { ARG_STR(SIOCGPGRP) },
51 { ARG_STR(SIOCATMARK) },
52 #ifdef SIOCGSTAMP_OLD
53 { ARG_STR(SIOCGSTAMP_OLD) },
54 #endif
55 #ifdef SIOCGSTAMP_NEW
56 { ARG_STR(SIOCGSTAMP_NEW) },
57 #endif
58 #ifdef SIOCGSTAMPNS_OLD
59 { ARG_STR(SIOCGSTAMPNS_OLD) },
60 #endif
61 #ifdef SIOCGSTAMPNS_NEW
62 { ARG_STR(SIOCGSTAMPNS_NEW) },
63 #endif
64 { ARG_STR(SIOCADDRT) },
65 { ARG_STR(SIOCDELRT) },
66 { ARG_STR(SIOCRTMSG) },
67 { ARG_STR(SIOCGIFNAME) },
68 { ARG_STR(SIOCSIFLINK) },
69 { ARG_STR(SIOCGIFCONF) },
70 { ARG_STR(SIOCGIFFLAGS) },
71 { ARG_STR(SIOCSIFFLAGS) },
72 { ARG_STR(SIOCGIFADDR) },
73 { ARG_STR(SIOCSIFADDR) },
74 { ARG_STR(SIOCGIFDSTADDR) },
75 { ARG_STR(SIOCSIFDSTADDR) },
76 { ARG_STR(SIOCGIFBRDADDR) },
77 { ARG_STR(SIOCSIFBRDADDR) },
78 { ARG_STR(SIOCGIFNETMASK) },
79 { ARG_STR(SIOCSIFNETMASK) },
80 { ARG_STR(SIOCGIFMETRIC) },
81 { ARG_STR(SIOCSIFMETRIC) },
82 { ARG_STR(SIOCGIFMEM) },
83 { ARG_STR(SIOCSIFMEM) },
84 { ARG_STR(SIOCGIFMTU) },
85 { ARG_STR(SIOCSIFMTU) },
86 { ARG_STR(SIOCSIFNAME) },
87 { ARG_STR(SIOCSIFHWADDR) },
88 { ARG_STR(SIOCGIFENCAP) },
89 { ARG_STR(SIOCSIFENCAP) },
90 { ARG_STR(SIOCGIFHWADDR) },
91 { ARG_STR(SIOCGIFSLAVE) },
92 { ARG_STR(SIOCSIFSLAVE) },
93 { ARG_STR(SIOCADDMULTI) },
94 { ARG_STR(SIOCDELMULTI) },
95 { ARG_STR(SIOCGIFINDEX) },
96 { ARG_STR(SIOCSIFPFLAGS) },
97 { ARG_STR(SIOCGIFPFLAGS) },
98 { ARG_STR(SIOCDIFADDR) },
99 { ARG_STR(SIOCSIFHWBROADCAST) },
100 { ARG_STR(SIOCGIFCOUNT) },
101 { ARG_STR(SIOCGIFBR) },
102 { ARG_STR(SIOCSIFBR) },
103 { ARG_STR(SIOCGIFTXQLEN) },
104 { ARG_STR(SIOCSIFTXQLEN) },
105 { ARG_STR(SIOCETHTOOL) },
106 { ARG_STR(SIOCGMIIPHY) },
107 { ARG_STR(SIOCGMIIREG) },
108 { ARG_STR(SIOCSMIIREG) },
109 { ARG_STR(SIOCWANDEV) },
110 #ifdef SIOCOUTQNSD
111 { ARG_STR(SIOCOUTQNSD) },
112 #endif
113 #ifdef SIOCGSKNS
114 { ARG_STR(SIOCGSKNS) },
115 #endif
116 { ARG_STR(SIOCDARP) },
117 { ARG_STR(SIOCGARP) },
118 { ARG_STR(SIOCSARP) },
119 { ARG_STR(SIOCDRARP) },
120 { ARG_STR(SIOCGRARP) },
121 { ARG_STR(SIOCSRARP) },
122 { ARG_STR(SIOCGIFMAP) },
123 { ARG_STR(SIOCSIFMAP) },
124 { ARG_STR(SIOCADDDLCI) },
125 { ARG_STR(SIOCDELDLCI) },
126 { ARG_STR(SIOCGIFVLAN) },
127 { ARG_STR(SIOCSIFVLAN) },
128 { ARG_STR(SIOCBONDENSLAVE) },
129 { ARG_STR(SIOCBONDRELEASE) },
130 { ARG_STR(SIOCBONDSETHWADDR) },
131 { ARG_STR(SIOCBONDSLAVEINFOQUERY) },
132 { ARG_STR(SIOCBONDINFOQUERY) },
133 { ARG_STR(SIOCBONDCHANGEACTIVE) },
134 { ARG_STR(SIOCBRADDBR) },
135 { ARG_STR(SIOCBRDELBR) },
136 { ARG_STR(SIOCBRADDIF) },
137 { ARG_STR(SIOCBRDELIF) },
138 #ifdef SIOCSHWTSTAMP
139 { ARG_STR(SIOCSHWTSTAMP) },
140 #endif
141 #ifdef SIOCGHWTSTAMP
142 { ARG_STR(SIOCGHWTSTAMP) },
143 #endif
144 },
145 unknown_cmd[] = {
146 { _IO(0x89, 0xff), "_IOC(_IOC_NONE, 0x89, 0xff, 0)" }
147 };
148
149 for (size_t i = 0; i < ARRAY_SIZE(cmd); ++i) {
150 do_ioctl(-1, cmd[i].cmd, 0);
151 printf("ioctl(%d, %s, NULL) = %s\n",
152 -1, cmd[i].str, errstr);
153
154 do_ioctl_ptr(-1, cmd[i].cmd, efault);
155 printf("ioctl(%d, %s, %p) = %s\n",
156 -1, cmd[i].str, efault, errstr);
157 }
158
159 for (size_t i = 0; i < ARRAY_SIZE(unknown_cmd); ++i) {
160 do_ioctl(-1, unknown_cmd[i].cmd, 0);
161 printf("ioctl(%d, %s, 0) = %s\n",
162 -1, unknown_cmd[i].str, errstr);
163
164 do_ioctl_ptr(-1, unknown_cmd[i].cmd, efault);
165 printf("ioctl(%d, %s, %p) = %s\n",
166 -1, unknown_cmd[i].str, efault, errstr);
167 }
168 }
169
170 static void
171 test_int(const int fd)
172 {
173 TAIL_ALLOC_OBJECT_CONST_PTR(int, p_int);
174 pid_t pid = getpid();
175
176 const struct {
177 uint32_t cmd;
178 const char *str;
179 int val;
180 } cmd[] = {
181 { ARG_STR(FIOGETOWN), -1 },
182 { ARG_STR(FIOSETOWN), pid },
183 { ARG_STR(SIOCATMARK), -1 },
184 { ARG_STR(SIOCGPGRP), -1 },
185 { ARG_STR(SIOCSPGRP), pid },
186 { ARG_STR(SIOCSIFENCAP), -1 },
187 };
188
189 for (size_t i = 0; i < ARRAY_SIZE(cmd); ++i) {
190 *p_int = cmd[i].val;
191
192 do_ioctl_ptr(fd, cmd[i].cmd, p_int + 1);
193 printf("ioctl(%d, %s, %p) = %s\n",
194 fd, cmd[i].str, p_int + 1, errstr);
195
196 do_ioctl_ptr(fd, cmd[i].cmd, p_int);
197 printf("ioctl(%d, %s, [%d]) = %s\n",
198 fd, cmd[i].str, *p_int, errstr);
199 }
200 }
201
202 static void
203 test_str(void)
204 {
205 char *const buf = tail_alloc(IFNAMSIZ);
206
207 static const struct {
208 uint32_t cmd;
209 const char *str;
210 } cmd[] = {
211 { ARG_STR(SIOCBRADDBR) },
212 { ARG_STR(SIOCBRDELBR) },
213 };
214
215 for (size_t i = 0; i < ARRAY_SIZE(cmd); ++i) {
216 fill_memory_ex(buf, IFNAMSIZ, '0', 10);
217 do_ioctl_ptr(-1, cmd[i].cmd, buf + 1);
218 printf("ioctl(%d, %s, %p) = %s\n",
219 -1, cmd[i].str, buf + 1, errstr);
220
221 do_ioctl_ptr(-1, cmd[i].cmd, buf);
222 printf("ioctl(%d, %s, \"%.*s\"...) = %s\n",
223 -1, cmd[i].str, IFNAMSIZ, buf, errstr);
224
225 buf[IFNAMSIZ - 1] = '\0';
226 do_ioctl_ptr(-1, cmd[i].cmd, buf);
227 printf("ioctl(%d, %s, \"%s\") = %s\n",
228 -1, cmd[i].str, buf, errstr);
229 }
230 }
231
232 static void
233 test_ifreq(const int fd)
234 {
235 TAIL_ALLOC_OBJECT_CONST_PTR(struct ifreq, ifr);
236
237 #define SRC_IFR_INT(name, val0, val1) \
238 static const struct ifreq src_ ## name[] = { \
239 { .name = val0 }, \
240 { .name = val1 } \
241 }; \
242 char str0_ ## name[sizeof(int) * 3]; \
243 snprintf(str0_ ## name, sizeof(str0_ ## name), "%d", val0); \
244 char str1_ ## name[sizeof(int) * 3]; \
245 snprintf(str1_ ## name, sizeof(str1_ ## name), "%d", val1); \
246 const char *const str_ ## name[] = { \
247 str0_ ## name, \
248 str1_ ## name \
249 } \
250 /* End of SRC_IFR_INT definition. */
251
252 SRC_IFR_INT(ifr_metric, 0x1defaced, 0xfacefed1);
253 SRC_IFR_INT(ifr_mtu, 0x2defaced, 0xfacefed2);
254 SRC_IFR_INT(ifr_qlen, 0x3defaced, 0xfacefed3);
255
256 #define SRC_IFR_FLAG(name, valid_flags, valid_str, \
257 invalid_flags, invalid_str) \
258 static const struct ifreq src_ ## name[] = { \
259 { .name = valid_flags }, \
260 { .name = invalid_flags } \
261 }; \
262 static const char *const str_ ## name[] = { \
263 valid_str, \
264 invalid_str, \
265 } \
266 /* End of SRC_IFR_FLAG definition. */
267
268 SRC_IFR_FLAG(ifr_flags,
269 0xffff, XLAT_KNOWN(0xffff,
270 "IFF_UP|"
271 "IFF_BROADCAST|"
272 "IFF_DEBUG|"
273 "IFF_LOOPBACK|"
274 "IFF_POINTOPOINT|"
275 "IFF_NOTRAILERS|"
276 "IFF_RUNNING|"
277 "IFF_NOARP|"
278 "IFF_PROMISC|"
279 "IFF_ALLMULTI|"
280 "IFF_MASTER|"
281 "IFF_SLAVE|"
282 "IFF_MULTICAST|"
283 "IFF_PORTSEL|"
284 "IFF_AUTOMEDIA|"
285 "IFF_DYNAMIC"),
286 0, "0");
287
288 #define SRC_IFR_STRING(name) \
289 struct ifreq src_ ## name[2]; \
290 fill_memory_ex(src_ ## name[0].name, \
291 sizeof(src_ ## name[0].name), \
292 'a', 'z' - 'a' + 1); \
293 memcpy(src_ ## name[1].name, src_ ## name[0].name, \
294 sizeof(src_ ## name[1].name) - 1); \
295 src_ ## name[1].name[sizeof(src_ ## name[1].name) - 1] = '\0'; \
296 char str0_ ## name[sizeof(src_ ## name[0].name) + 5]; \
297 snprintf(str0_ ## name, sizeof(str0_ ## name), \
298 "\"%.*s\"...", \
299 (int) sizeof(src_ ## name[0].name) - 1, \
300 src_ ## name[0].name); \
301 char str1_ ## name[sizeof(src_ ## name[1].name) + 2]; \
302 snprintf(str1_ ## name, sizeof(str1_ ## name), \
303 "\"%s\"", src_ ## name[1].name); \
304 const char *const str_ ## name[] = { \
305 str0_ ## name, \
306 str1_ ## name \
307 } \
308 /* End of SRC_IFR_STRING definition. */
309
310 SRC_IFR_STRING(ifr_newname);
311 SRC_IFR_STRING(ifr_slave);
312
313 #define SRC_IFR_ADDR(name, port) \
314 const struct sockaddr_in src_in_ ## name = { \
315 .sin_family = AF_INET, \
316 .sin_addr.s_addr = htonl(INADDR_LOOPBACK), \
317 .sin_port = htons(port) \
318 }; \
319 struct ifreq src_ ## name[2]; \
320 memcpy(&src_ ## name[0].name, &src_in_ ## name, \
321 sizeof(src_in_ ## name)); \
322 memset(&src_ ## name[1], 0, sizeof(src_ ## name[1])); \
323 char str_in_ ## name[256]; \
324 snprintf(str_in_ ## name, sizeof(str_in_ ## name), \
325 "{sa_family=AF_INET, sin_port=htons(%u)" \
326 ", sin_addr=inet_addr(\"%s\")}", \
327 ntohs((src_in_ ## name).sin_port), \
328 inet_ntoa((src_in_ ## name).sin_addr)); \
329 const char *const str_ ## name[] = { \
330 str_in_ ## name, NULL \
331 } \
332 /* End of SRC_IFR_ADDR definition. */
333
334 SRC_IFR_ADDR(ifr_addr, 0x1bad);
335 SRC_IFR_ADDR(ifr_dstaddr, 0x2bad);
336 SRC_IFR_ADDR(ifr_broadaddr, 0x3bad);
337 SRC_IFR_ADDR(ifr_netmask, 0x4bad);
338
339 #define SRC_IFR_HWADDR(name) \
340 struct ifreq src_ ## name[2]; \
341 fill_memory(src_ ## name, sizeof(src_ ## name)); \
342 src_ ## name[0].name.sa_family = 1; \
343 char str_hw_ ## name[256]; \
344 snprintf(str_hw_ ## name, sizeof(str_hw_ ## name), \
345 "{sa_family=ARPHRD_ETHER" \
346 ", sa_data=%02x:%02x:%02x:%02x:%02x:%02x}", \
347 (uint8_t) src_ ## name[0].name.sa_data[0], \
348 (uint8_t) src_ ## name[0].name.sa_data[1], \
349 (uint8_t) src_ ## name[0].name.sa_data[2], \
350 (uint8_t) src_ ## name[0].name.sa_data[3], \
351 (uint8_t) src_ ## name[0].name.sa_data[4], \
352 (uint8_t) src_ ## name[0].name.sa_data[5]); \
353 const char *const str_ ## name[] = { \
354 str_hw_ ## name, NULL \
355 } \
356 /* End of SRC_IFR_HWADDR definition. */
357
358 SRC_IFR_HWADDR(ifr_hwaddr);
359
360 #define SRC_IFR_MAP(name) \
361 struct ifreq src_ ## name[2]; \
362 fill_memory(src_ ## name, sizeof(src_ ## name)); \
363 char str_map_ ## name[256]; \
364 snprintf(str_map_ ## name, sizeof(str_map_ ## name), \
365 "{mem_start=%#lx, mem_end=%#lx, base_addr=%#x" \
366 ", irq=%#x, dma=%#x, port=%#x}", \
367 src_ ## name[0].name.mem_start, \
368 src_ ## name[0].name.mem_end, \
369 src_ ## name[0].name.base_addr, \
370 src_ ## name[0].name.irq, \
371 src_ ## name[0].name.dma, \
372 src_ ## name[0].name.port); \
373 const char *const str_ ## name[] = { \
374 str_map_ ## name, NULL \
375 } \
376 /* End of SRC_IFR_MAP definition. */
377
378 SRC_IFR_MAP(ifr_map);
379
380 const struct {
381 const uint32_t cmd;
382 const char *const str;
383 const char *const name;
384 const struct ifreq src_addr[2];
385 const char *const src_str[2];
386 } cmd[] = {
387 #define ARG_IFREQ(name) \
388 #name, \
389 { src_ ## name[0], src_ ## name[1]}, \
390 { str_ ## name[0], str_ ## name[1]}, \
391 /* End of ARG_IFREQ definition. */
392
393 { ARG_STR(SIOCSIFADDR), ARG_IFREQ(ifr_addr) },
394 { ARG_STR(SIOCSIFBRDADDR), ARG_IFREQ(ifr_broadaddr) },
395 { ARG_STR(SIOCSIFDSTADDR), ARG_IFREQ(ifr_dstaddr) },
396 { ARG_STR(SIOCSIFFLAGS), ARG_IFREQ(ifr_flags) },
397 { ARG_STR(SIOCSIFHWADDR), ARG_IFREQ(ifr_hwaddr) },
398 { ARG_STR(SIOCSIFHWBROADCAST), ARG_IFREQ(ifr_hwaddr) },
399 { ARG_STR(SIOCADDMULTI), ARG_IFREQ(ifr_hwaddr) },
400 { ARG_STR(SIOCDELMULTI), ARG_IFREQ(ifr_hwaddr) },
401 { ARG_STR(SIOCSIFMAP), ARG_IFREQ(ifr_map) },
402 { ARG_STR(SIOCSIFMETRIC), ARG_IFREQ(ifr_metric) },
403 { ARG_STR(SIOCSIFMTU), ARG_IFREQ(ifr_mtu) },
404 { ARG_STR(SIOCSIFNAME), ARG_IFREQ(ifr_newname) },
405 { ARG_STR(SIOCSIFNETMASK), ARG_IFREQ(ifr_netmask) },
406 { ARG_STR(SIOCSIFSLAVE), ARG_IFREQ(ifr_slave) },
407 { ARG_STR(SIOCSIFTXQLEN), ARG_IFREQ(ifr_qlen) },
408 };
409
410 for (size_t i = 0; i < ARRAY_SIZE(cmd); ++i) {
411 do_ioctl_ptr(-1, cmd[i].cmd, (char *) ifr + 1);
412 printf("ioctl(%d, %s, %p) = %s\n",
413 -1, cmd[i].str, (char *) ifr + 1, errstr);
414
415 for (size_t j = 0; j < ARRAY_SIZE(cmd[i].src_addr); ++j) {
416 if (!cmd[i].src_str[j])
417 continue;
418 memcpy(ifr, &cmd[i].src_addr[j], sizeof(*ifr));
419 fill_memory_ex(ifr->ifr_name, sizeof(ifr->ifr_name),
420 '0', 10);
421 do_ioctl_ptr(-1, cmd[i].cmd, ifr);
422 printf("ioctl(%d, %s, {ifr_name=\"%.*s\"..., %s=%s})"
423 " = %s\n",
424 -1, cmd[i].str, IFNAMSIZ - 1, ifr->ifr_name,
425 cmd[i].name, cmd[i].src_str[j], errstr);
426
427 ifr->ifr_name[IFNAMSIZ - 1] = '\0';
428 do_ioctl_ptr(-1, cmd[i].cmd, ifr);
429 printf("ioctl(%d, %s, {ifr_name=\"%s\", %s=%s}) = %s\n",
430 -1, cmd[i].str, ifr->ifr_name,
431 cmd[i].name, cmd[i].src_str[j], errstr);
432 }
433 }
434
435 memset(ifr, 0, sizeof(*ifr));
436 strcpy(ifr->ifr_name, "lo");
437 if (do_ioctl_ptr(fd, SIOCGIFINDEX, ifr)) {
438 printf("ioctl(%d, %s, {ifr_name=\"%s\"}) = %s\n",
439 fd, "SIOCGIFINDEX", ifr->ifr_name, errstr);
440 ifr->ifr_ifindex = 1;
441 } else {
442 printf("ioctl(%d, %s, {ifr_name=\"%s\", ifr_ifindex=%d})"
443 " = %s\n",
444 fd, "SIOCGIFINDEX", ifr->ifr_name, ifr->ifr_ifindex,
445 errstr);
446 }
447
448 int ifindex = ifr->ifr_ifindex;
449
450 do_ioctl_ptr(-1, SIOCGIFNAME, ifr);
451 printf("ioctl(%d, %s, {ifr_ifindex=%d}) = %s\n",
452 -1, "SIOCGIFNAME", ifr->ifr_ifindex, errstr);
453
454 if (do_ioctl_ptr(fd, SIOCGIFNAME, ifr))
455 printf("ioctl(%d, %s, {ifr_ifindex=%d}) = %s\n",
456 fd, "SIOCGIFNAME", ifr->ifr_ifindex, errstr);
457 else
458 printf("ioctl(%d, %s, {ifr_ifindex=%d, ifr_name=\"%s\"})"
459 " = %s\n",
460 fd, "SIOCGIFNAME", ifr->ifr_ifindex, ifr->ifr_name,
461 errstr);
462
463 static const struct {
464 const uint32_t cmd;
465 const char *const str;
466 } ifindex_cmd[] = {
467 { ARG_STR(SIOCBRADDIF) },
468 { ARG_STR(SIOCBRDELIF) },
469 };
470
471 for (size_t i = 0; i < ARRAY_SIZE(ifindex_cmd); ++i) {
472 ifr->ifr_ifindex = ifindex;
473 do_ioctl_ptr(-1, ifindex_cmd[i].cmd, ifr);
474 printf("ioctl(%d, %s, {ifr_ifindex=%s}) = %s\n",
475 -1, ifindex_cmd[i].str, IFINDEX_LO_STR, errstr);
476 }
477 }
478
479 int
480 main(void)
481 {
482 const int fd = socket(AF_INET, SOCK_STREAM, 0);
483 if (fd < 0)
484 perror_msg_and_skip("socket AF_INET");
485
486 test_ptr();
487 test_int(fd);
488 test_str();
489 test_ifreq(fd);
490
491 puts("+++ exited with 0 +++");
492 return 0;
493 }