Skip to content

Commit af67f56

Browse files
committed
Fix PTRACE_TRACEME emulation to try to find the "real parent" task if possible.
Resolves #3929
1 parent f50ce15 commit af67f56

File tree

3 files changed

+76
-26
lines changed

3 files changed

+76
-26
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,7 @@ set(BASIC_TESTS
13121312
ptrace_trace_clone
13131313
ptrace_trace_exit
13141314
ptrace_traceme
1315+
ptrace_traceme_thread
13151316
ptrace_waiter_thread
13161317
ptracer_death
13171318
ptracer_death_multithread

src/record_syscall.cc

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,60 +2561,63 @@ static bool verify_ptrace_options(RecordTask* t,
25612561
return true;
25622562
}
25632563

2564-
static bool check_ptracer_compatible(RecordTask* tracer, RecordTask* tracee) {
2564+
static void check_ptracer_compatible(RecordTask* tracer, RecordTask* tracee) {
25652565
// Don't allow a 32-bit process to trace a 64-bit process. That doesn't
25662566
// make much sense (manipulating registers gets crazy), and would be hard to
25672567
// support.
2568-
if (tracee->emulated_ptracer || tracee->tgid() == tracer->tgid() ||
2569-
(tracer->arch() == x86 && tracee->arch() == x86_64)) {
2570-
return false;
2571-
}
2572-
return true;
2568+
ASSERT(tracer, !(tracer->arch() == x86 && tracee->arch() == x86_64));
25732569
}
25742570

2575-
static RecordTask* get_ptrace_partner(RecordTask* t, pid_t pid) {
2571+
static RecordTask* prepare_ptrace_attach(RecordTask* tracer, pid_t attach_to_tid,
2572+
TaskSyscallState& syscall_state) {
25762573
// To simplify things, require that a ptracer be in the same pid
25772574
// namespace as rr itself. I.e., tracee tasks sandboxed in a pid
25782575
// namespace can't use ptrace. This is normally a requirement of
25792576
// sandboxes anyway.
25802577
// This could be supported, but would require some work to translate
25812578
// rr's pids to/from the ptracer's pid namespace.
2582-
ASSERT(t, is_same_namespace("pid", t->tid, getpid()));
2583-
RecordTask* partner = t->session().find_task(pid);
2584-
if (!partner) {
2585-
// XXX This prevents a tracee from attaching to a process which isn't
2579+
ASSERT(tracer, is_same_namespace("pid", tracer->tid, getpid()));
2580+
RecordTask* tracee = tracer->session().find_task(attach_to_tid);
2581+
if (!tracee) {
2582+
// XXX This prevents a tracer from attaching to a process which isn't
25862583
// under rr's control. We could support this but it would complicate
25872584
// things.
2588-
return nullptr;
2589-
}
2590-
return partner;
2591-
}
2592-
2593-
static RecordTask* prepare_ptrace_attach(RecordTask* t, pid_t pid,
2594-
TaskSyscallState& syscall_state) {
2595-
RecordTask* tracee = get_ptrace_partner(t, pid);
2596-
if (!tracee) {
25972585
syscall_state.emulate_result(-ESRCH);
25982586
return nullptr;
25992587
}
2600-
if (!check_ptracer_compatible(t, tracee)) {
2588+
if (tracee->emulated_ptracer || tracee->tgid() == tracer->tgid()) {
26012589
syscall_state.emulate_result(-EPERM);
26022590
return nullptr;
26032591
}
2592+
check_ptracer_compatible(tracer, tracee);
26042593
return tracee;
26052594
}
26062595

2607-
static RecordTask* prepare_ptrace_traceme(RecordTask* t,
2596+
static RecordTask* prepare_ptrace_traceme(RecordTask* tracee,
26082597
TaskSyscallState& syscall_state) {
2609-
RecordTask* tracer = get_ptrace_partner(t, t->get_parent_pid());
2598+
RecordTask* tracer = nullptr;
2599+
pid_t parent_pid = tracee->get_parent_pid();
2600+
if (tracee->creator_tid != 0) {
2601+
RecordTask* creator = tracee->session().find_task(tracee->creator_tid);
2602+
if (creator && creator->thread_group()->tgid == parent_pid) {
2603+
tracer = creator;
2604+
}
2605+
}
26102606
if (!tracer) {
2611-
syscall_state.emulate_result(-ESRCH);
2612-
return nullptr;
2607+
ThreadGroup* tg = tracee->session().find_thread_group(parent_pid);
2608+
if (tg) {
2609+
tracer = static_cast<RecordTask*>(tg->first_running_task());
2610+
}
2611+
if (!tracer) {
2612+
syscall_state.emulate_result(0);
2613+
return nullptr;
2614+
}
26132615
}
2614-
if (!check_ptracer_compatible(tracer, t)) {
2616+
if (tracee->emulated_ptracer || tracee->tgid() == tracer->tgid()) {
26152617
syscall_state.emulate_result(-EPERM);
26162618
return nullptr;
26172619
}
2620+
check_ptracer_compatible(tracer, tracee);
26182621
return tracer;
26192622
}
26202623

src/test/ptrace_traceme_thread.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
2+
3+
#include "util.h"
4+
5+
static int parent_to_child_fds[2];
6+
static int child_to_parent_fds[2];
7+
8+
static void* do_thread(__attribute__((unused)) void* p) {
9+
pid_t child = fork();
10+
char ch;
11+
if (!child) {
12+
test_assert(0 == ptrace(PTRACE_TRACEME, 0, 0, 0));
13+
test_assert(1 == write(child_to_parent_fds[1], "x", 1));
14+
raise(SIGSTOP);
15+
test_assert(1 == read(parent_to_child_fds[0], &ch, 1));
16+
return NULL;
17+
}
18+
19+
test_assert(1 == read(child_to_parent_fds[0], &ch, 1));
20+
/* Wait until the tracee stops */
21+
int status;
22+
test_assert(child == waitpid(child, &status, 0));
23+
test_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
24+
25+
/* Ask for a ptrace notification on exit */
26+
test_assert(0 == ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACEEXIT));
27+
test_assert(0 == ptrace(PTRACE_CONT, child, 0, 0));
28+
29+
test_assert(1 == write(parent_to_child_fds[1], "p", 1));
30+
31+
/* Child is now exiting. Check for the PTRACE_EVENT_EXIT notification */
32+
test_assert(child == waitpid(child, &status, 0));
33+
test_assert(status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8)));
34+
return NULL;
35+
}
36+
37+
int main(void) {
38+
test_assert(0 == pipe(parent_to_child_fds));
39+
test_assert(0 == pipe(child_to_parent_fds));
40+
41+
pthread_t thread;
42+
pthread_create(&thread, NULL, do_thread, NULL);
43+
pthread_join(thread, NULL);
44+
atomic_puts("EXIT-SUCCESS");
45+
return 0;
46+
}

0 commit comments

Comments
 (0)