[GWP-ASan] [heapprofd] Fix infinite recursion between malloc-racing threads.
When the heapprofd lazy-initialization dispatch table is installed, two
threads can call malloc() at the same time. One will do the
lazy-initialization, the other will fail the atomic_exchange of
gHeapprofdInitHookInstalled and go to system malloc. The system malloc
still contains the lazy-init malloc, and will recurse.
Fix it so the second thread goes directly to the previous dispatch
table, or to the system allocator directly (instead of through libc
malloc()).
Bug: 150085813
Test: atest perfetto_integrationtests
Test: TracedPerfCtsTest.SystemWideDebuggableApp on cuttlefish x86.
Change-Id: Ia85ad619a0d5e3f558136d84c34dbada4e8b845d
diff --git a/libc/bionic/malloc_heapprofd.cpp b/libc/bionic/malloc_heapprofd.cpp
index 198d2f0..94c6b5c 100644
--- a/libc/bionic/malloc_heapprofd.cpp
+++ b/libc/bionic/malloc_heapprofd.cpp
@@ -301,10 +301,17 @@
error_log("%s: heapprod: failed to pthread_setname_np", getprogname());
}
}
- // Get an allocation from libc malloc. If we had a previous dispatch table,
- // this will come from it - otherwise, we'll get it from the system
- // allocator.
- return malloc(bytes);
+ // If we had a previous dispatch table, use that to service the allocation,
+ // otherwise fall back to the native allocator.
+ // `gPreviousDefaultDispatchTable` won't change underneath us, as it's
+ // protected by the `gHeapProfdInitInProgress` lock (which we currently hold).
+ // The lock was originally taken by our caller in `HandleHeapprofdSignal()`,
+ // and will be released by `CommonInstallHooks()` via. our `InitHeapprofd()`
+ // thread that we just created.
+ if (gPreviousDefaultDispatchTable) {
+ return gPreviousDefaultDispatchTable->malloc(bytes);
+ }
+ return NativeAllocatorDispatch()->malloc(bytes);
}
bool HeapprofdInitZygoteChildProfiling() {