[scudo] Zero- and pattern-initialization of memory.

Summary:
Implement pattern initialization of memory (excluding the secondary
allocator because it already has predictable memory contents).
Expose both zero and pattern initialization through the C API.

Reviewers: pcc, cryptoad

Subscribers: #sanitizers, llvm-commits

Tags: #sanitizers

Differential Revision: https://reviews.llvm.org/D79133
This commit is contained in:
Evgenii Stepanov 2020-04-29 14:39:23 -07:00
parent 6937251f01
commit 45b7d44ecb
6 changed files with 87 additions and 11 deletions

View File

@ -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<bool>(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<const char *>(&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;

View File

@ -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_

View File

@ -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 "

View File

@ -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 <class CacheT>
void *MapAllocator<CacheT>::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<CacheT>::allocate(uptr Size, uptr AlignmentHint,
*BlockEnd = H->BlockEnd;
void *Ptr = reinterpret_cast<void *>(reinterpret_cast<uptr>(H) +
LargeBlock::getHeaderSize());
if (ZeroContents)
memset(Ptr, 0, H->BlockEnd - reinterpret_cast<uptr>(Ptr));
if (FillContents)
memset(Ptr, FillContents == ZeroFill ? 0 : PatternFillByte,
H->BlockEnd - reinterpret_cast<uptr>(Ptr));
const uptr BlockSize = H->BlockEnd - reinterpret_cast<uptr>(H);
{
ScopedLock L(Mutex);

View File

@ -115,7 +115,44 @@ template <class Config> 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<char *>(P))[I], 0);
ASSERT_EQ((reinterpret_cast<char *>(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<char *>(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<unsigned char *>(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);
}

View File

@ -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"