Remove support for systems without PTRACE_SETOPTIONS
Assume that the kernel is v2.5.46 or newer, i.e. PTRACE_SETOPTIONS and PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACECLONE are universally available. This change removes all code that implemented post-execve SIGTRAP handling and fork/vfork/clone->CLONE_PTRACE substitution. * defs.h (TCB_BPTSET, TCB_WAITEXECVE): Remove macros. (need_fork_exec_workarounds, setbpt, clearbpt): Remove declarations. * strace.c (need_fork_exec_workarounds, test_ptrace_setoptions_followfork, test_ptrace_setoptions_for_all): Remove. (syscall_trap_sig): Set to (SIGTRAP | 0x80). (ptrace_setoptions): Set to (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC). (detach): Do not test for TCB_BPTSET. (init): Do not call test_ptrace_setoptions_followfork and test_ptrace_setoptions_for_all. Do not test for TCB_BPTSET. * syscall.c (syscall_fixup_on_sysenter, internal_fork, internal_exec, syscall_fixup_for_fork_exec, syscall_fixup_on_sysexit): Remove. (trace_syscall_entering): Do not test for TCB_WAITEXECVE. Do not call syscall_fixup_on_sysenter and syscall_fixup_for_fork_exec. (trace_syscall_exiting): Do not call syscall_fixup_on_sysexit and syscall_fixup_for_fork_exec. [IA64] (ia64_ia32mode): Make static. * linux/ia64/arch_regs.h (ia64_ia32mode): Remove declaration. * util.c: Do not include "syscall.h". (arg_setup, get_arg0, get_arg1, set_arg0, set_arg1, restore_arg0, restore_arg1, arg_finish_change, change_syscall, setbpt, clearbpt): Remove. * tests/ptrace_setoptions.test: Remove. * tests/Makefile.am (TESTS): Remove it.
This commit is contained in:
parent
1c5feadf75
commit
23ce9e48b5
41
defs.h
41
defs.h
@ -274,41 +274,13 @@ struct tcb {
|
|||||||
* are limited to trace(), this condition is never observed in trace_syscall()
|
* are limited to trace(), this condition is never observed in trace_syscall()
|
||||||
* and below.
|
* and below.
|
||||||
* The bit is cleared after all syscall exit processing is done.
|
* The bit is cleared after all syscall exit processing is done.
|
||||||
* User-generated SIGTRAPs and post-execve SIGTRAP make it necessary
|
|
||||||
* to be very careful and NOT set TCB_INSYSCALL bit when they are encountered.
|
|
||||||
* TCB_WAITEXECVE bit is used for this purpose (see below).
|
|
||||||
*
|
*
|
||||||
* Use entering(tcp) / exiting(tcp) to check this bit to make code more readable.
|
* Use entering(tcp) / exiting(tcp) to check this bit to make code more readable.
|
||||||
*/
|
*/
|
||||||
#define TCB_INSYSCALL 0x04
|
#define TCB_INSYSCALL 0x04
|
||||||
#define TCB_ATTACHED 0x08 /* We attached to it already */
|
#define TCB_ATTACHED 0x08 /* We attached to it already */
|
||||||
#define TCB_BPTSET 0x10 /* "Breakpoint" set after fork(2) */
|
#define TCB_REPRINT 0x10 /* We should reprint this syscall on exit */
|
||||||
#define TCB_REPRINT 0x20 /* We should reprint this syscall on exit */
|
#define TCB_FILTERED 0x20 /* This system call has been filtered out */
|
||||||
#define TCB_FILTERED 0x40 /* This system call has been filtered out */
|
|
||||||
/*
|
|
||||||
* x86 does not need TCB_WAITEXECVE.
|
|
||||||
* It can detect post-execve SIGTRAP by looking at eax/rax.
|
|
||||||
* See "not a syscall entry (eax = %ld)\n" message.
|
|
||||||
*
|
|
||||||
* Note! On new kernels (about 2.5.46+), we use PTRACE_O_TRACEEXEC, which
|
|
||||||
* suppresses post-execve SIGTRAP. If you are adding a new arch which is
|
|
||||||
* only supported by newer kernels, you most likely don't need to define
|
|
||||||
* TCB_WAITEXECVE!
|
|
||||||
*/
|
|
||||||
#if defined(ALPHA) \
|
|
||||||
|| defined(SPARC) || defined(SPARC64) \
|
|
||||||
|| defined(POWERPC) \
|
|
||||||
|| defined(IA64) \
|
|
||||||
|| defined(HPPA) \
|
|
||||||
|| defined(SH) || defined(SH64) \
|
|
||||||
|| defined(S390) || defined(S390X) \
|
|
||||||
|| defined(ARM) \
|
|
||||||
|| defined(MIPS)
|
|
||||||
/* This tracee has entered into execve syscall. Expect post-execve SIGTRAP
|
|
||||||
* to happen. (When it is detected, tracee is continued and this bit is cleared.)
|
|
||||||
*/
|
|
||||||
# define TCB_WAITEXECVE 0x80
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* qualifier flags */
|
/* qualifier flags */
|
||||||
#define QUAL_TRACE 0x001 /* this system call should be traced */
|
#define QUAL_TRACE 0x001 /* this system call should be traced */
|
||||||
@ -405,7 +377,6 @@ extern bool hide_log_until_execve;
|
|||||||
/* are we filtering traces based on paths? */
|
/* are we filtering traces based on paths? */
|
||||||
extern const char **paths_selected;
|
extern const char **paths_selected;
|
||||||
#define tracing_paths (paths_selected != NULL)
|
#define tracing_paths (paths_selected != NULL)
|
||||||
extern bool need_fork_exec_workarounds;
|
|
||||||
extern unsigned xflag;
|
extern unsigned xflag;
|
||||||
extern unsigned followfork;
|
extern unsigned followfork;
|
||||||
#ifdef USE_LIBUNWIND
|
#ifdef USE_LIBUNWIND
|
||||||
@ -455,14 +426,6 @@ extern int upeek(int pid, long, long *);
|
|||||||
#if defined(SPARC) || defined(SPARC64) || defined(IA64) || defined(SH)
|
#if defined(SPARC) || defined(SPARC64) || defined(IA64) || defined(SH)
|
||||||
extern long getrval2(struct tcb *);
|
extern long getrval2(struct tcb *);
|
||||||
#endif
|
#endif
|
||||||
/*
|
|
||||||
* On Linux, "setbpt" is a misnomer: we don't set a breakpoint
|
|
||||||
* (IOW: no poking in user's text segment),
|
|
||||||
* instead we change fork/vfork/clone into clone(CLONE_PTRACE).
|
|
||||||
* On newer kernels, we use PTRACE_O_TRACECLONE/TRACE[V]FORK instead.
|
|
||||||
*/
|
|
||||||
extern int setbpt(struct tcb *);
|
|
||||||
extern int clearbpt(struct tcb *);
|
|
||||||
|
|
||||||
extern const char *signame(const int);
|
extern const char *signame(const int);
|
||||||
extern void pathtrace_select(const char *);
|
extern void pathtrace_select(const char *);
|
||||||
|
@ -1,4 +1,2 @@
|
|||||||
#include <asm/ptrace_offsets.h>
|
#include <asm/ptrace_offsets.h>
|
||||||
#include <asm/rse.h>
|
#include <asm/rse.h>
|
||||||
|
|
||||||
extern bool ia64_ia32mode;
|
|
||||||
|
253
strace.c
253
strace.c
@ -74,18 +74,17 @@ bool stack_trace_enabled = false;
|
|||||||
# define fork() vfork()
|
# define fork() vfork()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const unsigned int syscall_trap_sig = SIGTRAP | 0x80;
|
||||||
|
|
||||||
cflag_t cflag = CFLAG_NONE;
|
cflag_t cflag = CFLAG_NONE;
|
||||||
unsigned int followfork = 0;
|
unsigned int followfork = 0;
|
||||||
unsigned int ptrace_setoptions = 0;
|
unsigned int ptrace_setoptions = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
|
||||||
unsigned int xflag = 0;
|
unsigned int xflag = 0;
|
||||||
bool need_fork_exec_workarounds = 0;
|
|
||||||
bool debug_flag = 0;
|
bool debug_flag = 0;
|
||||||
bool Tflag = 0;
|
bool Tflag = 0;
|
||||||
bool iflag = 0;
|
bool iflag = 0;
|
||||||
bool count_wallclock = 0;
|
bool count_wallclock = 0;
|
||||||
unsigned int qflag = 0;
|
unsigned int qflag = 0;
|
||||||
/* Which WSTOPSIG(status) value marks syscall traps? */
|
|
||||||
static unsigned int syscall_trap_sig = SIGTRAP;
|
|
||||||
static unsigned int tflag = 0;
|
static unsigned int tflag = 0;
|
||||||
static bool rflag = 0;
|
static bool rflag = 0;
|
||||||
static bool print_pid_pfx = 0;
|
static bool print_pid_pfx = 0;
|
||||||
@ -347,7 +346,7 @@ ptrace_attach_or_seize(int pid)
|
|||||||
int r;
|
int r;
|
||||||
if (!use_seize)
|
if (!use_seize)
|
||||||
return ptrace(PTRACE_ATTACH, pid, 0L, 0L);
|
return ptrace(PTRACE_ATTACH, pid, 0L, 0L);
|
||||||
r = ptrace(PTRACE_SEIZE, pid, 0L, (unsigned long)ptrace_setoptions);
|
r = ptrace(PTRACE_SEIZE, pid, 0L, (unsigned long) ptrace_setoptions);
|
||||||
if (r)
|
if (r)
|
||||||
return r;
|
return r;
|
||||||
r = ptrace(PTRACE_INTERRUPT, pid, 0L, 0L);
|
r = ptrace(PTRACE_INTERRUPT, pid, 0L, 0L);
|
||||||
@ -766,9 +765,6 @@ detach(struct tcb *tcp)
|
|||||||
int error;
|
int error;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (tcp->flags & TCB_BPTSET)
|
|
||||||
clearbpt(tcp);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Linux wrongly insists the child be stopped
|
* Linux wrongly insists the child be stopped
|
||||||
* before detaching. Arghh. We go through hoops
|
* before detaching. Arghh. We go through hoops
|
||||||
@ -1321,219 +1317,6 @@ startup_child(char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Test whether the kernel support PTRACE_O_TRACECLONE et al options.
|
|
||||||
* First fork a new child, call ptrace with PTRACE_SETOPTIONS on it,
|
|
||||||
* and then see which options are supported by the kernel.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
test_ptrace_setoptions_followfork(void)
|
|
||||||
{
|
|
||||||
int pid, expected_grandchild = 0, found_grandchild = 0;
|
|
||||||
const unsigned int test_options = PTRACE_O_TRACECLONE |
|
|
||||||
PTRACE_O_TRACEFORK |
|
|
||||||
PTRACE_O_TRACEVFORK;
|
|
||||||
|
|
||||||
/* Need fork for test. NOMMU has no forks */
|
|
||||||
if (NOMMU_SYSTEM)
|
|
||||||
goto worked; /* be bold, and pretend that test succeeded */
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
if (pid < 0)
|
|
||||||
perror_msg_and_die("fork");
|
|
||||||
if (pid == 0) {
|
|
||||||
pid = getpid();
|
|
||||||
if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0)
|
|
||||||
perror_msg_and_die("%s: PTRACE_TRACEME doesn't work",
|
|
||||||
__func__);
|
|
||||||
kill_save_errno(pid, SIGSTOP);
|
|
||||||
if (fork() < 0)
|
|
||||||
perror_msg_and_die("fork");
|
|
||||||
_exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int status, tracee_pid;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
tracee_pid = wait(&status);
|
|
||||||
if (tracee_pid <= 0) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
if (errno == ECHILD)
|
|
||||||
break;
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
perror_msg_and_die("%s: unexpected wait result %d",
|
|
||||||
__func__, tracee_pid);
|
|
||||||
}
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
if (WEXITSTATUS(status)) {
|
|
||||||
if (tracee_pid != pid)
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
error_msg_and_die("%s: unexpected exit status %u",
|
|
||||||
__func__, WEXITSTATUS(status));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (WIFSIGNALED(status)) {
|
|
||||||
if (tracee_pid != pid)
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
error_msg_and_die("%s: unexpected signal %u",
|
|
||||||
__func__, WTERMSIG(status));
|
|
||||||
}
|
|
||||||
if (!WIFSTOPPED(status)) {
|
|
||||||
if (tracee_pid != pid)
|
|
||||||
kill_save_errno(tracee_pid, SIGKILL);
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
error_msg_and_die("%s: unexpected wait status %x",
|
|
||||||
__func__, status);
|
|
||||||
}
|
|
||||||
if (tracee_pid != pid) {
|
|
||||||
found_grandchild = tracee_pid;
|
|
||||||
if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0) {
|
|
||||||
kill_save_errno(tracee_pid, SIGKILL);
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
perror_msg_and_die("PTRACE_CONT doesn't work");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (WSTOPSIG(status)) {
|
|
||||||
case SIGSTOP:
|
|
||||||
if (ptrace(PTRACE_SETOPTIONS, pid, 0, test_options) < 0
|
|
||||||
&& errno != EINVAL && errno != EIO)
|
|
||||||
perror_msg("PTRACE_SETOPTIONS");
|
|
||||||
break;
|
|
||||||
case SIGTRAP:
|
|
||||||
if (status >> 16 == PTRACE_EVENT_FORK) {
|
|
||||||
long msg = 0;
|
|
||||||
|
|
||||||
if (ptrace(PTRACE_GETEVENTMSG, pid,
|
|
||||||
NULL, (long) &msg) == 0)
|
|
||||||
expected_grandchild = msg;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) {
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
perror_msg_and_die("PTRACE_SYSCALL doesn't work");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (expected_grandchild && expected_grandchild == found_grandchild) {
|
|
||||||
worked:
|
|
||||||
ptrace_setoptions |= test_options;
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "ptrace_setoptions = %#x\n",
|
|
||||||
ptrace_setoptions);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
error_msg("Test for PTRACE_O_TRACECLONE failed, "
|
|
||||||
"giving up using this feature.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Test whether the kernel support PTRACE_O_TRACESYSGOOD.
|
|
||||||
* First fork a new child, call ptrace(PTRACE_SETOPTIONS) on it,
|
|
||||||
* and then see whether it will stop with (SIGTRAP | 0x80).
|
|
||||||
*
|
|
||||||
* Use of this option enables correct handling of user-generated SIGTRAPs,
|
|
||||||
* and SIGTRAPs generated by special instructions such as int3 on x86:
|
|
||||||
|
|
||||||
# compile with: gcc -nostartfiles -nostdlib -o int3 int3.S
|
|
||||||
_start: .globl _start
|
|
||||||
int3
|
|
||||||
movl $42, %ebx
|
|
||||||
movl $1, %eax
|
|
||||||
int $0x80
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
test_ptrace_setoptions_for_all(void)
|
|
||||||
{
|
|
||||||
const unsigned int test_options = PTRACE_O_TRACESYSGOOD |
|
|
||||||
PTRACE_O_TRACEEXEC;
|
|
||||||
int pid;
|
|
||||||
int it_worked = 0;
|
|
||||||
|
|
||||||
/* Need fork for test. NOMMU has no forks */
|
|
||||||
if (NOMMU_SYSTEM)
|
|
||||||
goto worked; /* be bold, and pretend that test succeeded */
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
if (pid < 0)
|
|
||||||
perror_msg_and_die("fork");
|
|
||||||
|
|
||||||
if (pid == 0) {
|
|
||||||
pid = getpid();
|
|
||||||
if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0)
|
|
||||||
/* Note: exits with exitcode 1 */
|
|
||||||
perror_msg_and_die("%s: PTRACE_TRACEME doesn't work",
|
|
||||||
__func__);
|
|
||||||
kill(pid, SIGSTOP);
|
|
||||||
_exit(0); /* parent should see entry into this syscall */
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int status, tracee_pid;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
tracee_pid = wait(&status);
|
|
||||||
if (tracee_pid <= 0) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
perror_msg_and_die("%s: unexpected wait result %d",
|
|
||||||
__func__, tracee_pid);
|
|
||||||
}
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
if (WEXITSTATUS(status) == 0)
|
|
||||||
break;
|
|
||||||
error_msg_and_die("%s: unexpected exit status %u",
|
|
||||||
__func__, WEXITSTATUS(status));
|
|
||||||
}
|
|
||||||
if (WIFSIGNALED(status)) {
|
|
||||||
error_msg_and_die("%s: unexpected signal %u",
|
|
||||||
__func__, WTERMSIG(status));
|
|
||||||
}
|
|
||||||
if (!WIFSTOPPED(status)) {
|
|
||||||
kill(pid, SIGKILL);
|
|
||||||
error_msg_and_die("%s: unexpected wait status %x",
|
|
||||||
__func__, status);
|
|
||||||
}
|
|
||||||
if (WSTOPSIG(status) == SIGSTOP) {
|
|
||||||
/*
|
|
||||||
* We don't check "options aren't accepted" error.
|
|
||||||
* If it happens, we'll never get (SIGTRAP | 0x80),
|
|
||||||
* and thus will decide to not use the option.
|
|
||||||
* IOW: the outcome of the test will be correct.
|
|
||||||
*/
|
|
||||||
if (ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options) < 0
|
|
||||||
&& errno != EINVAL && errno != EIO)
|
|
||||||
perror_msg("PTRACE_SETOPTIONS");
|
|
||||||
}
|
|
||||||
if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
|
|
||||||
it_worked = 1;
|
|
||||||
}
|
|
||||||
if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) {
|
|
||||||
kill_save_errno(pid, SIGKILL);
|
|
||||||
perror_msg_and_die("PTRACE_SYSCALL doesn't work");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it_worked) {
|
|
||||||
worked:
|
|
||||||
syscall_trap_sig = (SIGTRAP | 0x80);
|
|
||||||
ptrace_setoptions |= test_options;
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "ptrace_setoptions = %#x\n",
|
|
||||||
ptrace_setoptions);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
error_msg("Test for PTRACE_O_TRACESYSGOOD failed, "
|
|
||||||
"giving up using this feature.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_SEIZE
|
#if USE_SEIZE
|
||||||
static void
|
static void
|
||||||
test_ptrace_seize(void)
|
test_ptrace_seize(void)
|
||||||
@ -1871,14 +1654,12 @@ init(int argc, char *argv[])
|
|||||||
run_gid = getgid();
|
run_gid = getgid();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* On any reasonably recent Linux kernel (circa about 2.5.46)
|
|
||||||
* need_fork_exec_workarounds should stay 0 after these tests:
|
|
||||||
*/
|
|
||||||
/*need_fork_exec_workarounds = 0; - already is */
|
|
||||||
if (followfork)
|
if (followfork)
|
||||||
need_fork_exec_workarounds = test_ptrace_setoptions_followfork();
|
ptrace_setoptions |= PTRACE_O_TRACECLONE |
|
||||||
need_fork_exec_workarounds |= test_ptrace_setoptions_for_all();
|
PTRACE_O_TRACEFORK |
|
||||||
|
PTRACE_O_TRACEVFORK;
|
||||||
|
if (debug_flag)
|
||||||
|
fprintf(stderr, "ptrace_setoptions = %#x\n", ptrace_setoptions);
|
||||||
test_ptrace_seize();
|
test_ptrace_seize();
|
||||||
|
|
||||||
/* Check if they want to redirect the output. */
|
/* Check if they want to redirect the output. */
|
||||||
@ -2226,20 +2007,7 @@ startup_tcb(struct tcb *tcp)
|
|||||||
|
|
||||||
tcp->flags &= ~TCB_STARTUP;
|
tcp->flags &= ~TCB_STARTUP;
|
||||||
|
|
||||||
if (tcp->flags & TCB_BPTSET) {
|
if (!use_seize) {
|
||||||
/*
|
|
||||||
* One example is a breakpoint inherited from
|
|
||||||
* parent through fork().
|
|
||||||
*/
|
|
||||||
if (clearbpt(tcp) < 0) {
|
|
||||||
/* Pretty fatal */
|
|
||||||
droptcb(tcp);
|
|
||||||
exit_code = 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!use_seize && ptrace_setoptions) {
|
|
||||||
if (debug_flag)
|
if (debug_flag)
|
||||||
fprintf(stderr, "setting opts 0x%x on pid %d\n",
|
fprintf(stderr, "setting opts 0x%x on pid %d\n",
|
||||||
ptrace_setoptions, tcp->pid);
|
ptrace_setoptions, tcp->pid);
|
||||||
@ -2460,7 +2228,6 @@ show_stopsig:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This should be syscall entry or exit.
|
* This should be syscall entry or exit.
|
||||||
* (Or it still can be that pesky post-execve SIGTRAP!)
|
|
||||||
* Handle it.
|
* Handle it.
|
||||||
*/
|
*/
|
||||||
if (trace_syscall(tcp) < 0) {
|
if (trace_syscall(tcp) < 0) {
|
||||||
|
213
syscall.c
213
syscall.c
@ -702,10 +702,10 @@ static struct iovec x86_io = {
|
|||||||
# define ARCH_REGS_FOR_GETREGSET x86_regs_union
|
# define ARCH_REGS_FOR_GETREGSET x86_regs_union
|
||||||
# define ARCH_IOVEC_FOR_GETREGSET x86_io
|
# define ARCH_IOVEC_FOR_GETREGSET x86_io
|
||||||
#elif defined(IA64)
|
#elif defined(IA64)
|
||||||
bool ia64_ia32mode = 0; /* not static */
|
static bool ia64_ia32mode;
|
||||||
static long ia64_r8, ia64_r10;
|
static long ia64_r8, ia64_r10;
|
||||||
#elif defined(POWERPC)
|
#elif defined(POWERPC)
|
||||||
struct pt_regs ppc_regs;
|
struct pt_regs ppc_regs; /* not static */
|
||||||
# define ARCH_REGS_FOR_GETREGS ppc_regs
|
# define ARCH_REGS_FOR_GETREGS ppc_regs
|
||||||
#elif defined(M68K)
|
#elif defined(M68K)
|
||||||
static long m68k_d0;
|
static long m68k_d0;
|
||||||
@ -771,7 +771,7 @@ static long sh64_r9;
|
|||||||
#elif defined(CRISV10) || defined(CRISV32)
|
#elif defined(CRISV10) || defined(CRISV32)
|
||||||
static long cris_r10;
|
static long cris_r10;
|
||||||
#elif defined(TILE)
|
#elif defined(TILE)
|
||||||
struct pt_regs tile_regs;
|
struct pt_regs tile_regs; /* not static */
|
||||||
# define ARCH_REGS_FOR_GETREGS tile_regs
|
# define ARCH_REGS_FOR_GETREGS tile_regs
|
||||||
#elif defined(MICROBLAZE)
|
#elif defined(MICROBLAZE)
|
||||||
static long microblaze_r3;
|
static long microblaze_r3;
|
||||||
@ -1607,173 +1607,6 @@ is_negated_errno(kernel_ulong_t val)
|
|||||||
return val >= max;
|
return val >= max;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called at each syscall entry.
|
|
||||||
* Returns:
|
|
||||||
* 0: "ignore this ptrace stop", bail out of trace_syscall_entering() silently.
|
|
||||||
* 1: ok, continue in trace_syscall_entering().
|
|
||||||
* other: error, trace_syscall_entering() should print error indicator
|
|
||||||
* ("????" etc) and bail out.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
syscall_fixup_on_sysenter(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
/* Do we have post-execve SIGTRAP suppressed? */
|
|
||||||
if (ptrace_setoptions & PTRACE_O_TRACEEXEC)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* No, unfortunately. Apply -ENOSYS heuristics.
|
|
||||||
* We don't have to workaround SECCOMP_RET_ERRNO side effects
|
|
||||||
* because any kernel with SECCOMP_RET_ERRNO support surely
|
|
||||||
* implements PTRACE_O_TRACEEXEC.
|
|
||||||
*/
|
|
||||||
#if defined(I386)
|
|
||||||
if (i386_regs.eax != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "not a syscall entry (eax = %ld)\n",
|
|
||||||
i386_regs.eax);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#elif defined(X86_64) || defined(X32)
|
|
||||||
if (x86_io.iov_len == sizeof(i386_regs)) {
|
|
||||||
if ((int) i386_regs.eax != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr,
|
|
||||||
"not a syscall entry (eax = %d)\n",
|
|
||||||
(int) i386_regs.eax);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((long long) x86_64_regs.rax != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr,
|
|
||||||
"not a syscall entry (rax = %lld)\n",
|
|
||||||
(long long) x86_64_regs.rax);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined(M68K)
|
|
||||||
/* TODO? Eliminate upeek's in arches below like we did in x86 */
|
|
||||||
if (upeek(tcp->pid, 4*PT_D0, &m68k_d0) < 0)
|
|
||||||
return -1;
|
|
||||||
if (m68k_d0 != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "not a syscall entry (d0 = %ld)\n", m68k_d0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#elif defined(IA64)
|
|
||||||
if (upeek(tcp->pid, PT_R10, &ia64_r10) < 0)
|
|
||||||
return -1;
|
|
||||||
if (upeek(tcp->pid, PT_R8, &ia64_r8) < 0)
|
|
||||||
return -1;
|
|
||||||
if (ia64_ia32mode && ia64_r8 != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "not a syscall entry (r8 = %ld)\n", ia64_r8);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#elif defined(CRISV10) || defined(CRISV32)
|
|
||||||
if (upeek(tcp->pid, 4*PT_R10, &cris_r10) < 0)
|
|
||||||
return -1;
|
|
||||||
if (cris_r10 != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "not a syscall entry (r10 = %ld)\n", cris_r10);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#elif defined(MICROBLAZE)
|
|
||||||
if (upeek(tcp->pid, 3 * 4, µblaze_r3) < 0)
|
|
||||||
return -1;
|
|
||||||
if (microblaze_r3 != -ENOSYS) {
|
|
||||||
if (debug_flag)
|
|
||||||
fprintf(stderr, "not a syscall entry (r3 = %ld)\n", microblaze_r3);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
internal_fork(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
#if defined S390 || defined S390X || defined CRISV10 || defined CRISV32
|
|
||||||
# define ARG_FLAGS 1
|
|
||||||
#else
|
|
||||||
# define ARG_FLAGS 0
|
|
||||||
#endif
|
|
||||||
#ifndef CLONE_UNTRACED
|
|
||||||
# define CLONE_UNTRACED 0x00800000
|
|
||||||
#endif
|
|
||||||
if ((ptrace_setoptions
|
|
||||||
& (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
|
|
||||||
== (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!followfork)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (entering(tcp)) {
|
|
||||||
/*
|
|
||||||
* We won't see the new child if clone is called with
|
|
||||||
* CLONE_UNTRACED, so we keep the same logic with that option
|
|
||||||
* and don't trace it.
|
|
||||||
*/
|
|
||||||
if ((tcp->s_ent->sys_func == sys_clone)
|
|
||||||
&& (tcp->u_arg[ARG_FLAGS] & CLONE_UNTRACED)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
setbpt(tcp);
|
|
||||||
} else {
|
|
||||||
if (tcp->flags & TCB_BPTSET)
|
|
||||||
clearbpt(tcp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(TCB_WAITEXECVE)
|
|
||||||
static void
|
|
||||||
internal_exec(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
/* Maybe we have post-execve SIGTRAP suppressed? */
|
|
||||||
if (ptrace_setoptions & PTRACE_O_TRACEEXEC)
|
|
||||||
return; /* yes, no need to do anything */
|
|
||||||
|
|
||||||
if (exiting(tcp) && syserror(tcp))
|
|
||||||
/* Error in execve, no post-execve SIGTRAP expected */
|
|
||||||
tcp->flags &= ~TCB_WAITEXECVE;
|
|
||||||
else
|
|
||||||
tcp->flags |= TCB_WAITEXECVE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
|
||||||
syscall_fixup_for_fork_exec(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We must always trace a few critical system calls in order to
|
|
||||||
* correctly support following forks in the presence of tracing
|
|
||||||
* qualifiers.
|
|
||||||
*/
|
|
||||||
int (*func)();
|
|
||||||
|
|
||||||
func = tcp->s_ent->sys_func;
|
|
||||||
|
|
||||||
if ( sys_fork == func
|
|
||||||
|| sys_clone == func
|
|
||||||
) {
|
|
||||||
internal_fork(tcp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(TCB_WAITEXECVE)
|
|
||||||
if ( sys_execve == func
|
|
||||||
# if defined(SPARC) || defined(SPARC64)
|
|
||||||
|| sys_execv == func
|
|
||||||
# endif
|
|
||||||
) {
|
|
||||||
internal_exec(tcp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return -1 on error or 1 on success (never 0!) */
|
/* Return -1 on error or 1 on success (never 0!) */
|
||||||
static int
|
static int
|
||||||
get_syscall_args(struct tcb *tcp)
|
get_syscall_args(struct tcb *tcp)
|
||||||
@ -2008,24 +1841,11 @@ trace_syscall_entering(struct tcb *tcp)
|
|||||||
{
|
{
|
||||||
int res, scno_good;
|
int res, scno_good;
|
||||||
|
|
||||||
#if defined TCB_WAITEXECVE
|
|
||||||
if (tcp->flags & TCB_WAITEXECVE) {
|
|
||||||
/* This is the post-execve SIGTRAP. */
|
|
||||||
tcp->flags &= ~TCB_WAITEXECVE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
scno_good = res = (get_regs_error ? -1 : get_scno(tcp));
|
scno_good = res = (get_regs_error ? -1 : get_scno(tcp));
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
return res;
|
return res;
|
||||||
if (res == 1) {
|
if (res == 1)
|
||||||
res = syscall_fixup_on_sysenter(tcp);
|
res = get_syscall_args(tcp);
|
||||||
if (res == 0)
|
|
||||||
return res;
|
|
||||||
if (res == 1)
|
|
||||||
res = get_syscall_args(tcp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
printleader(tcp);
|
printleader(tcp);
|
||||||
@ -2068,9 +1888,6 @@ trace_syscall_entering(struct tcb *tcp)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (need_fork_exec_workarounds)
|
|
||||||
syscall_fixup_for_fork_exec(tcp);
|
|
||||||
|
|
||||||
if (!(tcp->qual_flg & QUAL_TRACE)
|
if (!(tcp->qual_flg & QUAL_TRACE)
|
||||||
|| (tracing_paths && !pathtrace_match(tcp))
|
|| (tracing_paths && !pathtrace_match(tcp))
|
||||||
) {
|
) {
|
||||||
@ -2170,23 +1987,6 @@ get_syscall_result(struct tcb *tcp)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called at each syscall exit */
|
|
||||||
static void
|
|
||||||
syscall_fixup_on_sysexit(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
#if defined(S390) || defined(S390X)
|
|
||||||
if ((tcp->flags & TCB_WAITEXECVE)
|
|
||||||
&& (s390_gpr2 == -ENOSYS || s390_gpr2 == tcp->scno)) {
|
|
||||||
/*
|
|
||||||
* Return from execve.
|
|
||||||
* Fake a return value of zero. We leave the TCB_WAITEXECVE
|
|
||||||
* flag set for the post-execve SIGTRAP to see and reset.
|
|
||||||
*/
|
|
||||||
s390_gpr2 = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns:
|
/* Returns:
|
||||||
* 1: ok, continue in trace_syscall_exiting().
|
* 1: ok, continue in trace_syscall_exiting().
|
||||||
* -1: error, trace_syscall_exiting() should print error indicator
|
* -1: error, trace_syscall_exiting() should print error indicator
|
||||||
@ -2510,10 +2310,7 @@ trace_syscall_exiting(struct tcb *tcp)
|
|||||||
#endif
|
#endif
|
||||||
res = (get_regs_error ? -1 : get_syscall_result(tcp));
|
res = (get_regs_error ? -1 : get_syscall_result(tcp));
|
||||||
if (res == 1) {
|
if (res == 1) {
|
||||||
syscall_fixup_on_sysexit(tcp); /* never fails */
|
|
||||||
get_error(tcp); /* never fails */
|
get_error(tcp); /* never fails */
|
||||||
if (need_fork_exec_workarounds)
|
|
||||||
syscall_fixup_for_fork_exec(tcp);
|
|
||||||
if (filtered(tcp) || hide_log_until_execve)
|
if (filtered(tcp) || hide_log_until_execve)
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@ stack_fcall_SOURCES = stack-fcall.c \
|
|||||||
stack-fcall-0.c stack-fcall-1.c stack-fcall-2.c stack-fcall-3.c
|
stack-fcall-0.c stack-fcall-1.c stack-fcall-2.c stack-fcall-3.c
|
||||||
|
|
||||||
TESTS = \
|
TESTS = \
|
||||||
ptrace_setoptions.test \
|
|
||||||
strace-f.test \
|
strace-f.test \
|
||||||
qual_syscall.test \
|
qual_syscall.test \
|
||||||
bexecve.test \
|
bexecve.test \
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Ensure that strace tests kernel PTRACE_O_TRACECLONE
|
|
||||||
# and PTRACE_O_TRACESYSGOOD support properly.
|
|
||||||
|
|
||||||
. "${srcdir=.}/init.sh"
|
|
||||||
|
|
||||||
check_prog grep
|
|
||||||
|
|
||||||
[ "$(uname -s)" = Linux ] ||
|
|
||||||
skip_ 'The kernel is not a Linux kernel'
|
|
||||||
case "$(uname -r)" in
|
|
||||||
2.[6-9]*|2.[1-5][0-9]*|[3-9].*|[12][0-9]*) ;;
|
|
||||||
*) skip_ 'The kernel is not Linux 2.6.* or newer' ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
$STRACE -df -enone / > /dev/null 2> $LOG
|
|
||||||
grep -F -x 'ptrace_setoptions = 0xe' $LOG > /dev/null || {
|
|
||||||
cat $LOG
|
|
||||||
fail_ 'strace -f failed to recognize proper kernel PTRACE_O_TRACECLONE support'
|
|
||||||
}
|
|
||||||
|
|
||||||
grep -F -x 'ptrace_setoptions = 0x1f' $LOG > /dev/null || {
|
|
||||||
cat $LOG
|
|
||||||
fail_ 'strace -f failed to recognize proper kernel PTRACE_O_TRACESYSGOOD support'
|
|
||||||
}
|
|
||||||
|
|
||||||
$STRACE -d -enone / > /dev/null 2> $LOG
|
|
||||||
grep -F -x 'ptrace_setoptions = 0x11' $LOG > /dev/null || {
|
|
||||||
cat $LOG
|
|
||||||
fail_ 'strace failed to recognize proper kernel PTRACE_O_TRACESYSGOOD support'
|
|
||||||
}
|
|
424
util.c
424
util.c
@ -1257,427 +1257,3 @@ upeek(int pid, long off, long *res)
|
|||||||
*res = val;
|
*res = val;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note! On new kernels (about 2.5.46+), we use PTRACE_O_TRACECLONE
|
|
||||||
* and PTRACE_O_TRACE[V]FORK for tracing children.
|
|
||||||
* If you are adding a new arch which is only supported by newer kernels,
|
|
||||||
* you most likely don't need to add any code below
|
|
||||||
* beside a dummy "return 0" block in change_syscall().
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These #if's are huge, please indent them correctly.
|
|
||||||
* It's easy to get confused otherwise.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "syscall.h"
|
|
||||||
|
|
||||||
#ifndef CLONE_PTRACE
|
|
||||||
# define CLONE_PTRACE 0x00002000
|
|
||||||
#endif
|
|
||||||
#ifndef CLONE_VFORK
|
|
||||||
# define CLONE_VFORK 0x00004000
|
|
||||||
#endif
|
|
||||||
#ifndef CLONE_VM
|
|
||||||
# define CLONE_VM 0x00000100
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef IA64
|
|
||||||
|
|
||||||
typedef unsigned long *arg_setup_state;
|
|
||||||
|
|
||||||
static int
|
|
||||||
arg_setup(struct tcb *tcp, arg_setup_state *state)
|
|
||||||
{
|
|
||||||
unsigned long cfm, sof, sol;
|
|
||||||
long bsp;
|
|
||||||
|
|
||||||
if (ia64_ia32mode) {
|
|
||||||
/* Satisfy a false GCC warning. */
|
|
||||||
*state = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upeek(tcp->pid, PT_AR_BSP, &bsp) < 0)
|
|
||||||
return -1;
|
|
||||||
if (upeek(tcp->pid, PT_CFM, (long *) &cfm) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
sof = (cfm >> 0) & 0x7f;
|
|
||||||
sol = (cfm >> 7) & 0x7f;
|
|
||||||
bsp = (long) ia64_rse_skip_regs((unsigned long *) bsp, -sof + sol);
|
|
||||||
|
|
||||||
*state = (unsigned long *) bsp;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
# define arg_finish_change(tcp, state) 0
|
|
||||||
|
|
||||||
static int
|
|
||||||
get_arg0(struct tcb *tcp, arg_setup_state *state, long *valp)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (ia64_ia32mode)
|
|
||||||
ret = upeek(tcp->pid, PT_R11, valp);
|
|
||||||
else
|
|
||||||
ret = umoven(tcp,
|
|
||||||
(unsigned long) ia64_rse_skip_regs(*state, 0),
|
|
||||||
sizeof(long), (void *) valp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
get_arg1(struct tcb *tcp, arg_setup_state *state, long *valp)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (ia64_ia32mode)
|
|
||||||
ret = upeek(tcp->pid, PT_R9, valp);
|
|
||||||
else
|
|
||||||
ret = umoven(tcp,
|
|
||||||
(unsigned long) ia64_rse_skip_regs(*state, 1),
|
|
||||||
sizeof(long), (void *) valp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
set_arg0(struct tcb *tcp, arg_setup_state *state, long val)
|
|
||||||
{
|
|
||||||
int req = PTRACE_POKEDATA;
|
|
||||||
void *ap;
|
|
||||||
|
|
||||||
if (ia64_ia32mode) {
|
|
||||||
ap = (void *) (intptr_t) PT_R11; /* r11 == EBX */
|
|
||||||
req = PTRACE_POKEUSER;
|
|
||||||
} else
|
|
||||||
ap = ia64_rse_skip_regs(*state, 0);
|
|
||||||
errno = 0;
|
|
||||||
ptrace(req, tcp->pid, ap, val);
|
|
||||||
return errno ? -1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
set_arg1(struct tcb *tcp, arg_setup_state *state, long val)
|
|
||||||
{
|
|
||||||
int req = PTRACE_POKEDATA;
|
|
||||||
void *ap;
|
|
||||||
|
|
||||||
if (ia64_ia32mode) {
|
|
||||||
ap = (void *) (intptr_t) PT_R9; /* r9 == ECX */
|
|
||||||
req = PTRACE_POKEUSER;
|
|
||||||
} else
|
|
||||||
ap = ia64_rse_skip_regs(*state, 1);
|
|
||||||
errno = 0;
|
|
||||||
ptrace(req, tcp->pid, ap, val);
|
|
||||||
return errno ? -1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ia64 does not return the input arguments from functions (and syscalls)
|
|
||||||
according to ia64 RSE (Register Stack Engine) behavior. */
|
|
||||||
|
|
||||||
# define restore_arg0(tcp, state, val) ((void) (state), 0)
|
|
||||||
# define restore_arg1(tcp, state, val) ((void) (state), 0)
|
|
||||||
|
|
||||||
#elif defined(SPARC) || defined(SPARC64)
|
|
||||||
|
|
||||||
# if defined(SPARC64)
|
|
||||||
# undef PTRACE_GETREGS
|
|
||||||
# define PTRACE_GETREGS PTRACE_GETREGS64
|
|
||||||
# undef PTRACE_SETREGS
|
|
||||||
# define PTRACE_SETREGS PTRACE_SETREGS64
|
|
||||||
# endif
|
|
||||||
|
|
||||||
typedef struct pt_regs arg_setup_state;
|
|
||||||
|
|
||||||
# define arg_setup(tcp, state) \
|
|
||||||
(ptrace(PTRACE_GETREGS, (tcp)->pid, (char *) (state), 0))
|
|
||||||
# define arg_finish_change(tcp, state) \
|
|
||||||
(ptrace(PTRACE_SETREGS, (tcp)->pid, (char *) (state), 0))
|
|
||||||
|
|
||||||
# define get_arg0(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O0], 0)
|
|
||||||
# define get_arg1(tcp, state, valp) (*(valp) = (state)->u_regs[U_REG_O1], 0)
|
|
||||||
# define set_arg0(tcp, state, val) ((state)->u_regs[U_REG_O0] = (val), 0)
|
|
||||||
# define set_arg1(tcp, state, val) ((state)->u_regs[U_REG_O1] = (val), 0)
|
|
||||||
# define restore_arg0(tcp, state, val) 0
|
|
||||||
|
|
||||||
#else /* other architectures */
|
|
||||||
|
|
||||||
# if defined S390 || defined S390X
|
|
||||||
/* Note: this is only true for the `clone' system call, which handles
|
|
||||||
arguments specially. We could as well say that its first two arguments
|
|
||||||
are swapped relative to other architectures, but that would just be
|
|
||||||
another #ifdef in the calls. */
|
|
||||||
# define arg0_offset PT_GPR3
|
|
||||||
# define arg1_offset PT_ORIGGPR2
|
|
||||||
# define restore_arg0(tcp, state, val) ((void) (state), 0)
|
|
||||||
# define restore_arg1(tcp, state, val) ((void) (state), 0)
|
|
||||||
# define arg0_index 1
|
|
||||||
# define arg1_index 0
|
|
||||||
# elif defined(ALPHA) || defined(MIPS)
|
|
||||||
# define arg0_offset REG_A0
|
|
||||||
# define arg1_offset (REG_A0+1)
|
|
||||||
# elif defined(POWERPC)
|
|
||||||
# define arg0_offset (sizeof(unsigned long)*PT_R3)
|
|
||||||
# define arg1_offset (sizeof(unsigned long)*PT_R4)
|
|
||||||
# define restore_arg0(tcp, state, val) ((void) (state), 0)
|
|
||||||
# elif defined(HPPA)
|
|
||||||
# define arg0_offset PT_GR26
|
|
||||||
# define arg1_offset (PT_GR26-4)
|
|
||||||
# elif defined(X86_64) || defined(X32)
|
|
||||||
# define arg0_offset ((long)(8*(current_personality ? RBX : RDI)))
|
|
||||||
# define arg1_offset ((long)(8*(current_personality ? RCX : RSI)))
|
|
||||||
# elif defined(SH)
|
|
||||||
# define arg0_offset (4*(REG_REG0+4))
|
|
||||||
# define arg1_offset (4*(REG_REG0+5))
|
|
||||||
# elif defined(SH64)
|
|
||||||
/* ABI defines arg0 & 1 in r2 & r3 */
|
|
||||||
# define arg0_offset (REG_OFFSET+16)
|
|
||||||
# define arg1_offset (REG_OFFSET+24)
|
|
||||||
# define restore_arg0(tcp, state, val) 0
|
|
||||||
# elif defined CRISV10 || defined CRISV32
|
|
||||||
# define arg0_offset (4*PT_R11)
|
|
||||||
# define arg1_offset (4*PT_ORIG_R10)
|
|
||||||
# define restore_arg0(tcp, state, val) 0
|
|
||||||
# define restore_arg1(tcp, state, val) 0
|
|
||||||
# define arg0_index 1
|
|
||||||
# define arg1_index 0
|
|
||||||
# else
|
|
||||||
# define arg0_offset 0
|
|
||||||
# define arg1_offset 4
|
|
||||||
# if defined ARM
|
|
||||||
# define restore_arg0(tcp, state, val) 0
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
|
|
||||||
typedef int arg_setup_state;
|
|
||||||
|
|
||||||
# define arg_setup(tcp, state) (0)
|
|
||||||
# define arg_finish_change(tcp, state) 0
|
|
||||||
# define get_arg0(tcp, cookie, valp) (upeek((tcp)->pid, arg0_offset, (valp)))
|
|
||||||
# define get_arg1(tcp, cookie, valp) (upeek((tcp)->pid, arg1_offset, (valp)))
|
|
||||||
|
|
||||||
static int
|
|
||||||
set_arg0(struct tcb *tcp, void *cookie, long val)
|
|
||||||
{
|
|
||||||
return ptrace(PTRACE_POKEUSER, tcp->pid, (char*)arg0_offset, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
set_arg1(struct tcb *tcp, void *cookie, long val)
|
|
||||||
{
|
|
||||||
return ptrace(PTRACE_POKEUSER, tcp->pid, (char*)arg1_offset, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* architectures */
|
|
||||||
|
|
||||||
#ifndef restore_arg0
|
|
||||||
# define restore_arg0(tcp, state, val) set_arg0((tcp), (state), (val))
|
|
||||||
#endif
|
|
||||||
#ifndef restore_arg1
|
|
||||||
# define restore_arg1(tcp, state, val) set_arg1((tcp), (state), (val))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef arg0_index
|
|
||||||
# define arg0_index 0
|
|
||||||
# define arg1_index 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int
|
|
||||||
change_syscall(struct tcb *tcp, arg_setup_state *state, int new)
|
|
||||||
{
|
|
||||||
#if defined(I386)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(ORIG_EAX * 4), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(X86_64)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(ORIG_RAX * 8), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(X32)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* X32 is only supported since about linux-3.0.30 */
|
|
||||||
#elif defined(POWERPC)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid,
|
|
||||||
(char*)(sizeof(unsigned long)*PT_R0), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(S390) || defined(S390X)
|
|
||||||
/* s390 linux after 2.4.7 has a hook in entry.S to allow this */
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_GPR2), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(M68K)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(4*PT_ORIG_D0), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(SPARC) || defined(SPARC64)
|
|
||||||
state->u_regs[U_REG_G1] = new;
|
|
||||||
return 0;
|
|
||||||
#elif defined(MIPS)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(REG_V0), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(ALPHA)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(REG_A3), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(AVR32)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* AVR32 is only supported since about linux-2.6.19 */
|
|
||||||
#elif defined(BFIN)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* Blackfin is only supported since about linux-2.6.23 */
|
|
||||||
#elif defined(IA64)
|
|
||||||
if (ia64_ia32mode) {
|
|
||||||
switch (new) {
|
|
||||||
case 2:
|
|
||||||
break; /* x86 SYS_fork */
|
|
||||||
case SYS_clone:
|
|
||||||
new = 120;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "%s: unexpected syscall %d\n",
|
|
||||||
__FUNCTION__, new);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_R1), new) < 0)
|
|
||||||
return -1;
|
|
||||||
} else if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_R15), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(HPPA)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(PT_GR20), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(SH)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(4*(REG_REG0+3)), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(SH64)
|
|
||||||
/* Top half of reg encodes the no. of args n as 0x1n.
|
|
||||||
Assume 0 args as kernel never actually checks... */
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(REG_SYSCALL),
|
|
||||||
0x100000 | new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(CRISV10) || defined(CRISV32)
|
|
||||||
if (ptrace(PTRACE_POKEUSER, tcp->pid, (char*)(4*PT_R9), new) < 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(ARM)
|
|
||||||
/* Some kernels support this, some (pre-2.6.16 or so) don't. */
|
|
||||||
# ifndef PTRACE_SET_SYSCALL
|
|
||||||
# define PTRACE_SET_SYSCALL 23
|
|
||||||
# endif
|
|
||||||
if (ptrace(PTRACE_SET_SYSCALL, tcp->pid, 0, new & 0xffff) != 0)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
#elif defined(AARCH64)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* AARCH64 is only supported since about linux-3.0.31 */
|
|
||||||
#elif defined(TILE)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* Tilera CPUs are only supported since about linux-2.6.34 */
|
|
||||||
#elif defined(MICROBLAZE)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* microblaze is only supported since about linux-2.6.30 */
|
|
||||||
#elif defined(OR1K)
|
|
||||||
/* never reached; OR1K is only supported by kernels since 3.1.0. */
|
|
||||||
#elif defined(METAG)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* Meta is only supported since linux-3.7 */
|
|
||||||
#elif defined(XTENSA)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* Xtensa is only supported since linux 2.6.13 */
|
|
||||||
#elif defined(ARC)
|
|
||||||
/* setbpt/clearbpt never used: */
|
|
||||||
/* ARC only supported since 3.9 */
|
|
||||||
#else
|
|
||||||
#warning Do not know how to handle change_syscall for this architecture
|
|
||||||
#endif /* architecture */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
setbpt(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
static int clone_scno[SUPPORTED_PERSONALITIES] = { SYS_clone };
|
|
||||||
arg_setup_state state;
|
|
||||||
|
|
||||||
if (tcp->flags & TCB_BPTSET) {
|
|
||||||
fprintf(stderr, "PANIC: TCB already set in pid %u\n", tcp->pid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* It's a silly kludge to initialize this with a search at runtime.
|
|
||||||
* But it's better than maintaining another magic thing in the
|
|
||||||
* godforsaken tables.
|
|
||||||
*/
|
|
||||||
if (clone_scno[current_personality] == 0) {
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i < nsyscalls; ++i)
|
|
||||||
if (sysent[i].sys_func == sys_clone) {
|
|
||||||
clone_scno[current_personality] = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tcp->s_ent->sys_func == sys_fork) {
|
|
||||||
if (arg_setup(tcp, &state) < 0
|
|
||||||
|| get_arg0(tcp, &state, &tcp->inst[0]) < 0
|
|
||||||
|| get_arg1(tcp, &state, &tcp->inst[1]) < 0
|
|
||||||
|| change_syscall(tcp, &state,
|
|
||||||
clone_scno[current_personality]) < 0
|
|
||||||
|| set_arg0(tcp, &state, CLONE_PTRACE|SIGCHLD) < 0
|
|
||||||
|| set_arg1(tcp, &state, 0) < 0
|
|
||||||
|| arg_finish_change(tcp, &state) < 0)
|
|
||||||
return -1;
|
|
||||||
tcp->u_arg[arg0_index] = CLONE_PTRACE|SIGCHLD;
|
|
||||||
tcp->u_arg[arg1_index] = 0;
|
|
||||||
tcp->flags |= TCB_BPTSET;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tcp->s_ent->sys_func == sys_clone) {
|
|
||||||
/* ia64 calls directly `clone (CLONE_VFORK | CLONE_VM)'
|
|
||||||
contrary to x86 vfork above. Even on x86 we turn the
|
|
||||||
vfork semantics into plain fork - each application must not
|
|
||||||
depend on the vfork specifics according to POSIX. We would
|
|
||||||
hang waiting for the parent resume otherwise. We need to
|
|
||||||
clear also CLONE_VM but only in the CLONE_VFORK case as
|
|
||||||
otherwise we would break pthread_create. */
|
|
||||||
|
|
||||||
long new_arg0 = (tcp->u_arg[arg0_index] | CLONE_PTRACE);
|
|
||||||
if (new_arg0 & CLONE_VFORK)
|
|
||||||
new_arg0 &= ~(unsigned long)(CLONE_VFORK | CLONE_VM);
|
|
||||||
if (arg_setup(tcp, &state) < 0
|
|
||||||
|| set_arg0(tcp, &state, new_arg0) < 0
|
|
||||||
|| arg_finish_change(tcp, &state) < 0)
|
|
||||||
return -1;
|
|
||||||
tcp->inst[0] = tcp->u_arg[arg0_index];
|
|
||||||
tcp->inst[1] = tcp->u_arg[arg1_index];
|
|
||||||
tcp->flags |= TCB_BPTSET;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "PANIC: setbpt for syscall %ld on %u???\n",
|
|
||||||
tcp->scno, tcp->pid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clearbpt(struct tcb *tcp)
|
|
||||||
{
|
|
||||||
arg_setup_state state;
|
|
||||||
if (arg_setup(tcp, &state) < 0
|
|
||||||
|| change_syscall(tcp, &state, tcp->scno) < 0
|
|
||||||
|| restore_arg0(tcp, &state, tcp->inst[0]) < 0
|
|
||||||
|| restore_arg1(tcp, &state, tcp->inst[1]) < 0
|
|
||||||
|| arg_finish_change(tcp, &state))
|
|
||||||
if (errno != ESRCH)
|
|
||||||
return -1;
|
|
||||||
tcp->flags &= ~TCB_BPTSET;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user