compel: set TRACESYSGOOD to distinguish breakpoints from syscalls

When delivering system call traps, set bit 7 in the  signal  number  (i.e.,
deliver SIGTRAP|0x80).  This makes it easy for the tracer  to  distinguish
normal traps from those caused by a system call.

Signed-off-by: Andrei Vagin <avagin@gmail.com>
This commit is contained in:
Andrei Vagin 2022-08-07 16:27:22 -07:00
parent c089159a46
commit d7477dac03
5 changed files with 27 additions and 21 deletions

View file

@ -5,6 +5,8 @@
#include <compel/asm/infect-types.h>
#include <compel/ptrace.h>
#define PTRACE_SYSCALL_TRAP 0x80
#define PTRACE_SI_EVENT(_si_code) (((_si_code)&0xFFFF) >> 8)
extern int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs);

View file

@ -80,9 +80,9 @@ enum trace_flags {
TRACE_EXIT,
};
extern int __must_check compel_stop_on_syscall(int tasks, int sys_nr, int sys_nr_compat, enum trace_flags trace);
extern int __must_check compel_stop_on_syscall(int tasks, int sys_nr, int sys_nr_compat);
extern int __must_check compel_stop_pie(pid_t pid, void *addr, enum trace_flags *tf, bool no_bp);
extern int __must_check compel_stop_pie(pid_t pid, void *addr, bool no_bp);
extern int __must_check compel_unmap(struct parasite_ctl *ctl, unsigned long addr);

View file

@ -304,6 +304,11 @@ try_again:
goto try_again;
}
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESYSGOOD)) {
pr_perror("Unable to set PTRACE_O_TRACESYSGOOD for %d", pid);
return -1;
}
if (ss->seccomp_mode != SECCOMP_MODE_DISABLED && ptrace_suspend_seccomp(pid) < 0)
goto err;
@ -1366,7 +1371,6 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
pid_t pid = ctl->rpid;
user_regs_struct_t regs;
int status, ret = 0;
enum trace_flags flag;
/* stop getting chld from parasite -- we're about to step-by-step it */
if (restore_child_handler(ctl))
@ -1407,11 +1411,11 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
return -1;
/* Go to sigreturn as closer as we can */
ret = compel_stop_pie(pid, ctl->sigreturn_addr, &flag, ctl->ictx.flags & INFECT_NO_BREAKPOINTS);
ret = compel_stop_pie(pid, ctl->sigreturn_addr, ctl->ictx.flags & INFECT_NO_BREAKPOINTS);
if (ret < 0)
return ret;
if (compel_stop_on_syscall(1, __NR(rt_sigreturn, 0), __NR(rt_sigreturn, 1), flag))
if (compel_stop_on_syscall(1, __NR(rt_sigreturn, 0), __NR(rt_sigreturn, 1)))
return -1;
if (ptrace_flush_breakpoints(pid))
@ -1546,7 +1550,7 @@ int compel_unmap(struct parasite_ctl *ctl, unsigned long addr)
if (ret)
goto err;
ret = compel_stop_on_syscall(1, __NR(munmap, 0), __NR(munmap, 1), TRACE_ENTER);
ret = compel_stop_on_syscall(1, __NR(munmap, 0), __NR(munmap, 1));
/*
* Don't touch extended registers here: they were restored
@ -1558,7 +1562,7 @@ err:
return ret;
}
int compel_stop_pie(pid_t pid, void *addr, enum trace_flags *tf, bool no_bp)
int compel_stop_pie(pid_t pid, void *addr, bool no_bp)
{
int ret;
@ -1575,7 +1579,6 @@ int compel_stop_pie(pid_t pid, void *addr, enum trace_flags *tf, bool no_bp)
* PIE will stop on a breakpoint, next
* stop after that will be syscall enter.
*/
*tf = TRACE_EXIT;
return 0;
}
@ -1588,14 +1591,12 @@ int compel_stop_pie(pid_t pid, void *addr, enum trace_flags *tf, bool no_bp)
pr_perror("Unable to restart the %d process", pid);
return -1;
}
*tf = TRACE_ENTER;
return 0;
}
static bool task_is_trapped(int status, pid_t pid)
{
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)
if (WIFSTOPPED(status) && (WSTOPSIG(status) & ~PTRACE_SYSCALL_TRAP) == SIGTRAP)
return true;
pr_err("Task %d is in unexpected state: %x\n", pid, status);
@ -1629,15 +1630,13 @@ static inline int is_required_syscall(user_regs_struct_t *regs, pid_t pid, const
* sys_nr - the required syscall number
* sys_nr_compat - the required compatible syscall number
*/
int compel_stop_on_syscall(int tasks, const int sys_nr, const int sys_nr_compat, enum trace_flags trace)
int compel_stop_on_syscall(int tasks, const int sys_nr, const int sys_nr_compat)
{
enum trace_flags trace = tasks > 1 ? TRACE_ALL : TRACE_ENTER;
user_regs_struct_t regs;
int status, ret;
pid_t pid;
if (tasks > 1)
trace = TRACE_ALL;
/* Stop all threads on the enter point in sys_rt_sigreturn */
while (tasks) {
pid = wait4(-1, &status, __WALL, NULL);
@ -1651,6 +1650,8 @@ int compel_stop_on_syscall(int tasks, const int sys_nr, const int sys_nr_compat,
pr_debug("%d was trapped\n", pid);
if ((WSTOPSIG(status) & PTRACE_SYSCALL_TRAP) == 0)
goto goon;
if (trace == TRACE_EXIT) {
trace = TRACE_ENTER;
pr_debug("`- Expecting exit\n");

View file

@ -23,7 +23,7 @@
int ptrace_suspend_seccomp(pid_t pid)
{
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_SUSPEND_SECCOMP) < 0) {
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_SUSPEND_SECCOMP | PTRACE_O_TRACESYSGOOD) < 0) {
pr_perror("suspending seccomp failed");
return -1;
}