http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
http://www.phreedom.org/research/exploits/asn1-bitstring/
http://illmatics.com/Understanding_the_LFH.pdf
+ http://www.alex-ionescu.com/?p=18
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
+#include <heap.h>
#define NDEBUG
#include <debug.h>
-// Various defines, to be moved to a separate header file
-#define HEAP_FREELISTS 128
-#define HEAP_SEGMENTS 64
-#define HEAP_MAX_PROCESS_HEAPS 16
-
-#define HEAP_ENTRY_SIZE ((ULONG)sizeof(HEAP_ENTRY))
-#define HEAP_ENTRY_SHIFT 3
-#define HEAP_MAX_BLOCK_SIZE ((0x80000 - PAGE_SIZE) >> HEAP_ENTRY_SHIFT)
-
-#define ARENA_INUSE_FILLER 0xBAADF00D
-#define ARENA_FREE_FILLER 0xFEEEFEEE
-#define HEAP_TAIL_FILL 0xab
-
-// from ntifs.h, should go to another header!
-#define HEAP_GLOBAL_TAG 0x0800
-#define HEAP_PSEUDO_TAG_FLAG 0x8000
-#define HEAP_TAG_MASK (HEAP_MAXIMUM_TAG << HEAP_TAG_SHIFT)
-
-#define HEAP_EXTRA_FLAGS_MASK (HEAP_CAPTURE_STACK_BACKTRACES | \
- HEAP_SETTABLE_USER_VALUE | \
- (HEAP_TAG_MASK ^ (0xFF << HEAP_TAG_SHIFT)))
-
-struct _HEAP_COMMON_ENTRY
-{
- union
- {
- struct
- {
- USHORT Size; // 0x0
- UCHAR Flags; // 0x2
- UCHAR SmallTagIndex; //0x3
- };
- struct
- {
- PVOID SubSegmentCode; // 0x0
- USHORT PreviousSize; // 0x4
- union
- {
- UCHAR SegmentOffset; // 0x6
- UCHAR LFHFlags; // 0x6
- };
- UCHAR UnusedBytes; // 0x7
- };
- struct
- {
- USHORT FunctionIndex; // 0x0
- USHORT ContextValue; // 0x2
- };
- struct
- {
- ULONG InterceptorValue; // 0x0
- USHORT UnusedBytesLength; // 0x4
- UCHAR EntryOffset; // 0x6
- UCHAR ExtendedBlockSignature; // 0x7
- };
- struct
- {
- ULONG Code1; // 0x0
- USHORT Code2; // 0x4
- UCHAR Code3; // 0x6
- UCHAR Code4; // 0x7
- };
- ULONGLONG AgregateCode; // 0x0
- };
-};
-
-typedef struct _HEAP_FREE_ENTRY
-{
- struct _HEAP_COMMON_ENTRY;
- LIST_ENTRY FreeList; // 0x8
-} HEAP_FREE_ENTRY, *PHEAP_FREE_ENTRY;
-
-typedef struct _HEAP_ENTRY
-{
- struct _HEAP_COMMON_ENTRY;
-} HEAP_ENTRY, *PHEAP_ENTRY;
-
-C_ASSERT(sizeof(HEAP_ENTRY) == 8);
-C_ASSERT((1 << HEAP_ENTRY_SHIFT) == sizeof(HEAP_ENTRY));
-
-typedef struct _HEAP_TAG_ENTRY
-{
- ULONG Allocs;
- ULONG Frees;
- ULONG Size;
- USHORT TagIndex;
- USHORT CreatorBackTraceIndex;
- WCHAR TagName[24];
-} HEAP_TAG_ENTRY, *PHEAP_TAG_ENTRY;
-
-typedef struct _HEAP_PSEUDO_TAG_ENTRY
-{
- ULONG Allocs;
- ULONG Frees;
- ULONG Size;
-} HEAP_PSEUDO_TAG_ENTRY, *PHEAP_PSEUDO_TAG_ENTRY;
-
-typedef struct _HEAP_COUNTERS
-{
- ULONG TotalMemoryReserved;
- ULONG TotalMemoryCommitted;
- ULONG TotalMemoryLargeUCR;
- ULONG TotalSizeInVirtualBlocks;
- ULONG TotalSegments;
- ULONG TotalUCRs;
- ULONG CommittOps;
- ULONG DeCommitOps;
- ULONG LockAcquires;
- ULONG LockCollisions;
- ULONG CommitRate;
- ULONG DecommittRate;
- ULONG CommitFailures;
- ULONG InBlockCommitFailures;
- ULONG CompactHeapCalls;
- ULONG CompactedUCRs;
- ULONG InBlockDeccommits;
- ULONG InBlockDeccomitSize;
-} HEAP_COUNTERS, *PHEAP_COUNTERS;
-
-typedef struct _HEAP_TUNING_PARAMETERS
-{
- ULONG CommittThresholdShift;
- ULONG MaxPreCommittThreshold;
-} HEAP_TUNING_PARAMETERS, *PHEAP_TUNING_PARAMETERS;
-
-typedef struct _HEAP
-{
- HEAP_ENTRY Entry;
- ULONG SegmentSignature;
- ULONG SegmentFlags;
- LIST_ENTRY SegmentListEntry;
- struct _HEAP *Heap;
- PVOID BaseAddress;
- ULONG NumberOfPages;
- PHEAP_ENTRY FirstEntry;
- PHEAP_ENTRY LastValidEntry;
- ULONG NumberOfUnCommittedPages;
- ULONG NumberOfUnCommittedRanges;
- USHORT SegmentAllocatorBackTraceIndex;
- USHORT Reserved;
- LIST_ENTRY UCRSegmentList;
- ULONG Flags;
- ULONG ForceFlags;
- ULONG CompatibilityFlags;
- ULONG EncodeFlagMask;
- HEAP_ENTRY Encoding;
- ULONG PointerKey;
- ULONG Interceptor;
- ULONG VirtualMemoryThreshold;
- ULONG Signature;
- ULONG SegmentReserve;
- ULONG SegmentCommit;
- ULONG DeCommitFreeBlockThreshold;
- ULONG DeCommitTotalFreeThreshold;
- ULONG TotalFreeSize;
- ULONG MaximumAllocationSize;
- USHORT ProcessHeapsListIndex;
- USHORT HeaderValidateLength;
- PVOID HeaderValidateCopy;
- USHORT NextAvailableTagIndex;
- USHORT MaximumTagIndex;
- PHEAP_TAG_ENTRY TagEntries;
- LIST_ENTRY UCRList;
- ULONG AlignRound;
- ULONG AlignMask;
- LIST_ENTRY VirtualAllocdBlocks;
- LIST_ENTRY SegmentList;
- struct _HEAP_SEGMENT *Segments[HEAP_SEGMENTS]; //FIXME: non-Vista
- USHORT AllocatorBackTraceIndex;
- ULONG NonDedicatedListLength;
- PVOID BlocksIndex;
- PVOID UCRIndex;
- PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries;
- LIST_ENTRY FreeLists[HEAP_FREELISTS]; //FIXME: non-Vista
- union
- {
- ULONG FreeListsInUseUlong[HEAP_FREELISTS / (sizeof(ULONG) * 8)]; //FIXME: non-Vista
- UCHAR FreeListsInUseBytes[HEAP_FREELISTS / (sizeof(UCHAR) * 8)]; //FIXME: non-Vista
- } u;
- PHEAP_LOCK LockVariable;
- PRTL_HEAP_COMMIT_ROUTINE CommitRoutine;
- PVOID FrontEndHeap;
- USHORT FrontHeapLockCount;
- UCHAR FrontEndHeapType;
- HEAP_COUNTERS Counters;
- HEAP_TUNING_PARAMETERS TuningParameters;
-} HEAP, *PHEAP;
-
-typedef struct _HEAP_SEGMENT
-{
- HEAP_ENTRY Entry;
- ULONG SegmentSignature;
- ULONG SegmentFlags;
- LIST_ENTRY SegmentListEntry;
- PHEAP Heap;
- PVOID BaseAddress;
- ULONG NumberOfPages;
- PHEAP_ENTRY FirstEntry;
- PHEAP_ENTRY LastValidEntry;
- ULONG NumberOfUnCommittedPages;
- ULONG NumberOfUnCommittedRanges;
- USHORT SegmentAllocatorBackTraceIndex;
- USHORT Reserved;
- LIST_ENTRY UCRSegmentList;
- PHEAP_ENTRY LastEntryInSegment; //FIXME: non-Vista
-} HEAP_SEGMENT, *PHEAP_SEGMENT;
-
-typedef struct _HEAP_UCR_DESCRIPTOR
-{
- LIST_ENTRY ListEntry;
- LIST_ENTRY SegmentEntry;
- PVOID Address;
- ULONG Size;
-} HEAP_UCR_DESCRIPTOR, *PHEAP_UCR_DESCRIPTOR;
-
-typedef struct _HEAP_ENTRY_EXTRA
-{
- union
- {
- struct
- {
- USHORT AllocatorBackTraceIndex;
- USHORT TagIndex;
- ULONG Settable;
- };
- UINT64 ZeroInit;
- };
-} HEAP_ENTRY_EXTRA, *PHEAP_ENTRY_EXTRA;
-
-typedef HEAP_ENTRY_EXTRA HEAP_FREE_ENTRY_EXTRA, *PHEAP_FREE_ENTRY_EXTRA;
-
-typedef struct _HEAP_VIRTUAL_ALLOC_ENTRY
-{
- LIST_ENTRY Entry;
- HEAP_ENTRY_EXTRA ExtraStuff;
- ULONG CommitSize;
- ULONG ReserveSize;
- HEAP_ENTRY BusyBlock;
-} HEAP_VIRTUAL_ALLOC_ENTRY, *PHEAP_VIRTUAL_ALLOC_ENTRY;
-
-extern BOOLEAN RtlpPageHeapEnabled;
-HANDLE NTAPI
-RtlpSpecialHeapCreate(ULONG Flags,
- PVOID Addr,
- SIZE_T TotalSize,
- SIZE_T CommitSize,
- PVOID Lock,
- PRTL_HEAP_PARAMETERS Parameters) { return NULL; };
-
HEAP_LOCK RtlpProcessHeapsListLock;
-PHEAP RtlpProcessHeaps[HEAP_MAX_PROCESS_HEAPS]; /* Usermode only */
-
-/* Heap entry flags */
-#define HEAP_ENTRY_BUSY 0x01
-#define HEAP_ENTRY_EXTRA_PRESENT 0x02
-#define HEAP_ENTRY_FILL_PATTERN 0x04
-#define HEAP_ENTRY_VIRTUAL_ALLOC 0x08
-#define HEAP_ENTRY_LAST_ENTRY 0x10
-#define HEAP_ENTRY_SETTABLE_FLAG1 0x20
-#define HEAP_ENTRY_SETTABLE_FLAG2 0x40
-#define HEAP_ENTRY_SETTABLE_FLAG3 0x80
-#define HEAP_ENTRY_SETTABLE_FLAGS (HEAP_ENTRY_SETTABLE_FLAG1 | HEAP_ENTRY_SETTABLE_FLAG2 | HEAP_ENTRY_SETTABLE_FLAG3)
-
-/* Signatures */
-#define HEAP_SIGNATURE 0xeefeeff
-#define HEAP_SEGMENT_SIGNATURE 0xffeeffee
-
-/* Segment flags */
-#define HEAP_USER_ALLOCATED 0x1
/* Bitmaps stuff */
}
}
-ULONG NTAPI
-RtlCompareMemoryUlong(PVOID Source, ULONG Length, ULONG Value);
+/* Maximum size of a tail-filling pattern used for compare operation */
+UCHAR FillPattern[HEAP_ENTRY_SIZE] =
+{
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL,
+ HEAP_TAIL_FILL
+};
-PHEAP_FREE_ENTRY NTAPI
-RtlpCoalesceFreeBlocks (PHEAP Heap,
- PHEAP_FREE_ENTRY FreeEntry,
- PSIZE_T FreeSize,
- BOOLEAN Remove);
-PHEAP_ENTRY_EXTRA NTAPI
-RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry);
+ULONG NTAPI
+RtlCompareMemoryUlong(PVOID Source, ULONG Length, ULONG Value);
/* FUNCTIONS *****************************************************************/
if (Segment->SegmentFlags & HEAP_USER_ALLOCATED) return;
BaseAddress = Segment->BaseAddress;
- DPRINT1("Destroying segment %p, BA %p\n", Segment, BaseAddress);
+ DPRINT("Destroying segment %p, BA %p\n", Segment, BaseAddress);
/* Release virtual memory */
Status = ZwFreeVirtualMemory(NtCurrentProcess(),
Use classic, lernt from university times algorithm for removing an entry
from a static array */
- Current = (PHEAP *)&Peb->ProcessHeaps[Heap->ProcessHeapsListIndex - 1];
- Next = Current + 1;
+ Current = (PHEAP *)&Peb->ProcessHeaps[Heap->ProcessHeapsListIndex - 1];
+ Next = Current + 1;
- /* How many items we need to shift to the left */
- Count = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
+ /* How many items we need to shift to the left */
+ Count = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
- /* Move them all in a loop */
- while (--Count)
- {
- /* Copy it and advance next pointer */
- *Current = *Next;
+ /* Move them all in a loop */
+ while (--Count)
+ {
+ /* Copy it and advance next pointer */
+ *Current = *Next;
- /* Update its index */
- (*Current)->ProcessHeapsListIndex -= 1;
+ /* Update its index */
+ (*Current)->ProcessHeapsListIndex -= 1;
- /* Advance pointers */
- Current++;
- Next++;
- }
+ /* Advance pointers */
+ Current++;
+ Next++;
+ }
- /* Decrease total number of heaps */
- Peb->NumberOfHeaps--;
+ /* Decrease total number of heaps */
+ Peb->NumberOfHeaps--;
- /* Zero last unused item */
- Peb->ProcessHeaps[Peb->NumberOfHeaps] = NULL;
- Heap->ProcessHeapsListIndex = 0;
+ /* Zero last unused item */
+ Peb->ProcessHeaps[Peb->NumberOfHeaps] = NULL;
+ Heap->ProcessHeapsListIndex = 0;
/* Release the lock */
RtlLeaveHeapLock(&RtlpProcessHeapsListLock);
/* Check for a special heap */
if (RtlpPageHeapEnabled && !Addr && !Lock)
{
- Heap = RtlpSpecialHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
+ Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
if (Heap) return Heap;
//ASSERT(FALSE);
if (NtGlobalFlags & FLG_HEAP_ENABLE_TAIL_CHECK)
Flags |= HEAP_TAIL_CHECKING_ENABLED;
- if (Flags & HEAP_TAIL_CHECKING_ENABLED)
- DPRINT1("TailChecking!\n");
-
if (RtlpGetMode() == UserMode)
{
/* Also check these flags if in usermode */
TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE);
}
+ if (RtlpGetMode() == UserMode)
+ {
+ /* TODO: Here should be a call to special "Debug" heap, which does parameters validation,
+ however we're just going to simulate setting correct flags here */
+ if (Flags & (HEAP_VALIDATE_ALL_ENABLED |
+ HEAP_VALIDATE_PARAMETERS_ENABLED |
+ HEAP_CAPTURE_STACK_BACKTRACES |
+ HEAP_FLAG_PAGE_ALLOCS |
+ HEAP_CREATE_ENABLE_TRACING) &&
+ !(Flags & HEAP_SKIP_VALIDATION_CHECKS))
+ {
+ // RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
+ Flags |= HEAP_SKIP_VALIDATION_CHECKS |
+ HEAP_TAIL_CHECKING_ENABLED |
+ HEAP_FREE_CHECKING_ENABLED;
+ }
+ }
+
/* Calculate header size */
HeaderSize = sizeof(HEAP);
if (!(Flags & HEAP_NO_SERIALIZE))
/* Zero the initial page ourselves */
RtlZeroMemory(CommittedAddress, PAGE_SIZE);
}
+ else
{
/* Commit routine is absent, so query how much memory caller reserved */
Status = ZwQueryVirtualMemory(NtCurrentProcess(),
// FIXME: What about lookasides?
}
+ DPRINT("Heap %p, flags 0x%08x\n", Heap, Heap->Flags);
return Heap;
}
{
VirtualEntry = CONTAINING_RECORD(Current, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
BaseAddress = (PVOID)VirtualEntry;
+ Current = Current->Flink;
Size = 0;
ZwFreeVirtualMemory(NtCurrentProcess(),
&BaseAddress,
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
+ DPRINT1("HEAP: Allocation failed!\n");
+ DPRINT1("Flags %x\n", Heap->Flags);
return NULL;
}
if (Size >= 0x80000000)
{
RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
- return FALSE;
+ DPRINT1("HEAP: Allocation failed!\n");
+ return NULL;
}
if (Flags & (
DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags);
}
- if (Flags & HEAP_TAIL_CHECKING_ENABLED)
- DPRINT1("TailChecking, Heap %p\n!", Heap);
+ //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
/* Calculate allocation size and index */
if (Size)
// Set STATUS!
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
+ DPRINT1("HEAP: Allocation failed!\n");
return NULL;
}
/* Release the lock */
if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
+ DPRINT1("HEAP: Allocation failed!\n");
return NULL;
}
if (!NT_SUCCESS(Status))
{
- DPRINT1("Failed releasing memory with Status 0x%08X\n", Status);
+ DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
+ Status, Heap, Ptr, VirtualEntry);
RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
}
}
IN SIZE_T Size,
IN SIZE_T Index)
{
- /* We always fail growing in place now */
- return FALSE;
+ UCHAR EntryFlags, RememberFlags;
+ PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry;
+ SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0;
+ PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
+
+ /* We can't grow beyond specified threshold */
+ if (Index > Heap->VirtualMemoryThreshold)
+ return FALSE;
+
+ /* Get entry flags */
+ EntryFlags = InUseEntry->Flags;
+
+ /* Get the next free entry */
+ FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size);
+
+ if (EntryFlags & HEAP_ENTRY_LAST_ENTRY)
+ {
+ /* There is no next block, just uncommitted space. Calculate how much is needed */
+ FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT;
+ FreeSize = ROUND_UP(FreeSize, PAGE_SIZE);
+
+ /* Find and commit those pages */
+ FreeEntry = RtlpFindAndCommitPages(Heap,
+ Heap->Segments[InUseEntry->SegmentOffset],
+ &FreeSize,
+ FreeEntry);
+
+ /* Fail if it failed... */
+ if (!FreeEntry) return FALSE;
+
+ /* It was successful, perform coalescing */
+ FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
+ FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
+
+ /* Check if it's enough */
+ if (FreeSize + InUseEntry->Size < Index)
+ {
+ /* Still not enough */
+ RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
+ Heap->TotalFreeSize += FreeSize;
+ return FALSE;
+ }
+
+ /* Remember flags of this free entry */
+ RememberFlags = FreeEntry->Flags;
+
+ /* Sum up sizes */
+ FreeSize += InUseEntry->Size;
+ }
+ else
+ {
+ /* The next block indeed exists. Check if it's free or in use */
+ if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE;
+
+ /* Next entry is free, check if it can fit the block we need */
+ FreeSize = InUseEntry->Size + FreeEntry->Size;
+ if (FreeSize < Index) return FALSE;
+
+ /* Remember flags of this free entry */
+ RememberFlags = FreeEntry->Flags;
+
+ /* Remove this block from the free list */
+ RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
+ Heap->TotalFreeSize -= FreeEntry->Size;
+ }
+
+ PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
+ FreeSize -= Index;
+
+ /* Don't produce too small blocks */
+ if (FreeSize <= 2)
+ {
+ Index += FreeSize;
+ FreeSize = 0;
+ }
+
+ /* Process extra stuff */
+ if (RememberFlags & HEAP_ENTRY_EXTRA_PRESENT)
+ {
+ /* Calculate pointers */
+ OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
+ NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
+
+ /* Copy contents */
+ *NewExtra = *OldExtra;
+
+ // FIXME Tagging
+ }
+
+ /* Update sizes */
+ InUseEntry->Size = Index;
+ InUseEntry->UnusedBytes = ((Index << HEAP_ENTRY_SHIFT) - Size);
+
+ /* Check if there is a free space remaining after merging those blocks */
+ if (!FreeSize)
+ {
+ /* Update flags and sizes */
+ InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY;
+
+ /* Either update previous size of the next entry or mark it as a last
+ entry in the segment*/
+ if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
+ Heap->Segments[InUseEntry->SegmentOffset]->LastEntryInSegment = InUseEntry;
+ else
+ (InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size;
+ }
+ else
+ {
+ /* Complex case, we need to split the block to give unused free space
+ back to the heap */
+ UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
+ UnusedEntry->PreviousSize = Index;
+ UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset;
+
+ /* Update the following block or set the last entry in the segment */
+ if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
+ {
+ /* Set last entry and set flags and size */
+ Heap->Segments[InUseEntry->SegmentOffset]->LastEntryInSegment = InUseEntry;
+ UnusedEntry->Flags = RememberFlags;
+ UnusedEntry->Size = FreeSize;
+
+ /* Insert it to the heap and update total size */
+ RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else
+ {
+ /* There is a block after this one */
+ FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize);
+
+ if (FollowingEntry->Flags & HEAP_ENTRY_BUSY)
+ {
+ /* Update flags and set size of the unused space entry */
+ UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY);
+ UnusedEntry->Size = FreeSize;
+
+ /* Update previous size of the following entry */
+ FollowingEntry->PreviousSize = FreeSize;
+
+ /* Insert it to the heap and update total free size */
+ RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else
+ {
+ /* That following entry is also free, what a fortune! */
+ RememberFlags = FollowingEntry->Flags;
+
+ /* Remove it */
+ RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE, FALSE);
+ Heap->TotalFreeSize -= FollowingEntry->Size;
+
+ /* And make up a new combined block */
+ FreeSize += FollowingEntry->Size;
+ UnusedEntry->Flags = RememberFlags;
+
+ /* Check where to put it */
+ if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
+ {
+ /* Fine for a dedicated list */
+ UnusedEntry->Size = FreeSize;
+
+ if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
+ Heap->Segments[UnusedEntry->SegmentOffset]->LastEntryInSegment = (PHEAP_ENTRY)UnusedEntry;
+ else
+ ((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = FreeSize;
+
+ /* Insert it back and update total size */
+ RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else
+ {
+ /* The block is very large, leave all the hassle to the insertion routine */
+ RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize);
+ }
+ }
+ }
+ }
+
+ /* Copy user settable flags */
+ InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
+ InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
+
+ /* Properly "zero out" (and fill!) the space */
+ if (Flags & HEAP_ZERO_MEMORY)
+ {
+ RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize);
+ }
+ else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
+ {
+ /* Calculate tail part which we need to fill */
+ TailPart = PrevSize & (sizeof(ULONG) - 1);
+
+ /* "Invert" it as usual */
+ if (TailPart) TailPart = 4 - TailPart;
+
+ if (Size > (PrevSize + TailPart))
+ AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1);
+
+ if (AddedSize)
+ {
+ RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart,
+ AddedSize,
+ ARENA_INUSE_FILLER);
+ }
+ }
+
+ /* Fill the new tail */
+ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
+ {
+ RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
+ HEAP_ENTRY_SIZE,
+ HEAP_TAIL_FILL);
+ }
+
+ /* Return success */
+ return TRUE;
}
PHEAP_ENTRY_EXTRA NTAPI
}
/* Calculate allocation size and index */
- if (!Size) Size = 1;
- AllocationSize = (Size + Heap->AlignRound) & Heap->AlignMask;
+ if (Size)
+ AllocationSize = Size;
+ else
+ AllocationSize = 1;
+ AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
/* Add up extra stuff, if it is present anywhere */
if (((((PHEAP_ENTRY)Ptr)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT) ||
Flags |= HEAP_SETTABLE_USER_VALUE | ((InUseEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
- UNIMPLEMENTED;
+ /* Get pointer to the old extra data */
+ OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
+
+ /* Save tag index if it was set */
+ if (OldExtra->TagIndex &&
+ !(OldExtra->TagIndex & HEAP_PSEUDO_TAG_FLAG))
+ {
+ Flags |= OldExtra->TagIndex << HEAP_TAG_SHIFT;
+ }
}
else if (InUseEntry->SmallTagIndex)
{
/* Check if it's really a heap */
if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
- /* Lock if it's lockable */
+ /* Unlock if it's lockable */
if (!(Heap->Flags & HEAP_NO_SERIALIZE))
{
RtlLeaveHeapLock(Heap->LockVariable);
return EntrySize;
}
+BOOLEAN NTAPI
+RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry)
+{
+ SIZE_T Size, Result;
+ PCHAR TailPart;
+
+ /* Calculate size */
+ if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
+ Size = RtlpGetSizeOfBigBlock(HeapEntry);
+ else
+ Size = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
+
+ /* Calculate pointer to the tail part of the block */
+ TailPart = (PCHAR)(HeapEntry + 1) + Size;
+
+ /* Compare tail pattern */
+ Result = RtlCompareMemory(TailPart,
+ FillPattern,
+ HEAP_ENTRY_SIZE);
+
+ if (Result != HEAP_ENTRY_SIZE)
+ {
+ DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size, HeapEntry, TailPart + Result);
+ return FALSE;
+ }
+
+ /* All is fine */
+ return TRUE;
+}
+
+BOOLEAN NTAPI
+RtlpValidateHeapHeaders(
+ PHEAP Heap,
+ BOOLEAN Recalculate)
+{
+ // We skip header validation for now
+ return TRUE;
+}
+
+BOOLEAN NTAPI
+RtlpValidateHeapEntry(
+ PHEAP Heap,
+ PHEAP_ENTRY HeapEntry)
+{
+ BOOLEAN BigAllocation, EntryFound = FALSE;
+ PHEAP_SEGMENT Segment;
+ ULONG SegmentOffset;
+
+ /* Perform various consistency checks of this entry */
+ if (!HeapEntry) goto invalid_entry;
+ if ((ULONG_PTR)HeapEntry & (HEAP_ENTRY_SIZE - 1)) goto invalid_entry;
+ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) goto invalid_entry;
+
+ BigAllocation = HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC;
+ Segment = Heap->Segments[HeapEntry->SegmentOffset];
+
+ if (BigAllocation &&
+ (((ULONG_PTR)HeapEntry & (PAGE_SIZE - 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock)))
+ goto invalid_entry;
+
+ if (!BigAllocation && (HeapEntry->SegmentOffset >= HEAP_SEGMENTS ||
+ !Segment ||
+ HeapEntry < Segment->FirstEntry ||
+ HeapEntry >= Segment->LastValidEntry))
+ goto invalid_entry;
+
+ if ((HeapEntry->Flags & HEAP_ENTRY_FILL_PATTERN) &&
+ !RtlpCheckInUsePattern(HeapEntry))
+ goto invalid_entry;
+
+ /* Checks are done, if this is a virtual entry, that's all */
+ if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) return TRUE;
+
+ /* Go through segments and check if this entry fits into any of them */
+ for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
+ {
+ Segment = Heap->Segments[SegmentOffset];
+ if (!Segment) continue;
+
+ if ((HeapEntry >= Segment->FirstEntry) &&
+ (HeapEntry < Segment->LastValidEntry))
+ {
+ /* Got it */
+ EntryFound = TRUE;
+ break;
+ }
+ }
+
+ /* Return our result of finding entry in the segments */
+ return EntryFound;
+
+invalid_entry:
+ DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry, Heap);
+ return FALSE;
+}
+
+BOOLEAN NTAPI
+RtlpValidateHeapSegment(
+ PHEAP Heap,
+ PHEAP_SEGMENT Segment,
+ UCHAR SegmentOffset,
+ PULONG FreeEntriesCount,
+ PSIZE_T TotalFreeSize,
+ PSIZE_T TagEntries,
+ PSIZE_T PseudoTagEntries)
+{
+ PHEAP_UCR_DESCRIPTOR UcrDescriptor;
+ PLIST_ENTRY UcrEntry;
+ SIZE_T ByteSize, Size, Result;
+ PHEAP_ENTRY CurrentEntry;
+ ULONG UnCommittedPages;
+ ULONG UnCommittedRanges;
+ ULONG PreviousSize;
+
+ UnCommittedPages = 0;
+ UnCommittedRanges = 0;
+
+ if (IsListEmpty(&Segment->UCRSegmentList))
+ {
+ UcrEntry = NULL;
+ UcrDescriptor = NULL;
+ }
+ else
+ {
+ UcrEntry = Segment->UCRSegmentList.Flink;
+ UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
+ }
+
+ if (Segment->BaseAddress == Heap)
+ CurrentEntry = &Heap->Entry;
+ else
+ CurrentEntry = &Segment->Entry;
+
+ while (CurrentEntry < Segment->LastValidEntry)
+ {
+ if (UcrDescriptor &&
+ ((PVOID)CurrentEntry >= UcrDescriptor->Address))
+ {
+ DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
+ CurrentEntry, UcrDescriptor->Address,
+ (PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
+
+ return FALSE;
+ }
+
+ PreviousSize = 0;
+
+ while (CurrentEntry < Segment->LastValidEntry)
+ {
+ if (PreviousSize != CurrentEntry->PreviousSize)
+ {
+ DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
+ CurrentEntry, CurrentEntry->PreviousSize, PreviousSize);
+
+ return FALSE;
+ }
+
+ PreviousSize = CurrentEntry->Size;
+ Size = CurrentEntry->Size << HEAP_ENTRY_SHIFT;
+
+ if (CurrentEntry->Flags & HEAP_ENTRY_BUSY)
+ {
+ if (TagEntries)
+ {
+ UNIMPLEMENTED;
+ }
+
+ /* Check fill pattern */
+ if (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN)
+ {
+ if (!RtlpCheckInUsePattern(CurrentEntry))
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* The entry is free, increase free entries count and total free size */
+ *FreeEntriesCount = *FreeEntriesCount + 1;
+ *TotalFreeSize += CurrentEntry->Size;
+
+ if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) &&
+ (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
+ {
+ ByteSize = Size - sizeof(HEAP_FREE_ENTRY);
+
+ if ((CurrentEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) &&
+ (ByteSize > sizeof(HEAP_FREE_ENTRY_EXTRA)))
+ {
+ ByteSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
+ }
+
+ Result = RtlCompareMemoryUlong((PCHAR)((PHEAP_FREE_ENTRY)CurrentEntry + 1),
+ ByteSize,
+ ARENA_FREE_FILLER);
+
+ if (Result != ByteSize)
+ {
+ DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
+ CurrentEntry,
+ (PCHAR)(CurrentEntry + 1) + Result);
+
+ return FALSE;
+ }
+ }
+ }
+
+ if (CurrentEntry->SegmentOffset != SegmentOffset)
+ {
+ DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", CurrentEntry, SegmentOffset, CurrentEntry->SegmentOffset);
+ return FALSE;
+ }
+
+ /* Check if it's the last entry */
+ if (CurrentEntry->Flags & HEAP_ENTRY_LAST_ENTRY)
+ {
+ CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
+
+ if (!UcrDescriptor)
+ {
+ /* Check if it's not really the last one */
+ if (CurrentEntry != Segment->LastValidEntry)
+ {
+ DPRINT1("HEAP: Heap entry %p is not last block in segment (%x)\n", CurrentEntry, Segment->LastValidEntry);
+ return FALSE;
+ }
+ }
+ else if (CurrentEntry != UcrDescriptor->Address)
+ {
+ DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
+ CurrentEntry, UcrDescriptor->Address);
+
+ return FALSE;
+ }
+ else
+ {
+ UnCommittedPages += (UcrDescriptor->Size / PAGE_SIZE);
+ UnCommittedRanges++;
+
+ CurrentEntry = (PHEAP_ENTRY)((PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
+
+ /* Go to the next UCR descriptor */
+ UcrEntry = UcrEntry->Flink;
+ if (UcrEntry == &Segment->UCRSegmentList)
+ {
+ UcrEntry = NULL;
+ UcrDescriptor = NULL;
+ }
+ else
+ {
+ UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
+ }
+ }
+
+ break;
+ }
+
+ /* Advance to the next entry */
+ CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
+ }
+ }
+
+ /* Check total numbers of UCP and UCR */
+ if (Segment->NumberOfUnCommittedPages != UnCommittedPages)
+ {
+ DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
+ Segment, Segment->NumberOfUnCommittedPages, UnCommittedPages);
+
+ return FALSE;
+ }
+
+ if (Segment->NumberOfUnCommittedRanges != UnCommittedRanges)
+ {
+ DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
+ Segment, Segment->NumberOfUnCommittedRanges, UnCommittedRanges);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN NTAPI
+RtlpValidateHeap(PHEAP Heap,
+ BOOLEAN ForceValidation)
+{
+ PHEAP_SEGMENT Segment;
+ BOOLEAN EmptyList;
+ UCHAR SegmentOffset;
+ SIZE_T Size, TotalFreeSize;
+ ULONG PreviousSize;
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+ PLIST_ENTRY ListHead, NextEntry;
+ PHEAP_FREE_ENTRY FreeEntry;
+ ULONG FreeBlocksCount, FreeListEntriesCount;
+
+ /* Check headers */
+ if (!RtlpValidateHeapHeaders(Heap, FALSE))
+ return FALSE;
+
+ /* Skip validation if it's not needed */
+ if (!ForceValidation && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED))
+ return TRUE;
+
+ /* Check free lists bitmaps */
+ FreeListEntriesCount = 0;
+ ListHead = &Heap->FreeLists[0];
+
+ for (Size = 0; Size < HEAP_FREELISTS; Size++)
+ {
+ if (Size)
+ {
+ /* This is a dedicated list. Check if it's empty */
+ EmptyList = IsListEmpty(ListHead);
+
+ if (Heap->u.FreeListsInUseBytes[Size >> 3] & (1 << (Size & 7)))
+ {
+ if (EmptyList)
+ {
+ DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!EmptyList)
+ {
+ DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size);
+ return FALSE;
+ }
+ }
+ }
+
+ /* Now check this list entries */
+ NextEntry = ListHead->Flink;
+ PreviousSize = 0;
+
+ while (ListHead != NextEntry)
+ {
+ FreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList);
+ NextEntry = NextEntry->Flink;
+
+ /* If there is an in-use entry in a free list - that's quite a big problem */
+ if (FreeEntry->Flags & HEAP_ENTRY_BUSY)
+ {
+ DPRINT1("HEAP: %x-dedicated list free element %x is marked in-use\n", Size, FreeEntry);
+ return FALSE;
+ }
+
+ /* Check sizes according to that specific list's size */
+ if ((Size == 0) && (FreeEntry->Size < HEAP_FREELISTS))
+ {
+ DPRINT1("HEAP: Non dedicated list free element %x has size %x which would fit a dedicated list\n", FreeEntry, FreeEntry->Size);
+ return FALSE;
+ }
+ else if (Size && (FreeEntry->Size != Size))
+ {
+ DPRINT1("HEAP: %x-dedicated list free element %x has incorrect size %x\n", Size, FreeEntry, FreeEntry->Size);
+ return FALSE;
+ }
+ else if ((Size == 0) && (FreeEntry->Size < PreviousSize))
+ {
+ DPRINT1("HEAP: Non dedicated list free element %x is not put in order\n", FreeEntry);
+ return FALSE;
+ }
+
+ /* Remember previous size*/
+ PreviousSize = FreeEntry->Size;
+
+ /* Add up to the total amount of free entries */
+ FreeListEntriesCount++;
+ }
+
+ /* Go to the head of the next free list */
+ ListHead++;
+ }
+
+ /* Check big allocations */
+ ListHead = &Heap->VirtualAllocdBlocks;
+ NextEntry = ListHead->Flink;
+
+ while (ListHead != NextEntry)
+ {
+ VirtualAllocBlock = CONTAINING_RECORD(NextEntry, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
+
+ /* We can only check the fill pattern */
+ if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN)
+ {
+ if (!RtlpCheckInUsePattern(&VirtualAllocBlock->BusyBlock))
+ return FALSE;
+ }
+
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Check all segments */
+ FreeBlocksCount = 0;
+ TotalFreeSize = 0;
+
+ for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
+ {
+ Segment = Heap->Segments[SegmentOffset];
+
+ /* Go to the next one if there is no segment */
+ if (!Segment) continue;
+
+ if (!RtlpValidateHeapSegment(Heap,
+ Segment,
+ SegmentOffset,
+ &FreeBlocksCount,
+ &TotalFreeSize,
+ NULL,
+ NULL))
+ {
+ return FALSE;
+ }
+ }
+
+ if (FreeListEntriesCount != FreeBlocksCount)
+ {
+ DPRINT1("HEAP: Free blocks count in arena (%d) does not match free blocks number in the free lists (%d)\n", FreeBlocksCount, FreeListEntriesCount);
+ return FALSE;
+ }
+
+ if (Heap->TotalFreeSize != TotalFreeSize)
+ {
+ DPRINT1("HEAP: Total size of free blocks in arena (%d) does not equal to the one in heap header (%d)\n", TotalFreeSize, Heap->TotalFreeSize);
+ return FALSE;
+ }
+
+ return TRUE;
+}
/***********************************************************************
* RtlValidateHeap
* @implemented
*/
BOOLEAN NTAPI RtlValidateHeap(
- HANDLE Heap,
+ HANDLE HeapPtr,
ULONG Flags,
PVOID Block
)
{
- UNIMPLEMENTED;
+ PHEAP Heap = (PHEAP)HeapPtr;
+ BOOLEAN HeapLocked = FALSE;
+ BOOLEAN HeapValid;
- /* Imitate success */
- return TRUE;
+ // FIXME Check for special heap
+
+ /* Check signature */
+ if (Heap->Signature != HEAP_SIGNATURE)
+ {
+ DPRINT1("HEAP: Signature %x is invalid for heap %p\n", Heap->Signature, Heap);
+ return FALSE;
+ }
+
+ /* Force flags */
+ Flags = Heap->ForceFlags;
+
+ /* Acquire the lock if necessary */
+ if (!(Flags & HEAP_NO_SERIALIZE))
+ {
+ RtlEnterHeapLock(Heap->LockVariable);
+ HeapLocked = TRUE;
+ }
+
+ /* Either validate whole heap or just one entry */
+ if (!Block)
+ HeapValid = RtlpValidateHeap(Heap, TRUE);
+ else
+ HeapValid = RtlpValidateHeapEntry(Heap, (PHEAP_ENTRY)Block - 1);
+
+ /* Unlock if it's lockable */
+ if (HeapLocked)
+ {
+ RtlLeaveHeapLock(Heap->LockVariable);
+ }
+
+ return HeapValid;
}
VOID
/* Initialize heap-related fields of PEB */
Peb->NumberOfHeaps = 0;
- Peb->MaximumNumberOfHeaps = HEAP_MAX_PROCESS_HEAPS;
- Peb->ProcessHeaps = (PVOID)RtlpProcessHeaps;
/* Initialize the process heaps list protecting lock */
RtlInitializeHeapLock(&RtlpProcessHeapsListLock);
IN PVOID BaseAddress,
IN PVOID UserValue)
{
- UNIMPLEMENTED;
- return FALSE;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY HeapEntry;
+ PHEAP_ENTRY_EXTRA Extra;
+ BOOLEAN HeapLocked = FALSE;
+
+ /* Force flags */
+ Flags |= Heap->Flags;
+
+ /* Lock if it's lockable */
+ if (!(Heap->Flags & HEAP_NO_SERIALIZE))
+ {
+ RtlEnterHeapLock(Heap->LockVariable);
+ HeapLocked = TRUE;
+ }
+
+ /* Get a pointer to the entry */
+ HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
+
+ /* If it's a free entry - return error */
+ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
+ {
+ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
+
+ /* Release the heap lock if it was acquired */
+ if (HeapLocked)
+ RtlLeaveHeapLock(Heap->LockVariable);
+
+ return FALSE;
+ }
+
+ /* Check if this entry has an extra stuff associated with it */
+ if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
+ {
+ /* Use extra to store the value */
+ Extra = RtlpGetExtraStuffPointer(HeapEntry);
+ Extra->Settable = (ULONG_PTR)UserValue;
+ }
+
+ /* Release the heap lock if it was acquired */
+ if (HeapLocked)
+ RtlLeaveHeapLock(Heap->LockVariable);
+
+ return TRUE;
}
/*
RtlSetUserFlagsHeap(IN PVOID HeapHandle,
IN ULONG Flags,
IN PVOID BaseAddress,
- IN ULONG UserFlags)
+ IN ULONG UserFlagsReset,
+ IN ULONG UserFlagsSet)
{
- return FALSE;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY HeapEntry;
+ BOOLEAN HeapLocked = FALSE;
+
+ /* Force flags */
+ Flags |= Heap->Flags;
+
+ /* Lock if it's lockable */
+ if (!(Heap->Flags & HEAP_NO_SERIALIZE))
+ {
+ RtlEnterHeapLock(Heap->LockVariable);
+ HeapLocked = TRUE;
+ }
+
+ /* Get a pointer to the entry */
+ HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
+
+ /* If it's a free entry - return error */
+ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
+ {
+ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
+
+ /* Release the heap lock if it was acquired */
+ if (HeapLocked)
+ RtlLeaveHeapLock(Heap->LockVariable);
+
+ return FALSE;
+ }
+
+ /* Set / reset flags */
+ HeapEntry->Flags &= ~(UserFlagsReset >> 4);
+ HeapEntry->Flags |= (UserFlagsSet >> 4);
+
+ /* Release the heap lock if it was acquired */
+ if (HeapLocked)
+ RtlLeaveHeapLock(Heap->LockVariable);
+
+ return TRUE;
}
/*
OUT PVOID *UserValue,
OUT PULONG UserFlags)
{
- UNIMPLEMENTED;
- return FALSE;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY HeapEntry;
+ PHEAP_ENTRY_EXTRA Extra;
+ BOOLEAN HeapLocked = FALSE;
+
+ /* Force flags */
+ Flags |= Heap->Flags;
+
+ /* Lock if it's lockable */
+ if (!(Heap->Flags & HEAP_NO_SERIALIZE))
+ {
+ RtlEnterHeapLock(Heap->LockVariable);
+ HeapLocked = TRUE;
+ }
+
+ /* Get a pointer to the entry */
+ HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
+
+ /* If it's a free entry - return error */
+ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
+ {
+ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
+
+ /* Release the heap lock if it was acquired */
+ if (HeapLocked)
+ RtlLeaveHeapLock(Heap->LockVariable);
+
+ return FALSE;
+ }
+
+ /* Check if this entry has an extra stuff associated with it */
+ if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
+ {
+ /* Get pointer to extra data */
+ Extra = RtlpGetExtraStuffPointer(HeapEntry);
+
+ /* Pass user value */
+ if (UserValue)
+ *UserValue = (PVOID)Extra->Settable;
+
+ /* Decode and return user flags */
+ if (UserFlags)
+ *UserFlags = (HeapEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
+ }
+
+ /* Release the heap lock if it was acquired */
+ if (HeapLocked)
+ RtlLeaveHeapLock(Heap->LockVariable);
+
+ return TRUE;
}
/*
return NULL;
}
-DWORD
+NTSTATUS
NTAPI
RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
IN HEAP_INFORMATION_CLASS HeapInformationClass,
IN PVOID HeapInformation,
IN SIZE_T HeapInformationLength)
{
- UNIMPLEMENTED;
- return 0;
+ /* Setting heap information is not really supported except for enabling LFH */
+ if (HeapInformationClass == 0) return STATUS_SUCCESS;
+
+ /* Check buffer length */
+ if (HeapInformationLength < sizeof(ULONG))
+ {
+ /* The provided buffer is too small */
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* Check for a special magic value for enabling LFH */
+ if (*(PULONG)HeapInformation == 2)
+ {
+ DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_UNSUCCESSFUL;
}
-DWORD
+NTSTATUS
NTAPI
RtlQueryHeapInformation(HANDLE HeapHandle,
HEAP_INFORMATION_CLASS HeapInformationClass,
SIZE_T HeapInformationLength OPTIONAL,
PSIZE_T ReturnLength OPTIONAL)
{
- UNIMPLEMENTED;
- return 0;
+ PHEAP Heap = (PHEAP)HeapHandle;
+
+ /* Only HeapCompatibilityInformation is supported */
+ if (HeapInformationClass != HeapCompatibilityInformation)
+ return STATUS_UNSUCCESSFUL;
+
+ /* Set result length */
+ if (ReturnLength) *ReturnLength = sizeof(ULONG);
+
+ /* Check buffer length */
+ if (HeapInformationLength < sizeof(ULONG))
+ {
+ /* It's too small, return needed length */
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* Return front end heap type */
+ *(PULONG)HeapInformation = Heap->FrontEndHeapType;
+
+ return STATUS_SUCCESS;
}
-DWORD
+NTSTATUS
NTAPI
RtlMultipleAllocateHeap(IN PVOID HeapHandle,
- IN DWORD Flags,
+ IN ULONG Flags,
IN SIZE_T Size,
- IN DWORD Count,
+ IN ULONG Count,
OUT PVOID *Array)
{
UNIMPLEMENTED;
return 0;
}
-DWORD
+NTSTATUS
NTAPI
RtlMultipleFreeHeap(IN PVOID HeapHandle,
- IN DWORD Flags,
- IN DWORD Count,
+ IN ULONG Flags,
+ IN ULONG Count,
OUT PVOID *Array)
{
UNIMPLEMENTED;