[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() {