fs_x_ioctl.c \
futex.c \
gcc_compat.h \
+ get_personality.c \
+ get_personality.h \
get_robust_list.c \
getcpu.c \
getcwd.c \
linux/64/ioctls_inc.h \
linux/64/syscallent.h \
linux/aarch64/arch_defs_.h \
+ linux/aarch64/arch_get_personality.c \
linux/aarch64/arch_regs.c \
linux/aarch64/arch_sigreturn.c \
linux/aarch64/get_error.c \
linux/bfin/set_scno.c \
linux/bfin/syscallent.h \
linux/bfin/userent.h \
+ linux/check_scno.c \
linux/dummy.h \
linux/errnoent.h \
linux/getregs_old.h \
linux/hppa/set_scno.c \
linux/hppa/signalent.h \
linux/hppa/syscallent.h \
- linux/i386/arch_kvm.c \
linux/i386/arch_defs_.h \
+ linux/i386/arch_kvm.c \
linux/i386/arch_regs.c \
linux/i386/arch_rt_sigframe.c \
linux/i386/arch_sigreturn.c \
linux/powerpc/syscallent.h \
linux/powerpc/userent.h \
linux/powerpc64/arch_defs_.h \
+ linux/powerpc64/arch_get_personality.c \
linux/powerpc64/arch_regs.c \
linux/powerpc64/arch_rt_sigframe.c \
linux/powerpc64/arch_sigreturn.c \
linux/ptrace_pokeuser.c \
linux/raw_syscall.h \
linux/riscv/arch_defs_.h \
+ linux/riscv/arch_get_personality.c \
linux/riscv/arch_regs.c \
linux/riscv/get_error.c \
linux/riscv/get_scno.c \
linux/s390/userent0.h \
linux/s390/userent1.h \
linux/s390x/arch_defs_.h \
+ linux/s390x/arch_get_personality.c \
linux/s390x/arch_regs.c \
linux/s390x/arch_sigreturn.c \
linux/s390x/get_error.c \
linux/sparc/syscallent.h \
linux/sparc/userent.h \
linux/sparc64/arch_defs_.h \
+ linux/sparc64/arch_get_personality.c \
linux/sparc64/arch_getrval2.c \
linux/sparc64/arch_regs.c \
linux/sparc64/arch_rt_sigframe.c \
linux/subcall.h \
linux/syscall.h \
linux/tile/arch_defs_.h \
+ linux/tile/arch_get_personality.c \
linux/tile/arch_regs.c \
linux/tile/arch_sigreturn.c \
linux/tile/get_error.c \
linux/userent.h \
linux/userent0.h \
linux/x32/arch_defs_.h \
+ linux/x32/arch_get_personality.c \
linux/x32/arch_kvm.c \
linux/x32/arch_regs.c \
linux/x32/arch_regs.h \
linux/x32/arch_rt_sigframe.c \
linux/x32/arch_sigreturn.c \
+ linux/x32/check_scno.c \
linux/x32/get_error.c \
linux/x32/get_scno.c \
linux/x32/get_syscall_args.c \
linux/x32/syscallent1.h \
linux/x32/userent.h \
linux/x86_64/arch_defs_.h \
+ linux/x86_64/arch_get_personality.c \
linux/x86_64/arch_kvm.c \
linux/x86_64/arch_regs.c \
linux/x86_64/arch_regs.h \
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "defs.h"
+
+#if SUPPORTED_PERSONALITIES > 1
+# include "get_personality.h"
+# include <linux/audit.h>
+# include "arch_get_personality.c"
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef STRACE_GET_PERSONALITY_H
+#define STRACE_GET_PERSONALITY_H
+
+#include "ptrace.h"
+
+extern int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *);
+
+#endif /* !STRACE_GET_PERSONALITY_H */
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_ARM
+# define AUDIT_ARCH_ARM 0x40000028
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ return sci->arch == AUDIT_ARCH_ARM;
+}
--- /dev/null
+/* Return codes: 1 - ok, 0 - ignore, other - error. */
+static int
+arch_check_scno(struct tcb *tcp)
+{
+ return 1;
+}
long
getrval2(struct tcb *tcp)
{
+ if (ptrace_syscall_info_is_valid() && get_regs(tcp) < 0)
+ return -1;
return ia64_regs.gr[9];
}
long
getrval2(struct tcb *tcp)
{
+ if (ptrace_syscall_info_is_valid() && get_regs(tcp) < 0)
+ return -1;
return mips_regs.uregs[3];
}
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_PPC
+# define AUDIT_ARCH_PPC 0x14
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ return sci->arch == AUDIT_ARCH_PPC;
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_RISCV32
+# define AUDIT_ARCH_RISCV32 0x400000f3
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ return sci->arch == AUDIT_ARCH_RISCV32;
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_S390
+# define AUDIT_ARCH_S390 0x16
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ return sci->arch == AUDIT_ARCH_S390;
+}
long
getrval2(struct tcb *tcp)
{
+ if (ptrace_syscall_info_is_valid() && get_regs(tcp) < 0)
+ return -1;
return sparc_regs.u_regs[U_REG_O1];
}
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_SPARC
+# define AUDIT_ARCH_SPARC 0x2
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ return sci->arch == AUDIT_ARCH_SPARC;
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_TILEGX32
+# define AUDIT_ARCH_TILEGX32 0x400000bf
+#endif
+#ifndef AUDIT_ARCH_TILEPRO
+# define AUDIT_ARCH_TILEPRO 0x400000bc
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ return sci->arch == AUDIT_ARCH_TILEGX32 ||
+ sci->arch == AUDIT_ARCH_TILEPRO;
+}
--- /dev/null
+#include "x86_64/arch_get_personality.c"
--- /dev/null
+/*
+ * Copyright (c) 2010-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+/* Return codes: 1 - ok, 0 - ignore, other - error. */
+static int
+arch_check_scno(struct tcb *tcp)
+{
+
+ const kernel_ulong_t scno = ptrace_sci.entry.nr;
+
+ if (tcp->currpers == 0 && !(scno & __X32_SYSCALL_BIT)) {
+ error_msg("syscall_%" PRI_klu "(...) in unsupported "
+ "64-bit mode of process PID=%d", scno, tcp->pid);
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef AUDIT_ARCH_I386
+# define AUDIT_ARCH_I386 0x40000003
+#endif
+
+int
+get_personality_from_syscall_info(const struct ptrace_syscall_info *sci)
+{
+ unsigned int pers = sci->arch == AUDIT_ARCH_I386;
+
+#ifndef X32
+ switch(sci->op) {
+ case PTRACE_SYSCALL_INFO_ENTRY:
+ case PTRACE_SYSCALL_INFO_SECCOMP:
+ break;
+ default:
+ return -1;
+ }
+
+ kernel_ulong_t scno = sci->entry.nr;
+
+#ifndef __X32_SYSCALL_BIT
+# define __X32_SYSCALL_BIT 0x40000000
+#endif
+
+ if (pers == 0 && (scno & __X32_SYSCALL_BIT)) {
+ /*
+ * Syscall number -1 requires special treatment:
+ * it might be a side effect of SECCOMP_RET_ERRNO
+ * filtering that sets orig_rax to -1
+ * in some versions of linux kernel.
+ * If that is the case, then
+ * __X32_SYSCALL_BIT logic does not apply.
+ */
+ if (scno != (kernel_ulong_t) -1)
+ pers = 2;
+ }
+#endif /* !X32 */
+
+ return pers;
+}
*/
#include "defs.h"
+#include "get_personality.h"
#include "mmap_notify.h"
#include "native_defs.h"
#include "ptrace.h"
+#include "ptrace_syscall_info.h"
#include "nsig.h"
#include "number_set.h"
#include "delay.h"
static void set_error(struct tcb *, unsigned long);
static void set_success(struct tcb *, kernel_long_t);
static int arch_get_scno(struct tcb *);
+static int arch_check_scno(struct tcb *);
static int arch_set_scno(struct tcb *, kernel_ulong_t);
static int arch_get_syscall_args(struct tcb *);
static void arch_get_error(struct tcb *, bool);
tcp->u_error = saved_u_error;
}
+static struct ptrace_syscall_info ptrace_sci;
+
+static bool
+ptrace_syscall_info_is_valid(void)
+{
+ return ptrace_get_syscall_info_supported &&
+ ptrace_sci.op <= PTRACE_SYSCALL_INFO_SECCOMP;
+}
+
#define XLAT_MACROS_ONLY
# include "xlat/nt_descriptor_types.h"
#undef XLAT_MACROS_ONLY
#define ARCH_MIGHT_USE_SET_REGS 1
+
#include "arch_regs.c"
#if HAVE_ARCH_GETRVAL2
#endif /* ARCH_REGS_FOR_GETREGSET || ARCH_REGS_FOR_GETREGS */
-#ifdef ptrace_getregset_or_getregs
-static long get_regs_error;
-#endif
+static long get_regs_error = -1;
void
clear_regs(struct tcb *tcp)
{
-#ifdef ptrace_getregset_or_getregs
+ ptrace_sci.op = 0xff;
get_regs_error = -1;
-#endif
}
static long
free(ptr);
}
+static bool
+ptrace_get_syscall_info(struct tcb *tcp)
+{
+ /*
+ * ptrace_get_syscall_info_supported should have been checked
+ * by the caller.
+ */
+ if (ptrace_sci.op == 0xff) {
+ const size_t size = sizeof(ptrace_sci);
+ if (ptrace(PTRACE_GET_SYSCALL_INFO, tcp->pid,
+ (void *) size, &ptrace_sci) < 0) {
+ get_regs_error = -2;
+ return false;
+ }
+#if SUPPORTED_PERSONALITIES > 1
+ int newpers = get_personality_from_syscall_info(&ptrace_sci);
+ if (newpers >= 0)
+ update_personality(tcp, newpers);
+#endif
+ }
+
+ if (entering(tcp)) {
+ if (ptrace_sci.op == PTRACE_SYSCALL_INFO_EXIT) {
+ error_msg("pid %d: entering"
+ ", ptrace_syscall_info.op == %u",
+ tcp->pid, ptrace_sci.op);
+ /* TODO: handle this. */
+ }
+ } else {
+ if (ptrace_sci.op == PTRACE_SYSCALL_INFO_ENTRY) {
+ error_msg("pid %d: exiting"
+ ", ptrace_syscall_info.op == %u",
+ tcp->pid, ptrace_sci.op);
+ /* TODO: handle this. */
+ }
+ }
+
+ return true;
+}
+
bool
get_instruction_pointer(struct tcb *tcp, kernel_ulong_t *ip)
{
+ if (get_regs_error < -1)
+ return false;
+
+ if (ptrace_get_syscall_info_supported) {
+ if (!ptrace_get_syscall_info(tcp))
+ return false;
+ *ip = (kernel_ulong_t) ptrace_sci.instruction_pointer;
+ return true;
+ }
+
#if defined ARCH_PC_REG
if (get_regs(tcp) < 0)
return false;
bool
get_stack_pointer(struct tcb *tcp, kernel_ulong_t *sp)
{
+ if (get_regs_error < -1)
+ return false;
+
+ if (ptrace_get_syscall_info_supported) {
+ if (!ptrace_get_syscall_info(tcp))
+ return false;
+ *sp = (kernel_ulong_t) ptrace_sci.stack_pointer;
+ return true;
+ }
+
#if defined ARCH_SP_REG
if (get_regs(tcp) < 0)
return false;
#endif
}
+static int
+get_syscall_regs(struct tcb *tcp)
+{
+ if (get_regs_error != -1)
+ return get_regs_error;
+
+ if (ptrace_get_syscall_info_supported)
+ return ptrace_get_syscall_info(tcp) ? 0 : get_regs_error;
+
+ return get_regs(tcp);
+}
+
/*
* Returns:
* 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail
int
get_scno(struct tcb *tcp)
{
- if (get_regs(tcp) < 0)
+ if (get_syscall_regs(tcp) < 0)
return -1;
- int rc = arch_get_scno(tcp);
- if (rc != 1)
- return rc;
+ if (ptrace_syscall_info_is_valid()) {
+ /*
+ * So far it's just a workaround for x32,
+ * but let's pretend it could be used elsewhere.
+ */
+ int rc = arch_check_scno(tcp);
+ if (rc != 1)
+ return rc;
+ tcp->scno = ptrace_sci.entry.nr;
+ } else {
+ int rc = arch_get_scno(tcp);
+ if (rc != 1)
+ return rc;
+ }
tcp->scno = shuffle_scno(tcp->scno);
static int
get_syscall_args(struct tcb *tcp)
{
+ if (ptrace_syscall_info_is_valid()) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(tcp->u_arg); ++i)
+ tcp->u_arg[i] = ptrace_sci.entry.args[i];
+#if SUPPORTED_PERSONALITIES > 1
+ if (tcp->s_ent->sys_flags & COMPAT_SYSCALL_TYPES) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(tcp->u_arg); ++i)
+ tcp->u_arg[i] = (uint32_t) tcp->u_arg[i];
+ }
+#endif
+ return 1;
+ }
return arch_get_syscall_args(tcp);
}
#ifdef ptrace_getregset_or_getregs
-# define get_syscall_result_regs get_regs
+# define get_syscall_result_regs get_syscall_regs
#else
static int get_syscall_result_regs(struct tcb *);
#endif
static void
get_error(struct tcb *tcp, const bool check_errno)
{
- tcp->u_error = 0;
- arch_get_error(tcp, check_errno);
+ if (ptrace_syscall_info_is_valid()) {
+ if (ptrace_sci.exit.is_error) {
+ tcp->u_rval = -1;
+ tcp->u_error = -ptrace_sci.exit.rval;
+ } else {
+ tcp->u_error = 0;
+ tcp->u_rval = ptrace_sci.exit.rval;
+ }
+ } else {
+ tcp->u_error = 0;
+ arch_get_error(tcp, check_errno);
+ }
}
static void
if (new_error == old_error || new_error > MAX_ERRNO_VALUE)
return;
+#ifdef ptrace_setregset_or_setregs
+ /* if we are going to invoke set_regs, call get_regs first */
+ if (get_regs(tcp) < 0)
+ return;
+#endif
+
tcp->u_error = new_error;
if (arch_set_error(tcp)) {
tcp->u_error = old_error;
/* arch_set_error does not update u_rval */
} else {
- get_error(tcp, !(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS));
+ if (ptrace_syscall_info_is_valid())
+ tcp->u_rval = -1;
+ else
+ get_error(tcp, !(tcp->s_ent->sys_flags &
+ SYSCALL_NEVER_FAILS));
}
}
{
const kernel_long_t old_rval = tcp->u_rval;
+#ifdef ptrace_setregset_or_setregs
+ /* if we are going to invoke set_regs, call get_regs first */
+ if (get_regs(tcp) < 0)
+ return;
+#endif
+
tcp->u_rval = new_rval;
if (arch_set_success(tcp)) {
tcp->u_rval = old_rval;
- /* arch_set_error does not update u_error */
+ /* arch_set_success does not update u_error */
} else {
- get_error(tcp, !(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS));
+ if (ptrace_syscall_info_is_valid())
+ tcp->u_error = 0;
+ else
+ get_error(tcp, !(tcp->s_ent->sys_flags &
+ SYSCALL_NEVER_FAILS));
}
}
#include "get_scno.c"
+#include "check_scno.c"
#include "set_scno.c"
#include "get_syscall_args.c"
#ifndef ptrace_getregset_or_getregs
fadvise64.test \
futex.test \
getuid.test \
+ int_0x80.test \
ioctl.test \
ioctl_evdev-success.test \
ioctl_evdev-success-v.test \
XFAIL_TESTS_ =
XFAIL_TESTS_m32 = $(STACKTRACE_TESTS)
XFAIL_TESTS_mx32 = $(STACKTRACE_TESTS)
-XFAIL_TESTS_x86_64 = int_0x80.gen.test
-XFAIL_TESTS_x32 = int_0x80.gen.test
XFAIL_TESTS = $(XFAIL_TESTS_$(MPERS_NAME)) $(XFAIL_TESTS_$(ARCH))
TEST_LOG_COMPILER = env
init_module -a27
inotify -a23 -e trace=inotify_add_watch,inotify_rm_watch
inotify_init1 -a27
-int_0x80 -a11 -e trace=getgid32
ioctl_block +ioctl.test
ioctl_dm +ioctl.test -s9
ioctl_dm-v +ioctl.test -v -s9
--- /dev/null
+#!/bin/sh
+#
+# Check decoding of int 0x80 on x86_64, x32, and x86.
+
+. "${srcdir=.}/init.sh"
+
+$STRACE -d -enone / > /dev/null 2> "$LOG"
+grep -x "[^:]*strace: PTRACE_GET_SYSCALL_INFO works" "$LOG" > /dev/null ||
+ skip_ 'PTRACE_GET_SYSCALL_INFO does not work'
+
+run_strace_match_diff -a11 -e trace=getgid32