]> granicus.if.org Git - strace/commitdiff
clone: implement clone3 syscall decoding
authorEugene Syromyatnikov <evgsyr@gmail.com>
Wed, 28 Aug 2019 00:35:53 +0000 (02:35 +0200)
committerDmitry V. Levin <ldv@altlinux.org>
Mon, 23 Sep 2019 11:58:41 +0000 (11:58 +0000)
* configure.ac (AC_CHECK_HEADERS): Check for linux/sched.h presence.
(AC_CHECK_TYPES): Check for struct clone_args in <linux/sched.h>.
* clone.c: Include "print_fields.h".
(struct strace_clone_args): New type.
(print_nonzero_bytes): New function.
(SYS_FUNC(clone3)): New decoder.
* linux/syscallent-common.h ([BASE_NR + 435]): Wire up clone3.
* NEWS: Mention this change.
* tests/clone3.c: New file.
* tests/clone3-Xabbrev.c: Likewise.
* tests/clone3-Xraw.c: Likewise.
* tests/clone3-Xverbose.c: Likewise.
* tests/clone3-success.c: Likewise.
* tests/clone3-success-Xabbrev.c: Likewise.
* tests/clone3-success-Xraw.c: Likewise.
* tests/clone3-success-Xverbose.c: Likewise.
* tests/clone3-success.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add clone3-success,
clone3-success-Xabbrev, clone3-success-Xraw, and
clone3-success-Xverbose.
(DECODER_TESTS): Add clone3-success.test.
* tests/gen_tests.in (clone3, clone3-Xabbrev, clone3-Xraw,
clone3-Xverbose, clone3-success-Xabbrev, clone3-success-Xraw,
clone3-success-Xverbose): New entries.
* tests/pure_executables.list: Add clone3, clone3-Xabbrev, clone3-Xraw,
and clone3-Xverbose.
* tests/.gitignore: Add clone3, clone3-Xabbrev, clone3-Xraw,
clone3-Xverbose, clone3-success, clone3-success-Xabbrev,
clone3-success-Xraw, and clone3-success-Xverbose.

Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
17 files changed:
NEWS
clone.c
configure.ac
linux/syscallent-common.h
tests/.gitignore
tests/Makefile.am
tests/clone3-Xabbrev.c [new file with mode: 0644]
tests/clone3-Xraw.c [new file with mode: 0644]
tests/clone3-Xverbose.c [new file with mode: 0644]
tests/clone3-success-Xabbrev.c [new file with mode: 0644]
tests/clone3-success-Xraw.c [new file with mode: 0644]
tests/clone3-success-Xverbose.c [new file with mode: 0644]
tests/clone3-success.c [new file with mode: 0644]
tests/clone3-success.test [new file with mode: 0755]
tests/clone3.c [new file with mode: 0644]
tests/gen_tests.in
tests/pure_executables.list

diff --git a/NEWS b/NEWS
index 76fdf58d6530fd74c53f303c7c5b82f950b050e8..867cfdcbe7873ab07dc5ee8c098449df57a5faac 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,7 +2,7 @@ Noteworthy changes in release ?.? (????-??-??)
 ==============================================
 
 * Improvements
-  * Implemented decoding of pidfd_open syscall.
+  * Implemented decoding of pidfd_open and clone3 syscalls.
   * Enhanced decoding of NETLINK_ROUTE protocol.
   * Implemented decoding of UNIX_DIAG_UID netlink attribute.
   * Updated lists of BPF_*, ETH_*, KEYCTL_*, KVM_*, MAP_*, SO_*, TCP_*, V4L2_*,
diff --git a/clone.c b/clone.c
index 7270cee946ed209a21c041bd32ff324be609c958..2a249eef71a99e687685f0da59125aa79b2cba4e 100644 (file)
--- a/clone.c
+++ b/clone.c
@@ -18,6 +18,8 @@
 # define CSIGNAL 0x000000ff
 #endif
 
+#include "print_fields.h"
+
 #include "xlat/clone_flags.h"
 #include "xlat/setns_types.h"
 #include "xlat/unshare_flags.h"
@@ -137,6 +139,193 @@ SYS_FUNC(clone)
        return 0;
 }
 
+
+struct strace_clone_args {
+       uint64_t flags;
+       uint64_t /* int * */ pidfd;
+       uint64_t /* int * */ child_tid;
+       uint64_t /* int * */ parent_tid;
+       uint64_t /* int */   exit_signal;
+       uint64_t stack;
+       uint64_t stack_size;
+       uint64_t tls;
+};
+
+/**
+ * Print a region of tracee memory only in case non-zero bytes are present
+ * there.  It almost fits into printstr_ex, but it has some pretty specific
+ * behaviour peculiarities (like printing of ellipsis on error) to readily
+ * integrate it there.
+ *
+ * Since it is expected to be used for printing tail of a structure in tracee's
+ * memory, it accepts a combination of start_addr/start_offs/total_len and does
+ * the relevant calculations itself.
+ *
+ * @param prefix     A string printed in cases something is going to be printed.
+ * @param start_addr Address of the beginning of a structure (whose tail
+ *                   is supposedly to be printed) in tracee's memory.
+ * @param start_offs Offset from the beginning of the structure where the tail
+ *                   data starts.
+ * @param total_len  Total size of the tracee's memory region containing
+ *                   the structure and the tail data.
+ *                   Caller is responsible for imposing a sensible (usually
+ *                   mandated by the kernel interface, like get_pagesize())
+ *                   limit here.
+ * @param style      Passed to string_quote as "style" parameter.
+ * @return           Returns true is anything was printed, false otherwise.
+ */
+static bool
+print_nonzero_bytes(struct tcb *const tcp, const char *prefix,
+                   const kernel_ulong_t start_addr,
+                   const unsigned int start_offs,
+                   const unsigned int total_len,
+                   const unsigned int style)
+{
+       if (start_offs >= total_len)
+               return false;
+
+       const kernel_ulong_t addr = start_addr + start_offs;
+       const unsigned int len = total_len - start_offs;
+       const unsigned int size = MIN(len, max_strlen);
+
+       char *str = malloc(len);
+
+       if (!str) {
+               error_func_msg("memory exhausted when tried to allocate"
+                               " %u bytes", len);
+               tprintf("%s???", prefix);
+               return true;
+       }
+
+       bool ret = true;
+
+       if (umoven(tcp, addr, len, str)) {
+               tprintf("%s???", prefix);
+       } else if (is_filled(str, 0, len)) {
+               ret = false;
+       } else {
+               tprints(prefix);
+               tprintf("/* bytes %u..%u */ ", start_offs, total_len - 1);
+
+               print_quoted_string(str, size, style);
+
+               if (size < len)
+                       tprints("...");
+       }
+
+       free(str);
+       return ret;
+}
+
+SYS_FUNC(clone3)
+{
+       static const size_t minsz = offsetofend(struct strace_clone_args, tls);
+
+       const kernel_ulong_t addr = tcp->u_arg[0];
+       const kernel_ulong_t size = tcp->u_arg[1];
+
+       struct strace_clone_args arg = { 0 };
+       kernel_ulong_t fetch_size;
+
+       fetch_size = MIN(size, sizeof(arg));
+
+       if (entering(tcp)) {
+               if (fetch_size < minsz) {
+                       printaddr(addr);
+                       goto out;
+               } else if (umoven_or_printaddr(tcp, addr, fetch_size, &arg)) {
+                       goto out;
+               }
+
+               PRINT_FIELD_FLAGS("{", arg, flags, clone_flags,
+                                 "CLONE_???");
+
+               if (arg.flags & CLONE_PIDFD)
+                       PRINT_FIELD_ADDR64(", ", arg, pidfd);
+
+               if (arg.flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID))
+                       PRINT_FIELD_ADDR64(", ", arg, child_tid);
+
+               if (arg.flags & CLONE_PARENT_SETTID)
+                       PRINT_FIELD_ADDR64(", ", arg, parent_tid);
+
+               tprints(", exit_signal=");
+               if (arg.exit_signal < INT_MAX)
+                       printsignal(arg.exit_signal);
+               else
+                       tprintf("%" PRIu64, arg.exit_signal);
+
+               PRINT_FIELD_ADDR64(", ", arg, stack);
+               PRINT_FIELD_X(", ", arg, stack_size);
+
+               if (arg.flags & CLONE_SETTLS) {
+                       tprints(", tls=");
+                       print_tls_arg(tcp, arg.tls);
+               }
+
+               if (size > fetch_size)
+                       print_nonzero_bytes(tcp, ", ", addr, fetch_size,
+                                           MIN(size, get_pagesize()),
+                                           QUOTE_FORCE_HEX);
+
+               tprints("}");
+
+               if ((arg.flags & (CLONE_PIDFD | CLONE_PARENT_SETTID)) ||
+                   (size > fetch_size))
+                       return 0;
+
+               goto out;
+       }
+
+       /* exiting */
+
+       if (syserror(tcp))
+               goto out;
+
+       if (umoven(tcp, addr, fetch_size, &arg)) {
+               tprints(" => ");
+               printaddr(addr);
+               goto out;
+       }
+
+       static const char initial_pfx[] = " => {";
+       const char *pfx = initial_pfx;
+
+       if (arg.flags & CLONE_PIDFD) {
+               tprintf("%spidfd=", pfx);
+               printnum_fd(tcp, arg.pidfd);
+               pfx = ", ";
+       }
+
+       if (arg.flags & CLONE_PARENT_SETTID) {
+               tprintf("%sparent_tid=", pfx);
+               printnum_int(tcp, arg.parent_tid, "%d"); /* TID */
+               pfx = ", ";
+       }
+
+       if (size > fetch_size) {
+               /*
+                * TODO: it is possible to also store the tail on entering
+                *       and then compare against it on exiting in order
+                *       to avoid double-printing, but it would also require yet
+                *       another complication of print_nonzero_bytes interface.
+                */
+               if (print_nonzero_bytes(tcp, pfx, addr, fetch_size,
+                                       MIN(size, get_pagesize()),
+                                       QUOTE_FORCE_HEX))
+                       pfx = ", ";
+       }
+
+       if (pfx != initial_pfx)
+               tprints("}");
+
+out:
+       tprintf(", %" PRI_klu, size);
+
+       return RVAL_DECODED;
+}
+
+
 SYS_FUNC(setns)
 {
        printfd(tcp, tcp->u_arg[0]);
index a7938e5c264c247c621e7c9775c4c5d8ea668707..9c1d16fa05962a315897e5855809678a447a0c31 100644 (file)
@@ -423,6 +423,7 @@ AC_CHECK_HEADERS(m4_normalize([
        linux/nsfs.h
        linux/perf_event.h
        linux/quota.h
+       linux/sched.h
        linux/seccomp.h
        linux/securebits.h
        linux/sem.h
@@ -613,6 +614,8 @@ AC_CHECK_MEMBERS(m4_normalize([
                struct iocb.aio_rw_flags
                ]),,, [#include <linux/aio_abi.h>])
 
+AC_CHECK_TYPES([struct clone_args],,, [#include <linux/sched.h>])
+
 CPPFLAGS="$saved_CPPFLAGS"
 
 AC_CHECK_HEADERS([linux/btrfs.h], [
index 5db092239dd5291ef321620cfef0bf3d28fc4851..8d4b463aa734da2159505435df7f2983dc63906c 100644 (file)
@@ -19,3 +19,4 @@
 [BASE_NR + 432] = { 3, TD,             SEN(fsmount),                   "fsmount"               },
 [BASE_NR + 433] = { 3, TD|TF,          SEN(fspick),                    "fspick"                },
 [BASE_NR + 434] = { 2, TD,             SEN(pidfd_open),                "pidfd_open"            },
+[BASE_NR + 435] = { 2, TP,             SEN(clone3),                    "clone3"                },
index 774d92be3f1ab0532e25686f2b64d719e8036b9a..378f9e095c3f69903a2c75134d798722f29dba8a 100644 (file)
@@ -42,6 +42,14 @@ clock_adjtime
 clock_nanosleep
 clock_xettime
 clone-flags
+clone3
+clone3-Xabbrev
+clone3-Xraw
+clone3-Xverbose
+clone3-success
+clone3-success-Xabbrev
+clone3-success-Xraw
+clone3-success-Xverbose
 clone_parent
 clone_ptrace
 copy_file_range
index ec52423a21d0281dc9244262f4d37273a2a12bfd..b89c4b03e2aa7070d535114c11f98b1549c70727 100644 (file)
@@ -86,6 +86,10 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
        check_sigign \
        clone_parent \
        clone_ptrace \
+       clone3-success \
+       clone3-success-Xabbrev \
+       clone3-success-Xraw \
+       clone3-success-Xverbose \
        count-f \
        delay \
        execve-v \
@@ -235,6 +239,7 @@ DECODER_TESTS = \
        caps-abbrev.test \
        caps.test \
        clone-flags.test \
+       clone3-success.test \
        eventfd.test \
        execve-v.test \
        execve.test \
diff --git a/tests/clone3-Xabbrev.c b/tests/clone3-Xabbrev.c
new file mode 100644 (file)
index 0000000..2b7e417
--- /dev/null
@@ -0,0 +1 @@
+#include "clone3.c"
diff --git a/tests/clone3-Xraw.c b/tests/clone3-Xraw.c
new file mode 100644 (file)
index 0000000..1e1facb
--- /dev/null
@@ -0,0 +1,2 @@
+#define XLAT_RAW 1
+#include "clone3.c"
diff --git a/tests/clone3-Xverbose.c b/tests/clone3-Xverbose.c
new file mode 100644 (file)
index 0000000..23c45a0
--- /dev/null
@@ -0,0 +1,2 @@
+#define XLAT_VERBOSE 1
+#include "clone3.c"
diff --git a/tests/clone3-success-Xabbrev.c b/tests/clone3-success-Xabbrev.c
new file mode 100644 (file)
index 0000000..8df14f9
--- /dev/null
@@ -0,0 +1,2 @@
+#define RETVAL_INJECTED 1
+#include "clone3.c"
diff --git a/tests/clone3-success-Xraw.c b/tests/clone3-success-Xraw.c
new file mode 100644 (file)
index 0000000..9a81c75
--- /dev/null
@@ -0,0 +1,3 @@
+#define RETVAL_INJECTED 1
+#define XLAT_RAW 1
+#include "clone3.c"
diff --git a/tests/clone3-success-Xverbose.c b/tests/clone3-success-Xverbose.c
new file mode 100644 (file)
index 0000000..2b2a3bd
--- /dev/null
@@ -0,0 +1,3 @@
+#define RETVAL_INJECTED 1
+#define XLAT_VERBOSE 1
+#include "clone3.c"
diff --git a/tests/clone3-success.c b/tests/clone3-success.c
new file mode 100644 (file)
index 0000000..8df14f9
--- /dev/null
@@ -0,0 +1,2 @@
+#define RETVAL_INJECTED 1
+#include "clone3.c"
diff --git a/tests/clone3-success.test b/tests/clone3-success.test
new file mode 100755 (executable)
index 0000000..4db29f4
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh -efu
+#
+# Copyright (c) 2019 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+. "${srcdir=.}/scno_tampering.sh"
+
+run_strace -a10 "$@" -e trace=clone3 -e inject=clone3:retval=42 \
+       "../$NAME" > "$EXP"
+match_diff "$LOG" "$EXP"
diff --git a/tests/clone3.c b/tests/clone3.c
new file mode 100644 (file)
index 0000000..8f9f5b1
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * Check decoding of clone3 syscall.
+ *
+ * Copyright (c) 2019 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "tests.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_LINUX_SCHED_H
+# include <linux/sched.h>
+#endif
+
+#ifdef HAVE_STRUCT_USER_DESC
+# include <asm/ldt.h>
+#endif
+
+#include "scno.h"
+
+#ifndef VERBOSE
+# define VERBOSE 0
+#endif
+#ifndef RETVAL_INJECTED
+# define RETVAL_INJECTED 0
+#endif
+
+#ifndef HAVE_STRUCT_CLONE_ARGS
+# include <stdint.h>
+# include <linux/types.h>
+
+# define XLAT_MACROS_ONLY
+#  include "xlat/clone_flags.h"
+# undef XLAT_MACROS_ONLY
+
+struct clone_args {
+       uint64_t flags;
+       uint64_t pidfd;
+       uint64_t child_tid;
+       uint64_t parent_tid;
+       uint64_t exit_signal;
+       uint64_t stack;
+       uint64_t stack_size;
+       uint64_t tls;
+};
+#endif /* !HAVE_STRUCT_CLONE_ARGS */
+
+enum validity_flag_bits {
+       STRUCT_VALID_BIT,
+       PIDFD_VALID_BIT,
+       CHILD_TID_VALID_BIT,
+       PARENT_TID_VALID_BIT,
+       TLS_VALID_BIT,
+};
+
+#define _(x_) x_ = 1 << x_##_BIT
+
+enum validity_flags {
+       _(STRUCT_VALID),
+       _(PIDFD_VALID),
+       _(CHILD_TID_VALID),
+       _(PARENT_TID_VALID),
+       _(TLS_VALID),
+};
+
+#undef _
+
+static const int child_exit_status = 42;
+
+#if RETVAL_INJECTED
+static const long injected_retval = 42;
+
+# define INJ_STR " (INJECTED)\n"
+#else /* !RETVAL_INJECTED */
+# define INJ_STR "\n"
+#endif /* RETVAL_INJECTED */
+
+
+#if !RETVAL_INJECTED
+static void
+wait_cloned(int pid)
+{
+       int status;
+
+       errno = 0;
+       while (waitpid(pid, &status, WEXITED | __WCLONE) != pid) {
+               if (errno != EINTR)
+                       perror_msg_and_fail("waitpid(%d)", pid);
+       }
+}
+#endif
+
+static long
+do_clone3_(void *args, kernel_ulong_t size, bool should_fail, int line)
+{
+       long rc = syscall(__NR_clone3, args, size);
+
+#if RETVAL_INJECTED
+       if (rc != injected_retval)
+               perror_msg_and_fail("%d: Unexpected injected return value "
+                                   "of a clone3() call (%ld instead of %ld)",
+                                   line, rc, injected_retval);
+#else
+       if (should_fail && rc >= 0)
+               error_msg_and_fail("%d: Unexpected success of a clone3() call",
+                                  line);
+
+       if (!should_fail && rc < 0 && errno != ENOSYS)
+               perror_msg_and_fail("%d: Unexpected failure of a clone3() call",
+                                   line);
+
+       if (!rc)
+               _exit(child_exit_status);
+
+       if (rc > 0 && ((struct clone_args *) args)->exit_signal)
+               wait_cloned(rc);
+#endif
+
+       return rc;
+}
+
+#define do_clone3(args_, size_, should_fail_) \
+       do_clone3_((args_), (size_), (should_fail_), __LINE__)
+
+static inline void
+print_addr64(const char *pfx, uint64_t addr)
+{
+       if (addr)
+               printf("%s%#" PRIx64, pfx, addr);
+       else
+               printf("%sNULL", pfx);
+}
+
+static void
+print_tls(const char *pfx, uint64_t arg_ptr, enum validity_flags vf)
+{
+# if defined HAVE_STRUCT_USER_DESC && defined __i386__
+       if (!(vf & TLS_VALID)) {
+               print_addr64(pfx, arg_ptr);
+               return;
+       }
+
+       struct user_desc *arg = (struct user_desc *) (uintptr_t) arg_ptr;
+
+       printf("%s{entry_number=%d"
+              ", base_addr=%#08x"
+              ", limit=%#08x"
+              ", seg_32bit=%u"
+              ", contents=%u"
+              ", read_exec_only=%u"
+              ", limit_in_pages=%u"
+              ", seg_not_present=%u"
+              ", useable=%u}",
+              pfx,
+              arg->entry_number,
+              arg->base_addr,
+              arg->limit,
+              arg->seg_32bit,
+              arg->contents,
+              arg->read_exec_only,
+              arg->limit_in_pages,
+              arg->seg_not_present,
+              arg->useable);
+# else
+       print_addr64(pfx, arg_ptr);
+# endif
+}
+
+static inline void
+print_clone3(struct clone_args *const arg, long rc, kernel_ulong_t sz,
+            enum validity_flags valid,
+            const char *flags_str, const char *es_str)
+{
+       int saved_errno = errno;
+
+       if (!(valid & STRUCT_VALID)) {
+               printf("%p", arg);
+               goto out;
+       }
+
+#if XLAT_RAW
+       printf("{flags=%#" PRIx64, (uint64_t) arg->flags);
+#elif XLAT_VERBOSE
+       if (flags_str[0] == '0')
+               printf("{flags=%#" PRIx64, (uint64_t) arg->flags);
+       else
+               printf("{flags=%#" PRIx64 " /* %s */",
+                      (uint64_t) arg->flags, flags_str);
+#else
+       printf("{flags=%s", flags_str);
+#endif
+
+       if (arg->flags & CLONE_PIDFD)
+               print_addr64(", pidfd=", arg->pidfd);
+
+       if (arg->flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) {
+               if (valid & CHILD_TID_VALID)
+                       printf(", child_tid=[%d]",
+                              *(int *) (uintptr_t) arg->child_tid);
+               else
+                       print_addr64(", child_tid=", arg->child_tid);
+       }
+
+       if (arg->flags & CLONE_PARENT_SETTID)
+               print_addr64(", parent_tid=", arg->parent_tid);
+
+       printf(", exit_signal=%s", es_str);
+       print_addr64(", stack=", arg->stack);
+       printf(", stack_size=%" PRIx64, (uint64_t) arg->stack_size);
+
+       if (arg->flags & CLONE_SETTLS)
+               print_tls("tls=", arg->tls, valid);
+
+       printf("}");
+
+       if (rc < 0)
+               goto out;
+
+       bool comma = false;
+
+       if (arg->flags & CLONE_PIDFD) {
+               if (valid & PIDFD_VALID)
+                       printf(" => {pidfd=[%d]",
+                              *(int *) (uintptr_t) arg->pidfd);
+               else
+                       print_addr64(" => {pidfd=", arg->pidfd);
+
+               comma = true;
+       }
+
+       if (arg->flags & CLONE_PARENT_SETTID) {
+               printf(comma ? ", " : " => {");
+
+               if (valid & PARENT_TID_VALID)
+                       printf("parent_tid=[%d]",
+                              *(int *) (uintptr_t) arg->parent_tid);
+               else
+                       print_addr64("parent_tid=", arg->parent_tid);
+
+               comma = true;
+       }
+
+       if (comma)
+               printf("}");
+
+out:
+       errno = saved_errno;
+}
+
+int
+main(int argc, char *argv[])
+{
+       static const struct {
+               struct clone_args args;
+               bool should_fail;
+               enum validity_flags vf;
+               const char *flags_str;
+               const char *es_str;
+       } arg_vals[] = {
+               { { .flags = 0 },
+                       false, 0, "0", "0" },
+               { { .flags = CLONE_PARENT_SETTID },
+                       false, 0, "CLONE_PARENT_SETTID", "0" },
+       };
+
+       struct clone_args *arg = tail_alloc(sizeof(*arg));
+       struct clone_args *arg2 = tail_alloc(sizeof(*arg2) + 8);
+       int *pidfd = tail_alloc(sizeof(*pidfd));
+       int *child_tid = tail_alloc(sizeof(*child_tid));
+       int *parent_tid = tail_alloc(sizeof(*parent_tid));
+       long rc;
+
+# if defined HAVE_STRUCT_USER_DESC
+       struct user_desc *tls = tail_alloc(sizeof(*tls));
+
+       fill_memory(tls, sizeof(*tls));
+# else
+       int *tls = tail_alloc(sizeof(*tls));
+# endif
+
+       *pidfd = 0xbadc0ded;
+       *child_tid = 0xdeadface;
+       *parent_tid = 0xfeedbeef;
+
+       rc = do_clone3(NULL, 0, true);
+       printf("clone3(NULL, 0) = %s" INJ_STR, sprintrc(rc));
+
+       rc = do_clone3(arg + 1, sizeof(*arg), true);
+       printf("clone3(%p, %zu) = %s" INJ_STR,
+              arg + 1, sizeof(*arg), sprintrc(rc));
+
+       rc = do_clone3((char *) arg + sizeof(uint64_t),
+                      sizeof(*arg) - sizeof(uint64_t), true);
+       printf("clone3(%p, %zu) = %s" INJ_STR,
+              (char *) arg + sizeof(uint64_t), sizeof(*arg) - sizeof(uint64_t),
+              sprintrc(rc));
+
+
+       memset(arg, 0, sizeof(*arg));
+       memset(arg2, 0, sizeof(*arg2) + 8);
+
+       rc = do_clone3(arg, 64, false);
+       printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0}, 64)"
+              " = %s" INJ_STR,
+              sprintrc(rc));
+
+       rc = do_clone3(arg, sizeof(*arg) + 8, true);
+       printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0, ???}"
+#if RETVAL_INJECTED
+              " => {???}"
+#endif
+              ", %zu) = %s" INJ_STR,
+              sizeof(*arg) + 8, sprintrc(rc));
+
+       rc = do_clone3(arg2, sizeof(*arg2) + 8, false);
+       printf("clone3({flags=0, exit_signal=0, stack=NULL, stack_size=0}"
+              ", %zu) = %s" INJ_STR,
+              sizeof(*arg2) + 8, sprintrc(rc));
+
+       /*
+        * NB: the following check is purposedly fragile (it will break
+        *     when system's struct clone_args has additional fields,
+        *     so it signalises that the decoder needs to be updated.
+        */
+       arg2[1].flags = 0xfacefeeddeadc0de;
+       arg2->exit_signal = 0xdeadface00000000ULL | SIGCHLD;
+       rc = do_clone3(arg2, sizeof(*arg2) + 8, true);
+       printf("clone3({flags=0, exit_signal=%llu, stack=NULL, stack_size=0"
+              ", /* bytes %zu..%zu */ "
+#if WORDS_BIGENDIAN
+              "\"\\xfa\\xce\\xfe\\xed\\xde\\xad\\xc0\\xde\""
+#else
+              "\"\\xde\\xc0\\xad\\xde\\xed\\xfe\\xce\\xfa\""
+#endif
+#if RETVAL_INJECTED
+              "} => {/* bytes %zu..%zu */ "
+# if WORDS_BIGENDIAN
+              "\"\\xfa\\xce\\xfe\\xed\\xde\\xad\\xc0\\xde\""
+# else
+              "\"\\xde\\xc0\\xad\\xde\\xed\\xfe\\xce\\xfa\""
+# endif
+#endif /* RETVAL_INJECTED */
+              "}, %zu) = %s" INJ_STR,
+              0xdeadface00000000ULL | SIGCHLD,
+              sizeof(*arg2), sizeof(*arg2) + 7,
+#if RETVAL_INJECTED
+              sizeof(*arg2), sizeof(*arg2) + 7,
+#endif
+              sizeof(*arg2) + 8, sprintrc(rc));
+
+       arg2->exit_signal = 0xdeadc0de;
+       rc = do_clone3(arg2, sizeof(*arg) + 16, true);
+       printf("clone3({flags=0, exit_signal=3735929054, stack=NULL"
+              ", stack_size=0, ???}"
+#if RETVAL_INJECTED
+              " => {???}"
+#endif
+              ", %zu) = %s" INJ_STR,
+              sizeof(*arg) + 16, sprintrc(rc));
+
+       arg->flags = 0xfacefeedbeefc0de;
+       arg->exit_signal = 0x1e55c0de;
+       rc = do_clone3(arg, 64, true);
+       printf("clone3({flags=%s, child_tid=NULL, exit_signal=508936414"
+              ", stack=NULL, stack_size=0, tls=NULL}, 64) = %s" INJ_STR,
+              XLAT_KNOWN(0xfacefeedbeefc0de, "CLONE_VFORK|CLONE_PARENT"
+              "|CLONE_THREAD|CLONE_NEWNS|CLONE_SYSVSEM|CLONE_SETTLS"
+              "|CLONE_CHILD_CLEARTID|CLONE_UNTRACED|CLONE_NEWCGROUP"
+              "|CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|CLONE_NEWPID|CLONE_IO"
+              "|0xfacefeed004000de"), sprintrc(rc));
+
+       arg->flags = 0xdec0dead004000ffULL;
+       arg->exit_signal = 250;
+       arg->stack = 0xface1e55beeff00dULL;
+       arg->stack_size = 0xcaffeedefacedca7ULL;
+       rc = do_clone3(arg, 64, true);
+       printf("clone3({flags=%s, exit_signal=250"
+              ", stack=0xface1e55beeff00d, stack_size=0xcaffeedefacedca7}, 64)"
+              " = %s" INJ_STR,
+              XLAT_UNKNOWN(0xdec0dead004000ff, "CLONE_???"),
+              sprintrc(rc));
+
+       arg->exit_signal = SIGCHLD;
+
+       struct {
+               uint64_t flag;
+               const char *flag_str;
+               uint64_t *field;
+               const char *field_name;
+               int *ptr;
+               bool deref_exiting;
+       } pid_fields[] = {
+               { ARG_STR(CLONE_PIDFD),
+                       (uint64_t *) &arg->pidfd,
+                       "pidfd", pidfd, true },
+               { ARG_STR(CLONE_CHILD_SETTID),
+                       (uint64_t *) &arg->child_tid,
+                       "child_tid", child_tid },
+               { ARG_STR(CLONE_CHILD_CLEARTID),
+                       (uint64_t *) &arg->child_tid,
+                       "child_tid", child_tid },
+               { ARG_STR(CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID),
+                       (uint64_t *) &arg->child_tid,
+                       "child_tid", child_tid },
+               { ARG_STR(CLONE_PARENT_SETTID),
+                       (uint64_t *) &arg->parent_tid,
+                       "parent_tid", parent_tid, true },
+       };
+
+       for (size_t i = 0; i < ARRAY_SIZE(pid_fields); i++) {
+               char flag_str[128];
+               const char *rc_str;
+
+               arg->flags = 0xbad0000000000001ULL | pid_fields[i].flag;
+
+#if XLAT_RAW
+               snprintf(flag_str, sizeof(flag_str), "%#" PRIx64,
+                        (uint64_t) arg->flags);
+#elif XLAT_VERBOSE
+               snprintf(flag_str, sizeof(flag_str),
+                        "%#" PRIx64 " /* %s|0xbad0000000000001 */",
+                        (uint64_t) arg->flags, pid_fields[i].flag_str);
+#else
+               snprintf(flag_str, sizeof(flag_str), "%s|0xbad0000000000001",
+                        pid_fields[i].flag_str);
+#endif
+
+               pid_fields[i].field[0] = 0;
+               rc = do_clone3(arg, 64, true);
+               rc_str = sprintrc(rc);
+               printf("clone3({flags=%s, %s=NULL"
+                      ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
+                      ", stack=0xface1e55beeff00d"
+                      ", stack_size=0xcaffeedefacedca7}",
+                      flag_str, pid_fields[i].field_name);
+#if RETVAL_INJECTED
+               if (pid_fields[i].deref_exiting)
+                       printf(" => {%s=NULL}", pid_fields[i].field_name);
+#endif /* RETVAL_INJECTED */
+               printf(", 64) = %s" INJ_STR, rc_str);
+
+               pid_fields[i].field[0] = (uintptr_t) (pid_fields[i].ptr + 1);
+               rc = do_clone3(arg, 64, true);
+               rc_str = sprintrc(rc);
+               printf("clone3({flags=%s, %s=%p"
+                      ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
+                      ", stack=0xface1e55beeff00d"
+                      ", stack_size=0xcaffeedefacedca7}",
+                      flag_str, pid_fields[i].field_name,
+                      pid_fields[i].ptr + 1);
+#if RETVAL_INJECTED
+               if (pid_fields[i].deref_exiting)
+                       printf(" => {%s=%p}",
+                              pid_fields[i].field_name, pid_fields[i].ptr + 1);
+#endif /* RETVAL_INJECTED */
+               printf(", 64) = %s" INJ_STR, rc_str);
+
+               pid_fields[i].field[0] = (uintptr_t) pid_fields[i].ptr;
+               rc = do_clone3(arg, 64, true);
+               rc_str = sprintrc(rc);
+               printf("clone3({flags=%s, %s=%p"
+                      ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
+                      ", stack=0xface1e55beeff00d"
+                      ", stack_size=0xcaffeedefacedca7}",
+                      flag_str, pid_fields[i].field_name,
+                      pid_fields[i].ptr);
+#if RETVAL_INJECTED
+               if (pid_fields[i].deref_exiting)
+                       printf(" => {%s=[%d]}",
+                              pid_fields[i].field_name, *pid_fields[i].ptr);
+#endif /* RETVAL_INJECTED */
+               printf(", 64) = %s" INJ_STR, rc_str);
+       }
+
+       arg->flags = 0xbad0000000000001ULL | CLONE_SETTLS;
+       rc = do_clone3(arg, 64, true);
+       printf("clone3({flags="
+              XLAT_KNOWN(0xbad0000000080001, "CLONE_SETTLS|0xbad0000000000001")
+              ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
+              ", stack=0xface1e55beeff00d"
+              ", stack_size=0xcaffeedefacedca7, tls=NULL}, 64) = %s" INJ_STR,
+              sprintrc(rc));
+
+       arg->tls = (uintptr_t) (tls + 1);
+       rc = do_clone3(arg, 64, true);
+       printf("clone3({flags="
+              XLAT_KNOWN(0xbad0000000080001, "CLONE_SETTLS|0xbad0000000000001")
+              ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
+              ", stack=0xface1e55beeff00d"
+              ", stack_size=0xcaffeedefacedca7, tls=%p}, 64) = %s" INJ_STR,
+              tls + 1, sprintrc(rc));
+
+       arg->tls = (uintptr_t) tls;
+       rc = do_clone3(arg, 64, true);
+       printf("clone3({flags="
+              XLAT_KNOWN(0xbad0000000080001, "CLONE_SETTLS|0xbad0000000000001")
+              ", exit_signal=" XLAT_KNOWN(SIGCHLD, "SIGCHLD")
+              ", stack=0xface1e55beeff00d, stack_size=0xcaffeedefacedca7, tls="
+#if defined HAVE_STRUCT_USER_DESC && defined __i386__
+              "{entry_number=2206368128, base_addr=0x87868584"
+              ", limit=0x8b8a8988, seg_32bit=0, contents=2, read_exec_only=1"
+              ", limit_in_pages=0, seg_not_present=0, useable=0}"
+#else
+              "%p"
+#endif
+              "}, 64) = %s" INJ_STR,
+#if !defined HAVE_STRUCT_USER_DESC || !defined __i386__
+              tls,
+#endif
+              sprintrc(rc));
+
+       for (size_t i = 0; i < ARRAY_SIZE(arg_vals); i++) {
+               memcpy(arg, &arg_vals[i].args, sizeof(*arg));
+
+               rc = do_clone3(arg, sizeof(*arg), arg_vals[i].should_fail);
+               printf("clone3(");
+               print_clone3(arg, rc, sizeof(*arg),
+                            arg_vals[i].vf | STRUCT_VALID,
+                            arg_vals[i].flags_str, arg_vals[i].es_str);
+               printf(", %zu) = %s" INJ_STR, sizeof(*arg), sprintrc(rc));
+       }
+
+       puts("+++ exited with 0 +++");
+
+       return 0;
+}
index 13a4619447e0cad9c79958698d18df530f982692..2b54d9a3c370af4f2f4607687c802195577b07e8 100644 (file)
@@ -31,6 +31,13 @@ clock        test_trace_expr 'times|fcntl.*' -e/clock
 clock_adjtime  -a37
 clock_nanosleep        -e trace=clock_nanosleep,clock_gettime
 clock_xettime  -a36 -e trace=clock_getres,clock_gettime,clock_settime
+clone3 -a16
+clone3-Xabbrev -a16 -Xabbrev  -e trace=clone3
+clone3-Xraw    -a16 -Xraw     -e trace=clone3
+clone3-Xverbose        -a16 -Xverbose -e trace=clone3
+clone3-success-Xabbrev +clone3-success.test -a16 -Xabbrev
+clone3-success-Xraw    +clone3-success.test -a16 -Xraw
+clone3-success-Xverbose        +clone3-success.test -a16 -Xverbose
 copy_file_range
 creat  -a20
 delete_module  -a23
index a8b03a825447e6d71e9de5afdc5ccd2c1509232a..f89dfd91718817bfef4d369f11fffd8ef04fe166 100755 (executable)
@@ -32,6 +32,10 @@ clock_adjtime
 clock_nanosleep
 clock_xettime
 clone-flags
+clone3
+clone3-Xabbrev
+clone3-Xraw
+clone3-Xverbose
 copy_file_range
 creat
 delete_module