[compiler-rt] 58c2a4e - [scudo] Add hooks to mark the range of realloc (#74353)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 7 13:49:10 PST 2023
Author: ChiaHungDuan
Date: 2023-12-07T13:49:06-08:00
New Revision: 58c2a4e806b2882c0622cbded923b32f94c5b47b
URL: https://github.com/llvm/llvm-project/commit/58c2a4e806b2882c0622cbded923b32f94c5b47b
DIFF: https://github.com/llvm/llvm-project/commit/58c2a4e806b2882c0622cbded923b32f94c5b47b.diff
LOG: [scudo] Add hooks to mark the range of realloc (#74353)
`realloc` may involve both allocation and deallocation. Given that the
reporting the events is not atomic and which may lead the hook user to a
false case that the double-use pattern happens. In general, this can be
resolved on the hook side. To alleviate the task of handling it, we add
two new hooks to mark the range so that the hook user can combine those
calls together.
Added:
Modified:
compiler-rt/lib/scudo/standalone/include/scudo/interface.h
compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp
compiler-rt/lib/scudo/standalone/wrappers_c.inc
Removed:
################################################################################
diff --git a/compiler-rt/lib/scudo/standalone/include/scudo/interface.h b/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
index a664b9825f209..a2dedea910cc0 100644
--- a/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
+++ b/compiler-rt/lib/scudo/standalone/include/scudo/interface.h
@@ -20,6 +20,19 @@ __attribute__((weak)) const char *__scudo_default_options(void);
__attribute__((weak)) void __scudo_allocate_hook(void *ptr, size_t size);
__attribute__((weak)) void __scudo_deallocate_hook(void *ptr);
+// `realloc` involves both deallocation and allocation but they are not reported
+// atomically. In one specific case which may keep taking a snapshot right in
+// the middle of `realloc` reporting the deallocation and allocation, it may
+// confuse the user by missing memory from `realloc`. To alleviate that case,
+// define the two `realloc` hooks to get the knowledge of the bundled hook
+// calls. These hooks are optional and should only be used when a hooks user
+// wants to track reallocs more closely.
+//
+// See more details in the comment of `realloc` in wrapper_c.inc.
+__attribute__((weak)) void
+__scudo_realloc_allocate_hook(void *old_ptr, void *new_ptr, size_t size);
+__attribute__((weak)) void __scudo_realloc_deallocate_hook(void *old_ptr);
+
void __scudo_print_stats(void);
typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg);
diff --git a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp
index 150688b5b70a5..f5e17d7214863 100644
--- a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp
@@ -61,8 +61,14 @@ struct AllocContext {
struct DeallocContext {
void *Ptr;
};
+struct ReallocContext {
+ void *AllocPtr;
+ void *DeallocPtr;
+ size_t Size;
+};
static AllocContext AC;
static DeallocContext DC;
+static ReallocContext RC;
#if (SCUDO_ENABLE_HOOKS_TESTS == 1)
__attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
@@ -73,6 +79,28 @@ __attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
__attribute__((visibility("default"))) void __scudo_deallocate_hook(void *Ptr) {
DC.Ptr = Ptr;
}
+__attribute__((visibility("default"))) void
+__scudo_realloc_allocate_hook(void *OldPtr, void *NewPtr, size_t Size) {
+ // Verify that __scudo_realloc_deallocate_hook is called first and set the
+ // right pointer.
+ EXPECT_EQ(OldPtr, RC.DeallocPtr);
+ RC.AllocPtr = NewPtr;
+ RC.Size = Size;
+
+ // Note that this is only used for testing. In general, only one pair of hooks
+ // will be invoked in `realloc`. if __scudo_realloc_*_hook are not defined,
+ // it'll call the general hooks only. To make the test easier, we call the
+ // general one here so that either case (whether __scudo_realloc_*_hook are
+ // defined) will be verified without separating them into
diff erent tests.
+ __scudo_allocate_hook(NewPtr, Size);
+}
+__attribute__((visibility("default"))) void
+__scudo_realloc_deallocate_hook(void *Ptr) {
+ RC.DeallocPtr = Ptr;
+
+ // See the comment in the __scudo_realloc_allocate_hook above.
+ __scudo_deallocate_hook(Ptr);
+}
#endif // (SCUDO_ENABLE_HOOKS_TESTS == 1)
}
@@ -88,6 +116,7 @@ class ScudoWrappersCTest : public Test {
void *InvalidPtr = reinterpret_cast<void *>(0xdeadbeef);
AC.Ptr = InvalidPtr;
DC.Ptr = InvalidPtr;
+ RC.AllocPtr = RC.DeallocPtr = InvalidPtr;
}
}
void verifyAllocHookPtr(UNUSED void *Ptr) {
@@ -102,6 +131,13 @@ class ScudoWrappersCTest : public Test {
if (SCUDO_ENABLE_HOOKS_TESTS)
EXPECT_EQ(Ptr, DC.Ptr);
}
+ void verifyReallocHookPtrs(UNUSED void *OldPtr, void *NewPtr, size_t Size) {
+ if (SCUDO_ENABLE_HOOKS_TESTS) {
+ EXPECT_EQ(OldPtr, RC.DeallocPtr);
+ EXPECT_EQ(NewPtr, RC.AllocPtr);
+ EXPECT_EQ(Size, RC.Size);
+ }
+ }
};
using ScudoWrappersCDeathTest = ScudoWrappersCTest;
@@ -297,6 +333,7 @@ TEST_F(ScudoWrappersCDeathTest, Realloc) {
verifyAllocHookSize(Size * 2U);
verifyDeallocHookPtr(OldP);
}
+ verifyReallocHookPtrs(OldP, P, Size * 2U);
invalidateHookPtrs();
OldP = P;
@@ -312,6 +349,7 @@ TEST_F(ScudoWrappersCDeathTest, Realloc) {
verifyAllocHookPtr(P);
verifyAllocHookSize(Size / 2U);
}
+ verifyReallocHookPtrs(OldP, P, Size / 2U);
free(P);
EXPECT_DEATH(P = realloc(P, Size), "");
diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c.inc b/compiler-rt/lib/scudo/standalone/wrappers_c.inc
index 0413ea49eac08..56d8ef20156e2 100644
--- a/compiler-rt/lib/scudo/standalone/wrappers_c.inc
+++ b/compiler-rt/lib/scudo/standalone/wrappers_c.inc
@@ -27,6 +27,24 @@ static void reportDeallocation(void *ptr) {
if (__scudo_deallocate_hook)
__scudo_deallocate_hook(ptr);
}
+static void reportReallocAllocation(void *old_ptr, void *new_ptr, size_t size) {
+ DCHECK_NE(new_ptr, nullptr);
+
+ if (SCUDO_ENABLE_HOOKS) {
+ if (__scudo_realloc_allocate_hook)
+ __scudo_realloc_allocate_hook(old_ptr, new_ptr, size);
+ else if (__scudo_allocate_hook)
+ __scudo_allocate_hook(new_ptr, size);
+ }
+}
+static void reportReallocDeallocation(void *old_ptr) {
+ if (SCUDO_ENABLE_HOOKS) {
+ if (__scudo_realloc_deallocate_hook)
+ __scudo_realloc_deallocate_hook(old_ptr);
+ else if (__scudo_deallocate_hook)
+ __scudo_deallocate_hook(old_ptr);
+ }
+}
extern "C" {
@@ -183,16 +201,17 @@ INTERFACE WEAK void *SCUDO_PREFIX(realloc)(void *ptr, size_t size) {
// new pointer. Before the reporting of both operations has been done, another
// thread may get the old pointer from `malloc`. It may be misinterpreted as
// double-use if it's not handled properly on the hook side.
- reportDeallocation(ptr);
+ reportReallocDeallocation(ptr);
void *NewPtr = SCUDO_ALLOCATOR.reallocate(ptr, size, SCUDO_MALLOC_ALIGNMENT);
if (NewPtr != nullptr) {
// Note that even if NewPtr == ptr, the size has changed. We still need to
// report the new size.
- reportAllocation(NewPtr, size);
+ reportReallocAllocation(/*OldPtr=*/ptr, NewPtr, size);
} else {
// If `realloc` fails, the old pointer is not released. Report the old
- // pointer as allocated back.
- reportAllocation(ptr, SCUDO_ALLOCATOR.getAllocSize(ptr));
+ // pointer as allocated again.
+ reportReallocAllocation(/*OldPtr=*/ptr, /*NewPtr=*/ptr,
+ SCUDO_ALLOCATOR.getAllocSize(ptr));
}
return scudo::setErrnoOnNull(NewPtr);
More information about the llvm-commits
mailing list