[Scudo] Use GWP-ASan's aligned allocations and fixup postalloc hooks.

This patch does a few cleanup things:
 1. The non-standalone scudo has a problem where GWP-ASan allocations
 may not meet alignment requirements where Scudo was requested to have
 alignment >= 16. Use the new GWP-ASan API to fix this.
 2. The standalone variant loses some debugging information inside of
 GWP-ASan because we ask GWP-ASan to allocate an aligned size in the
 frontend. This means reports end up with 'UaF on a 16-byte allocation'
 for a 1-byte allocation with 16-byte alignment. Also use the new API to
 fix this.
 3. Add post-alloc hooks for GWP-ASan intercepted allocations, and add
 stats tracking for GWP-ASan allocations.
 4. Add a small test that checks the alignment of the frontend
 allocator, so that it can be used under GWP-ASan torture mode.
 5. Add GWP-ASan torture mode as a testing configuration to catch these
 regressions.

Depends on D94830, D95889.

Reviewed By: cryptoad

Differential Revision: https://reviews.llvm.org/D95884

GitOrigin-RevId: a683abe5c026cffff12a943564f4cb1b20972abf
diff --git a/combined.h b/combined.h
index 6d68d6d..7ae6086 100644
--- a/combined.h
+++ b/combined.h
@@ -290,19 +290,7 @@
                           bool ZeroContents = false) {
     initThreadMaybe();
 
-#ifdef GWP_ASAN_HOOKS
-    if (UNLIKELY(GuardedAlloc.shouldSample())) {
-      if (void *Ptr = GuardedAlloc.allocate(roundUpTo(Size, Alignment)))
-        return Ptr;
-    }
-#endif // GWP_ASAN_HOOKS
-
     const Options Options = Primary.Options.load();
-    const FillContentsMode FillContents = ZeroContents ? ZeroFill
-                                          : TSDRegistry.getDisableMemInit()
-                                              ? NoFill
-                                              : Options.getFillContentsMode();
-
     if (UNLIKELY(Alignment > MaxAlignment)) {
       if (Options.get(OptionBit::MayReturnNull))
         return nullptr;
@@ -311,6 +299,22 @@
     if (Alignment < MinAlignment)
       Alignment = MinAlignment;
 
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.shouldSample())) {
+      if (void *Ptr = GuardedAlloc.allocate(Size, Alignment)) {
+        if (UNLIKELY(&__scudo_allocate_hook))
+          __scudo_allocate_hook(Ptr, Size);
+        Stats.add(StatAllocated, Size);
+        return Ptr;
+      }
+    }
+#endif // GWP_ASAN_HOOKS
+
+    const FillContentsMode FillContents = ZeroContents ? ZeroFill
+                                          : TSDRegistry.getDisableMemInit()
+                                              ? NoFill
+                                              : Options.getFillContentsMode();
+
     // If the requested size happens to be 0 (more common than you might think),
     // allocate MinAlignment bytes on top of the header. Then add the extra
     // bytes required to fulfill the alignment requirements: we allocate enough
@@ -503,18 +507,20 @@
     // being destroyed properly. Any other heap operation will do a full init.
     initThreadMaybe(/*MinimalInit=*/true);
 
-#ifdef GWP_ASAN_HOOKS
-    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) {
-      GuardedAlloc.deallocate(Ptr);
-      return;
-    }
-#endif // GWP_ASAN_HOOKS
-
     if (UNLIKELY(&__scudo_deallocate_hook))
       __scudo_deallocate_hook(Ptr);
 
     if (UNLIKELY(!Ptr))
       return;
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) {
+      GuardedAlloc.deallocate(Ptr);
+      Stats.add(StatFree, GuardedAlloc.getSize(Ptr));
+      return;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment)))
       reportMisalignedPointer(AllocatorAction::Deallocating, Ptr);
 
@@ -571,6 +577,7 @@
       if (NewPtr)
         memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize);
       GuardedAlloc.deallocate(OldPtr);
+      Stats.add(StatFree, OldSize);
       return NewPtr;
     }
 #endif // GWP_ASAN_HOOKS
diff --git a/tests/wrappers_c_test.cpp b/tests/wrappers_c_test.cpp
index e8872a1..eed8f03 100644
--- a/tests/wrappers_c_test.cpp
+++ b/tests/wrappers_c_test.cpp
@@ -94,6 +94,18 @@
   EXPECT_EQ(errno, ENOMEM);
 }
 
+TEST(ScudoWrappersCTest, SmallAlign) {
+  void *P;
+  for (size_t Size = 1; Size <= 0x10000; Size <<= 1) {
+    for (size_t Align = 1; Align <= 0x10000; Align <<= 1) {
+      for (size_t Count = 0; Count < 3; ++Count) {
+        P = memalign(Align, Size);
+        EXPECT_TRUE(reinterpret_cast<uintptr_t>(P) % Align == 0);
+      }
+    }
+  }
+}
+
 TEST(ScudoWrappersCTest, Memalign) {
   void *P;
   for (size_t I = FIRST_32_SECOND_64(2U, 3U); I <= 18U; I++) {
diff --git a/tests/wrappers_cpp_test.cpp b/tests/wrappers_cpp_test.cpp
index d24b665..9df06dc 100644
--- a/tests/wrappers_cpp_test.cpp
+++ b/tests/wrappers_cpp_test.cpp
@@ -66,6 +66,10 @@
 };
 
 TEST(ScudoWrappersCppTest, New) {
+  if (getenv("SKIP_TYPE_MISMATCH")) {
+    printf("Skipped type mismatch tests.\n");
+    return;
+  }
   testCxxNew<bool>();
   testCxxNew<uint8_t>();
   testCxxNew<uint16_t>();