1 /*
2 * Check decoding of clone3 syscall.
3 *
4 * Copyright (c) 2019-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
12 #include <errno.h>
13 #include <stdint.h>
14 #include <inttypes.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/wait.h>
19
20 #include <linux/sched.h>
21
22 #ifdef HAVE_STRUCT_USER_DESC
23 # include <asm/ldt.h>
24 #endif
25
26 #include "scno.h"
27
28 #ifndef VERBOSE
29 # define VERBOSE 0
30 #endif
31 #ifndef RETVAL_INJECTED
32 # define RETVAL_INJECTED 0
33 #endif
34
35 enum validity_flag_bits {
36 STRUCT_VALID_BIT,
37 PIDFD_VALID_BIT,
38 CHILD_TID_VALID_BIT,
39 PARENT_TID_VALID_BIT,
40 TLS_VALID_BIT,
41 };
42
43 enum validity_flags {
44 FLAG(STRUCT_VALID),
45 FLAG(PIDFD_VALID),
46 FLAG(CHILD_TID_VALID),
47 FLAG(PARENT_TID_VALID),
48 FLAG(TLS_VALID),
49 };
50
51 #define MAX_SET_TID_SIZE 32
52
53 static const int child_exit_status = 42;
54
55 #if RETVAL_INJECTED
56 static const long injected_retval = 42;
57
58 # define INJ_STR " (INJECTED)\n"
59 #else /* !RETVAL_INJECTED */
60 # define INJ_STR "\n"
61 #endif /* RETVAL_INJECTED */
62
63 #define ERR(b_) ((1ULL << (b_)) + FAIL_BUILD_ON_ZERO((b_) < 64))
64
65
66 #if !RETVAL_INJECTED
67 static void
68 wait_cloned(int pid)
69 {
70 int status;
71
72 errno = 0;
73 while (waitpid(pid, &status, WEXITED | __WCLONE) != pid) {
74 if (errno != EINTR)
75 perror_msg_and_fail("waitpid(%d)", pid);
76 }
77 }
78 #endif
79
80 static long
81 do_clone3_(void *args, kernel_ulong_t size, uint64_t possible_errors, int line)
82 {
83 long rc = syscall(__NR_clone3, args, size);
84
85 #if RETVAL_INJECTED
86 if (rc != injected_retval)
87 perror_msg_and_fail("%d: Unexpected injected return value "
88 "of a clone3() call (%ld instead of %ld)",
89 line, rc, injected_retval);
90 #else
91
92 static int unimplemented_error = -1;
93
94 if (!(possible_errors & ERR(0))) {
95 if (rc >= 0)
96 error_msg_and_fail("%d: Unexpected success"
97 " of a clone3() call", line);
98 if (unimplemented_error < 0)
99 unimplemented_error =
100 (errno == EINVAL) ? ENOSYS : errno;
101 }
102
103 /*
104 * This code works as long as all the errors we care about (EFAULT
105 * and EINVAL so far) fit inside 64 bits, otherwise it should
106 * be rewritten.
107 */
108 if (rc < 0 && errno != unimplemented_error
109 && (errno >= 64 || errno < 0 || !(ERR(errno) & possible_errors))) {
110 perror_msg_and_fail("%d: Unexpected failure of a clone3() call"
111 " (got errno %d, expected errno bitmask"
112 " %#" PRIx64 ")",
113 line, errno, possible_errors);
114 }
115
116 if (!rc)
117 _exit(child_exit_status);
118
119 if (rc > 0 && ((struct clone_args *) args)->exit_signal)
120 wait_cloned(rc);
121 #endif
122
123 return rc;
124 }
125
126 #define do_clone3(args_, size_, errors_) \
127 do_clone3_((args_), (size_), (errors_), __LINE__)
128
129 static void
130 print_addr64(const char *pfx, uint64_t addr)
131 {
132 if (addr)
133 printf("%s%#" PRIx64, pfx, addr);
134 else
135 printf("%sNULL", pfx);
136 }
137
138 static void
139 print_tls(const char *pfx, uint64_t arg_ptr, enum validity_flags vf)
140 {
141 #if defined HAVE_STRUCT_USER_DESC && defined __i386__
142 if (!(vf & TLS_VALID)) {
143 print_addr64(pfx, arg_ptr);
144 return;
145 }
146
147 struct user_desc *arg = (struct user_desc *) (uintptr_t) arg_ptr;
148
149 printf("%s{entry_number=%d"
150 ", base_addr=%#08x"
151 ", limit=%#08x"
152 ", seg_32bit=%u"
153 ", contents=%u"
154 ", read_exec_only=%u"
155 ", limit_in_pages=%u"
156 ", seg_not_present=%u"
157 ", useable=%u}",
158 pfx,
159 arg->entry_number,
160 arg->base_addr,
161 arg->limit,
162 arg->seg_32bit,
163 arg->contents,
164 arg->read_exec_only,
165 arg->limit_in_pages,
166 arg->seg_not_present,
167 arg->useable);
168 #else
169 print_addr64(pfx, arg_ptr);
170 #endif
171 }
172
173 static void
174 print_set_tid(uint64_t set_tid, uint64_t set_tid_size)
175 {
176 if (!set_tid || set_tid != (uintptr_t) set_tid ||
177 !set_tid_size || set_tid_size > MAX_SET_TID_SIZE) {
178 print_addr64(", set_tid=", set_tid);
179 } else {
180 printf(", set_tid=");
181 int *tids = (int *) (uintptr_t) set_tid;
182 for (unsigned int i = 0; i < set_tid_size; ++i)
183 printf("%s%d", i ? ", " : "[", tids[i]);
184 printf("]");
185 }
186
187 printf(", set_tid_size=%" PRIu64, set_tid_size);
188 }
189
190 static void
191 print_clone3(struct clone_args *const arg, long rc, kernel_ulong_t sz,
192 enum validity_flags valid,
193 const char *flags_str, const char *es_str)
194 {
195 int saved_errno = errno;
196
197 printf("clone3(");
198 if (!(valid & STRUCT_VALID)) {
199 printf("%p", arg);
200 goto out;
201 }
202
203 #if XLAT_RAW
204 printf("{flags=%#" PRIx64, (uint64_t) arg->flags);
205 #elif XLAT_VERBOSE
206 if (flags_str[0] == '0')
207 printf("{flags=%#" PRIx64, (uint64_t) arg->flags);
208 else
209 printf("{flags=%#" PRIx64 " /* %s */",
210 (uint64_t) arg->flags, flags_str);
211 #else
212 printf("{flags=%s", flags_str);
213 #endif
214
215 if (arg->flags & CLONE_PIDFD)
216 print_addr64(", pidfd=", arg->pidfd);
217
218 if (arg->flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) {
219 if (valid & CHILD_TID_VALID)
220 printf(", child_tid=[%d]",
221 *(int *) (uintptr_t) arg->child_tid);
222 else
223 print_addr64(", child_tid=", arg->child_tid);
224 }
225
226 if (arg->flags & CLONE_PARENT_SETTID)
227 print_addr64(", parent_tid=", arg->parent_tid);
228
229 printf(", exit_signal=%s", es_str);
230 print_addr64(", stack=", arg->stack);
231 printf(", stack_size=%" PRIx64, (uint64_t) arg->stack_size);
232
233 if (arg->flags & CLONE_SETTLS)
234 print_tls("tls=", arg->tls, valid);
235
236 if (sz >= offsetofend(struct clone_args, set_tid_size) &&
237 (arg->set_tid || arg->set_tid_size))
238 print_set_tid(arg->set_tid, arg->set_tid_size);
239
240 if (sz > offsetof(struct clone_args, cgroup) &&
241 (arg->cgroup || arg->flags & CLONE_INTO_CGROUP))
242 printf(", cgroup=%" PRIu64, (uint64_t) arg->cgroup);
243
244 printf("}");
245
246 if (rc < 0)
247 goto out;
248
249 bool comma = false;
250
251 if (arg->flags & CLONE_PIDFD) {
252 if (valid & PIDFD_VALID)
253 printf(" => {pidfd=[%d]",
254 *(int *) (uintptr_t) arg->pidfd);
255 else
256 print_addr64(" => {pidfd=", arg->pidfd);
257
258 comma = true;
259 }
260
261 if (arg->flags & CLONE_PARENT_SETTID) {
262 printf(comma ? ", " : " => {");
263
264 if (valid & PARENT_TID_VALID)
265 printf("parent_tid=[%d]",
266 *(int *) (uintptr_t) arg->parent_tid);
267 else
268 print_addr64("parent_tid=", arg->parent_tid);
269
270 comma = true;
271 }
272
273 if (comma)
274 printf("}");
275
276 out:
277 errno = saved_errno;
278 }
279
280 int
281 main(int argc, char *argv[])
282 {
283 static const struct {
284 struct clone_args args;
285 uint64_t possible_errors;
286 enum validity_flags vf;
287 const char *flags_str;
288 const char *es_str;
289 } arg_vals[] = {
290 { { .flags = 0 },
291 ERR(0), 0, "0", "0" },
292 { { .flags = CLONE_PARENT_SETTID },
293 ERR(0), 0, "CLONE_PARENT_SETTID", "0" },
294
295 /* check clone3_flags/clone_flags interoperation */
296 { { .flags = CLONE_CLEAR_SIGHAND },
297 ERR(EINVAL) | ERR(0), 0, "CLONE_CLEAR_SIGHAND", "0" },
298 { { .flags = CLONE_PARENT_SETTID | CLONE_CLEAR_SIGHAND },
299 ERR(EINVAL) | ERR(0), 0,
300 "CLONE_PARENT_SETTID|CLONE_CLEAR_SIGHAND", "0" },
301
302 { { .set_tid = 0xfacefeedcafebabe },
303 ERR(E2BIG) | ERR(EINVAL), 0, "0", "0" },
304 { { .set_tid_size = 0xfacecafefeedbabe },
305 ERR(E2BIG) | ERR(EINVAL), 0, "0", "0" },
306 { { .set_tid = 0xfacefeedcafebabe,
307 .set_tid_size = MAX_SET_TID_SIZE + 1 },
308 ERR(E2BIG) | ERR(EINVAL), 0, "0", "0" },
309 };
310 TAIL_ALLOC_OBJECT_CONST_PTR(struct clone_args, arg);
311 const size_t arg1_size = offsetofend(struct clone_args, tls);
312 struct clone_args *const arg1 = tail_alloc(arg1_size);
313 const size_t arg2_size = sizeof(*arg) + 8;
314 struct clone_args *const arg2 = tail_alloc(arg2_size);
315 TAIL_ALLOC_OBJECT_CONST_PTR(int, pidfd);
316 TAIL_ALLOC_OBJECT_CONST_PTR(int, child_tid);
317 TAIL_ALLOC_OBJECT_CONST_PTR(int, parent_tid);
318 int *const tids = tail_alloc(sizeof(*tids) * MAX_SET_TID_SIZE);
319 long rc;
320
321 #if defined HAVE_STRUCT_USER_DESC
322 TAIL_ALLOC_OBJECT_CONST_PTR(struct user_desc, tls);
323
324 fill_memory(tls, sizeof(*tls));
325 #else
326 TAIL_ALLOC_OBJECT_CONST_PTR(int, tls);
327 #endif
328
329 *pidfd = 0xbadc0ded;
330 *child_tid = 0xdeadface;
331 *parent_tid = 0xfeedbeef;
332 fill_memory(tids, sizeof(*tids) * MAX_SET_TID_SIZE);
333
334 rc = do_clone3(NULL, 0, ERR(EINVAL));
335 printf("clone3(NULL, 0) = %s" INJ_STR, sprintrc(rc));
336
337 rc = do_clone3(arg + 1, sizeof(*arg), ERR(EFAULT));
338 printf("clone3(%p, %zu) = %s" INJ_STR,
339 arg + 1, sizeof(*arg), sprintrc(rc));
340
341 size_t short_size = arg1_size - sizeof(uint64_t);
342 char *short_start = (char *) arg1 + sizeof(uint64_t);
343 rc = do_clone3(short_start, short_size, ERR(EINVAL));
344 printf("clone3(%p, %zu) = %s" INJ_STR,
345 short_start, short_size, sprintrc(rc));
346
347
348 memset(arg, 0, sizeof(*arg));
349 memset(arg1, 0, arg1_size);
350 memset(arg2, 0, arg2_size);
351
352 rc = do_clone3(arg, 64, ERR(0));
353 printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0}, 64)"
354 " = %s" INJ_STR,
355 sprintrc(rc));
356
357 rc = do_clone3(arg, sizeof(*arg) + 8, ERR(EFAULT));
358 printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0, ???}"
359 #if RETVAL_INJECTED
360 " => {???}"
361 #endif
362 ", %zu) = %s" INJ_STR,
363 sizeof(*arg) + 8, sprintrc(rc));
364
365 rc = do_clone3(arg1, arg1_size, ERR(0));
366 printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0}"
367 ", %zu) = %s" INJ_STR,
368 arg1_size, sprintrc(rc));
369
370 rc = do_clone3(arg2, arg2_size, ERR(0));
371 printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0}"
372 ", %zu) = %s" INJ_STR,
373 arg2_size, sprintrc(rc));
374
375 arg->set_tid = (uintptr_t) tids;
376 arg->set_tid_size = MAX_SET_TID_SIZE;
377 rc = do_clone3(arg, sizeof(*arg), ERR(E2BIG) | ERR(EINVAL));
378 print_clone3(arg, rc, sizeof(*arg), STRUCT_VALID, "0", "0");
379 printf(", %zu) = %s" INJ_STR, sizeof(*arg), sprintrc(rc));
380 memset(arg, 0, sizeof(*arg));
381
382 arg->cgroup = 0xfacefeedbadc0ded;
383 rc = do_clone3(arg, sizeof(*arg), ERR(0) | ERR(E2BIG));
384 print_clone3(arg, rc, sizeof(*arg), STRUCT_VALID, "0", "0");
385 printf(", %zu) = %s" INJ_STR, sizeof(*arg), sprintrc(rc));
386 memset(arg, 0, sizeof(*arg));
387
388 arg->flags = CLONE_INTO_CGROUP;
389 rc = do_clone3(arg, sizeof(*arg), ERR(0) | ERR(EINVAL) | ERR(EBADF));
390 print_clone3(arg, rc, sizeof(*arg), STRUCT_VALID,
391 "CLONE_INTO_CGROUP", "0");
392 printf(", %zu) = %s" INJ_STR, sizeof(*arg), sprintrc(rc));
393 memset(arg, 0, sizeof(*arg));
394
395 /*
396 * NB: the following check is purposefully fragile (it will break
397 * when system's struct clone_args has additional fields,
398 * so it signalises that the decoder needs to be updated.
399 */
400 arg2[1].flags = 0xfacefeeddeadc0de;
401 arg2->exit_signal = 0xdeadface00000000ULL | SIGCHLD;
402 rc = do_clone3(arg2, sizeof(*arg2) + 8, ERR(E2BIG));
403 printf("clone3({flags=0, exit_signal=%llu, stack=NULL, stack_size=0"
404 ", /* bytes %zu..%zu */ "
405 BE_LE("\"\\xfa\\xce\\xfe\\xed\\xde\\xad\\xc0\\xde\"",
406 "\"\\xde\\xc0\\xad\\xde\\xed\\xfe\\xce\\xfa\"")
407 #if RETVAL_INJECTED
408 "} => {/* bytes %zu..%zu */ "
409 BE_LE("\"\\xfa\\xce\\xfe\\xed\\xde\\xad\\xc0\\xde\"",
410 "\"\\xde\\xc0\\xad\\xde\\xed\\xfe\\xce\\xfa\"")
411 #endif /* RETVAL_INJECTED */
412 "}, %zu) = %s" INJ_STR,
413 0xdeadface00000000ULL | SIGCHLD,
414 sizeof(*arg2), sizeof(*arg2) + 7,
415 #if RETVAL_INJECTED
416 sizeof(*arg2), sizeof(*arg2) + 7,
417 #endif
418 sizeof(*arg2) + 8, sprintrc(rc));
419
420 arg2->exit_signal = 0xdeadc0de;
421 rc = do_clone3(arg2, sizeof(*arg) + 16, ERR(E2BIG));
422 printf("clone3({flags=0, exit_signal=3735929054, stack=NULL"
423 ", stack_size=0, ???}"
424 #if RETVAL_INJECTED
425 " => {???}"
426 #endif
427 ", %zu) = %s" INJ_STR,
428 sizeof(*arg) + 16, sprintrc(rc));
429
430 arg->flags = 0xface3eefbeefc0de;
431 arg->exit_signal = 0x1e55c0de;
432 rc = do_clone3(arg, 64, ERR(EINVAL));
433 printf("clone3({flags=%s, child_tid=NULL, exit_signal=508936414"
434 ", stack=NULL, stack_size=0, tls=NULL}, 64) = %s" INJ_STR,
435 XLAT_KNOWN(0xface3eefbeefc0de, "CLONE_VFORK|CLONE_PARENT"
436 "|CLONE_THREAD|CLONE_NEWNS|CLONE_SYSVSEM|CLONE_SETTLS"
437 "|CLONE_CHILD_CLEARTID|CLONE_UNTRACED|CLONE_NEWCGROUP"
438 "|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|CLONE_NEWPID|CLONE_IO"
439 "|CLONE_NEWTIME|CLONE_CLEAR_SIGHAND|CLONE_INTO_CGROUP"
440 "|0xface3eec0040005e"),
441 sprintrc(rc));
442
443 arg->flags = 0xdec0deac0040007fULL;
444 arg->exit_signal = 250;
445 arg->stack = 0xface1e55beeff00dULL;
446 arg->stack_size = 0xcaffeedefacedca7ULL;
447 rc = do_clone3(arg, 64, ERR(EINVAL));
448 printf("clone3({flags=%s, exit_signal=250"
449 ", stack=0xface1e55beeff00d, stack_size=0xcaffeedefacedca7}, 64)"
450 " = %s" INJ_STR,
451 XLAT_UNKNOWN(0xdec0deac0040007f, "CLONE_???"),
452 sprintrc(rc));
453
454 arg->exit_signal = SIGCHLD;
455
456 struct {
457 uint64_t flag;
458 const char *flag_str;
459 uint64_t *field;
460 const char *field_name;
461 int *ptr;
462 bool deref_exiting;
463 } pid_fields[] = {
464 { ARG_STR(CLONE_PIDFD),
465 (uint64_t *) &arg->pidfd,
466 "pidfd", pidfd, true },
467 { ARG_STR(CLONE_CHILD_SETTID),
468 (uint64_t *) &arg->child_tid,
469 "child_tid", child_tid },
470 { ARG_STR(CLONE_CHILD_CLEARTID),
471 (uint64_t *) &arg->child_tid,
472 "child_tid", child_tid },
473 { ARG_STR(CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID),
474 (uint64_t *) &arg->child_tid,
475 "child_tid", child_tid },
476 { ARG_STR(CLONE_PARENT_SETTID),
477 (uint64_t *) &arg->parent_tid,
478 "parent_tid", parent_tid, true },
479 };
480
481 for (size_t i = 0; i < ARRAY_SIZE(pid_fields); i++) {
482 char flag_str[128];
483 const char *rc_str;
484
485 arg->flags = 0xbad0000000000001ULL | pid_fields[i].flag;
486
487 #if XLAT_RAW
488 snprintf(flag_str, sizeof(flag_str), "%#" PRIx64,
489 (uint64_t) arg->flags);
490 #elif XLAT_VERBOSE
491 snprintf(flag_str, sizeof(flag_str),
492 "%#" PRIx64 " /* %s|0xbad0000000000001 */",
493 (uint64_t) arg->flags, pid_fields[i].flag_str);
494 #else
495 snprintf(flag_str, sizeof(flag_str), "%s|0xbad0000000000001",
496 pid_fields[i].flag_str);
497 #endif
498
499 pid_fields[i].field[0] = 0;
500 rc = do_clone3(arg, 64, ERR(EINVAL));
501 rc_str = sprintrc(rc);
502 printf("clone3({flags=%s, %s=NULL"
503 ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
504 ", stack=0xface1e55beeff00d"
505 ", stack_size=0xcaffeedefacedca7}",
506 flag_str, pid_fields[i].field_name);
507 #if RETVAL_INJECTED
508 if (pid_fields[i].deref_exiting)
509 printf(" => {%s=NULL}", pid_fields[i].field_name);
510 #endif /* RETVAL_INJECTED */
511 printf(", 64) = %s" INJ_STR, rc_str);
512
513 pid_fields[i].field[0] = (uintptr_t) (pid_fields[i].ptr + 1);
514 rc = do_clone3(arg, 64, ERR(EINVAL));
515 rc_str = sprintrc(rc);
516 printf("clone3({flags=%s, %s=%p"
517 ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
518 ", stack=0xface1e55beeff00d"
519 ", stack_size=0xcaffeedefacedca7}",
520 flag_str, pid_fields[i].field_name,
521 pid_fields[i].ptr + 1);
522 #if RETVAL_INJECTED
523 if (pid_fields[i].deref_exiting)
524 printf(" => {%s=%p}",
525 pid_fields[i].field_name, pid_fields[i].ptr + 1);
526 #endif /* RETVAL_INJECTED */
527 printf(", 64) = %s" INJ_STR, rc_str);
528
529 pid_fields[i].field[0] = (uintptr_t) pid_fields[i].ptr;
530 rc = do_clone3(arg, 64, ERR(EINVAL));
531 rc_str = sprintrc(rc);
532 printf("clone3({flags=%s, %s=%p"
533 ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
534 ", stack=0xface1e55beeff00d"
535 ", stack_size=0xcaffeedefacedca7}",
536 flag_str, pid_fields[i].field_name,
537 pid_fields[i].ptr);
538 #if RETVAL_INJECTED
539 if (pid_fields[i].deref_exiting)
540 printf(" => {%s=[%d]}",
541 pid_fields[i].field_name, *pid_fields[i].ptr);
542 #endif /* RETVAL_INJECTED */
543 printf(", 64) = %s" INJ_STR, rc_str);
544 }
545
546 arg->flags = 0xbad0000000000001ULL | CLONE_SETTLS;
547 rc = do_clone3(arg, 64, ERR(EINVAL));
548 printf("clone3({flags="
549 XLAT_KNOWN(0xbad0000000080001, "CLONE_SETTLS|0xbad0000000000001")
550 ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
551 ", stack=0xface1e55beeff00d"
552 ", stack_size=0xcaffeedefacedca7, tls=NULL}, 64) = %s" INJ_STR,
553 sprintrc(rc));
554
555 arg->tls = (uintptr_t) (tls + 1);
556 rc = do_clone3(arg, 64, ERR(EINVAL));
557 printf("clone3({flags="
558 XLAT_KNOWN(0xbad0000000080001, "CLONE_SETTLS|0xbad0000000000001")
559 ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
560 ", stack=0xface1e55beeff00d"
561 ", stack_size=0xcaffeedefacedca7, tls=%p}, 64) = %s" INJ_STR,
562 tls + 1, sprintrc(rc));
563
564 arg->tls = (uintptr_t) tls;
565 rc = do_clone3(arg, 64, ERR(EINVAL));
566 printf("clone3({flags="
567 XLAT_KNOWN(0xbad0000000080001, "CLONE_SETTLS|0xbad0000000000001")
568 ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
569 ", stack=0xface1e55beeff00d, stack_size=0xcaffeedefacedca7, tls="
570 #if defined HAVE_STRUCT_USER_DESC && defined __i386__
571 "{entry_number=2206368128, base_addr=0x87868584"
572 ", limit=0x8b8a8988, seg_32bit=0, contents=2, read_exec_only=1"
573 ", limit_in_pages=0, seg_not_present=0, useable=0}"
574 #else
575 "%p"
576 #endif
577 "}, 64) = %s" INJ_STR,
578 #if !defined HAVE_STRUCT_USER_DESC || !defined __i386__
579 tls,
580 #endif
581 sprintrc(rc));
582
583 for (size_t i = 0; i < ARRAY_SIZE(arg_vals); i++) {
584 memcpy(arg, &arg_vals[i].args, sizeof(*arg));
585
586 rc = do_clone3(arg, sizeof(*arg), arg_vals[i].possible_errors);
587 print_clone3(arg, rc, sizeof(*arg),
588 arg_vals[i].vf | STRUCT_VALID,
589 arg_vals[i].flags_str, arg_vals[i].es_str);
590 printf(", %zu) = %s" INJ_STR, sizeof(*arg), sprintrc(rc));
591 }
592
593 puts("+++ exited with 0 +++");
594
595 return 0;
596 }