Introduce PTRACE_GET_SYSCALL_INFO support

When PTRACE_GET_SYSCALL_INFO API works,
use it instead of traditional upeek/get_regs API.

Tested on x86_64 and x86.

* get_personality.c: New file.
* get_personality.h: Likewise.
* Makefile.am (strace_SOURCES): Add them.
* linux/aarch64/arch_get_personality.c: New file.
* linux/powerpc64/arch_get_personality.c: Likewise.
* linux/riscv/arch_get_personality.c: Likewise.
* linux/s390x/arch_get_personality.c: Likewise.
* linux/sparc64/arch_get_personality.c: Likewise.
* linux/tile/arch_get_personality.c: Likewise.
* linux/x32/arch_get_personality.c: Likewise.
* linux/x86_64/arch_get_personality.c: Likewise.
* linux/check_scno.c: Likewise.
* linux/x32/check_scno.c: Likewise.
* Makefile.am (EXTRA_DIST): Add them.
* linux/ia64/arch_getrval2.c (getrval2): Invoke get_regs()
if ptrace_syscall_info is in use.
* linux/mips/arch_getrval2.c: Likewise.
* linux/sparc/arch_getrval2.c: Likewise.
* defs.h (get_personality_from_syscall_info): New prototype.
* syscall.c: Include "get_personality.h" and "ptrace_syscall_info.h".
(get_regs_error): Define unconditionally.
(ptrace_sci): New static variable.
(clear_regs): Reset ptrace_sci.op.
(ptrace_syscall_info_is_valid, ptrace_get_syscall_info,
get_syscall_regs): New functions.
(get_syscall_result_regs): Change to get_syscall_regs.
(get_instruction_pointer, get_stack_pointer, get_scno, get_syscall_args,
get_error, set_error, set_success): Add ptrace_syscall_info support.
* tests/int_0x80.test: New test.
* tests/Makefile.am (DECODER_TESTS): Add int_0x80.test.
(XFAIL_TESTS_x86_64, XFAIL_TESTS_x32): Remove.
* tests/gen_tests.in (int_0x80): Remove.
This commit is contained in:
Дмитрий Левин 2018-11-21 01:44:16 +00:00
parent 7488ce9e23
commit e3b80cb65f
20 changed files with 387 additions and 19 deletions

View File

@ -134,6 +134,8 @@ strace_SOURCES = \
fs_x_ioctl.c \
futex.c \
gcc_compat.h \
get_personality.c \
get_personality.h \
get_robust_list.c \
getcpu.c \
getcwd.c \
@ -417,6 +419,7 @@ EXTRA_DIST = \
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 \
@ -505,6 +508,7 @@ EXTRA_DIST = \
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 \
@ -524,8 +528,8 @@ EXTRA_DIST = \
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 \
@ -663,6 +667,7 @@ EXTRA_DIST = \
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 \
@ -686,6 +691,7 @@ EXTRA_DIST = \
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 \
@ -717,6 +723,7 @@ EXTRA_DIST = \
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 \
@ -786,6 +793,7 @@ EXTRA_DIST = \
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 \
@ -809,6 +817,7 @@ EXTRA_DIST = \
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 \
@ -829,11 +838,13 @@ EXTRA_DIST = \
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 \
@ -851,6 +862,7 @@ EXTRA_DIST = \
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 \

14
get_personality.c Normal file
View File

@ -0,0 +1,14 @@
/*
* 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

16
get_personality.h Normal file
View File

@ -0,0 +1,16 @@
/*
* 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 */

View File

@ -0,0 +1,16 @@
/*
* 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;
}

6
linux/check_scno.c Normal file
View File

@ -0,0 +1,6 @@
/* Return codes: 1 - ok, 0 - ignore, other - error. */
static int
arch_check_scno(struct tcb *tcp)
{
return 1;
}

View File

@ -1,5 +1,7 @@
long
getrval2(struct tcb *tcp)
{
if (ptrace_syscall_info_is_valid() && get_regs(tcp) < 0)
return -1;
return ia64_regs.gr[9];
}

View File

@ -1,5 +1,7 @@
long
getrval2(struct tcb *tcp)
{
if (ptrace_syscall_info_is_valid() && get_regs(tcp) < 0)
return -1;
return mips_regs.uregs[3];
}

View File

@ -0,0 +1,16 @@
/*
* 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;
}

View File

@ -0,0 +1,16 @@
/*
* 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;
}

View File

@ -0,0 +1,16 @@
/*
* 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;
}

View File

@ -1,5 +1,7 @@
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];
}

View File

@ -0,0 +1,16 @@
/*
* 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;
}

View File

@ -0,0 +1,20 @@
/*
* 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;
}

View File

@ -0,0 +1 @@
#include "x86_64/arch_get_personality.c"

22
linux/x32/check_scno.c Normal file
View File

@ -0,0 +1,22 @@
/*
* 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;
}

View File

@ -0,0 +1,47 @@
/*
* 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;
}

165
syscall.c
View File

@ -13,9 +13,11 @@
*/
#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"
@ -461,6 +463,7 @@ static void get_error(struct tcb *, bool);
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);
@ -943,11 +946,21 @@ restore_cleared_syserror(struct tcb *tcp)
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
@ -1035,16 +1048,13 @@ ptrace_setregs(pid_t pid)
#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
@ -1139,9 +1149,59 @@ free_sysent_buf(void *ptr)
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;
@ -1159,6 +1219,16 @@ get_instruction_pointer(struct tcb *tcp, kernel_ulong_t *ip)
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;
@ -1173,6 +1243,18 @@ get_stack_pointer(struct tcb *tcp, kernel_ulong_t *sp)
#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
@ -1184,12 +1266,23 @@ get_stack_pointer(struct tcb *tcp, kernel_ulong_t *sp)
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);
@ -1229,11 +1322,22 @@ get_scno(struct tcb *tcp)
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
@ -1259,8 +1363,18 @@ get_syscall_result(struct tcb *tcp)
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
@ -1271,12 +1385,22 @@ set_error(struct tcb *tcp, unsigned long new_error)
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));
}
}
@ -1285,16 +1409,27 @@ set_success(struct tcb *tcp, kernel_long_t new_rval)
{
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

View File

@ -228,6 +228,7 @@ DECODER_TESTS = \
fadvise64.test \
futex.test \
getuid.test \
int_0x80.test \
ioctl.test \
ioctl_evdev-success.test \
ioctl_evdev-success-v.test \
@ -347,8 +348,6 @@ TESTS = $(GEN_TESTS) $(DECODER_TESTS) $(MISC_TESTS) $(STACKTRACE_TESTS)
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

View File

@ -118,7 +118,6 @@ inet-cmsg -e trace=recvmsg
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

11
tests/int_0x80.test Executable file
View File

@ -0,0 +1,11 @@
#!/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