1 //===-- combined.h ----------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef SCUDO_COMBINED_H_
10 #define SCUDO_COMBINED_H_
15 #include "flags_parser.h"
16 #include "local_cache.h"
19 #include "quarantine.h"
21 #include "secondary.h"
22 #include "stack_depot.h"
23 #include "string_utils.h"
26 #include "scudo/interface.h"
29 #include "gwp_asan/guarded_pool_allocator.h"
30 #include "gwp_asan/optional/backtrace.h"
31 #include "gwp_asan/optional/segv_handler.h"
32 #endif // GWP_ASAN_HOOKS
34 extern "C" inline void EmptyCallback() {}
36 #ifdef HAVE_ANDROID_UNSAFE_FRAME_POINTER_CHASE
37 // This function is not part of the NDK so it does not appear in any public
38 // header files. We only declare/use it when targeting the platform.
39 extern "C" size_t android_unsafe_frame_pointer_chase(scudo::uptr
*buf
,
45 template <class Config
, void (*PostInitCallback
)(void) = EmptyCallback
>
48 using PrimaryT
= typename
Config::template PrimaryT
<Config
>;
49 using SecondaryT
= typename
Config::template SecondaryT
<Config
>;
50 using CacheT
= typename
PrimaryT::CacheT
;
51 typedef Allocator
<Config
, PostInitCallback
> ThisT
;
52 typedef typename
Config::template TSDRegistryT
<ThisT
> TSDRegistryT
;
54 void callPostInitCallback() {
55 pthread_once(&PostInitNonce
, PostInitCallback
);
58 struct QuarantineCallback
{
59 explicit QuarantineCallback(ThisT
&Instance
, CacheT
&LocalCache
)
60 : Allocator(Instance
), Cache(LocalCache
) {}
62 // Chunk recycling function, returns a quarantined chunk to the backend,
63 // first making sure it hasn't been tampered with.
64 void recycle(void *Ptr
) {
65 Chunk::UnpackedHeader Header
;
66 Chunk::loadHeader(Allocator
.Cookie
, Ptr
, &Header
);
67 if (UNLIKELY(Header
.State
!= Chunk::State::Quarantined
))
68 reportInvalidChunkState(AllocatorAction::Recycling
, Ptr
);
70 Header
.State
= Chunk::State::Available
;
71 Chunk::storeHeader(Allocator
.Cookie
, Ptr
, &Header
);
73 if (allocatorSupportsMemoryTagging
<Config
>())
74 Ptr
= untagPointer(Ptr
);
75 void *BlockBegin
= Allocator::getBlockBegin(Ptr
, &Header
);
76 Cache
.deallocate(Header
.ClassId
, BlockBegin
);
79 // We take a shortcut when allocating a quarantine batch by working with the
80 // appropriate class ID instead of using Size. The compiler should optimize
81 // the class ID computation and work with the associated cache directly.
82 void *allocate(UNUSED uptr Size
) {
83 const uptr QuarantineClassId
= SizeClassMap::getClassIdBySize(
84 sizeof(QuarantineBatch
) + Chunk::getHeaderSize());
85 void *Ptr
= Cache
.allocate(QuarantineClassId
);
86 // Quarantine batch allocation failure is fatal.
88 reportOutOfMemory(SizeClassMap::getSizeByClassId(QuarantineClassId
));
90 Ptr
= reinterpret_cast<void *>(reinterpret_cast<uptr
>(Ptr
) +
91 Chunk::getHeaderSize());
92 Chunk::UnpackedHeader Header
= {};
93 Header
.ClassId
= QuarantineClassId
& Chunk::ClassIdMask
;
94 Header
.SizeOrUnusedBytes
= sizeof(QuarantineBatch
);
95 Header
.State
= Chunk::State::Allocated
;
96 Chunk::storeHeader(Allocator
.Cookie
, Ptr
, &Header
);
98 // Reset tag to 0 as this chunk may have been previously used for a tagged
100 if (UNLIKELY(useMemoryTagging
<Config
>(Allocator
.Primary
.Options
.load())))
101 storeTags(reinterpret_cast<uptr
>(Ptr
),
102 reinterpret_cast<uptr
>(Ptr
) + sizeof(QuarantineBatch
));
107 void deallocate(void *Ptr
) {
108 const uptr QuarantineClassId
= SizeClassMap::getClassIdBySize(
109 sizeof(QuarantineBatch
) + Chunk::getHeaderSize());
110 Chunk::UnpackedHeader Header
;
111 Chunk::loadHeader(Allocator
.Cookie
, Ptr
, &Header
);
113 if (UNLIKELY(Header
.State
!= Chunk::State::Allocated
))
114 reportInvalidChunkState(AllocatorAction::Deallocating
, Ptr
);
115 DCHECK_EQ(Header
.ClassId
, QuarantineClassId
);
116 DCHECK_EQ(Header
.Offset
, 0);
117 DCHECK_EQ(Header
.SizeOrUnusedBytes
, sizeof(QuarantineBatch
));
119 Header
.State
= Chunk::State::Available
;
120 Chunk::storeHeader(Allocator
.Cookie
, Ptr
, &Header
);
121 Cache
.deallocate(QuarantineClassId
,
122 reinterpret_cast<void *>(reinterpret_cast<uptr
>(Ptr
) -
123 Chunk::getHeaderSize()));
131 typedef GlobalQuarantine
<QuarantineCallback
, void> QuarantineT
;
132 typedef typename
QuarantineT::CacheT QuarantineCacheT
;
135 performSanityChecks();
137 // Check if hardware CRC32 is supported in the binary and by the platform,
138 // if so, opt for the CRC32 hardware version of the checksum.
139 if (&computeHardwareCRC32
&& hasHardwareCRC32())
140 HashAlgorithm
= Checksum::HardwareCRC32
;
142 if (UNLIKELY(!getRandom(&Cookie
, sizeof(Cookie
))))
143 Cookie
= static_cast<u32
>(getMonotonicTime() ^
144 (reinterpret_cast<uptr
>(this) >> 4));
147 reportUnrecognizedFlags();
149 // Store some flags locally.
150 if (getFlags()->may_return_null
)
151 Primary
.Options
.set(OptionBit::MayReturnNull
);
152 if (getFlags()->zero_contents
)
153 Primary
.Options
.setFillContentsMode(ZeroFill
);
154 else if (getFlags()->pattern_fill_contents
)
155 Primary
.Options
.setFillContentsMode(PatternOrZeroFill
);
156 if (getFlags()->dealloc_type_mismatch
)
157 Primary
.Options
.set(OptionBit::DeallocTypeMismatch
);
158 if (getFlags()->delete_size_mismatch
)
159 Primary
.Options
.set(OptionBit::DeleteSizeMismatch
);
160 if (allocatorSupportsMemoryTagging
<Config
>() &&
161 systemSupportsMemoryTagging())
162 Primary
.Options
.set(OptionBit::UseMemoryTagging
);
164 QuarantineMaxChunkSize
=
165 static_cast<u32
>(getFlags()->quarantine_max_chunk_size
);
168 const s32 ReleaseToOsIntervalMs
= getFlags()->release_to_os_interval_ms
;
169 Primary
.init(ReleaseToOsIntervalMs
);
170 Secondary
.init(&Stats
, ReleaseToOsIntervalMs
);
172 static_cast<uptr
>(getFlags()->quarantine_size_kb
<< 10),
173 static_cast<uptr
>(getFlags()->thread_local_quarantine_size_kb
<< 10));
175 mapAndInitializeRingBuffer();
178 // Initialize the embedded GWP-ASan instance. Requires the main allocator to
179 // be functional, best called from PostInitCallback.
181 #ifdef GWP_ASAN_HOOKS
182 gwp_asan::options::Options Opt
;
183 Opt
.Enabled
= getFlags()->GWP_ASAN_Enabled
;
184 Opt
.MaxSimultaneousAllocations
=
185 getFlags()->GWP_ASAN_MaxSimultaneousAllocations
;
186 Opt
.SampleRate
= getFlags()->GWP_ASAN_SampleRate
;
187 Opt
.InstallSignalHandlers
= getFlags()->GWP_ASAN_InstallSignalHandlers
;
188 Opt
.Recoverable
= getFlags()->GWP_ASAN_Recoverable
;
189 // Embedded GWP-ASan is locked through the Scudo atfork handler (via
190 // Allocator::disable calling GWPASan.disable). Disable GWP-ASan's atfork
192 Opt
.InstallForkHandlers
= false;
193 Opt
.Backtrace
= gwp_asan::backtrace::getBacktraceFunction();
194 GuardedAlloc
.init(Opt
);
196 if (Opt
.InstallSignalHandlers
)
197 gwp_asan::segv_handler::installSignalHandlers(
198 &GuardedAlloc
, Printf
,
199 gwp_asan::backtrace::getPrintBacktraceFunction(),
200 gwp_asan::backtrace::getSegvBacktraceFunction(),
203 GuardedAllocSlotSize
=
204 GuardedAlloc
.getAllocatorState()->maximumAllocationSize();
205 Stats
.add(StatFree
, static_cast<uptr
>(Opt
.MaxSimultaneousAllocations
) *
206 GuardedAllocSlotSize
);
207 #endif // GWP_ASAN_HOOKS
210 #ifdef GWP_ASAN_HOOKS
211 const gwp_asan::AllocationMetadata
*getGwpAsanAllocationMetadata() {
212 return GuardedAlloc
.getMetadataRegion();
215 const gwp_asan::AllocatorState
*getGwpAsanAllocatorState() {
216 return GuardedAlloc
.getAllocatorState();
218 #endif // GWP_ASAN_HOOKS
220 ALWAYS_INLINE
void initThreadMaybe(bool MinimalInit
= false) {
221 TSDRegistry
.initThreadMaybe(this, MinimalInit
);
224 void unmapTestOnly() {
226 TSDRegistry
.unmapTestOnly(this);
227 Primary
.unmapTestOnly();
228 Secondary
.unmapTestOnly();
229 #ifdef GWP_ASAN_HOOKS
230 if (getFlags()->GWP_ASAN_InstallSignalHandlers
)
231 gwp_asan::segv_handler::uninstallSignalHandlers();
232 GuardedAlloc
.uninitTestOnly();
233 #endif // GWP_ASAN_HOOKS
236 TSDRegistryT
*getTSDRegistry() { return &TSDRegistry
; }
237 QuarantineT
*getQuarantine() { return &Quarantine
; }
239 // The Cache must be provided zero-initialized.
240 void initCache(CacheT
*Cache
) { Cache
->init(&Stats
, &Primary
); }
242 // Release the resources used by a TSD, which involves:
243 // - draining the local quarantine cache to the global quarantine;
244 // - releasing the cached pointers back to the Primary;
245 // - unlinking the local stats from the global ones (destroying the cache does
246 // the last two items).
247 void commitBack(TSD
<ThisT
> *TSD
) {
248 TSD
->assertLocked(/*BypassCheck=*/true);
249 Quarantine
.drain(&TSD
->getQuarantineCache(),
250 QuarantineCallback(*this, TSD
->getCache()));
251 TSD
->getCache().destroy(&Stats
);
254 void drainCache(TSD
<ThisT
> *TSD
) {
255 TSD
->assertLocked(/*BypassCheck=*/true);
256 Quarantine
.drainAndRecycle(&TSD
->getQuarantineCache(),
257 QuarantineCallback(*this, TSD
->getCache()));
258 TSD
->getCache().drain();
260 void drainCaches() { TSDRegistry
.drainCaches(this); }
262 ALWAYS_INLINE
void *getHeaderTaggedPointer(void *Ptr
) {
263 if (!allocatorSupportsMemoryTagging
<Config
>())
265 auto UntaggedPtr
= untagPointer(Ptr
);
266 if (UntaggedPtr
!= Ptr
)
268 // Secondary, or pointer allocated while memory tagging is unsupported or
269 // disabled. The tag mismatch is okay in the latter case because tags will
271 return addHeaderTag(Ptr
);
274 ALWAYS_INLINE uptr
addHeaderTag(uptr Ptr
) {
275 if (!allocatorSupportsMemoryTagging
<Config
>())
277 return addFixedTag(Ptr
, 2);
280 ALWAYS_INLINE
void *addHeaderTag(void *Ptr
) {
281 return reinterpret_cast<void *>(addHeaderTag(reinterpret_cast<uptr
>(Ptr
)));
284 NOINLINE u32
collectStackTrace() {
285 #ifdef HAVE_ANDROID_UNSAFE_FRAME_POINTER_CHASE
286 // Discard collectStackTrace() frame and allocator function frame.
287 constexpr uptr DiscardFrames
= 2;
288 uptr Stack
[MaxTraceSize
+ DiscardFrames
];
290 android_unsafe_frame_pointer_chase(Stack
, MaxTraceSize
+ DiscardFrames
);
291 Size
= Min
<uptr
>(Size
, MaxTraceSize
+ DiscardFrames
);
292 return Depot
.insert(Stack
+ Min
<uptr
>(DiscardFrames
, Size
), Stack
+ Size
);
298 uptr
computeOddEvenMaskForPointerMaybe(const Options
&Options
, uptr Ptr
,
300 if (!Options
.get(OptionBit::UseOddEvenTags
))
303 // If a chunk's tag is odd, we want the tags of the surrounding blocks to be
304 // even, and vice versa. Blocks are laid out Size bytes apart, and adding
305 // Size to Ptr will flip the least significant set bit of Size in Ptr, so
306 // that bit will have the pattern 010101... for consecutive blocks, which we
307 // can use to determine which tag mask to use.
308 return 0x5555U
<< ((Ptr
>> SizeClassMap::getSizeLSBByClassId(ClassId
)) & 1);
311 NOINLINE
void *allocate(uptr Size
, Chunk::Origin Origin
,
312 uptr Alignment
= MinAlignment
,
313 bool ZeroContents
= false) NO_THREAD_SAFETY_ANALYSIS
{
316 const Options Options
= Primary
.Options
.load();
317 if (UNLIKELY(Alignment
> MaxAlignment
)) {
318 if (Options
.get(OptionBit::MayReturnNull
))
320 reportAlignmentTooBig(Alignment
, MaxAlignment
);
322 if (Alignment
< MinAlignment
)
323 Alignment
= MinAlignment
;
325 #ifdef GWP_ASAN_HOOKS
326 if (UNLIKELY(GuardedAlloc
.shouldSample())) {
327 if (void *Ptr
= GuardedAlloc
.allocate(Size
, Alignment
)) {
329 Stats
.add(StatAllocated
, GuardedAllocSlotSize
);
330 Stats
.sub(StatFree
, GuardedAllocSlotSize
);
335 #endif // GWP_ASAN_HOOKS
337 const FillContentsMode FillContents
= ZeroContents
? ZeroFill
338 : TSDRegistry
.getDisableMemInit()
340 : Options
.getFillContentsMode();
342 // If the requested size happens to be 0 (more common than you might think),
343 // allocate MinAlignment bytes on top of the header. Then add the extra
344 // bytes required to fulfill the alignment requirements: we allocate enough
345 // to be sure that there will be an address in the block that will satisfy
347 const uptr NeededSize
=
348 roundUp(Size
, MinAlignment
) +
349 ((Alignment
> MinAlignment
) ? Alignment
: Chunk::getHeaderSize());
351 // Takes care of extravagantly large sizes as well as integer overflows.
352 static_assert(MaxAllowedMallocSize
< UINTPTR_MAX
- MaxAlignment
, "");
353 if (UNLIKELY(Size
>= MaxAllowedMallocSize
)) {
354 if (Options
.get(OptionBit::MayReturnNull
))
356 reportAllocationSizeTooBig(Size
, NeededSize
, MaxAllowedMallocSize
);
358 DCHECK_LE(Size
, NeededSize
);
360 void *Block
= nullptr;
362 uptr SecondaryBlockEnd
= 0;
363 if (LIKELY(PrimaryT::canAllocate(NeededSize
))) {
364 ClassId
= SizeClassMap::getClassIdBySize(NeededSize
);
365 DCHECK_NE(ClassId
, 0U);
367 auto *TSD
= TSDRegistry
.getTSDAndLock(&UnlockRequired
);
368 TSD
->assertLocked(/*BypassCheck=*/!UnlockRequired
);
369 Block
= TSD
->getCache().allocate(ClassId
);
370 // If the allocation failed, retry in each successively larger class until
371 // it fits. If it fails to fit in the largest class, fallback to the
373 if (UNLIKELY(!Block
)) {
374 while (ClassId
< SizeClassMap::LargestClassId
&& !Block
)
375 Block
= TSD
->getCache().allocate(++ClassId
);
382 if (UNLIKELY(ClassId
== 0)) {
383 Block
= Secondary
.allocate(Options
, Size
, Alignment
, &SecondaryBlockEnd
,
387 if (UNLIKELY(!Block
)) {
388 if (Options
.get(OptionBit::MayReturnNull
))
391 reportOutOfMemory(NeededSize
);
394 const uptr BlockUptr
= reinterpret_cast<uptr
>(Block
);
395 const uptr UnalignedUserPtr
= BlockUptr
+ Chunk::getHeaderSize();
396 const uptr UserPtr
= roundUp(UnalignedUserPtr
, Alignment
);
398 void *Ptr
= reinterpret_cast<void *>(UserPtr
);
399 void *TaggedPtr
= Ptr
;
400 if (LIKELY(ClassId
)) {
401 // We only need to zero or tag the contents for Primary backed
402 // allocations. We only set tags for primary allocations in order to avoid
403 // faulting potentially large numbers of pages for large secondary
404 // allocations. We assume that guard pages are enough to protect these
407 // FIXME: When the kernel provides a way to set the background tag of a
408 // mapping, we should be able to tag secondary allocations as well.
410 // When memory tagging is enabled, zeroing the contents is done as part of
412 if (UNLIKELY(useMemoryTagging
<Config
>(Options
))) {
414 Chunk::UnpackedHeader Header
;
415 const uptr BlockSize
= PrimaryT::getSizeByClassId(ClassId
);
416 const uptr BlockEnd
= BlockUptr
+ BlockSize
;
417 // If possible, try to reuse the UAF tag that was set by deallocate().
418 // For simplicity, only reuse tags if we have the same start address as
419 // the previous allocation. This handles the majority of cases since
420 // most allocations will not be more aligned than the minimum alignment.
422 // We need to handle situations involving reclaimed chunks, and retag
423 // the reclaimed portions if necessary. In the case where the chunk is
424 // fully reclaimed, the chunk's header will be zero, which will trigger
425 // the code path for new mappings and invalid chunks that prepares the
426 // chunk from scratch. There are three possibilities for partial
429 // (1) Header was reclaimed, data was partially reclaimed.
430 // (2) Header was not reclaimed, all data was reclaimed (e.g. because
431 // data started on a page boundary).
432 // (3) Header was not reclaimed, data was partially reclaimed.
434 // Case (1) will be handled in the same way as for full reclaiming,
435 // since the header will be zero.
437 // We can detect case (2) by loading the tag from the start
438 // of the chunk. If it is zero, it means that either all data was
439 // reclaimed (since we never use zero as the chunk tag), or that the
440 // previous allocation was of size zero. Either way, we need to prepare
441 // a new chunk from scratch.
443 // We can detect case (3) by moving to the next page (if covered by the
444 // chunk) and loading the tag of its first granule. If it is zero, it
445 // means that all following pages may need to be retagged. On the other
446 // hand, if it is nonzero, we can assume that all following pages are
447 // still tagged, according to the logic that if any of the pages
448 // following the next page were reclaimed, the next page would have been
449 // reclaimed as well.
451 if (getChunkFromBlock(BlockUptr
, &PrevUserPtr
, &Header
) &&
452 PrevUserPtr
== UserPtr
&&
453 (TaggedUserPtr
= loadTag(UserPtr
)) != UserPtr
) {
454 uptr PrevEnd
= TaggedUserPtr
+ Header
.SizeOrUnusedBytes
;
455 const uptr NextPage
= roundUp(TaggedUserPtr
, getPageSizeCached());
456 if (NextPage
< PrevEnd
&& loadTag(NextPage
) != NextPage
)
458 TaggedPtr
= reinterpret_cast<void *>(TaggedUserPtr
);
459 resizeTaggedChunk(PrevEnd
, TaggedUserPtr
+ Size
, Size
, BlockEnd
);
460 if (UNLIKELY(FillContents
!= NoFill
&& !Header
.OriginOrWasZeroed
)) {
461 // If an allocation needs to be zeroed (i.e. calloc) we can normally
462 // avoid zeroing the memory now since we can rely on memory having
463 // been zeroed on free, as this is normally done while setting the
464 // UAF tag. But if tagging was disabled per-thread when the memory
465 // was freed, it would not have been retagged and thus zeroed, and
466 // therefore it needs to be zeroed now.
468 Min(Size
, roundUp(PrevEnd
- TaggedUserPtr
,
469 archMemoryTagGranuleSize())));
471 // Clear any stack metadata that may have previously been stored in
473 memset(TaggedPtr
, 0, archMemoryTagGranuleSize());
476 const uptr OddEvenMask
=
477 computeOddEvenMaskForPointerMaybe(Options
, BlockUptr
, ClassId
);
478 TaggedPtr
= prepareTaggedChunk(Ptr
, Size
, OddEvenMask
, BlockEnd
);
480 storePrimaryAllocationStackMaybe(Options
, Ptr
);
482 Block
= addHeaderTag(Block
);
483 Ptr
= addHeaderTag(Ptr
);
484 if (UNLIKELY(FillContents
!= NoFill
)) {
485 // This condition is not necessarily unlikely, but since memset is
486 // costly, we might as well mark it as such.
487 memset(Block
, FillContents
== ZeroFill
? 0 : PatternFillByte
,
488 PrimaryT::getSizeByClassId(ClassId
));
492 Block
= addHeaderTag(Block
);
493 Ptr
= addHeaderTag(Ptr
);
494 if (UNLIKELY(useMemoryTagging
<Config
>(Options
))) {
495 storeTags(reinterpret_cast<uptr
>(Block
), reinterpret_cast<uptr
>(Ptr
));
496 storeSecondaryAllocationStackMaybe(Options
, Ptr
, Size
);
500 Chunk::UnpackedHeader Header
= {};
501 if (UNLIKELY(UnalignedUserPtr
!= UserPtr
)) {
502 const uptr Offset
= UserPtr
- UnalignedUserPtr
;
503 DCHECK_GE(Offset
, 2 * sizeof(u32
));
504 // The BlockMarker has no security purpose, but is specifically meant for
505 // the chunk iteration function that can be used in debugging situations.
506 // It is the only situation where we have to locate the start of a chunk
507 // based on its block address.
508 reinterpret_cast<u32
*>(Block
)[0] = BlockMarker
;
509 reinterpret_cast<u32
*>(Block
)[1] = static_cast<u32
>(Offset
);
510 Header
.Offset
= (Offset
>> MinAlignmentLog
) & Chunk::OffsetMask
;
512 Header
.ClassId
= ClassId
& Chunk::ClassIdMask
;
513 Header
.State
= Chunk::State::Allocated
;
514 Header
.OriginOrWasZeroed
= Origin
& Chunk::OriginMask
;
515 Header
.SizeOrUnusedBytes
=
516 (ClassId
? Size
: SecondaryBlockEnd
- (UserPtr
+ Size
)) &
517 Chunk::SizeOrUnusedBytesMask
;
518 Chunk::storeHeader(Cookie
, Ptr
, &Header
);
523 NOINLINE
void deallocate(void *Ptr
, Chunk::Origin Origin
, uptr DeleteSize
= 0,
524 UNUSED uptr Alignment
= MinAlignment
) {
525 // For a deallocation, we only ensure minimal initialization, meaning thread
526 // local data will be left uninitialized for now (when using ELF TLS). The
527 // fallback cache will be used instead. This is a workaround for a situation
528 // where the only heap operation performed in a thread would be a free past
529 // the TLS destructors, ending up in initialized thread specific data never
530 // being destroyed properly. Any other heap operation will do a full init.
531 initThreadMaybe(/*MinimalInit=*/true);
536 #ifdef GWP_ASAN_HOOKS
537 if (UNLIKELY(GuardedAlloc
.pointerIsMine(Ptr
))) {
538 GuardedAlloc
.deallocate(Ptr
);
540 Stats
.add(StatFree
, GuardedAllocSlotSize
);
541 Stats
.sub(StatAllocated
, GuardedAllocSlotSize
);
545 #endif // GWP_ASAN_HOOKS
547 if (UNLIKELY(!isAligned(reinterpret_cast<uptr
>(Ptr
), MinAlignment
)))
548 reportMisalignedPointer(AllocatorAction::Deallocating
, Ptr
);
550 void *TaggedPtr
= Ptr
;
551 Ptr
= getHeaderTaggedPointer(Ptr
);
553 Chunk::UnpackedHeader Header
;
554 Chunk::loadHeader(Cookie
, Ptr
, &Header
);
556 if (UNLIKELY(Header
.State
!= Chunk::State::Allocated
))
557 reportInvalidChunkState(AllocatorAction::Deallocating
, Ptr
);
559 const Options Options
= Primary
.Options
.load();
560 if (Options
.get(OptionBit::DeallocTypeMismatch
)) {
561 if (UNLIKELY(Header
.OriginOrWasZeroed
!= Origin
)) {
562 // With the exception of memalign'd chunks, that can be still be free'd.
563 if (Header
.OriginOrWasZeroed
!= Chunk::Origin::Memalign
||
564 Origin
!= Chunk::Origin::Malloc
)
565 reportDeallocTypeMismatch(AllocatorAction::Deallocating
, Ptr
,
566 Header
.OriginOrWasZeroed
, Origin
);
570 const uptr Size
= getSize(Ptr
, &Header
);
571 if (DeleteSize
&& Options
.get(OptionBit::DeleteSizeMismatch
)) {
572 if (UNLIKELY(DeleteSize
!= Size
))
573 reportDeleteSizeMismatch(Ptr
, DeleteSize
, Size
);
576 quarantineOrDeallocateChunk(Options
, TaggedPtr
, &Header
, Size
);
579 void *reallocate(void *OldPtr
, uptr NewSize
, uptr Alignment
= MinAlignment
) {
582 const Options Options
= Primary
.Options
.load();
583 if (UNLIKELY(NewSize
>= MaxAllowedMallocSize
)) {
584 if (Options
.get(OptionBit::MayReturnNull
))
586 reportAllocationSizeTooBig(NewSize
, 0, MaxAllowedMallocSize
);
589 // The following cases are handled by the C wrappers.
590 DCHECK_NE(OldPtr
, nullptr);
591 DCHECK_NE(NewSize
, 0);
593 #ifdef GWP_ASAN_HOOKS
594 if (UNLIKELY(GuardedAlloc
.pointerIsMine(OldPtr
))) {
595 uptr OldSize
= GuardedAlloc
.getSize(OldPtr
);
596 void *NewPtr
= allocate(NewSize
, Chunk::Origin::Malloc
, Alignment
);
598 memcpy(NewPtr
, OldPtr
, (NewSize
< OldSize
) ? NewSize
: OldSize
);
599 GuardedAlloc
.deallocate(OldPtr
);
601 Stats
.add(StatFree
, GuardedAllocSlotSize
);
602 Stats
.sub(StatAllocated
, GuardedAllocSlotSize
);
606 #endif // GWP_ASAN_HOOKS
608 void *OldTaggedPtr
= OldPtr
;
609 OldPtr
= getHeaderTaggedPointer(OldPtr
);
611 if (UNLIKELY(!isAligned(reinterpret_cast<uptr
>(OldPtr
), MinAlignment
)))
612 reportMisalignedPointer(AllocatorAction::Reallocating
, OldPtr
);
614 Chunk::UnpackedHeader Header
;
615 Chunk::loadHeader(Cookie
, OldPtr
, &Header
);
617 if (UNLIKELY(Header
.State
!= Chunk::State::Allocated
))
618 reportInvalidChunkState(AllocatorAction::Reallocating
, OldPtr
);
620 // Pointer has to be allocated with a malloc-type function. Some
621 // applications think that it is OK to realloc a memalign'ed pointer, which
622 // will trigger this check. It really isn't.
623 if (Options
.get(OptionBit::DeallocTypeMismatch
)) {
624 if (UNLIKELY(Header
.OriginOrWasZeroed
!= Chunk::Origin::Malloc
))
625 reportDeallocTypeMismatch(AllocatorAction::Reallocating
, OldPtr
,
626 Header
.OriginOrWasZeroed
,
627 Chunk::Origin::Malloc
);
630 void *BlockBegin
= getBlockBegin(OldTaggedPtr
, &Header
);
633 const uptr ClassId
= Header
.ClassId
;
634 if (LIKELY(ClassId
)) {
635 BlockEnd
= reinterpret_cast<uptr
>(BlockBegin
) +
636 SizeClassMap::getSizeByClassId(ClassId
);
637 OldSize
= Header
.SizeOrUnusedBytes
;
639 BlockEnd
= SecondaryT::getBlockEnd(BlockBegin
);
640 OldSize
= BlockEnd
- (reinterpret_cast<uptr
>(OldTaggedPtr
) +
641 Header
.SizeOrUnusedBytes
);
643 // If the new chunk still fits in the previously allocated block (with a
644 // reasonable delta), we just keep the old block, and update the chunk
645 // header to reflect the size change.
646 if (reinterpret_cast<uptr
>(OldTaggedPtr
) + NewSize
<= BlockEnd
) {
647 if (NewSize
> OldSize
|| (OldSize
- NewSize
) < getPageSizeCached()) {
648 Header
.SizeOrUnusedBytes
=
651 (reinterpret_cast<uptr
>(OldTaggedPtr
) + NewSize
)) &
652 Chunk::SizeOrUnusedBytesMask
;
653 Chunk::storeHeader(Cookie
, OldPtr
, &Header
);
654 if (UNLIKELY(useMemoryTagging
<Config
>(Options
))) {
656 resizeTaggedChunk(reinterpret_cast<uptr
>(OldTaggedPtr
) + OldSize
,
657 reinterpret_cast<uptr
>(OldTaggedPtr
) + NewSize
,
658 NewSize
, untagPointer(BlockEnd
));
659 storePrimaryAllocationStackMaybe(Options
, OldPtr
);
661 storeSecondaryAllocationStackMaybe(Options
, OldPtr
, NewSize
);
668 // Otherwise we allocate a new one, and deallocate the old one. Some
669 // allocators will allocate an even larger chunk (by a fixed factor) to
670 // allow for potential further in-place realloc. The gains of such a trick
671 // are currently unclear.
672 void *NewPtr
= allocate(NewSize
, Chunk::Origin::Malloc
, Alignment
);
673 if (LIKELY(NewPtr
)) {
674 memcpy(NewPtr
, OldTaggedPtr
, Min(NewSize
, OldSize
));
675 quarantineOrDeallocateChunk(Options
, OldTaggedPtr
, &Header
, OldSize
);
680 // TODO(kostyak): disable() is currently best-effort. There are some small
681 // windows of time when an allocation could still succeed after
682 // this function finishes. We will revisit that later.
683 void disable() NO_THREAD_SAFETY_ANALYSIS
{
685 #ifdef GWP_ASAN_HOOKS
686 GuardedAlloc
.disable();
688 TSDRegistry
.disable();
690 Quarantine
.disable();
695 void enable() NO_THREAD_SAFETY_ANALYSIS
{
701 TSDRegistry
.enable();
702 #ifdef GWP_ASAN_HOOKS
703 GuardedAlloc
.enable();
707 // The function returns the amount of bytes required to store the statistics,
708 // which might be larger than the amount of bytes provided. Note that the
709 // statistics buffer is not necessarily constant between calls to this
710 // function. This can be called with a null buffer or zero size for buffer
712 uptr
getStats(char *Buffer
, uptr Size
) {
714 const uptr Length
= getStats(&Str
) + 1;
717 if (Buffer
&& Size
) {
718 memcpy(Buffer
, Str
.data(), Size
);
719 Buffer
[Size
- 1] = '\0';
730 void printFragmentationInfo() {
732 Primary
.getFragmentationInfo(&Str
);
733 // Secondary allocator dumps the fragmentation data in getStats().
737 void releaseToOS(ReleaseToOS ReleaseType
) {
739 if (ReleaseType
== ReleaseToOS::ForceAll
)
741 Primary
.releaseToOS(ReleaseType
);
742 Secondary
.releaseToOS();
745 // Iterate over all chunks and call a callback for all busy chunks located
746 // within the provided memory range. Said callback must not use this allocator
747 // or a deadlock can ensue. This fits Android's malloc_iterate() needs.
748 void iterateOverChunks(uptr Base
, uptr Size
, iterate_callback Callback
,
751 if (archSupportsMemoryTagging())
752 Base
= untagPointer(Base
);
753 const uptr From
= Base
;
754 const uptr To
= Base
+ Size
;
755 bool MayHaveTaggedPrimary
= allocatorSupportsMemoryTagging
<Config
>() &&
756 systemSupportsMemoryTagging();
757 auto Lambda
= [this, From
, To
, MayHaveTaggedPrimary
, Callback
,
759 if (Block
< From
|| Block
>= To
)
762 Chunk::UnpackedHeader Header
;
763 if (MayHaveTaggedPrimary
) {
764 // A chunk header can either have a zero tag (tagged primary) or the
765 // header tag (secondary, or untagged primary). We don't know which so
767 ScopedDisableMemoryTagChecks x
;
768 if (!getChunkFromBlock(Block
, &Chunk
, &Header
) &&
769 !getChunkFromBlock(addHeaderTag(Block
), &Chunk
, &Header
))
772 if (!getChunkFromBlock(addHeaderTag(Block
), &Chunk
, &Header
))
775 if (Header
.State
== Chunk::State::Allocated
) {
776 uptr TaggedChunk
= Chunk
;
777 if (allocatorSupportsMemoryTagging
<Config
>())
778 TaggedChunk
= untagPointer(TaggedChunk
);
779 if (useMemoryTagging
<Config
>(Primary
.Options
.load()))
780 TaggedChunk
= loadTag(Chunk
);
781 Callback(TaggedChunk
, getSize(reinterpret_cast<void *>(Chunk
), &Header
),
785 Primary
.iterateOverBlocks(Lambda
);
786 Secondary
.iterateOverBlocks(Lambda
);
787 #ifdef GWP_ASAN_HOOKS
788 GuardedAlloc
.iterate(reinterpret_cast<void *>(Base
), Size
, Callback
, Arg
);
792 bool canReturnNull() {
794 return Primary
.Options
.load().get(OptionBit::MayReturnNull
);
797 bool setOption(Option O
, sptr Value
) {
799 if (O
== Option::MemtagTuning
) {
800 // Enabling odd/even tags involves a tradeoff between use-after-free
801 // detection and buffer overflow detection. Odd/even tags make it more
802 // likely for buffer overflows to be detected by increasing the size of
803 // the guaranteed "red zone" around the allocation, but on the other hand
804 // use-after-free is less likely to be detected because the tag space for
805 // any particular chunk is cut in half. Therefore we use this tuning
806 // setting to control whether odd/even tags are enabled.
807 if (Value
== M_MEMTAG_TUNING_BUFFER_OVERFLOW
)
808 Primary
.Options
.set(OptionBit::UseOddEvenTags
);
809 else if (Value
== M_MEMTAG_TUNING_UAF
)
810 Primary
.Options
.clear(OptionBit::UseOddEvenTags
);
813 // We leave it to the various sub-components to decide whether or not they
814 // want to handle the option, but we do not want to short-circuit
815 // execution if one of the setOption was to return false.
816 const bool PrimaryResult
= Primary
.setOption(O
, Value
);
817 const bool SecondaryResult
= Secondary
.setOption(O
, Value
);
818 const bool RegistryResult
= TSDRegistry
.setOption(O
, Value
);
819 return PrimaryResult
&& SecondaryResult
&& RegistryResult
;
824 // Return the usable size for a given chunk. Technically we lie, as we just
825 // report the actual size of a chunk. This is done to counteract code actively
826 // writing past the end of a chunk (like sqlite3) when the usable size allows
827 // for it, which then forces realloc to copy the usable size of a chunk as
828 // opposed to its actual size.
829 uptr
getUsableSize(const void *Ptr
) {
834 #ifdef GWP_ASAN_HOOKS
835 if (UNLIKELY(GuardedAlloc
.pointerIsMine(Ptr
)))
836 return GuardedAlloc
.getSize(Ptr
);
837 #endif // GWP_ASAN_HOOKS
839 Ptr
= getHeaderTaggedPointer(const_cast<void *>(Ptr
));
840 Chunk::UnpackedHeader Header
;
841 Chunk::loadHeader(Cookie
, Ptr
, &Header
);
842 // Getting the usable size of a chunk only makes sense if it's allocated.
843 if (UNLIKELY(Header
.State
!= Chunk::State::Allocated
))
844 reportInvalidChunkState(AllocatorAction::Sizing
, const_cast<void *>(Ptr
));
845 return getSize(Ptr
, &Header
);
848 void getStats(StatCounters S
) {
853 // Returns true if the pointer provided was allocated by the current
854 // allocator instance, which is compliant with tcmalloc's ownership concept.
855 // A corrupted chunk will not be reported as owned, which is WAI.
856 bool isOwned(const void *Ptr
) {
858 #ifdef GWP_ASAN_HOOKS
859 if (GuardedAlloc
.pointerIsMine(Ptr
))
861 #endif // GWP_ASAN_HOOKS
862 if (!Ptr
|| !isAligned(reinterpret_cast<uptr
>(Ptr
), MinAlignment
))
864 Ptr
= getHeaderTaggedPointer(const_cast<void *>(Ptr
));
865 Chunk::UnpackedHeader Header
;
866 return Chunk::isValid(Cookie
, Ptr
, &Header
) &&
867 Header
.State
== Chunk::State::Allocated
;
870 bool useMemoryTaggingTestOnly() const {
871 return useMemoryTagging
<Config
>(Primary
.Options
.load());
873 void disableMemoryTagging() {
874 // If we haven't been initialized yet, we need to initialize now in order to
875 // prevent a future call to initThreadMaybe() from enabling memory tagging
876 // based on feature detection. But don't call initThreadMaybe() because it
877 // may end up calling the allocator (via pthread_atfork, via the post-init
878 // callback), which may cause mappings to be created with memory tagging
880 TSDRegistry
.initOnceMaybe(this);
881 if (allocatorSupportsMemoryTagging
<Config
>()) {
882 Secondary
.disableMemoryTagging();
883 Primary
.Options
.clear(OptionBit::UseMemoryTagging
);
887 void setTrackAllocationStacks(bool Track
) {
889 if (getFlags()->allocation_ring_buffer_size
== 0) {
890 DCHECK(!Primary
.Options
.load().get(OptionBit::TrackAllocationStacks
));
894 Primary
.Options
.set(OptionBit::TrackAllocationStacks
);
896 Primary
.Options
.clear(OptionBit::TrackAllocationStacks
);
899 void setFillContents(FillContentsMode FillContents
) {
901 Primary
.Options
.setFillContentsMode(FillContents
);
904 void setAddLargeAllocationSlack(bool AddSlack
) {
907 Primary
.Options
.set(OptionBit::AddLargeAllocationSlack
);
909 Primary
.Options
.clear(OptionBit::AddLargeAllocationSlack
);
912 const char *getStackDepotAddress() const {
913 return reinterpret_cast<const char *>(&Depot
);
916 const char *getRegionInfoArrayAddress() const {
917 return Primary
.getRegionInfoArrayAddress();
920 static uptr
getRegionInfoArraySize() {
921 return PrimaryT::getRegionInfoArraySize();
924 const char *getRingBufferAddress() {
926 return RawRingBuffer
;
929 uptr
getRingBufferSize() {
931 auto *RingBuffer
= getRingBuffer();
932 return RingBuffer
? ringBufferSizeInBytes(RingBuffer
->Size
) : 0;
935 static bool setRingBufferSizeForBuffer(char *Buffer
, size_t Size
) {
936 // Need at least one entry.
937 if (Size
< sizeof(AllocationRingBuffer
) +
938 sizeof(typename
AllocationRingBuffer::Entry
)) {
941 AllocationRingBuffer
*RingBuffer
=
942 reinterpret_cast<AllocationRingBuffer
*>(Buffer
);
943 RingBuffer
->Size
= (Size
- sizeof(AllocationRingBuffer
)) /
944 sizeof(typename
AllocationRingBuffer::Entry
);
948 static const uptr MaxTraceSize
= 64;
950 static void collectTraceMaybe(const StackDepot
*Depot
,
951 uintptr_t (&Trace
)[MaxTraceSize
], u32 Hash
) {
953 if (!Depot
->find(Hash
, &RingPos
, &Size
))
955 for (unsigned I
= 0; I
!= Size
&& I
!= MaxTraceSize
; ++I
)
956 Trace
[I
] = static_cast<uintptr_t>((*Depot
)[RingPos
+ I
]);
959 static void getErrorInfo(struct scudo_error_info
*ErrorInfo
,
960 uintptr_t FaultAddr
, const char *DepotPtr
,
961 const char *RegionInfoPtr
, const char *RingBufferPtr
,
962 const char *Memory
, const char *MemoryTags
,
963 uintptr_t MemoryAddr
, size_t MemorySize
) {
965 if (!allocatorSupportsMemoryTagging
<Config
>() ||
966 MemoryAddr
+ MemorySize
< MemoryAddr
)
969 auto *Depot
= reinterpret_cast<const StackDepot
*>(DepotPtr
);
970 size_t NextErrorReport
= 0;
972 // Check for OOB in the current block and the two surrounding blocks. Beyond
973 // that, UAF is more likely.
974 if (extractTag(FaultAddr
) != 0)
975 getInlineErrorInfo(ErrorInfo
, NextErrorReport
, FaultAddr
, Depot
,
976 RegionInfoPtr
, Memory
, MemoryTags
, MemoryAddr
,
979 // Check the ring buffer. For primary allocations this will only find UAF;
980 // for secondary allocations we can find either UAF or OOB.
981 getRingBufferErrorInfo(ErrorInfo
, NextErrorReport
, FaultAddr
, Depot
,
984 // Check for OOB in the 28 blocks surrounding the 3 we checked earlier.
985 // Beyond that we are likely to hit false positives.
986 if (extractTag(FaultAddr
) != 0)
987 getInlineErrorInfo(ErrorInfo
, NextErrorReport
, FaultAddr
, Depot
,
988 RegionInfoPtr
, Memory
, MemoryTags
, MemoryAddr
,
993 typedef typename
PrimaryT::SizeClassMap SizeClassMap
;
995 static const uptr MinAlignmentLog
= SCUDO_MIN_ALIGNMENT_LOG
;
996 static const uptr MaxAlignmentLog
= 24U; // 16 MB seems reasonable.
997 static const uptr MinAlignment
= 1UL << MinAlignmentLog
;
998 static const uptr MaxAlignment
= 1UL << MaxAlignmentLog
;
999 static const uptr MaxAllowedMallocSize
=
1000 FIRST_32_SECOND_64(1UL << 31, 1ULL << 40);
1002 static_assert(MinAlignment
>= sizeof(Chunk::PackedHeader
),
1003 "Minimal alignment must at least cover a chunk header.");
1004 static_assert(!allocatorSupportsMemoryTagging
<Config
>() ||
1005 MinAlignment
>= archMemoryTagGranuleSize(),
1008 static const u32 BlockMarker
= 0x44554353U
;
1010 // These are indexes into an "array" of 32-bit values that store information
1011 // inline with a chunk that is relevant to diagnosing memory tag faults, where
1012 // 0 corresponds to the address of the user memory. This means that only
1013 // negative indexes may be used. The smallest index that may be used is -2,
1014 // which corresponds to 8 bytes before the user memory, because the chunk
1015 // header size is 8 bytes and in allocators that support memory tagging the
1016 // minimum alignment is at least the tag granule size (16 on aarch64).
1017 static const sptr MemTagAllocationTraceIndex
= -2;
1018 static const sptr MemTagAllocationTidIndex
= -1;
1021 u32 QuarantineMaxChunkSize
= 0;
1025 SecondaryT Secondary
;
1026 QuarantineT Quarantine
;
1027 TSDRegistryT TSDRegistry
;
1028 pthread_once_t PostInitNonce
= PTHREAD_ONCE_INIT
;
1030 #ifdef GWP_ASAN_HOOKS
1031 gwp_asan::GuardedPoolAllocator GuardedAlloc
;
1032 uptr GuardedAllocSlotSize
= 0;
1033 #endif // GWP_ASAN_HOOKS
1037 struct AllocationRingBuffer
{
1040 atomic_uptr AllocationSize
;
1041 atomic_u32 AllocationTrace
;
1042 atomic_u32 AllocationTid
;
1043 atomic_u32 DeallocationTrace
;
1044 atomic_u32 DeallocationTid
;
1050 // An array of Size (at least one) elements of type Entry is immediately
1051 // following to this struct.
1053 // Pointer to memory mapped area starting with AllocationRingBuffer struct,
1054 // and immediately followed by Size elements of type Entry.
1055 char *RawRingBuffer
= {};
1057 // The following might get optimized out by the compiler.
1058 NOINLINE
void performSanityChecks() {
1059 // Verify that the header offset field can hold the maximum offset. In the
1060 // case of the Secondary allocator, it takes care of alignment and the
1061 // offset will always be small. In the case of the Primary, the worst case
1062 // scenario happens in the last size class, when the backend allocation
1063 // would already be aligned on the requested alignment, which would happen
1064 // to be the maximum alignment that would fit in that size class. As a
1065 // result, the maximum offset will be at most the maximum alignment for the
1066 // last size class minus the header size, in multiples of MinAlignment.
1067 Chunk::UnpackedHeader Header
= {};
1068 const uptr MaxPrimaryAlignment
= 1UL << getMostSignificantSetBitIndex(
1069 SizeClassMap::MaxSize
- MinAlignment
);
1070 const uptr MaxOffset
=
1071 (MaxPrimaryAlignment
- Chunk::getHeaderSize()) >> MinAlignmentLog
;
1072 Header
.Offset
= MaxOffset
& Chunk::OffsetMask
;
1073 if (UNLIKELY(Header
.Offset
!= MaxOffset
))
1074 reportSanityCheckError("offset");
1076 // Verify that we can fit the maximum size or amount of unused bytes in the
1077 // header. Given that the Secondary fits the allocation to a page, the worst
1078 // case scenario happens in the Primary. It will depend on the second to
1079 // last and last class sizes, as well as the dynamic base for the Primary.
1080 // The following is an over-approximation that works for our needs.
1081 const uptr MaxSizeOrUnusedBytes
= SizeClassMap::MaxSize
- 1;
1082 Header
.SizeOrUnusedBytes
= MaxSizeOrUnusedBytes
;
1083 if (UNLIKELY(Header
.SizeOrUnusedBytes
!= MaxSizeOrUnusedBytes
))
1084 reportSanityCheckError("size (or unused bytes)");
1086 const uptr LargestClassId
= SizeClassMap::LargestClassId
;
1087 Header
.ClassId
= LargestClassId
;
1088 if (UNLIKELY(Header
.ClassId
!= LargestClassId
))
1089 reportSanityCheckError("class ID");
1092 static inline void *getBlockBegin(const void *Ptr
,
1093 Chunk::UnpackedHeader
*Header
) {
1094 return reinterpret_cast<void *>(
1095 reinterpret_cast<uptr
>(Ptr
) - Chunk::getHeaderSize() -
1096 (static_cast<uptr
>(Header
->Offset
) << MinAlignmentLog
));
1099 // Return the size of a chunk as requested during its allocation.
1100 inline uptr
getSize(const void *Ptr
, Chunk::UnpackedHeader
*Header
) {
1101 const uptr SizeOrUnusedBytes
= Header
->SizeOrUnusedBytes
;
1102 if (LIKELY(Header
->ClassId
))
1103 return SizeOrUnusedBytes
;
1104 if (allocatorSupportsMemoryTagging
<Config
>())
1105 Ptr
= untagPointer(const_cast<void *>(Ptr
));
1106 return SecondaryT::getBlockEnd(getBlockBegin(Ptr
, Header
)) -
1107 reinterpret_cast<uptr
>(Ptr
) - SizeOrUnusedBytes
;
1110 void quarantineOrDeallocateChunk(const Options
&Options
, void *TaggedPtr
,
1111 Chunk::UnpackedHeader
*Header
,
1112 uptr Size
) NO_THREAD_SAFETY_ANALYSIS
{
1113 void *Ptr
= getHeaderTaggedPointer(TaggedPtr
);
1114 // If the quarantine is disabled, the actual size of a chunk is 0 or larger
1115 // than the maximum allowed, we return a chunk directly to the backend.
1116 // This purposefully underflows for Size == 0.
1117 const bool BypassQuarantine
= !Quarantine
.getCacheSize() ||
1118 ((Size
- 1) >= QuarantineMaxChunkSize
) ||
1120 if (BypassQuarantine
)
1121 Header
->State
= Chunk::State::Available
;
1123 Header
->State
= Chunk::State::Quarantined
;
1124 Header
->OriginOrWasZeroed
= useMemoryTagging
<Config
>(Options
) &&
1126 !TSDRegistry
.getDisableMemInit();
1127 Chunk::storeHeader(Cookie
, Ptr
, Header
);
1129 if (UNLIKELY(useMemoryTagging
<Config
>(Options
))) {
1130 u8 PrevTag
= extractTag(reinterpret_cast<uptr
>(TaggedPtr
));
1131 storeDeallocationStackMaybe(Options
, Ptr
, PrevTag
, Size
);
1132 if (Header
->ClassId
) {
1133 if (!TSDRegistry
.getDisableMemInit()) {
1134 uptr TaggedBegin
, TaggedEnd
;
1135 const uptr OddEvenMask
= computeOddEvenMaskForPointerMaybe(
1136 Options
, reinterpret_cast<uptr
>(getBlockBegin(Ptr
, Header
)),
1138 // Exclude the previous tag so that immediate use after free is
1139 // detected 100% of the time.
1140 setRandomTag(Ptr
, Size
, OddEvenMask
| (1UL << PrevTag
), &TaggedBegin
,
1145 if (BypassQuarantine
) {
1146 if (allocatorSupportsMemoryTagging
<Config
>())
1147 Ptr
= untagPointer(Ptr
);
1148 void *BlockBegin
= getBlockBegin(Ptr
, Header
);
1149 const uptr ClassId
= Header
->ClassId
;
1150 if (LIKELY(ClassId
)) {
1151 bool UnlockRequired
;
1152 auto *TSD
= TSDRegistry
.getTSDAndLock(&UnlockRequired
);
1153 TSD
->assertLocked(/*BypassCheck=*/!UnlockRequired
);
1154 const bool CacheDrained
=
1155 TSD
->getCache().deallocate(ClassId
, BlockBegin
);
1158 // When we have drained some blocks back to the Primary from TSD, that
1159 // implies that we may have the chance to release some pages as well.
1160 // Note that in order not to block other thread's accessing the TSD,
1161 // release the TSD first then try the page release.
1163 Primary
.tryReleaseToOS(ClassId
, ReleaseToOS::Normal
);
1165 if (UNLIKELY(useMemoryTagging
<Config
>(Options
)))
1166 storeTags(reinterpret_cast<uptr
>(BlockBegin
),
1167 reinterpret_cast<uptr
>(Ptr
));
1168 Secondary
.deallocate(Options
, BlockBegin
);
1171 bool UnlockRequired
;
1172 auto *TSD
= TSDRegistry
.getTSDAndLock(&UnlockRequired
);
1173 TSD
->assertLocked(/*BypassCheck=*/!UnlockRequired
);
1174 Quarantine
.put(&TSD
->getQuarantineCache(),
1175 QuarantineCallback(*this, TSD
->getCache()), Ptr
, Size
);
1181 bool getChunkFromBlock(uptr Block
, uptr
*Chunk
,
1182 Chunk::UnpackedHeader
*Header
) {
1184 Block
+ getChunkOffsetFromBlock(reinterpret_cast<const char *>(Block
));
1185 return Chunk::isValid(Cookie
, reinterpret_cast<void *>(*Chunk
), Header
);
1188 static uptr
getChunkOffsetFromBlock(const char *Block
) {
1190 if (reinterpret_cast<const u32
*>(Block
)[0] == BlockMarker
)
1191 Offset
= reinterpret_cast<const u32
*>(Block
)[1];
1192 return Offset
+ Chunk::getHeaderSize();
1195 // Set the tag of the granule past the end of the allocation to 0, to catch
1196 // linear overflows even if a previous larger allocation used the same block
1197 // and tag. Only do this if the granule past the end is in our block, because
1198 // this would otherwise lead to a SEGV if the allocation covers the entire
1199 // block and our block is at the end of a mapping. The tag of the next block's
1200 // header granule will be set to 0, so it will serve the purpose of catching
1201 // linear overflows in this case.
1203 // For allocations of size 0 we do not end up storing the address tag to the
1204 // memory tag space, which getInlineErrorInfo() normally relies on to match
1205 // address tags against chunks. To allow matching in this case we store the
1206 // address tag in the first byte of the chunk.
1207 void storeEndMarker(uptr End
, uptr Size
, uptr BlockEnd
) {
1208 DCHECK_EQ(BlockEnd
, untagPointer(BlockEnd
));
1209 uptr UntaggedEnd
= untagPointer(End
);
1210 if (UntaggedEnd
!= BlockEnd
) {
1211 storeTag(UntaggedEnd
);
1213 *reinterpret_cast<u8
*>(UntaggedEnd
) = extractTag(End
);
1217 void *prepareTaggedChunk(void *Ptr
, uptr Size
, uptr ExcludeMask
,
1219 // Prepare the granule before the chunk to store the chunk header by setting
1220 // its tag to 0. Normally its tag will already be 0, but in the case where a
1221 // chunk holding a low alignment allocation is reused for a higher alignment
1222 // allocation, the chunk may already have a non-zero tag from the previous
1224 storeTag(reinterpret_cast<uptr
>(Ptr
) - archMemoryTagGranuleSize());
1226 uptr TaggedBegin
, TaggedEnd
;
1227 setRandomTag(Ptr
, Size
, ExcludeMask
, &TaggedBegin
, &TaggedEnd
);
1229 storeEndMarker(TaggedEnd
, Size
, BlockEnd
);
1230 return reinterpret_cast<void *>(TaggedBegin
);
1233 void resizeTaggedChunk(uptr OldPtr
, uptr NewPtr
, uptr NewSize
,
1235 uptr RoundOldPtr
= roundUp(OldPtr
, archMemoryTagGranuleSize());
1237 if (RoundOldPtr
>= NewPtr
) {
1238 // If the allocation is shrinking we just need to set the tag past the end
1239 // of the allocation to 0. See explanation in storeEndMarker() above.
1240 RoundNewPtr
= roundUp(NewPtr
, archMemoryTagGranuleSize());
1242 // Set the memory tag of the region
1243 // [RoundOldPtr, roundUp(NewPtr, archMemoryTagGranuleSize()))
1244 // to the pointer tag stored in OldPtr.
1245 RoundNewPtr
= storeTags(RoundOldPtr
, NewPtr
);
1247 storeEndMarker(RoundNewPtr
, NewSize
, BlockEnd
);
1250 void storePrimaryAllocationStackMaybe(const Options
&Options
, void *Ptr
) {
1251 if (!UNLIKELY(Options
.get(OptionBit::TrackAllocationStacks
)))
1253 auto *Ptr32
= reinterpret_cast<u32
*>(Ptr
);
1254 Ptr32
[MemTagAllocationTraceIndex
] = collectStackTrace();
1255 Ptr32
[MemTagAllocationTidIndex
] = getThreadID();
1258 void storeRingBufferEntry(void *Ptr
, u32 AllocationTrace
, u32 AllocationTid
,
1259 uptr AllocationSize
, u32 DeallocationTrace
,
1260 u32 DeallocationTid
) {
1261 uptr Pos
= atomic_fetch_add(&getRingBuffer()->Pos
, 1, memory_order_relaxed
);
1262 typename
AllocationRingBuffer::Entry
*Entry
=
1263 getRingBufferEntry(RawRingBuffer
, Pos
% getRingBuffer()->Size
);
1265 // First invalidate our entry so that we don't attempt to interpret a
1266 // partially written state in getSecondaryErrorInfo(). The fences below
1267 // ensure that the compiler does not move the stores to Ptr in between the
1268 // stores to the other fields.
1269 atomic_store_relaxed(&Entry
->Ptr
, 0);
1271 __atomic_signal_fence(__ATOMIC_SEQ_CST
);
1272 atomic_store_relaxed(&Entry
->AllocationTrace
, AllocationTrace
);
1273 atomic_store_relaxed(&Entry
->AllocationTid
, AllocationTid
);
1274 atomic_store_relaxed(&Entry
->AllocationSize
, AllocationSize
);
1275 atomic_store_relaxed(&Entry
->DeallocationTrace
, DeallocationTrace
);
1276 atomic_store_relaxed(&Entry
->DeallocationTid
, DeallocationTid
);
1277 __atomic_signal_fence(__ATOMIC_SEQ_CST
);
1279 atomic_store_relaxed(&Entry
->Ptr
, reinterpret_cast<uptr
>(Ptr
));
1282 void storeSecondaryAllocationStackMaybe(const Options
&Options
, void *Ptr
,
1284 if (!UNLIKELY(Options
.get(OptionBit::TrackAllocationStacks
)))
1287 u32 Trace
= collectStackTrace();
1288 u32 Tid
= getThreadID();
1290 auto *Ptr32
= reinterpret_cast<u32
*>(Ptr
);
1291 Ptr32
[MemTagAllocationTraceIndex
] = Trace
;
1292 Ptr32
[MemTagAllocationTidIndex
] = Tid
;
1294 storeRingBufferEntry(untagPointer(Ptr
), Trace
, Tid
, Size
, 0, 0);
1297 void storeDeallocationStackMaybe(const Options
&Options
, void *Ptr
,
1298 u8 PrevTag
, uptr Size
) {
1299 if (!UNLIKELY(Options
.get(OptionBit::TrackAllocationStacks
)))
1302 auto *Ptr32
= reinterpret_cast<u32
*>(Ptr
);
1303 u32 AllocationTrace
= Ptr32
[MemTagAllocationTraceIndex
];
1304 u32 AllocationTid
= Ptr32
[MemTagAllocationTidIndex
];
1306 u32 DeallocationTrace
= collectStackTrace();
1307 u32 DeallocationTid
= getThreadID();
1309 storeRingBufferEntry(addFixedTag(untagPointer(Ptr
), PrevTag
),
1310 AllocationTrace
, AllocationTid
, Size
,
1311 DeallocationTrace
, DeallocationTid
);
1314 static const size_t NumErrorReports
=
1315 sizeof(((scudo_error_info
*)nullptr)->reports
) /
1316 sizeof(((scudo_error_info
*)nullptr)->reports
[0]);
1318 static void getInlineErrorInfo(struct scudo_error_info
*ErrorInfo
,
1319 size_t &NextErrorReport
, uintptr_t FaultAddr
,
1320 const StackDepot
*Depot
,
1321 const char *RegionInfoPtr
, const char *Memory
,
1322 const char *MemoryTags
, uintptr_t MemoryAddr
,
1323 size_t MemorySize
, size_t MinDistance
,
1324 size_t MaxDistance
) {
1325 uptr UntaggedFaultAddr
= untagPointer(FaultAddr
);
1326 u8 FaultAddrTag
= extractTag(FaultAddr
);
1328 PrimaryT::findNearestBlock(RegionInfoPtr
, UntaggedFaultAddr
);
1330 auto GetGranule
= [&](uptr Addr
, const char **Data
, uint8_t *Tag
) -> bool {
1331 if (Addr
< MemoryAddr
|| Addr
+ archMemoryTagGranuleSize() < Addr
||
1332 Addr
+ archMemoryTagGranuleSize() > MemoryAddr
+ MemorySize
)
1334 *Data
= &Memory
[Addr
- MemoryAddr
];
1335 *Tag
= static_cast<u8
>(
1336 MemoryTags
[(Addr
- MemoryAddr
) / archMemoryTagGranuleSize()]);
1340 auto ReadBlock
= [&](uptr Addr
, uptr
*ChunkAddr
,
1341 Chunk::UnpackedHeader
*Header
, const u32
**Data
,
1343 const char *BlockBegin
;
1345 if (!GetGranule(Addr
, &BlockBegin
, &BlockBeginTag
))
1347 uptr ChunkOffset
= getChunkOffsetFromBlock(BlockBegin
);
1348 *ChunkAddr
= Addr
+ ChunkOffset
;
1350 const char *ChunkBegin
;
1351 if (!GetGranule(*ChunkAddr
, &ChunkBegin
, Tag
))
1353 *Header
= *reinterpret_cast<const Chunk::UnpackedHeader
*>(
1354 ChunkBegin
- Chunk::getHeaderSize());
1355 *Data
= reinterpret_cast<const u32
*>(ChunkBegin
);
1357 // Allocations of size 0 will have stashed the tag in the first byte of
1358 // the chunk, see storeEndMarker().
1359 if (Header
->SizeOrUnusedBytes
== 0)
1360 *Tag
= static_cast<u8
>(*ChunkBegin
);
1365 if (NextErrorReport
== NumErrorReports
)
1368 auto CheckOOB
= [&](uptr BlockAddr
) {
1369 if (BlockAddr
< Info
.RegionBegin
|| BlockAddr
>= Info
.RegionEnd
)
1373 Chunk::UnpackedHeader Header
;
1376 if (!ReadBlock(BlockAddr
, &ChunkAddr
, &Header
, &Data
, &Tag
) ||
1377 Header
.State
!= Chunk::State::Allocated
|| Tag
!= FaultAddrTag
)
1380 auto *R
= &ErrorInfo
->reports
[NextErrorReport
++];
1382 UntaggedFaultAddr
< ChunkAddr
? BUFFER_UNDERFLOW
: BUFFER_OVERFLOW
;
1383 R
->allocation_address
= ChunkAddr
;
1384 R
->allocation_size
= Header
.SizeOrUnusedBytes
;
1385 collectTraceMaybe(Depot
, R
->allocation_trace
,
1386 Data
[MemTagAllocationTraceIndex
]);
1387 R
->allocation_tid
= Data
[MemTagAllocationTidIndex
];
1388 return NextErrorReport
== NumErrorReports
;
1391 if (MinDistance
== 0 && CheckOOB(Info
.BlockBegin
))
1394 for (size_t I
= Max
<size_t>(MinDistance
, 1); I
!= MaxDistance
; ++I
)
1395 if (CheckOOB(Info
.BlockBegin
+ I
* Info
.BlockSize
) ||
1396 CheckOOB(Info
.BlockBegin
- I
* Info
.BlockSize
))
1400 static void getRingBufferErrorInfo(struct scudo_error_info
*ErrorInfo
,
1401 size_t &NextErrorReport
,
1402 uintptr_t FaultAddr
,
1403 const StackDepot
*Depot
,
1404 const char *RingBufferPtr
) {
1406 reinterpret_cast<const AllocationRingBuffer
*>(RingBufferPtr
);
1407 if (!RingBuffer
|| RingBuffer
->Size
== 0)
1409 uptr Pos
= atomic_load_relaxed(&RingBuffer
->Pos
);
1411 for (uptr I
= Pos
- 1;
1412 I
!= Pos
- 1 - RingBuffer
->Size
&& NextErrorReport
!= NumErrorReports
;
1414 auto *Entry
= getRingBufferEntry(RingBufferPtr
, I
% RingBuffer
->Size
);
1415 uptr EntryPtr
= atomic_load_relaxed(&Entry
->Ptr
);
1419 uptr UntaggedEntryPtr
= untagPointer(EntryPtr
);
1420 uptr EntrySize
= atomic_load_relaxed(&Entry
->AllocationSize
);
1421 u32 AllocationTrace
= atomic_load_relaxed(&Entry
->AllocationTrace
);
1422 u32 AllocationTid
= atomic_load_relaxed(&Entry
->AllocationTid
);
1423 u32 DeallocationTrace
= atomic_load_relaxed(&Entry
->DeallocationTrace
);
1424 u32 DeallocationTid
= atomic_load_relaxed(&Entry
->DeallocationTid
);
1426 if (DeallocationTid
) {
1427 // For UAF we only consider in-bounds fault addresses because
1428 // out-of-bounds UAF is rare and attempting to detect it is very likely
1429 // to result in false positives.
1430 if (FaultAddr
< EntryPtr
|| FaultAddr
>= EntryPtr
+ EntrySize
)
1433 // Ring buffer OOB is only possible with secondary allocations. In this
1434 // case we are guaranteed a guard region of at least a page on either
1435 // side of the allocation (guard page on the right, guard page + tagged
1436 // region on the left), so ignore any faults outside of that range.
1437 if (FaultAddr
< EntryPtr
- getPageSizeCached() ||
1438 FaultAddr
>= EntryPtr
+ EntrySize
+ getPageSizeCached())
1441 // For UAF the ring buffer will contain two entries, one for the
1442 // allocation and another for the deallocation. Don't report buffer
1443 // overflow/underflow using the allocation entry if we have already
1444 // collected a report from the deallocation entry.
1446 for (uptr J
= 0; J
!= NextErrorReport
; ++J
) {
1447 if (ErrorInfo
->reports
[J
].allocation_address
== UntaggedEntryPtr
) {
1456 auto *R
= &ErrorInfo
->reports
[NextErrorReport
++];
1457 if (DeallocationTid
)
1458 R
->error_type
= USE_AFTER_FREE
;
1459 else if (FaultAddr
< EntryPtr
)
1460 R
->error_type
= BUFFER_UNDERFLOW
;
1462 R
->error_type
= BUFFER_OVERFLOW
;
1464 R
->allocation_address
= UntaggedEntryPtr
;
1465 R
->allocation_size
= EntrySize
;
1466 collectTraceMaybe(Depot
, R
->allocation_trace
, AllocationTrace
);
1467 R
->allocation_tid
= AllocationTid
;
1468 collectTraceMaybe(Depot
, R
->deallocation_trace
, DeallocationTrace
);
1469 R
->deallocation_tid
= DeallocationTid
;
1473 uptr
getStats(ScopedString
*Str
) {
1474 Primary
.getStats(Str
);
1475 Secondary
.getStats(Str
);
1476 Quarantine
.getStats(Str
);
1477 TSDRegistry
.getStats(Str
);
1478 return Str
->length();
1481 static typename
AllocationRingBuffer::Entry
*
1482 getRingBufferEntry(char *RawRingBuffer
, uptr N
) {
1483 return &reinterpret_cast<typename
AllocationRingBuffer::Entry
*>(
1484 &RawRingBuffer
[sizeof(AllocationRingBuffer
)])[N
];
1486 static const typename
AllocationRingBuffer::Entry
*
1487 getRingBufferEntry(const char *RawRingBuffer
, uptr N
) {
1488 return &reinterpret_cast<const typename
AllocationRingBuffer::Entry
*>(
1489 &RawRingBuffer
[sizeof(AllocationRingBuffer
)])[N
];
1492 void mapAndInitializeRingBuffer() {
1493 u32 AllocationRingBufferSize
=
1494 static_cast<u32
>(getFlags()->allocation_ring_buffer_size
);
1495 if (AllocationRingBufferSize
< 1)
1500 roundUp(ringBufferSizeInBytes(AllocationRingBufferSize
),
1501 getPageSizeCached()),
1502 "scudo:ring_buffer");
1503 RawRingBuffer
= reinterpret_cast<char *>(MemMap
.getBase());
1504 auto *RingBuffer
= reinterpret_cast<AllocationRingBuffer
*>(RawRingBuffer
);
1505 RingBuffer
->MemMap
= MemMap
;
1506 RingBuffer
->Size
= AllocationRingBufferSize
;
1507 static_assert(sizeof(AllocationRingBuffer
) %
1508 alignof(typename
AllocationRingBuffer::Entry
) ==
1510 "invalid alignment");
1513 void unmapRingBuffer() {
1514 auto *RingBuffer
= getRingBuffer();
1515 if (RingBuffer
!= nullptr) {
1516 MemMapT MemMap
= RingBuffer
->MemMap
;
1517 MemMap
.unmap(MemMap
.getBase(), MemMap
.getCapacity());
1519 RawRingBuffer
= nullptr;
1522 static constexpr size_t ringBufferSizeInBytes(u32 AllocationRingBufferSize
) {
1523 return sizeof(AllocationRingBuffer
) +
1524 AllocationRingBufferSize
*
1525 sizeof(typename
AllocationRingBuffer::Entry
);
1528 inline AllocationRingBuffer
*getRingBuffer() {
1529 return reinterpret_cast<AllocationRingBuffer
*>(RawRingBuffer
);
1533 } // namespace scudo
1535 #endif // SCUDO_COMBINED_H_