Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ class GtestTaskBuilder(
args.add("-D__musl__")
}

// Mark unit-test builds so test-only production APIs are compiled in.
args.add("-DUNIT_TEST")

return args
}
}
22 changes: 19 additions & 3 deletions ddprof-lib/src/main/cpp/libraryPatcher_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ class RoutineInfo {
}
};

// Unregister the current thread from the profiler and release its TLS under a
// single SignalBlocker to close the race window between unregisterThread()
// returning and release() acquiring its internal guard (PROF-14603). Without
// this, a SIGVTALRM delivered in that window could call currentSignalSafe()
// and dereference a now-freed ProfiledThread. Kept noinline so the
// SignalBlocker's sigset_t does not appear in the caller's stack frame on
// musl/aarch64 where the deopt blob may corrupt the wrapper's stack guard.
__attribute__((noinline))
static void unregister_and_release(int tid) {
SignalBlocker blocker;
Profiler::unregisterThread(tid);
ProfiledThread::release();
}

#ifdef __aarch64__
// Delete RoutineInfo with profiling signals blocked to prevent ASAN
// allocator lock reentrancy. Kept noinline so SignalBlocker's sigset_t
Expand Down Expand Up @@ -129,8 +143,9 @@ static void* start_routine_wrapper_spec(void* args) {
delete_routine_info(thr);
init_tls_and_register();
routine(params);
Profiler::unregisterThread(ProfiledThread::currentTid());
ProfiledThread::release();
// unregister_and_release() holds SignalBlocker for the duration, closing the
// race window between unregisterThread() and release() (PROF-14603).
unregister_and_release(ProfiledThread::currentTid());
// pthread_exit instead of 'return': the saved LR in this frame is corrupted
// by DEOPT PACKING; returning would jump to a garbage address.
pthread_exit(nullptr);
Expand Down Expand Up @@ -184,8 +199,9 @@ static void* start_routine_wrapper(void* args) {
}
// RAII cleanup: reads tid from TLS in the destructor (same rationale as
// start_routine_wrapper_spec: avoids storing state on a potentially corruptible frame).
// unregister_and_release() wraps the two calls under SignalBlocker (PROF-14603).
struct Cleanup {
~Cleanup() { Profiler::unregisterThread(ProfiledThread::currentTid()); ProfiledThread::release(); }
~Cleanup() { unregister_and_release(ProfiledThread::currentTid()); }
} cleanup;
routine(params);
return nullptr;
Expand Down
7 changes: 5 additions & 2 deletions ddprof-lib/src/main/cpp/perfEvents_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,11 @@ static int pthread_setspecific_hook(pthread_key_t key, const void *value) {
return result;
} else {
int tid = ProfiledThread::currentTid();
Profiler::unregisterThread(tid);
ProfiledThread::release();
{
SignalBlocker blocker;
Profiler::unregisterThread(tid);
ProfiledThread::release();
}
return pthread_setspecific(key, value);
}
}
Expand Down
7 changes: 4 additions & 3 deletions ddprof-lib/src/main/cpp/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,15 @@ class ProfiledThread : public ThreadLocalData {

static void initCurrentThread();
static void release();
// Clears TLS without deleting the ProfiledThread. For unit tests only:
// simulates the moment inside release() after pthread_setspecific(NULL) but
// before delete, which is the race window the _thread_ptr fix covers.
#ifdef UNIT_TEST
// Simulates the moment inside release() after pthread_setspecific(NULL) but
// before deletethe race window the clearCurrentThreadTLS fix covers.
static void clearCurrentThreadTLS() {
if (__atomic_load_n(&_tls_key_initialized, __ATOMIC_ACQUIRE)) {
pthread_setspecific(_tls_key, nullptr);
}
}
#endif

static ProfiledThread *current();
static ProfiledThread *currentSignalSafe(); // Signal-safe version that never allocates
Expand Down
Loading
Loading