Skip to content

Commit 8510f71

Browse files
committed
scudo: Support free_sized and free_aligned_sized from C23
Signed-off-by: Justin King <jcking@google.com>
1 parent 569ca0f commit 8510f71

File tree

13 files changed

+387
-73
lines changed

13 files changed

+387
-73
lines changed

compiler-rt/lib/scudo/standalone/chunk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ enum Origin : u8 {
5656
Malloc = 0,
5757
New = 1,
5858
NewArray = 2,
59-
Memalign = 3,
59+
AlignedAlloc = 3,
6060
};
6161

6262
enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 };

compiler-rt/lib/scudo/standalone/combined.h

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ class Allocator {
170170
Primary.Options.set(OptionBit::DeallocTypeMismatch);
171171
if (getFlags()->delete_size_mismatch)
172172
Primary.Options.set(OptionBit::DeleteSizeMismatch);
173+
if (getFlags()->free_size_mismatch)
174+
Primary.Options.set(OptionBit::FreeSizeMismatch);
175+
if (getFlags()->free_alignment_mismatch)
176+
Primary.Options.set(OptionBit::FreeAlignmentMismatch);
177+
if (getFlags()->delete_alignment_mismatch)
178+
Primary.Options.set(OptionBit::DeleteAlignmentMismatch);
173179
if (allocatorSupportsMemoryTagging<AllocatorConfig>() &&
174180
systemSupportsMemoryTagging())
175181
Primary.Options.set(OptionBit::UseMemoryTagging);
@@ -433,7 +439,8 @@ class Allocator {
433439
}
434440

435441
NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0,
436-
UNUSED uptr Alignment = MinAlignment) {
442+
bool HasDeleteSize = false, uptr DeleteAlignment = 0,
443+
bool HasDeleteAlignment = false) {
437444
if (UNLIKELY(!Ptr))
438445
return;
439446

@@ -456,6 +463,9 @@ class Allocator {
456463
}
457464
#endif // GWP_ASAN_HOOKS
458465

466+
if (UNLIKELY(HasDeleteAlignment && !isPowerOfTwo(DeleteAlignment)))
467+
reportAlignmentNotPowerOfTwo(DeleteAlignment);
468+
459469
if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment)))
460470
reportMisalignedPointer(AllocatorAction::Deallocating, Ptr);
461471

@@ -470,20 +480,35 @@ class Allocator {
470480

471481
const Options Options = Primary.Options.load();
472482
if (Options.get(OptionBit::DeallocTypeMismatch)) {
473-
if (UNLIKELY(Header.OriginOrWasZeroed != Origin)) {
474-
// With the exception of memalign'd chunks, that can be still be free'd.
475-
if (Header.OriginOrWasZeroed != Chunk::Origin::Memalign ||
476-
Origin != Chunk::Origin::Malloc)
477-
reportDeallocTypeMismatch(AllocatorAction::Deallocating, Ptr,
478-
Header.OriginOrWasZeroed, Origin);
479-
}
483+
if (UNLIKELY(isOriginMismatch(
484+
static_cast<Chunk::Origin>(Header.OriginOrWasZeroed), Origin,
485+
HasDeleteSize)))
486+
reportDeallocTypeMismatch(AllocatorAction::Deallocating, Ptr,
487+
Header.OriginOrWasZeroed, Origin);
480488
}
481489

482490
const uptr Size = getSize(Ptr, &Header);
483-
if (DeleteSize && Options.get(OptionBit::DeleteSizeMismatch)) {
491+
if ((Origin == Chunk::Origin::New || Origin == Chunk::Origin::NewArray) &&
492+
HasDeleteSize && Options.get(OptionBit::DeleteSizeMismatch)) {
484493
if (UNLIKELY(DeleteSize != Size))
485494
reportDeleteSizeMismatch(Ptr, DeleteSize, Size);
486495
}
496+
if ((Origin == Chunk::Origin::New || Origin == Chunk::Origin::NewArray) &&
497+
HasDeleteAlignment && Options.get(OptionBit::DeleteAlignmentMismatch)) {
498+
if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), DeleteAlignment)))
499+
reportDeleteAlignmentMismatch(Ptr, DeleteAlignment);
500+
}
501+
if ((Origin == Chunk::Origin::Malloc ||
502+
Origin == Chunk::Origin::AlignedAlloc) &&
503+
HasDeleteSize && Options.get(OptionBit::FreeSizeMismatch)) {
504+
if (UNLIKELY(DeleteSize != Size))
505+
reportFreeSizeMismatch(Ptr, DeleteSize, Size);
506+
}
507+
if (Origin == Chunk::Origin::AlignedAlloc && HasDeleteAlignment &&
508+
Options.get(OptionBit::FreeAlignmentMismatch)) {
509+
if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), DeleteAlignment)))
510+
reportFreeAlignmentMismatch(Ptr, DeleteAlignment);
511+
}
487512

488513
quarantineOrDeallocateChunk(Options, TaggedPtr, &Header, Size);
489514
}
@@ -520,7 +545,7 @@ class Allocator {
520545
void *OldTaggedPtr = OldPtr;
521546
OldPtr = getHeaderTaggedPointer(OldPtr);
522547

523-
if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(OldPtr), MinAlignment)))
548+
if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(OldPtr), Alignment)))
524549
reportMisalignedPointer(AllocatorAction::Reallocating, OldPtr);
525550

526551
Chunk::UnpackedHeader Header;
@@ -529,11 +554,14 @@ class Allocator {
529554
if (UNLIKELY(Header.State != Chunk::State::Allocated))
530555
reportInvalidChunkState(AllocatorAction::Reallocating, OldPtr);
531556

532-
// Pointer has to be allocated with a malloc-type function. Some
533-
// applications think that it is OK to realloc a memalign'ed pointer, which
534-
// will trigger this check. It really isn't.
535557
if (Options.get(OptionBit::DeallocTypeMismatch)) {
536-
if (UNLIKELY(Header.OriginOrWasZeroed != Chunk::Origin::Malloc))
558+
// There is no language prohibiting the use of realloc with
559+
// aligned_alloc/posix_memalign/memalign and etc. However there is no
560+
// guarantee that the allocator being used with malloc is the same as
561+
// operator new. There is also no guarantee that they share the same
562+
// minimum alignment guarantees. So we reject these.
563+
if (UNLIKELY(Header.OriginOrWasZeroed == Chunk::Origin::New ||
564+
Header.OriginOrWasZeroed == Chunk::Origin::NewArray))
537565
reportDeallocTypeMismatch(AllocatorAction::Reallocating, OldPtr,
538566
Header.OriginOrWasZeroed,
539567
Chunk::Origin::Malloc);
@@ -1746,6 +1774,18 @@ class Allocator {
17461774
return (Bytes - sizeof(AllocationRingBuffer)) /
17471775
sizeof(typename AllocationRingBuffer::Entry);
17481776
}
1777+
1778+
static bool isOriginMismatch(Chunk::Origin Alloc, Chunk::Origin Dealloc,
1779+
bool HasDeleteSize) {
1780+
if (Alloc == Dealloc) {
1781+
return false;
1782+
}
1783+
if (Alloc == Chunk::Origin::AlignedAlloc &&
1784+
Dealloc == Chunk::Origin::Malloc && !HasDeleteSize) {
1785+
return false;
1786+
}
1787+
return true;
1788+
}
17491789
};
17501790

17511791
} // namespace scudo

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ SCUDO_FLAG(int, quarantine_max_chunk_size, 0,
2424
"Size (in bytes) up to which chunks will be quarantined (if lower "
2525
"than or equal to).")
2626

27-
SCUDO_FLAG(bool, dealloc_type_mismatch, false,
27+
SCUDO_FLAG(bool, dealloc_type_mismatch, !SCUDO_ANDROID,
2828
"Terminate on a type mismatch in allocation-deallocation functions, "
2929
"eg: malloc/delete, new/free, new/delete[], etc.")
3030

@@ -49,3 +49,18 @@ SCUDO_FLAG(int, release_to_os_interval_ms, 5000,
4949
SCUDO_FLAG(int, allocation_ring_buffer_size, 32768,
5050
"Entries to keep in the allocation ring buffer for scudo. "
5151
"Values less or equal to zero disable the buffer.")
52+
53+
SCUDO_FLAG(bool, delete_alignment_mismatch, true,
54+
"Terminate on an alignment mismatch between a aligned-delete and "
55+
"the actual "
56+
"alignment of a chunk (as provided to new/new[]).")
57+
58+
SCUDO_FLAG(bool, free_size_mismatch, true,
59+
"Terminate on a size mismatch between a free_sized and the actual "
60+
"size of a chunk (as provided to malloc/calloc/realloc).")
61+
62+
SCUDO_FLAG(bool, free_alignment_mismatch, true,
63+
"Terminate on an alignment mismatch between a free_aligned_sized "
64+
"and the actual "
65+
"alignment of a chunk (as provided to "
66+
"aligned_alloc/posix_memalign/memalign).")

compiler-rt/lib/scudo/standalone/options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ enum class OptionBit {
2525
UseOddEvenTags,
2626
UseMemoryTagging,
2727
AddLargeAllocationSlack,
28+
DeleteAlignmentMismatch,
29+
FreeSizeMismatch,
30+
FreeAlignmentMismatch,
2831
};
2932

3033
struct Options {

compiler-rt/lib/scudo/standalone/report.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,55 @@ void NORETURN reportMisalignedPointer(AllocatorAction Action, const void *Ptr) {
142142
stringifyAction(Action), Ptr);
143143
}
144144

145+
static const char *stringifyAllocOrigin(Chunk::Origin AllocOrigin) {
146+
switch (AllocOrigin) {
147+
case Chunk::Origin::Malloc:
148+
return "malloc";
149+
case Chunk::Origin::New:
150+
return "operator new";
151+
case Chunk::Origin::NewArray:
152+
return "operator new []";
153+
case Chunk::Origin::AlignedAlloc:
154+
return "aligned_alloc";
155+
}
156+
return "<invalid origin>";
157+
}
158+
159+
static const char *stringifyDeallocOrigin(AllocatorAction Action,
160+
Chunk::Origin AllocOrigin,
161+
Chunk::Origin DeallocOrigin) {
162+
switch (DeallocOrigin) {
163+
case Chunk::Origin::Malloc:
164+
if (AllocOrigin == Chunk::Origin::AlignedAlloc) {
165+
if (Action == AllocatorAction::Reallocating)
166+
return "realloc";
167+
return "free_sized";
168+
}
169+
return "free";
170+
case Chunk::Origin::New:
171+
return "operator delete";
172+
case Chunk::Origin::NewArray:
173+
return "operator delete []";
174+
case Chunk::Origin::AlignedAlloc:
175+
if (AllocOrigin == Chunk::Origin::Malloc) {
176+
return "free_aligned_sized";
177+
}
178+
return "free";
179+
}
180+
return "<invalid origin>";
181+
}
182+
145183
// The deallocation function used is at odds with the one used to allocate the
146184
// chunk (eg: new[]/delete or malloc/delete, and so on).
147185
void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, const void *Ptr,
148186
u8 TypeA, u8 TypeB) {
149187
ScopedErrorReport Report;
150-
Report.append("allocation type mismatch when %s address %p (%d vs %d)\n",
151-
stringifyAction(Action), Ptr, TypeA, TypeB);
188+
Report.append("allocation type mismatch when %s address %p (%s vs %s)\n",
189+
stringifyAction(Action), Ptr,
190+
stringifyAllocOrigin(static_cast<Chunk::Origin>(TypeA)),
191+
stringifyDeallocOrigin(Action,
192+
static_cast<Chunk::Origin>(TypeA),
193+
static_cast<Chunk::Origin>(TypeB)));
152194
}
153195

154196
// The size specified to the delete operator does not match the one that was
@@ -161,6 +203,26 @@ void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
161203
Size, ExpectedSize);
162204
}
163205

206+
void NORETURN reportDeleteAlignmentMismatch(const void *Ptr, uptr Alignment) {
207+
ScopedErrorReport Report;
208+
Report.append("invalid aligned delete when deallocating address %p (%zu)\n",
209+
Ptr, Alignment);
210+
}
211+
212+
void NORETURN reportFreeSizeMismatch(const void *Ptr, uptr Size,
213+
uptr ExpectedSize) {
214+
ScopedErrorReport Report;
215+
Report.append(
216+
"invalid sized free when deallocating address %p (%zu vs %zu)\n", Ptr,
217+
Size, ExpectedSize);
218+
}
219+
220+
void NORETURN reportFreeAlignmentMismatch(const void *Ptr, uptr Alignment) {
221+
ScopedErrorReport Report;
222+
Report.append("invalid aligned free when deallocating address %p (%zu)\n",
223+
Ptr, Alignment);
224+
}
225+
164226
void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment) {
165227
ScopedErrorReport Report;
166228
Report.append(

compiler-rt/lib/scudo/standalone/report.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ void NORETURN reportDeallocTypeMismatch(AllocatorAction Action, const void *Ptr,
4747
u8 TypeA, u8 TypeB);
4848
void NORETURN reportDeleteSizeMismatch(const void *Ptr, uptr Size,
4949
uptr ExpectedSize);
50+
void NORETURN reportDeleteAlignmentMismatch(const void *Ptr, uptr Alignment);
51+
void NORETURN reportFreeSizeMismatch(const void *Ptr, uptr Size,
52+
uptr ExpectedSize);
53+
void NORETURN reportFreeAlignmentMismatch(const void *Ptr, uptr Alignment);
5054

5155
// C wrappers errors.
5256
void NORETURN reportAlignmentNotPowerOfTwo(uptr Alignment);

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ void ScudoCombinedTest<Config>::BasicTest(scudo::uptr SizeLog) {
327327
EXPECT_LE(Size, Allocator->getUsableSize(P));
328328
memset(P, 0xaa, Size);
329329
checkMemoryTaggingMaybe(Allocator, P, Size, Align);
330-
Allocator->deallocate(P, Origin, Size);
330+
Allocator->deallocate(P, Origin, Size, true);
331331
}
332332
}
333333

@@ -374,7 +374,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ZeroContents) {
374374
for (scudo::uptr I = 0; I < Size; I++)
375375
ASSERT_EQ((reinterpret_cast<char *>(P))[I], '\0');
376376
memset(P, 0xaa, Size);
377-
Allocator->deallocate(P, Origin, Size);
377+
Allocator->deallocate(P, Origin, Size, true);
378378
}
379379
}
380380
}
@@ -392,7 +392,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ZeroFill) {
392392
for (scudo::uptr I = 0; I < Size; I++)
393393
ASSERT_EQ((reinterpret_cast<char *>(P))[I], '\0');
394394
memset(P, 0xaa, Size);
395-
Allocator->deallocate(P, Origin, Size);
395+
Allocator->deallocate(P, Origin, Size, true);
396396
}
397397
}
398398
}
@@ -709,7 +709,7 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, ThreadedCombined) {
709709

710710
while (!V.empty()) {
711711
auto Pair = V.back();
712-
Allocator->deallocate(Pair.first, Origin, Pair.second);
712+
Allocator->deallocate(Pair.first, Origin, Pair.second, true);
713713
V.pop_back();
714714
}
715715
});
@@ -782,26 +782,26 @@ TEST(ScudoCombinedDeathTest, DeathCombined) {
782782
EXPECT_NE(P, nullptr);
783783

784784
// Invalid sized deallocation.
785-
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size + 8U), "");
785+
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size + 8U, true), "");
786786

787787
// Misaligned pointer. Potentially unused if EXPECT_DEATH isn't available.
788788
UNUSED void *MisalignedP =
789789
reinterpret_cast<void *>(reinterpret_cast<scudo::uptr>(P) | 1U);
790-
EXPECT_DEATH(Allocator->deallocate(MisalignedP, Origin, Size), "");
791-
EXPECT_DEATH(Allocator->reallocate(MisalignedP, Size * 2U), "");
790+
EXPECT_DEATH(Allocator->deallocate(MisalignedP, Origin, Size, true), "");
791+
EXPECT_DEATH(Allocator->reallocate(MisalignedP, Size * 2U, true), "");
792792

793793
// Header corruption.
794794
scudo::u64 *H =
795795
reinterpret_cast<scudo::u64 *>(scudo::Chunk::getAtomicHeader(P));
796796
*H ^= 0x42U;
797-
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
797+
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size, true), "");
798798
*H ^= 0x420042U;
799-
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
799+
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size, true), "");
800800
*H ^= 0x420000U;
801801

802802
// Invalid chunk state.
803-
Allocator->deallocate(P, Origin, Size);
804-
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size), "");
803+
Allocator->deallocate(P, Origin, Size, true);
804+
EXPECT_DEATH(Allocator->deallocate(P, Origin, Size, true), "");
805805
EXPECT_DEATH(Allocator->reallocate(P, Size * 2U), "");
806806
EXPECT_DEATH(Allocator->getUsableSize(P), "");
807807
}
@@ -908,13 +908,13 @@ SCUDO_TYPED_TEST(ScudoCombinedTest, DisableMemInit) {
908908
memset(Ptrs[I], 0xaa, Size);
909909
}
910910
for (unsigned I = 0; I != Ptrs.size(); ++I)
911-
Allocator->deallocate(Ptrs[I], Origin, Size);
911+
Allocator->deallocate(Ptrs[I], Origin, Size, true);
912912
for (unsigned I = 0; I != Ptrs.size(); ++I) {
913913
Ptrs[I] = Allocator->allocate(Size - 8, Origin);
914914
memset(Ptrs[I], 0xbb, Size - 8);
915915
}
916916
for (unsigned I = 0; I != Ptrs.size(); ++I)
917-
Allocator->deallocate(Ptrs[I], Origin, Size - 8);
917+
Allocator->deallocate(Ptrs[I], Origin, Size - 8, true);
918918
for (unsigned I = 0; I != Ptrs.size(); ++I) {
919919
Ptrs[I] = Allocator->allocate(Size, Origin, 1U << MinAlignLog, true);
920920
for (scudo::uptr J = 0; J < Size; ++J)

compiler-rt/lib/scudo/standalone/tests/scudo_unit_test.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,14 @@ using Test = ::testing::Test;
5858
#define SCUDO_NO_TEST_MAIN
5959
#endif
6060

61+
// Match Android's default configuration, which disables Scudo's mismatch
62+
// allocation check, as it is being triggered by some third party code.
63+
#if SCUDO_ANDROID
64+
#define DEALLOC_TYPE_MISMATCH "false"
65+
#define SKIP_ON_ANDROID(T) DISABLED_##T
66+
#else
67+
#define DEALLOC_TYPE_MISMATCH "true"
68+
#define SKIP_ON_ANDROID(T) T
69+
#endif
70+
6171
extern bool UseQuarantine;

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@
99
#include "memtag.h"
1010
#include "tests/scudo_unit_test.h"
1111

12-
// Match Android's default configuration, which disables Scudo's mismatch
13-
// allocation check, as it is being triggered by some third party code.
14-
#if SCUDO_ANDROID
15-
#define DEALLOC_TYPE_MISMATCH "false"
16-
#else
17-
#define DEALLOC_TYPE_MISMATCH "true"
18-
#endif
19-
2012
static void EnableMemoryTaggingIfSupported() {
2113
if (!scudo::archSupportsMemoryTagging())
2214
return;

0 commit comments

Comments
 (0)