Skip to content

Commit dbb117b

Browse files
committed
[scudo] Add hooks to mark the range of realloc
`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.
1 parent f368e64 commit dbb117b

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

compiler-rt/lib/scudo/standalone/include/scudo/interface.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ __attribute__((weak)) const char *__scudo_default_options(void);
2020
__attribute__((weak)) void __scudo_allocate_hook(void *ptr, size_t size);
2121
__attribute__((weak)) void __scudo_deallocate_hook(void *ptr);
2222

23+
// These hooks are used to mark the scope of doing realloc(). Note that the
24+
// allocation/deallocation are still reported through the hooks above, this is
25+
// only used when a hook user wants to know that the allocation/deallocation
26+
// operations are a single realloc operation.
27+
__attribute__((weak)) void __scudo_realloc_begin_hook(void *old_ptr);
28+
__attribute__((weak)) void __scudo_realloc_end_hook(void *old_ptr);
29+
2330
void __scudo_print_stats(void);
2431

2532
typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg);

compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,13 @@ struct AllocContext {
6161
struct DeallocContext {
6262
void *Ptr;
6363
};
64+
struct ReallocContext {
65+
void *Begin;
66+
void *End;
67+
};
6468
static AllocContext AC;
6569
static DeallocContext DC;
70+
static ReallocContext RC;
6671

6772
#if (SCUDO_ENABLE_HOOKS_TESTS == 1)
6873
__attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
@@ -73,6 +78,14 @@ __attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,
7378
__attribute__((visibility("default"))) void __scudo_deallocate_hook(void *Ptr) {
7479
DC.Ptr = Ptr;
7580
}
81+
__attribute__((visibility("default"))) void
82+
__scudo_realloc_begin_hook(void *Ptr) {
83+
RC.Begin = Ptr;
84+
}
85+
__attribute__((visibility("default"))) void
86+
__scudo_realloc_end_hook(void *Ptr) {
87+
RC.End = Ptr;
88+
}
7689
#endif // (SCUDO_ENABLE_HOOKS_TESTS == 1)
7790
}
7891

@@ -88,6 +101,7 @@ class ScudoWrappersCTest : public Test {
88101
void *InvalidPtr = reinterpret_cast<void *>(0xdeadbeef);
89102
AC.Ptr = InvalidPtr;
90103
DC.Ptr = InvalidPtr;
104+
RC.Begin = RC.End = InvalidPtr;
91105
}
92106
}
93107
void verifyAllocHookPtr(UNUSED void *Ptr) {
@@ -102,6 +116,12 @@ class ScudoWrappersCTest : public Test {
102116
if (SCUDO_ENABLE_HOOKS_TESTS)
103117
EXPECT_EQ(Ptr, DC.Ptr);
104118
}
119+
void verifyReallocHooksScope(UNUSED void *Ptr) {
120+
if (SCUDO_ENABLE_HOOKS_TESTS) {
121+
EXPECT_EQ(Ptr, RC.Begin);
122+
EXPECT_EQ(Ptr, RC.End);
123+
}
124+
}
105125
};
106126
using ScudoWrappersCDeathTest = ScudoWrappersCTest;
107127

@@ -297,6 +317,7 @@ TEST_F(ScudoWrappersCDeathTest, Realloc) {
297317
verifyAllocHookSize(Size * 2U);
298318
verifyDeallocHookPtr(OldP);
299319
}
320+
verifyReallocHooksScope(OldP);
300321

301322
invalidateHookPtrs();
302323
OldP = P;
@@ -312,6 +333,7 @@ TEST_F(ScudoWrappersCDeathTest, Realloc) {
312333
verifyAllocHookPtr(P);
313334
verifyAllocHookSize(Size / 2U);
314335
}
336+
verifyReallocHooksScope(OldP);
315337
free(P);
316338

317339
EXPECT_DEATH(P = realloc(P, Size), "");

compiler-rt/lib/scudo/standalone/wrappers_c.inc

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,40 @@ static void reportDeallocation(void *ptr) {
2727
if (__scudo_deallocate_hook)
2828
__scudo_deallocate_hook(ptr);
2929
}
30+
static void reportReallocBegin(void *old_ptr) {
31+
if (SCUDO_ENABLE_HOOKS)
32+
if (__scudo_realloc_begin_hook)
33+
__scudo_realloc_begin_hook(old_ptr);
34+
}
35+
static void reportReallocEnd(void *old_ptr) {
36+
if (SCUDO_ENABLE_HOOKS)
37+
if (__scudo_realloc_end_hook)
38+
__scudo_realloc_end_hook(old_ptr);
39+
}
40+
41+
static void *reallocImpl(void *ptr, size_t size) {
42+
// Given that the reporting of deallocation and allocation are not atomic, we
43+
// always pretend the old pointer will be released so that the user doesn't
44+
// need to worry about the false double-use case from the view of hooks.
45+
//
46+
// For example, assume that `realloc` releases the old pointer and allocates a
47+
// new pointer. Before the reporting of both operations has been done, another
48+
// thread may get the old pointer from `malloc`. It may be misinterpreted as
49+
// double-use if it's not handled properly on the hook side.
50+
reportDeallocation(ptr);
51+
void *NewPtr = SCUDO_ALLOCATOR.reallocate(ptr, size, SCUDO_MALLOC_ALIGNMENT);
52+
if (NewPtr != nullptr) {
53+
// Note that even if NewPtr == ptr, the size has changed. We still need to
54+
// report the new size.
55+
reportAllocation(NewPtr, size);
56+
} else {
57+
// If `realloc` fails, the old pointer is not released. Report the old
58+
// pointer as allocated back.
59+
reportAllocation(ptr, SCUDO_ALLOCATOR.getAllocSize(ptr));
60+
}
61+
62+
return NewPtr;
63+
}
3064

3165
extern "C" {
3266

@@ -175,25 +209,11 @@ INTERFACE WEAK void *SCUDO_PREFIX(realloc)(void *ptr, size_t size) {
175209
return nullptr;
176210
}
177211

178-
// Given that the reporting of deallocation and allocation are not atomic, we
179-
// always pretend the old pointer will be released so that the user doesn't
180-
// need to worry about the false double-use case from the view of hooks.
181-
//
182-
// For example, assume that `realloc` releases the old pointer and allocates a
183-
// new pointer. Before the reporting of both operations has been done, another
184-
// thread may get the old pointer from `malloc`. It may be misinterpreted as
185-
// double-use if it's not handled properly on the hook side.
186-
reportDeallocation(ptr);
187-
void *NewPtr = SCUDO_ALLOCATOR.reallocate(ptr, size, SCUDO_MALLOC_ALIGNMENT);
188-
if (NewPtr != nullptr) {
189-
// Note that even if NewPtr == ptr, the size has changed. We still need to
190-
// report the new size.
191-
reportAllocation(NewPtr, size);
192-
} else {
193-
// If `realloc` fails, the old pointer is not released. Report the old
194-
// pointer as allocated back.
195-
reportAllocation(ptr, SCUDO_ALLOCATOR.getAllocSize(ptr));
196-
}
212+
reportReallocBegin(ptr);
213+
214+
void *NewPtr = reallocImpl(ptr, size);
215+
216+
reportReallocEnd(ptr);
197217

198218
return scudo::setErrnoOnNull(NewPtr);
199219
}

0 commit comments

Comments
 (0)