diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -437,7 +437,7 @@ if (NextPage < PrevEnd && loadTag(NextPage) != NextPage) PrevEnd = NextPage; TaggedPtr = reinterpret_cast(TaggedUserPtr); - resizeTaggedChunk(PrevEnd, TaggedUserPtr + Size, BlockEnd); + resizeTaggedChunk(PrevEnd, TaggedUserPtr + Size, Size, BlockEnd); if (UNLIKELY(FillContents != NoFill && !Header.OriginOrWasZeroed)) { // If an allocation needs to be zeroed (i.e. calloc) we can normally // avoid zeroing the memory now since we can rely on memory having @@ -643,7 +643,7 @@ if (ClassId) { resizeTaggedChunk(reinterpret_cast(OldTaggedPtr) + OldSize, reinterpret_cast(OldTaggedPtr) + NewSize, - BlockEnd); + NewSize, BlockEnd); storePrimaryAllocationStackMaybe(Options, OldPtr); } else { storeSecondaryAllocationStackMaybe(Options, OldPtr, NewSize); @@ -1143,6 +1143,27 @@ return Offset + Chunk::getHeaderSize(); } + // Set the tag of the granule past the end of the allocation to 0, to catch + // linear overflows even if a previous larger allocation used the same block + // and tag. Only do this if the granule past the end is in our block, because + // this would otherwise lead to a SEGV if the allocation covers the entire + // block and our block is at the end of a mapping. The tag of the next block's + // header granule will be set to 0, so it will serve the purpose of catching + // linear overflows in this case. + // + // For allocations of size 0 we do not end up storing the address tag to the + // memory tag space, which getInlineErrorInfo() normally relies on to match + // address tags against chunks. To allow matching in this case we store the + // address tag in the first byte of the chunk. + void storeEndMarker(uptr End, uptr Size, uptr BlockEnd) { + uptr UntaggedEnd = untagPointer(End); + if (UntaggedEnd != BlockEnd) { + storeTag(UntaggedEnd); + if (Size == 0) + *reinterpret_cast(UntaggedEnd) = extractTag(End); + } + } + void *prepareTaggedChunk(void *Ptr, uptr Size, uptr ExcludeMask, uptr BlockEnd) { // Prepare the granule before the chunk to store the chunk header by setting @@ -1155,25 +1176,17 @@ uptr TaggedBegin, TaggedEnd; setRandomTag(Ptr, Size, ExcludeMask, &TaggedBegin, &TaggedEnd); - // Finally, set the tag of the granule past the end of the allocation to 0, - // to catch linear overflows even if a previous larger allocation used the - // same block and tag. Only do this if the granule past the end is in our - // block, because this would otherwise lead to a SEGV if the allocation - // covers the entire block and our block is at the end of a mapping. The tag - // of the next block's header granule will be set to 0, so it will serve the - // purpose of catching linear overflows in this case. - uptr UntaggedEnd = untagPointer(TaggedEnd); - if (UntaggedEnd != BlockEnd) - storeTag(UntaggedEnd); + storeEndMarker(TaggedEnd, Size, BlockEnd); return reinterpret_cast(TaggedBegin); } - void resizeTaggedChunk(uptr OldPtr, uptr NewPtr, uptr BlockEnd) { + void resizeTaggedChunk(uptr OldPtr, uptr NewPtr, uptr NewSize, + uptr BlockEnd) { uptr RoundOldPtr = roundUpTo(OldPtr, archMemoryTagGranuleSize()); uptr RoundNewPtr; if (RoundOldPtr >= NewPtr) { // If the allocation is shrinking we just need to set the tag past the end - // of the allocation to 0. See explanation in prepareTaggedChunk above. + // of the allocation to 0. See explanation in storeEndMarker() above. RoundNewPtr = roundUpTo(NewPtr, archMemoryTagGranuleSize()); } else { // Set the memory tag of the region @@ -1181,10 +1194,7 @@ // to the pointer tag stored in OldPtr. RoundNewPtr = storeTags(RoundOldPtr, NewPtr); } - - uptr UntaggedNewPtr = untagPointer(RoundNewPtr); - if (UntaggedNewPtr != BlockEnd) - storeTag(UntaggedNewPtr); + storeEndMarker(RoundNewPtr, NewSize, BlockEnd); } void storePrimaryAllocationStackMaybe(Options Options, void *Ptr) { @@ -1293,6 +1303,12 @@ *Header = *reinterpret_cast( ChunkBegin - Chunk::getHeaderSize()); *Data = reinterpret_cast(ChunkBegin); + + // Allocations of size 0 will have stashed the tag in the first byte of + // the chunk, see storeEndMarker(). + if (Header->SizeOrUnusedBytes == 0) + *Tag = static_cast(*ChunkBegin); + return true; };