[HEAP]
[reactos.git] / reactos / lib / rtl / heap_rewrite.c
index dbe7785..fd3bf04 100644 (file)
    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_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;
 
-/* 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 */
 
 /* How many least significant bits are clear */
@@ -331,17 +67,22 @@ RtlpFindLeastSetBit(ULONG Bits)
     }
 }
 
-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 *****************************************************************/
 
@@ -1153,7 +894,7 @@ RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment)
     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(),
@@ -1228,32 +969,32 @@ RtlpRemoveHeapFromProcessList(PHEAP Heap)
        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);
@@ -1545,7 +1286,7 @@ RtlCreateHeap(ULONG Flags,
     /* 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);
@@ -2250,6 +1991,8 @@ RtlpAllocateNonDedicated(PHEAP Heap,
 
     /* Release the lock */
     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
+    DPRINT1("HEAP: Allocation failed!\n");
+    DPRINT1("Flags %x\n", Heap->Flags);
     return NULL;
 }
 
@@ -2289,7 +2032,8 @@ RtlAllocateHeap(IN PVOID HeapPtr,
     if (Size >= 0x80000000)
     {
         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
-        return FALSE;
+        DPRINT1("HEAP: Allocation failed!\n");
+        return NULL;
     }
 
     if (Flags & (
@@ -2440,6 +2184,7 @@ RtlAllocateHeap(IN PVOID HeapPtr,
             // Set STATUS!
             /* Release the lock */
             if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
+            DPRINT1("HEAP: Allocation failed!\n");
             return NULL;
         }
 
@@ -2475,6 +2220,7 @@ RtlAllocateHeap(IN PVOID HeapPtr,
 
     /* Release the lock */
     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
+    DPRINT1("HEAP: Allocation failed!\n");
     return NULL;
 }
 
@@ -2550,7 +2296,8 @@ BOOLEAN NTAPI RtlFreeHeap(
 
         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);
         }
     }
@@ -2624,8 +2371,225 @@ RtlpGrowBlockInPlace (IN PHEAP Heap,
                       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
@@ -2983,7 +2947,15 @@ RtlReAllocateHeap(HANDLE HeapPtr,
 
                     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)
                 {
@@ -3131,7 +3103,7 @@ RtlUnlockHeap(HANDLE HeapPtr)
     /* 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);
@@ -3202,6 +3174,437 @@ RtlSizeHeap(
     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
@@ -3222,15 +3625,47 @@ RtlSizeHeap(
  * @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
@@ -3307,8 +3742,49 @@ RtlSetUserValueHeap(IN PVOID HeapHandle,
                     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;
 }
 
 /*
@@ -3319,9 +3795,47 @@ NTAPI
 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;
 }
 
 /*
@@ -3335,8 +3849,56 @@ RtlGetUserInfoHeap(IN PVOID HeapHandle,
                    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;
 }
 
 /*
@@ -3408,18 +3970,34 @@ RtlProtectHeap(IN PVOID HeapHandle,
     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,
@@ -3427,27 +4005,45 @@ RtlQueryHeapInformation(HANDLE HeapHandle,
                         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;