Skip to content

Commit bf817d1

Browse files
committed
Fix the ptrace(PTRACE_ATTACH, ...) protocol
After we attach, the first signal we receive may not be our stop signal. Check for the SIGSTOP, and if that's not what we received, then redeliver, and continue until we get the actual stop.
1 parent f066f08 commit bf817d1

3 files changed

Lines changed: 49 additions & 24 deletions

File tree

CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cmake_minimum_required(VERSION 3.10)
22
set(PSTACK_SOVERSION 2.16)
3-
set(PSTACK_VERSION 2.16)
3+
set(PSTACK_VERSION 2.16.1)
44
project(pstack LANGUAGES C CXX VERSION "${PSTACK_VERSION}" )
55

66
include (GNUInstallDirs)
@@ -9,6 +9,7 @@ add_subdirectory(tests)
99

1010
option(PYTHON3 "Compile with python 3 support" OFF)
1111
option(PYTHON2 "Compile with python 2 support" ON)
12+
option(PTRACE_TESTS "Run extra tests that require unrestricted access to ptrace" OFF)
1213

1314
math(EXPR PLATFORM_BITS "${CMAKE_SIZEOF_VOID_P} * 8")
1415
set(PSTACK_BIN "pstack" CACHE STRING "Name of the 'pstack' binary")
@@ -175,7 +176,11 @@ add_test(NAME segv COMMAND env PSTACK_BIN=${PSTACK_BIN} ${CMAKE_SOURCE_DIR}/test
175176
add_test(NAME thread COMMAND env PSTACK_BIN=${PSTACK_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/tests/thread-test.py)
176177
add_test(NAME jsondump COMMAND env PSTACK_BIN=${PSTACK_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/tests/dump-test.py)
177178
add_test(NAME procself COMMAND tests/procself)
179+
180+
# need to remove this test for environments with more restrictive ptrace
181+
if (PTRACE_TESTS)
178182
add_test(NAME suspend COMMAND tests/suspend)
183+
endif()
179184
if (PYTHON3 AND Python3_Development_FOUND)
180185
add_test(NAME pydict COMMAND env PSTACK_BIN=${PSTACK_BIN}${CMAKE_CURRENT_SOURCE_DIR}/tests/pydict_test.py)
181186
endif()

live.cc

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,44 @@ LiveProcess::stop(lwpid_t tid) {
180180
}
181181
tcb.ptraceErr = 0;
182182

183-
int status = 0;
184-
pid_t waitedpid = waitpid(tid, &status, tid == this->pid ? 0 : __WCLONE);
185-
if (waitedpid == -1)
186-
*context.debug << "failed to stop LWP " << tid << ": wait failed: " << strerror(errno) << "\n";
187-
else if (context.verbose >= 1)
188-
*context.debug << "suspend LWP " << tid << std::endl;
183+
// If anything breaks from here on, we'll PT_DETACH, and send a SIGCONT in
184+
// case there's any confusion
185+
186+
for (int count = 0; tcb.ptraceErr == 0; ++count) {
187+
int status = 0;
188+
pid_t waitedpid = waitpid(tid, &status, tid == this->pid ? 0 : __WCLONE);
189+
if (waitedpid == -1) {
190+
if (errno != EINTR) {
191+
tcb.ptraceErr = errno;
192+
*context.debug << "failed to stop LWP " << tid << ": wait failed: " << strerror(errno) << "\n";
193+
} else {
194+
*context.debug << "interrupted stopping LWP " << tid << ": retry\n";
195+
}
196+
} else if (!WIFSTOPPED(status)) {
197+
tcb.ptraceErr = EINVAL;
198+
*context.debug << "wait doesn't report LWP stopped? " << status << "\n";
199+
} else if (WSTOPSIG(status) != SIGSTOP) {
200+
*context.debug << "got signal " << WSTOPSIG(status)
201+
<< " while waiting for " << tid << " to stop - deliver and retry.";
202+
siginfo_t si;
203+
if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, &si) != -1) {
204+
*context.debug << " new siginfo: " << SigInfo{si};
205+
}
206+
*context.debug << "\n";
207+
208+
if (ptrace(PTRACE_CONT, tid, nullptr, WSTOPSIG(status)) == -1) {
209+
tcb.ptraceErr = errno;
210+
*context.debug << "...failed " << errno << "\n";
211+
}
212+
} else {
213+
if (context.verbose >= 1)
214+
*context.debug << "suspended LWP " << tid << " (attempt " << count+1 << ")" << std::endl;
215+
return;
216+
}
217+
}
218+
*context.debug << "sending SIGCONT to " << tid << " and detaching\n";
219+
kill(tid, SIGCONT); // don't leave it hanging if we messed up.
220+
ptrace(PT_DETACH, tid, caddr_t(1), 0);
189221
}
190222

191223
// Parse [s]maps for this pid. We use "smaps" just for vmflags for now.

process.cc

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,11 @@ std::ostream &operator << (std::ostream &os, const SigInfo &sip) {
14221422
#else
14231423
<< "si_signo " << si.si_signo
14241424
#endif
1425-
<< ", si_code " << si.si_code;
1425+
<< ", si_code " << si.si_code
1426+
<< ", si_pid " << si.si_pid
1427+
<< ", si_fd " << si.si_fd
1428+
<< ", si_addr " << si.si_addr
1429+
;
14261430

14271431
auto codesforsig = codes.find( si.si_signo );
14281432
if (codesforsig == codes.end()) {
@@ -1434,22 +1438,6 @@ std::ostream &operator << (std::ostream &os, const SigInfo &sip) {
14341438
if (code != codesforsig->second.end())
14351439
os << " - " << code->second;
14361440
}
1437-
1438-
switch (si.si_signo) {
1439-
case SIGILL:
1440-
case SIGFPE:
1441-
case SIGBUS:
1442-
case SIGTRAP:
1443-
#ifdef SIGEMT
1444-
case SIGEMT:
1445-
#endif
1446-
case SIGSEGV: {
1447-
os << ", fault address " << std::hex << si.si_addr << std::dec;
1448-
break;
1449-
}
1450-
1451-
1452-
}
14531441
return os;
14541442
}}
14551443

0 commit comments

Comments
 (0)