diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h index 66638e7dd5db..112aefdc3930 100644 --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -147,7 +147,10 @@ public: // Store some flags locally. Options.MayReturnNull = getFlags()->may_return_null; - Options.ZeroContents = getFlags()->zero_contents; + Options.FillContents = + getFlags()->zero_contents + ? ZeroFill + : (getFlags()->pattern_fill_contents ? PatternOrZeroFill : NoFill); Options.DeallocTypeMismatch = getFlags()->dealloc_type_mismatch; Options.DeleteSizeMismatch = getFlags()->delete_size_mismatch; Options.TrackAllocationStacks = false; @@ -256,7 +259,8 @@ public: } #endif // GWP_ASAN_HOOKS - ZeroContents |= static_cast(Options.ZeroContents); + FillContentsMode FillContents = + ZeroContents ? ZeroFill : Options.FillContents; if (UNLIKELY(Alignment > MaxAlignment)) { if (Options.MayReturnNull) @@ -309,7 +313,7 @@ public: } if (UNLIKELY(ClassId == 0)) Block = Secondary.allocate(NeededSize, Alignment, &SecondaryBlockEnd, - ZeroContents); + FillContents); if (UNLIKELY(!Block)) { if (Options.MayReturnNull) @@ -391,10 +395,11 @@ public: TaggedPtr = prepareTaggedChunk(Ptr, Size, BlockEnd); } storeAllocationStackMaybe(Ptr); - } else if (UNLIKELY(ZeroContents)) { + } else if (UNLIKELY(FillContents != NoFill)) { // This condition is not necessarily unlikely, but since memset is // costly, we might as well mark it as such. - memset(Block, 0, PrimaryT::getSizeByClassId(ClassId)); + memset(Block, FillContents == ZeroFill ? 0 : PatternFillByte, + PrimaryT::getSizeByClassId(ClassId)); } } @@ -725,6 +730,11 @@ public: Options.TrackAllocationStacks = Track; } + void setFillContents(FillContentsMode FillContents) { + initThreadMaybe(); + Options.FillContents = FillContents; + } + const char *getStackDepotAddress() const { return reinterpret_cast(&Depot); } @@ -899,7 +909,7 @@ private: struct { u8 MayReturnNull : 1; // may_return_null - u8 ZeroContents : 1; // zero_contents + FillContentsMode FillContents : 2; // zero_contents, pattern_fill_contents u8 DeallocTypeMismatch : 1; // dealloc_type_mismatch u8 DeleteSizeMismatch : 1; // delete_size_mismatch u8 TrackAllocationStacks : 1; diff --git a/compiler-rt/lib/scudo/standalone/common.h b/compiler-rt/lib/scudo/standalone/common.h index 350d8d9fcd91..9037f92b4976 100644 --- a/compiler-rt/lib/scudo/standalone/common.h +++ b/compiler-rt/lib/scudo/standalone/common.h @@ -182,6 +182,15 @@ struct BlockInfo { uptr RegionEnd; }; +constexpr unsigned char PatternFillByte = 0xAB; + +enum FillContentsMode { + NoFill = 0, + ZeroFill = 1, + PatternOrZeroFill = 2 // Pattern fill unless the memory is known to be + // zero-initialized already. +}; + } // namespace scudo #endif // SCUDO_COMMON_H_ diff --git a/compiler-rt/lib/scudo/standalone/flags.inc b/compiler-rt/lib/scudo/standalone/flags.inc index 342af1c79ad6..b5cab4734166 100644 --- a/compiler-rt/lib/scudo/standalone/flags.inc +++ b/compiler-rt/lib/scudo/standalone/flags.inc @@ -34,6 +34,9 @@ SCUDO_FLAG(bool, delete_size_mismatch, true, SCUDO_FLAG(bool, zero_contents, false, "Zero chunk contents on allocation.") +SCUDO_FLAG(bool, pattern_fill_contents, false, + "Pattern fill chunk contents on allocation.") + SCUDO_FLAG(int, rss_limit_mb, -1, "Enforce an upper limit (in megabytes) to the process RSS. The " "allocator will terminate or return NULL when allocations are " diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h index 9d5f130f2d45..84eaa5091b43 100644 --- a/compiler-rt/lib/scudo/standalone/secondary.h +++ b/compiler-rt/lib/scudo/standalone/secondary.h @@ -236,7 +236,7 @@ public: } void *allocate(uptr Size, uptr AlignmentHint = 0, uptr *BlockEnd = nullptr, - bool ZeroContents = false); + FillContentsMode FillContents = NoFill); void deallocate(void *Ptr); @@ -299,7 +299,8 @@ private: // (pending rounding and headers). template void *MapAllocator::allocate(uptr Size, uptr AlignmentHint, - uptr *BlockEnd, bool ZeroContents) { + uptr *BlockEnd, + FillContentsMode FillContents) { DCHECK_GE(Size, AlignmentHint); const uptr PageSize = getPageSizeCached(); const uptr RoundedSize = @@ -312,8 +313,9 @@ void *MapAllocator::allocate(uptr Size, uptr AlignmentHint, *BlockEnd = H->BlockEnd; void *Ptr = reinterpret_cast(reinterpret_cast(H) + LargeBlock::getHeaderSize()); - if (ZeroContents) - memset(Ptr, 0, H->BlockEnd - reinterpret_cast(Ptr)); + if (FillContents) + memset(Ptr, FillContents == ZeroFill ? 0 : PatternFillByte, + H->BlockEnd - reinterpret_cast(Ptr)); const uptr BlockSize = H->BlockEnd - reinterpret_cast(H); { ScopedLock L(Mutex); diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp index a6f29a2610ed..b9e1be1f32e8 100644 --- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp @@ -115,7 +115,44 @@ template static void testAllocator() { void *P = Allocator->allocate(Size, Origin, 1U << MinAlignLog, true); EXPECT_NE(P, nullptr); for (scudo::uptr I = 0; I < Size; I++) - EXPECT_EQ((reinterpret_cast(P))[I], 0); + ASSERT_EQ((reinterpret_cast(P))[I], 0); + memset(P, 0xaa, Size); + Allocator->deallocate(P, Origin, Size); + } + } + Allocator->releaseToOS(); + + // Ensure that specifying ZeroContents returns a zero'd out block. + Allocator->setFillContents(scudo::ZeroFill); + for (scudo::uptr SizeLog = 0U; SizeLog <= 20U; SizeLog++) { + for (scudo::uptr Delta = 0U; Delta <= 4U; Delta++) { + const scudo::uptr Size = (1U << SizeLog) + Delta * 128U; + void *P = Allocator->allocate(Size, Origin, 1U << MinAlignLog, false); + EXPECT_NE(P, nullptr); + for (scudo::uptr I = 0; I < Size; I++) + ASSERT_EQ((reinterpret_cast(P))[I], 0); + memset(P, 0xaa, Size); + Allocator->deallocate(P, Origin, Size); + } + } + Allocator->releaseToOS(); + + // Ensure that specifying PatternOrZeroFill returns a pattern-filled block in + // the primary allocator, and either pattern or zero filled block in the + // secondary. + Allocator->setFillContents(scudo::PatternOrZeroFill); + for (scudo::uptr SizeLog = 0U; SizeLog <= 20U; SizeLog++) { + for (scudo::uptr Delta = 0U; Delta <= 4U; Delta++) { + const scudo::uptr Size = (1U << SizeLog) + Delta * 128U; + void *P = Allocator->allocate(Size, Origin, 1U << MinAlignLog, false); + EXPECT_NE(P, nullptr); + for (scudo::uptr I = 0; I < Size; I++) { + unsigned char V = (reinterpret_cast(P))[I]; + if (AllocatorT::PrimaryT::canAllocate(Size)) + ASSERT_EQ(V, scudo::PatternFillByte); + else + ASSERT_TRUE(V == scudo::PatternFillByte || V == 0); + } memset(P, 0xaa, Size); Allocator->deallocate(P, Origin, Size); } diff --git a/compiler-rt/lib/scudo/standalone/wrappers_c.inc b/compiler-rt/lib/scudo/standalone/wrappers_c.inc index 765d7daa349d..4396dfc50d1d 100644 --- a/compiler-rt/lib/scudo/standalone/wrappers_c.inc +++ b/compiler-rt/lib/scudo/standalone/wrappers_c.inc @@ -228,4 +228,19 @@ SCUDO_PREFIX(malloc_set_track_allocation_stacks)(int track) { SCUDO_ALLOCATOR.setTrackAllocationStacks(track); } +// Sets whether scudo zero-initializes all allocated memory. The program must +// be single threaded at the point when the function is called. +INTERFACE WEAK void SCUDO_PREFIX(malloc_set_zero_contents)(int zero_contents) { + SCUDO_ALLOCATOR.setFillContents(zero_contents ? scudo::ZeroFill + : scudo::NoFill); +} + +// Sets whether scudo pattern-initializes all allocated memory. The program must +// be single threaded at the point when the function is called. +INTERFACE WEAK void +SCUDO_PREFIX(malloc_set_pattern_fill_contents)(int pattern_fill_contents) { + SCUDO_ALLOCATOR.setFillContents( + pattern_fill_contents ? scudo::PatternOrZeroFill : scudo::NoFill); +} + } // extern "C"