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