[RTL] Avoid some corner case ASSERT's in heap.c and fix heap allocations larger than MAXUSHORT. (#5750)

CORE-18196

With most code copied from Thomas Faber's PR #4915 as follows:
[RTL] Correctly initialize segments larger than MAXUSHORT.
GuardEntry->PreviousSize would overflow in this case.
RtlpInsertFreeBlock already handles this correctly by creating multiple free entries.
So we just let it return the last entry and use that for the PreviousSize.

This does not include all of the ASSERT's in PR #4915.
This commit is contained in:
Doug Lyons 2024-07-08 23:09:34 -05:00 committed by GitHub
parent f16e625c52
commit 7983297bd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -353,7 +353,8 @@ RtlpInsertFreeBlockHelper(PHEAP Heap,
Heap->FreeHints[HintIndex] = &FreeEntry->FreeList;
}
VOID NTAPI
PHEAP_FREE_ENTRY
NTAPI
RtlpInsertFreeBlock(PHEAP Heap,
PHEAP_FREE_ENTRY FreeEntry,
SIZE_T BlockSize)
@ -361,6 +362,7 @@ RtlpInsertFreeBlock(PHEAP Heap,
USHORT Size, PreviousSize;
UCHAR SegmentOffset, Flags;
PHEAP_SEGMENT Segment;
PHEAP_FREE_ENTRY LastEntry;
DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap, FreeEntry, BlockSize);
@ -368,6 +370,7 @@ RtlpInsertFreeBlock(PHEAP Heap,
Heap->TotalFreeSize += BlockSize;
/* Remember certain values */
LastEntry = FreeEntry;
Flags = FreeEntry->Flags;
PreviousSize = FreeEntry->PreviousSize;
SegmentOffset = FreeEntry->SegmentOffset;
@ -406,15 +409,21 @@ RtlpInsertFreeBlock(PHEAP Heap,
BlockSize -= Size;
/* Go to the next entry */
LastEntry = FreeEntry;
FreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
/* Check if that's all */
if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry) return;
if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry)
{
return LastEntry;
}
}
/* Update previous size if needed */
if (!(Flags & HEAP_ENTRY_LAST_ENTRY))
FreeEntry->PreviousSize = PreviousSize;
return LastEntry;
}
static
@ -426,6 +435,10 @@ RtlpRemoveFreeBlock(PHEAP Heap,
SIZE_T Result, RealSize;
ULONG HintIndex;
/* This was a problem before we handled segments > MAXUSHORT.
* It may not be needed now, but is left just for safety. */
ASSERT(FreeEntry->Size != 0);
/* Remove the free block */
if (FreeEntry->Size > Heap->DeCommitFreeBlockThreshold)
HintIndex = 0;
@ -1060,17 +1073,20 @@ RtlpInitializeHeapSegment(IN OUT PHEAP Heap,
{
/* Ensure we put our guard entry at the end of the last committed page */
PHEAP_ENTRY GuardEntry = &Segment->Entry + (SegmentCommit >> HEAP_ENTRY_SHIFT) - 1;
SIZE_T PreviousSize;
ASSERT(GuardEntry > &Segment->Entry);
GuardEntry->Size = 1;
GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY;
GuardEntry->SegmentOffset = SegmentIndex;
GuardEntry->PreviousSize = GuardEntry - Segment->FirstEntry;
PreviousSize = GuardEntry - Segment->FirstEntry;
/* Chack what is left behind us */
switch (GuardEntry->PreviousSize)
/* Check what is left behind us */
switch (PreviousSize)
{
case 1:
GuardEntry->PreviousSize = PreviousSize;
/* There is not enough space for a free entry. Double the guard entry */
GuardEntry--;
GuardEntry->Size = 1;
@ -1087,8 +1103,12 @@ RtlpInitializeHeapSegment(IN OUT PHEAP Heap,
FreeEntry = Segment->FirstEntry;
FreeEntry->PreviousSize = Segment->Entry.Size;
FreeEntry->SegmentOffset = SegmentIndex;
FreeEntry->Size = GuardEntry->PreviousSize;
FreeEntry->Size = PreviousSize;
FreeEntry->Flags = 0;
/* Register the Free Heap Entry */
FreeEntry = (PHEAP_ENTRY)RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)FreeEntry, PreviousSize);
GuardEntry->PreviousSize = FreeEntry->Size;
break;
}
}
@ -1100,11 +1120,10 @@ RtlpInitializeHeapSegment(IN OUT PHEAP Heap,
FreeEntry->SegmentOffset = SegmentIndex;
FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
FreeEntry->Size = (SegmentCommit >> HEAP_ENTRY_SHIFT) - Segment->Entry.Size;
}
/* Register the Free Heap Entry */
if (FreeEntry)
/* Register the Free Heap Entry */
RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)FreeEntry, FreeEntry->Size);
}
}
/* Register the UnCommitted Range of the Heap Segment */