[scudo] Ensure that reallocate copies everything

getUsableSize returns the actual capacity of the underlying block, which
may be larger than the size originally requested by the user. If the
user writes data into this extra space accessible via getUsableSize and
subsequently calls reallocate, the existing implementation only copies
the original requested number of bytes. This resulted in data loss for
any information stored beyond the requested size but within the usable
bounds.
This commit is contained in:
Sadaf Ebrahimi 2026-04-01 12:30:47 -05:00 committed by GitHub
parent 33ca7a4667
commit 7c4b6dee74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 25 additions and 4 deletions

View File

@ -614,16 +614,13 @@ public:
void *BlockBegin = getBlockBegin(OldTaggedPtr, &Header);
uptr BlockEnd;
uptr OldSize;
uptr OldSize = getUsableSize(OldTaggedPtr, &Header);
const uptr ClassId = Header.ClassId;
if (LIKELY(ClassId)) {
BlockEnd = reinterpret_cast<uptr>(BlockBegin) +
SizeClassMap::getSizeByClassId(ClassId);
OldSize = Header.SizeOrUnusedBytes;
} else {
BlockEnd = SecondaryT::getBlockEnd(BlockBegin);
OldSize = BlockEnd - (reinterpret_cast<uptr>(OldTaggedPtr) +
Header.SizeOrUnusedBytes);
}
// If the new chunk still fits in the previously allocated block (with a
// reasonable delta), we just keep the old block, and update the chunk

View File

@ -1471,6 +1471,30 @@ TEST(ScudoCombinedTest, FullUsableSize) {
VerifyIterateOverUsableSize<AllocatorT>(*Allocator);
}
TEST(ScudoCombinedTest, ReallocUsableSize) {
using AllocatorT = TestAllocator<TestFullUsableSizeConfig>;
auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
scudo::uptr Size = 1000;
void *P = Allocator->allocate(Size, Origin);
scudo::uptr UsableSize = Allocator->getUsableSize(P);
std::vector<unsigned char> Buffer(UsableSize);
for (size_t I = 0; I < UsableSize; I++) {
Buffer[I] = I & 0xff;
}
memcpy(P, Buffer.data(), UsableSize);
EXPECT_LE(Size, UsableSize);
scudo::uptr NewSize = 2 * UsableSize;
void *NewP = Allocator->reallocate(P, NewSize);
EXPECT_NE(NewP, P);
for (size_t I = 0; I < UsableSize; I++) {
EXPECT_EQ((reinterpret_cast<unsigned char *>(NewP))[I], I & 0xff)
<< "Failed at index " << I;
}
Allocator->deallocate(NewP, Origin);
}
struct TestFullUsableSizeMTEConfig : TestFullUsableSizeConfig {
static const bool MaySupportMemoryTagging = true;
};