From: Eugene Syromyatnikov Date: Wed, 28 Aug 2019 00:35:53 +0000 (+0200) Subject: clone: implement clone3 syscall decoding X-Git-Tag: v5.3~20 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=91fbffc06dd364b528eb564807c99c73a43a88f9;p=strace clone: implement clone3 syscall decoding * configure.ac (AC_CHECK_HEADERS): Check for linux/sched.h presence. (AC_CHECK_TYPES): Check for struct clone_args in . * 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 --- diff --git a/NEWS b/NEWS index 76fdf58d..867cfdcb 100644 --- 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 7270cee9..2a249eef 100644 --- 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]); diff --git a/configure.ac b/configure.ac index a7938e5c..9c1d16fa 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ]) +AC_CHECK_TYPES([struct clone_args],,, [#include ]) + CPPFLAGS="$saved_CPPFLAGS" AC_CHECK_HEADERS([linux/btrfs.h], [ diff --git a/linux/syscallent-common.h b/linux/syscallent-common.h index 5db09223..8d4b463a 100644 --- a/linux/syscallent-common.h +++ b/linux/syscallent-common.h @@ -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" }, diff --git a/tests/.gitignore b/tests/.gitignore index 774d92be..378f9e09 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index ec52423a..b89c4b03 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..2b7e4175 --- /dev/null +++ b/tests/clone3-Xabbrev.c @@ -0,0 +1 @@ +#include "clone3.c" diff --git a/tests/clone3-Xraw.c b/tests/clone3-Xraw.c new file mode 100644 index 00000000..1e1facb4 --- /dev/null +++ b/tests/clone3-Xraw.c @@ -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 index 00000000..23c45a0b --- /dev/null +++ b/tests/clone3-Xverbose.c @@ -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 index 00000000..8df14f94 --- /dev/null +++ b/tests/clone3-success-Xabbrev.c @@ -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 index 00000000..9a81c752 --- /dev/null +++ b/tests/clone3-success-Xraw.c @@ -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 index 00000000..2b2a3bd1 --- /dev/null +++ b/tests/clone3-success-Xverbose.c @@ -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 index 00000000..8df14f94 --- /dev/null +++ b/tests/clone3-success.c @@ -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 index 00000000..4db29f41 --- /dev/null +++ b/tests/clone3-success.test @@ -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 index 00000000..8f9f5b1e --- /dev/null +++ b/tests/clone3.c @@ -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 +#include +#include +#include +#include +#include + +#ifdef HAVE_LINUX_SCHED_H +# include +#endif + +#ifdef HAVE_STRUCT_USER_DESC +# include +#endif + +#include "scno.h" + +#ifndef VERBOSE +# define VERBOSE 0 +#endif +#ifndef RETVAL_INJECTED +# define RETVAL_INJECTED 0 +#endif + +#ifndef HAVE_STRUCT_CLONE_ARGS +# include +# include + +# 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; +} diff --git a/tests/gen_tests.in b/tests/gen_tests.in index 13a46194..2b54d9a3 100644 --- a/tests/gen_tests.in +++ b/tests/gen_tests.in @@ -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 diff --git a/tests/pure_executables.list b/tests/pure_executables.list index a8b03a82..f89dfd91 100755 --- a/tests/pure_executables.list +++ b/tests/pure_executables.list @@ -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