forked from OSchip/llvm-project
[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:
parent
6937251f01
commit
45b7d44ecb
|
@ -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;
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue