Doxygen

heap.c
Go to the documentation of this file.
00001 /* COPYRIGHT:       See COPYING in the top level directory
00002  * PROJECT:         ReactOS system libraries
00003  * FILE:            lib/rtl/heap.c
00004  * PURPOSE:         RTL Heap backend allocator
00005  * PROGRAMMERS:     Copyright 2010 Aleksey Bragin
00006  */
00007 
00008 /* Useful references:
00009    http://msdn.microsoft.com/en-us/library/ms810466.aspx
00010    http://msdn.microsoft.com/en-us/library/ms810603.aspx
00011    http://www.securitylab.ru/analytics/216376.php
00012    http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry
00013    http://www.phreedom.org/research/exploits/asn1-bitstring/
00014    http://illmatics.com/Understanding_the_LFH.pdf
00015    http://www.alex-ionescu.com/?p=18
00016 */
00017 
00018 /* INCLUDES *****************************************************************/
00019 
00020 #include <rtl.h>
00021 #include <heap.h>
00022 
00023 #define NDEBUG
00024 #include <debug.h>
00025 
00026 /* Bitmaps stuff */
00027 
00028 /* How many least significant bits are clear */
00029 UCHAR RtlpBitsClearLow[] =
00030 {
00031     8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00032     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00033     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00034     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00035     6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00036     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00037     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00038     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00039     7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00040     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00041     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00042     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00043     6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00044     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00045     5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
00046     4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
00047 };
00048 
00049 UCHAR FORCEINLINE
00050 RtlpFindLeastSetBit(ULONG Bits)
00051 {
00052     if (Bits & 0xFFFF)
00053     {
00054         if (Bits & 0xFF)
00055             return RtlpBitsClearLow[Bits & 0xFF]; /* Lowest byte */
00056         else
00057             return RtlpBitsClearLow[(Bits >> 8) & 0xFF] + 8; /* 2nd byte */
00058     }
00059     else
00060     {
00061         if ((Bits >> 16) & 0xFF)
00062             return RtlpBitsClearLow[(Bits >> 16) & 0xFF] + 16; /* 3rd byte */
00063         else
00064             return RtlpBitsClearLow[(Bits >> 24) & 0xFF] + 24; /* Highest byte */
00065     }
00066 }
00067 
00068 /* Maximum size of a tail-filling pattern used for compare operation */
00069 UCHAR FillPattern[HEAP_ENTRY_SIZE] =
00070 {
00071     HEAP_TAIL_FILL,
00072     HEAP_TAIL_FILL,
00073     HEAP_TAIL_FILL,
00074     HEAP_TAIL_FILL,
00075     HEAP_TAIL_FILL,
00076     HEAP_TAIL_FILL,
00077     HEAP_TAIL_FILL,
00078     HEAP_TAIL_FILL
00079 };
00080 
00081 /* FUNCTIONS *****************************************************************/
00082 
00083 NTSTATUS NTAPI
00084 RtlpInitializeHeap(OUT PHEAP Heap,
00085                    IN ULONG Flags,
00086                    IN PHEAP_LOCK Lock OPTIONAL,
00087                    IN PRTL_HEAP_PARAMETERS Parameters)
00088 {
00089     ULONG NumUCRs = 8;
00090     ULONG Index;
00091     SIZE_T HeaderSize;
00092     NTSTATUS Status;
00093     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
00094 
00095     /* Preconditions */
00096     ASSERT(Heap != NULL);
00097     ASSERT(Parameters != NULL);
00098     ASSERT(!(Flags & HEAP_LOCK_USER_ALLOCATED));
00099     ASSERT(!(Flags & HEAP_NO_SERIALIZE) || (Lock == NULL));  /* HEAP_NO_SERIALIZE => no lock */
00100 
00101     /* Start out with the size of a plain Heap header */
00102     HeaderSize = ROUND_UP(sizeof(HEAP), sizeof(HEAP_ENTRY));
00103 
00104     /* Check if space needs to be added for the Heap Lock */
00105     if (!(Flags & HEAP_NO_SERIALIZE))
00106     {
00107         if (Lock != NULL)
00108             /* The user manages the Heap Lock */
00109             Flags |= HEAP_LOCK_USER_ALLOCATED;
00110         else
00111         if (RtlpGetMode() == UserMode)
00112         {
00113             /* In user mode, the Heap Lock trails the Heap header */
00114             Lock = (PHEAP_LOCK) ((ULONG_PTR) (Heap) + HeaderSize);
00115             HeaderSize += ROUND_UP(sizeof(HEAP_LOCK), sizeof(HEAP_ENTRY));
00116         }
00117     }
00118 
00119     /* Add space for the initial Heap UnCommitted Range Descriptor list */
00120     UcrDescriptor = (PHEAP_UCR_DESCRIPTOR) ((ULONG_PTR) (Heap) + HeaderSize);
00121     HeaderSize += ROUND_UP(NumUCRs * sizeof(HEAP_UCR_DESCRIPTOR), sizeof(HEAP_ENTRY));
00122 
00123     /* Sanity check */
00124     ASSERT(HeaderSize <= PAGE_SIZE);
00125 
00126     /* Initialise the Heap Entry header containing the Heap header */
00127     Heap->Entry.Size = (USHORT)(HeaderSize >> HEAP_ENTRY_SHIFT);
00128     Heap->Entry.Flags = HEAP_ENTRY_BUSY;
00129     Heap->Entry.SmallTagIndex = LOBYTE(Heap->Entry.Size) ^ HIBYTE(Heap->Entry.Size) ^ Heap->Entry.Flags;
00130     Heap->Entry.PreviousSize = 0;
00131     Heap->Entry.SegmentOffset = 0;
00132     Heap->Entry.UnusedBytes = 0;
00133 
00134     /* Initialise the Heap header */
00135     Heap->Signature = HEAP_SIGNATURE;
00136     Heap->Flags = Flags;
00137     Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE |
00138                                  HEAP_GENERATE_EXCEPTIONS |
00139                                  HEAP_ZERO_MEMORY |
00140                                  HEAP_REALLOC_IN_PLACE_ONLY |
00141                                  HEAP_VALIDATE_PARAMETERS_ENABLED |
00142                                  HEAP_VALIDATE_ALL_ENABLED |
00143                                  HEAP_TAIL_CHECKING_ENABLED |
00144                                  HEAP_CREATE_ALIGN_16 |
00145                                  HEAP_FREE_CHECKING_ENABLED));
00146 
00147     /* Initialise the Heap parameters */
00148     Heap->VirtualMemoryThreshold = ROUND_UP(Parameters->VirtualMemoryThreshold, sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT;
00149     Heap->SegmentReserve = Parameters->SegmentReserve;
00150     Heap->SegmentCommit = Parameters->SegmentCommit;
00151     Heap->DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_ENTRY_SHIFT;
00152     Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_ENTRY_SHIFT;
00153     Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize;
00154     Heap->CommitRoutine = Parameters->CommitRoutine;
00155 
00156     /* Initialise the Heap validation info */
00157     Heap->HeaderValidateCopy = NULL;
00158     Heap->HeaderValidateLength = (USHORT)HeaderSize;
00159 
00160     /* Initialise the Heap Lock */
00161     if (!(Flags & HEAP_NO_SERIALIZE) && !(Flags & HEAP_LOCK_USER_ALLOCATED))
00162     {
00163         Status = RtlInitializeHeapLock(&Lock);
00164         if (!NT_SUCCESS(Status))
00165             return Status;
00166     }
00167     Heap->LockVariable = Lock;
00168 
00169     /* Initialise the Heap alignment info */
00170     if (Flags & HEAP_CREATE_ALIGN_16)
00171     {
00172         Heap->AlignMask = (ULONG) ~15;
00173         Heap->AlignRound = 15 + sizeof(HEAP_ENTRY);
00174     }
00175     else
00176     {
00177         Heap->AlignMask = (ULONG) ~(sizeof(HEAP_ENTRY) - 1);
00178         Heap->AlignRound = 2 * sizeof(HEAP_ENTRY) - 1;
00179     }
00180 
00181     if (Flags & HEAP_TAIL_CHECKING_ENABLED)
00182         Heap->AlignRound += sizeof(HEAP_ENTRY);
00183 
00184     /* Initialise the Heap Segment list */
00185     for (Index = 0; Index < HEAP_SEGMENTS; ++Index)
00186         Heap->Segments[Index] = NULL;
00187 
00188     /* Initialise the Heap Free Heap Entry lists */
00189     for (Index = 0; Index < HEAP_FREELISTS; ++Index)
00190         InitializeListHead(&Heap->FreeLists[Index]);
00191 
00192     /* Initialise the Heap Virtual Allocated Blocks list */
00193     InitializeListHead(&Heap->VirtualAllocdBlocks);
00194 
00195     /* Initialise the Heap UnCommitted Region lists */
00196     InitializeListHead(&Heap->UCRSegments);
00197     InitializeListHead(&Heap->UCRList);
00198 
00199     /* Register the initial Heap UnCommitted Region Descriptors */
00200     for (Index = 0; Index < NumUCRs; ++Index)
00201         InsertTailList(&Heap->UCRList, &UcrDescriptor[Index].ListEntry);
00202 
00203     return STATUS_SUCCESS;
00204 }
00205 
00206 VOID FORCEINLINE
00207 RtlpSetFreeListsBit(PHEAP Heap,
00208                     PHEAP_FREE_ENTRY FreeEntry)
00209 {
00210     ULONG Index, Bit;
00211 
00212     ASSERT(FreeEntry->Size < HEAP_FREELISTS);
00213 
00214     /* Calculate offset in the free list bitmap */
00215     Index = FreeEntry->Size >> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
00216     Bit = 1 << (FreeEntry->Size & 7);
00217 
00218     /* Assure it's not already set */
00219     ASSERT((Heap->u.FreeListsInUseBytes[Index] & Bit) == 0);
00220 
00221     /* Set it */
00222     Heap->u.FreeListsInUseBytes[Index] |= Bit;
00223 }
00224 
00225 VOID FORCEINLINE
00226 RtlpClearFreeListsBit(PHEAP Heap,
00227                       PHEAP_FREE_ENTRY FreeEntry)
00228 {
00229     ULONG Index, Bit;
00230 
00231     ASSERT(FreeEntry->Size < HEAP_FREELISTS);
00232 
00233     /* Calculate offset in the free list bitmap */
00234     Index = FreeEntry->Size >> 3; /* = FreeEntry->Size / (sizeof(UCHAR) * 8)*/
00235     Bit = 1 << (FreeEntry->Size & 7);
00236 
00237     /* Assure it was set and the corresponding free list is empty */
00238     ASSERT(Heap->u.FreeListsInUseBytes[Index] & Bit);
00239     ASSERT(IsListEmpty(&Heap->FreeLists[FreeEntry->Size]));
00240 
00241     /* Clear it */
00242     Heap->u.FreeListsInUseBytes[Index] ^= Bit;
00243 }
00244 
00245 VOID NTAPI
00246 RtlpInsertFreeBlockHelper(PHEAP Heap,
00247                           PHEAP_FREE_ENTRY FreeEntry,
00248                           SIZE_T BlockSize,
00249                           BOOLEAN NoFill)
00250 {
00251     PLIST_ENTRY FreeListHead, Current;
00252     PHEAP_FREE_ENTRY CurrentEntry;
00253 
00254     ASSERT(FreeEntry->Size == BlockSize);
00255 
00256     /* Fill if it's not denied */
00257     if (!NoFill)
00258     {
00259         FreeEntry->Flags &= ~(HEAP_ENTRY_FILL_PATTERN |
00260                               HEAP_ENTRY_EXTRA_PRESENT |
00261                               HEAP_ENTRY_BUSY);
00262 
00263         if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
00264         {
00265             RtlFillMemoryUlong((PCHAR)(FreeEntry + 1),
00266                                (BlockSize << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry),
00267                                ARENA_FREE_FILLER);
00268 
00269             FreeEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
00270         }
00271     }
00272     else
00273     {
00274         /* Clear out all flags except the last entry one */
00275         FreeEntry->Flags &= HEAP_ENTRY_LAST_ENTRY;
00276     }
00277 
00278     /* Insert it either into dedicated or non-dedicated list */
00279     if (BlockSize < HEAP_FREELISTS)
00280     {
00281         /* Dedicated list */
00282         FreeListHead = &Heap->FreeLists[BlockSize];
00283 
00284         if (IsListEmpty(FreeListHead))
00285         {
00286             RtlpSetFreeListsBit(Heap, FreeEntry);
00287         }
00288     }
00289     else
00290     {
00291         /* Non-dedicated one */
00292         FreeListHead = &Heap->FreeLists[0];
00293         Current = FreeListHead->Flink;
00294 
00295         /* Find a position where to insert it to (the list must be sorted) */
00296         while (FreeListHead != Current)
00297         {
00298             CurrentEntry = CONTAINING_RECORD(Current, HEAP_FREE_ENTRY, FreeList);
00299 
00300             if (BlockSize <= CurrentEntry->Size)
00301                 break;
00302 
00303             Current = Current->Flink;
00304         }
00305 
00306         FreeListHead = Current;
00307     }
00308 
00309     /* Actually insert it into the list */
00310     InsertTailList(FreeListHead, &FreeEntry->FreeList);
00311 }
00312 
00313 VOID NTAPI
00314 RtlpInsertFreeBlock(PHEAP Heap,
00315                     PHEAP_FREE_ENTRY FreeEntry,
00316                     SIZE_T BlockSize)
00317 {
00318     USHORT Size, PreviousSize;
00319     UCHAR SegmentOffset, Flags;
00320     PHEAP_SEGMENT Segment;
00321 
00322     DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap, FreeEntry, BlockSize);
00323 
00324     /* Increase the free size counter */
00325     Heap->TotalFreeSize += BlockSize;
00326 
00327     /* Remember certain values */
00328     Flags = FreeEntry->Flags;
00329     PreviousSize = FreeEntry->PreviousSize;
00330     SegmentOffset = FreeEntry->SegmentOffset;
00331     Segment = Heap->Segments[SegmentOffset];
00332 
00333     /* Process it */
00334     while (BlockSize)
00335     {
00336         /* Check for the max size */
00337         if (BlockSize > HEAP_MAX_BLOCK_SIZE)
00338         {
00339             Size = HEAP_MAX_BLOCK_SIZE;
00340 
00341             /* Special compensation if it goes above limit just by 1 */
00342             if (BlockSize == (HEAP_MAX_BLOCK_SIZE + 1))
00343                 Size -= 16;
00344 
00345             FreeEntry->Flags = 0;
00346         }
00347         else
00348         {
00349             Size = (USHORT)BlockSize;
00350             FreeEntry->Flags = Flags;
00351         }
00352 
00353         /* Change its size and insert it into a free list */
00354         FreeEntry->Size = Size;
00355         FreeEntry->PreviousSize = PreviousSize;
00356         FreeEntry->SegmentOffset = SegmentOffset;
00357 
00358         /* Call a helper to actually insert the block */
00359         RtlpInsertFreeBlockHelper(Heap, FreeEntry, Size, FALSE);
00360 
00361         /* Update sizes */
00362         PreviousSize = Size;
00363         BlockSize -= Size;
00364 
00365         /* Go to the next entry */
00366         FreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
00367 
00368         /* Check if that's all */
00369         if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry) return;
00370     }
00371 
00372     /* Update previous size if needed */
00373     if (!(Flags & HEAP_ENTRY_LAST_ENTRY))
00374         FreeEntry->PreviousSize = PreviousSize;
00375 }
00376 
00377 VOID NTAPI
00378 RtlpRemoveFreeBlock(PHEAP Heap,
00379                     PHEAP_FREE_ENTRY FreeEntry,
00380                     BOOLEAN Dedicated,
00381                     BOOLEAN NoFill)
00382 {
00383     SIZE_T Result, RealSize;
00384 
00385     /* Remove the free block and update the freelists bitmap */
00386     if (RemoveEntryList(&FreeEntry->FreeList) &&
00387         (Dedicated || (!Dedicated && FreeEntry->Size < HEAP_FREELISTS)))
00388     {
00389         RtlpClearFreeListsBit(Heap, FreeEntry);
00390     }
00391 
00392     /* Fill with pattern if necessary */
00393     if (!NoFill &&
00394         (FreeEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
00395     {
00396         RealSize = (FreeEntry->Size << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry);
00397 
00398         /* Deduct extra stuff from block's real size */
00399         if (FreeEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT &&
00400             RealSize > sizeof(HEAP_FREE_ENTRY_EXTRA))
00401         {
00402             RealSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
00403         }
00404 
00405         /* Check if the free filler is intact */
00406         Result = RtlCompareMemoryUlong((PCHAR)(FreeEntry + 1),
00407                                         RealSize,
00408                                         ARENA_FREE_FILLER);
00409 
00410         if (Result != RealSize)
00411         {
00412             DPRINT1("Free heap block %p modified at %p after it was freed\n",
00413                 FreeEntry,
00414                 (PCHAR)(FreeEntry + 1) + Result);
00415         }
00416     }
00417 }
00418 
00419 SIZE_T NTAPI
00420 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry)
00421 {
00422     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
00423 
00424     /* Get pointer to the containing record */
00425     VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
00426 
00427     /* Restore the real size */
00428     return VirtualEntry->CommitSize - HeapEntry->Size;
00429 }
00430 
00431 PHEAP_UCR_DESCRIPTOR NTAPI
00432 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment)
00433 {
00434     PLIST_ENTRY Entry;
00435     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
00436     PHEAP_UCR_SEGMENT UcrSegment;
00437     PHEAP Heap = Segment->Heap;
00438     SIZE_T ReserveSize = 16 * PAGE_SIZE;
00439     SIZE_T CommitSize = 1 * PAGE_SIZE;
00440     NTSTATUS Status;
00441 
00442     DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment);
00443 
00444     /* Check if we have unused UCRs */
00445     if (IsListEmpty(&Heap->UCRList))
00446     {
00447         /* Get a pointer to the first UCR segment */
00448         UcrSegment = CONTAINING_RECORD(Heap->UCRSegments.Flink, HEAP_UCR_SEGMENT, ListEntry);
00449 
00450         /* Check the list of UCR segments */
00451         if (IsListEmpty(&Heap->UCRSegments) ||
00452             UcrSegment->ReservedSize == UcrSegment->CommittedSize)
00453         {
00454             /* We need to create a new one. Reserve 16 pages for it */
00455             UcrSegment = NULL;
00456             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
00457                                              (PVOID *)&UcrSegment,
00458                                              0,
00459                                              &ReserveSize,
00460                                              MEM_RESERVE,
00461                                              PAGE_READWRITE);
00462 
00463             if (!NT_SUCCESS(Status)) return NULL;
00464 
00465             /* Commit one page */
00466             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
00467                                              (PVOID *)&UcrSegment,
00468                                              0,
00469                                              &CommitSize,
00470                                              MEM_COMMIT,
00471                                              PAGE_READWRITE);
00472 
00473             if (!NT_SUCCESS(Status))
00474             {
00475                 /* Release reserved memory */
00476                 ZwFreeVirtualMemory(NtCurrentProcess(),
00477                                     (PVOID *)&UcrSegment,
00478                                     &ReserveSize,
00479                                     MEM_RELEASE);
00480                 return NULL;
00481             }
00482 
00483             /* Set it's data */
00484             UcrSegment->ReservedSize = ReserveSize;
00485             UcrSegment->CommittedSize = CommitSize;
00486 
00487             /* Add it to the head of the list */
00488             InsertHeadList(&Heap->UCRSegments, &UcrSegment->ListEntry);
00489 
00490             /* Get a pointer to the first available UCR descriptor */
00491             UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)(UcrSegment + 1);
00492         }
00493         else
00494         {
00495             /* It's possible to use existing UCR segment. Commit one more page */
00496             UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)((PCHAR)UcrSegment + UcrSegment->CommittedSize);
00497             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
00498                                              (PVOID *)&UcrDescriptor,
00499                                              0,
00500                                              &CommitSize,
00501                                              MEM_COMMIT,
00502                                              PAGE_READWRITE);
00503 
00504             if (!NT_SUCCESS(Status)) return NULL;
00505 
00506             /* Update sizes */
00507             UcrSegment->CommittedSize += CommitSize;
00508         }
00509 
00510         /* There is a whole bunch of new UCR descriptors. Put them into the unused list */
00511         while ((PCHAR)(UcrDescriptor + 1) <= (PCHAR)UcrSegment + UcrSegment->CommittedSize)
00512         {
00513             InsertTailList(&Heap->UCRList, &UcrDescriptor->ListEntry);
00514             UcrDescriptor++;
00515         }
00516     }
00517 
00518     /* There are unused UCRs, just get the first one */
00519     Entry = RemoveHeadList(&Heap->UCRList);
00520     UcrDescriptor = CONTAINING_RECORD(Entry, HEAP_UCR_DESCRIPTOR, ListEntry);
00521     return UcrDescriptor;
00522 }
00523 
00524 VOID NTAPI
00525 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment,
00526                             PHEAP_UCR_DESCRIPTOR UcrDescriptor)
00527 {
00528     /* Zero it out */
00529     UcrDescriptor->Address = NULL;
00530     UcrDescriptor->Size = 0;
00531 
00532     /* Put it into the heap's list of unused UCRs */
00533     InsertHeadList(&Segment->Heap->UCRList, &UcrDescriptor->ListEntry);
00534 }
00535 
00536 VOID NTAPI
00537 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment,
00538                            ULONG_PTR Address,
00539                            SIZE_T Size)
00540 {
00541     PLIST_ENTRY Current;
00542     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
00543 
00544     DPRINT("RtlpInsertUnCommittedPages(%p %08Ix %Ix)\n", Segment, Address, Size);
00545 
00546     /* Go through the list of UCR descriptors, they are sorted from lowest address
00547        to the highest */
00548     Current = Segment->UCRSegmentList.Flink;
00549     while (Current != &Segment->UCRSegmentList)
00550     {
00551         UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
00552 
00553         if ((ULONG_PTR)UcrDescriptor->Address > Address)
00554         {
00555             /* Check for a really lucky case */
00556             if ((Address + Size) == (ULONG_PTR)UcrDescriptor->Address)
00557             {
00558                 /* Exact match */
00559                 UcrDescriptor->Address = (PVOID)Address;
00560                 UcrDescriptor->Size += Size;
00561                 return;
00562             }
00563 
00564             /* We found the block before which the new one should go */
00565             break;
00566         }
00567         else if (((ULONG_PTR)UcrDescriptor->Address + UcrDescriptor->Size) == Address)
00568         {
00569             /* Modify this entry */
00570             Address = (ULONG_PTR)UcrDescriptor->Address;
00571             Size += UcrDescriptor->Size;
00572 
00573             /* Advance to the next descriptor */
00574             Current = Current->Flink;
00575 
00576             /* Remove the current descriptor from the list and destroy it */
00577             RemoveEntryList(&UcrDescriptor->SegmentEntry);
00578             RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
00579 
00580             Segment->NumberOfUnCommittedRanges--;
00581         }
00582         else
00583         {
00584             /* Advance to the next descriptor */
00585             Current = Current->Flink;
00586         }
00587     }
00588 
00589     /* Create a new UCR descriptor */
00590     UcrDescriptor = RtlpCreateUnCommittedRange(Segment);
00591     if (!UcrDescriptor) return;
00592 
00593     UcrDescriptor->Address = (PVOID)Address;
00594     UcrDescriptor->Size = Size;
00595 
00596     /* "Current" is the descriptor before which our one should go */
00597     InsertTailList(Current, &UcrDescriptor->SegmentEntry);
00598 
00599     DPRINT("Added segment UCR with base %08Ix, size 0x%x\n", Address, Size);
00600 
00601     /* Increase counters */
00602     Segment->NumberOfUnCommittedRanges++;
00603 }
00604 
00605 PHEAP_FREE_ENTRY NTAPI
00606 RtlpFindAndCommitPages(PHEAP Heap,
00607                        PHEAP_SEGMENT Segment,
00608                        PSIZE_T Size,
00609                        PVOID AddressRequested)
00610 {
00611     PLIST_ENTRY Current;
00612     ULONG_PTR Address = 0;
00613     PHEAP_UCR_DESCRIPTOR UcrDescriptor, PreviousUcr = NULL;
00614     PHEAP_ENTRY FirstEntry, LastEntry;
00615     NTSTATUS Status;
00616 
00617     DPRINT("RtlpFindAndCommitPages(%p %p %Ix %08Ix)\n", Heap, Segment, *Size, Address);
00618 
00619     /* Go through UCRs in a segment */
00620     Current = Segment->UCRSegmentList.Flink;
00621     while (Current != &Segment->UCRSegmentList)
00622     {
00623         UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
00624 
00625         /* Check if we can use that one right away */
00626         if (UcrDescriptor->Size >= *Size &&
00627             (UcrDescriptor->Address == AddressRequested || !AddressRequested))
00628         {
00629             /* Get the address */
00630             Address = (ULONG_PTR)UcrDescriptor->Address;
00631 
00632             /* Commit it */
00633             if (Heap->CommitRoutine)
00634             {
00635                 Status = Heap->CommitRoutine(Heap, (PVOID *)&Address, Size);
00636             }
00637             else
00638             {
00639                 Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
00640                                                  (PVOID *)&Address,
00641                                                  0,
00642                                                  Size,
00643                                                  MEM_COMMIT,
00644                                                  PAGE_READWRITE);
00645             }
00646 
00647             DPRINT("Committed %Iu bytes at base %08Ix, UCR size is %lu\n", *Size, Address, UcrDescriptor->Size);
00648 
00649             /* Fail in unsuccessful case */
00650             if (!NT_SUCCESS(Status))
00651             {
00652                 DPRINT1("Committing page failed with status 0x%08X\n", Status);
00653                 return NULL;
00654             }
00655 
00656             /* Update tracking numbers */
00657             Segment->NumberOfUnCommittedPages -= (ULONG)(*Size / PAGE_SIZE);
00658 
00659             /* Calculate first and last entries */
00660             FirstEntry = (PHEAP_ENTRY)Address;
00661 
00662             /* Go through the entries to find the last one */
00663             if (PreviousUcr)
00664                 LastEntry = (PHEAP_ENTRY)((ULONG_PTR)PreviousUcr->Address + PreviousUcr->Size);
00665             else
00666                 LastEntry = &Segment->Entry;
00667 
00668             while (!(LastEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
00669             {
00670                 ASSERT(LastEntry->Size != 0);
00671                 LastEntry += LastEntry->Size;
00672             }
00673             ASSERT((LastEntry + LastEntry->Size) == FirstEntry);
00674 
00675             /* Unmark it as a last entry */
00676             LastEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
00677 
00678             /* Update UCR descriptor */
00679             UcrDescriptor->Address = (PVOID)((ULONG_PTR)UcrDescriptor->Address + *Size);
00680             UcrDescriptor->Size -= *Size;
00681 
00682             DPRINT("Updating UcrDescriptor %p, new Address %p, size %lu\n",
00683                 UcrDescriptor, UcrDescriptor->Address, UcrDescriptor->Size);
00684 
00685             /* Set various first entry fields */
00686             FirstEntry->SegmentOffset = LastEntry->SegmentOffset;
00687             FirstEntry->Size = (USHORT)(*Size >> HEAP_ENTRY_SHIFT);
00688             FirstEntry->PreviousSize = LastEntry->Size;
00689 
00690             /* Check if anything left in this UCR */
00691             if (UcrDescriptor->Size == 0)
00692             {
00693                 /* It's fully exhausted */
00694 
00695                 /* Check if this is the end of the segment */
00696                 if(UcrDescriptor->Address == Segment->LastValidEntry)
00697                 {
00698                     FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
00699                 }
00700                 else
00701                 {
00702                     FirstEntry->Flags = 0;
00703                     /* Update field of next entry */
00704                     ASSERT((FirstEntry + FirstEntry->Size)->PreviousSize == 0);
00705                     (FirstEntry + FirstEntry->Size)->PreviousSize = FirstEntry->Size;
00706                 }
00707 
00708                 /* This UCR needs to be removed because it became useless */
00709                 RemoveEntryList(&UcrDescriptor->SegmentEntry);
00710 
00711                 RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
00712                 Segment->NumberOfUnCommittedRanges--;
00713             }
00714             else
00715             {
00716                 FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
00717             }
00718 
00719             /* We're done */
00720             return (PHEAP_FREE_ENTRY)FirstEntry;
00721         }
00722 
00723         /* Advance to the next descriptor */
00724         PreviousUcr = UcrDescriptor;
00725         Current = Current->Flink;
00726     }
00727 
00728     return NULL;
00729 }
00730 
00731 VOID NTAPI
00732 RtlpDeCommitFreeBlock(PHEAP Heap,
00733                       PHEAP_FREE_ENTRY FreeEntry,
00734                       SIZE_T Size)
00735 {
00736     PHEAP_SEGMENT Segment;
00737     PHEAP_ENTRY PrecedingInUseEntry = NULL, NextInUseEntry = NULL;
00738     PHEAP_FREE_ENTRY NextFreeEntry;
00739     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
00740     SIZE_T PrecedingSize, NextSize, DecommitSize;
00741     ULONG_PTR DecommitBase;
00742     NTSTATUS Status;
00743 
00744     DPRINT("Decommitting %p %p %x\n", Heap, FreeEntry, Size);
00745 
00746     /* We can't decommit if there is a commit routine! */
00747     if (Heap->CommitRoutine)
00748     {
00749         /* Just add it back the usual way */
00750         RtlpInsertFreeBlock(Heap, FreeEntry, Size);
00751         return;
00752     }
00753 
00754     /* Get the segment */
00755     Segment = Heap->Segments[FreeEntry->SegmentOffset];
00756 
00757     /* Get the preceding entry */
00758     DecommitBase = ROUND_UP(FreeEntry, PAGE_SIZE);
00759     PrecedingSize = (PHEAP_ENTRY)DecommitBase - (PHEAP_ENTRY)FreeEntry;
00760 
00761     if (PrecedingSize == 1)
00762     {
00763         /* Just 1 heap entry, increase the base/size */
00764         DecommitBase += PAGE_SIZE;
00765         PrecedingSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT;
00766     }
00767     else if (FreeEntry->PreviousSize &&
00768              (DecommitBase == (ULONG_PTR)FreeEntry))
00769     {
00770         PrecedingInUseEntry = (PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize;
00771     }
00772 
00773     /* Get the next entry */
00774     NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
00775     DecommitSize = ROUND_DOWN(NextFreeEntry, PAGE_SIZE);
00776     NextSize = (PHEAP_ENTRY)NextFreeEntry - (PHEAP_ENTRY)DecommitSize;
00777 
00778     if (NextSize == 1)
00779     {
00780         /* Just 1 heap entry, increase the size */
00781         DecommitSize -= PAGE_SIZE;
00782         NextSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT;
00783     }
00784     else if (NextSize == 0 &&
00785              !(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
00786     {
00787         NextInUseEntry = (PHEAP_ENTRY)NextFreeEntry;
00788     }
00789 
00790     NextFreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry - NextSize);
00791 
00792     /* Calculate real decommit size */
00793     if (DecommitSize > DecommitBase)
00794     {
00795         DecommitSize -= DecommitBase;
00796     }
00797     else
00798     {
00799         /* Nothing to decommit */
00800         RtlpInsertFreeBlock(Heap, FreeEntry, Size);
00801         return;
00802     }
00803 
00804     /* A decommit is necessary. Create a UCR descriptor */
00805     UcrDescriptor = RtlpCreateUnCommittedRange(Segment);
00806     if (!UcrDescriptor)
00807     {
00808         DPRINT1("HEAP: Failed to create UCR descriptor\n");
00809         RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize);
00810         return;
00811     }
00812 
00813     /* Decommit the memory */
00814     Status = ZwFreeVirtualMemory(NtCurrentProcess(),
00815                                  (PVOID *)&DecommitBase,
00816                                  &DecommitSize,
00817                                  MEM_DECOMMIT);
00818 
00819     /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */
00820     RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
00821 
00822     if (!NT_SUCCESS(Status))
00823     {
00824         RtlpInsertFreeBlock(Heap, FreeEntry, Size);
00825         return;
00826     }
00827 
00828     /* Insert uncommitted pages */
00829     RtlpInsertUnCommittedPages(Segment, DecommitBase, DecommitSize);
00830     Segment->NumberOfUnCommittedPages += (ULONG)(DecommitSize / PAGE_SIZE);
00831 
00832     if (PrecedingSize)
00833     {
00834         /* Adjust size of this free entry and insert it */
00835         FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
00836         FreeEntry->Size = (USHORT)PrecedingSize;
00837         Heap->TotalFreeSize += PrecedingSize;
00838 
00839         /* Insert it into the free list */
00840         RtlpInsertFreeBlockHelper(Heap, FreeEntry, PrecedingSize, FALSE);
00841     }
00842     else if (PrecedingInUseEntry)
00843     {
00844         /* Adjust preceding in use entry */
00845         PrecedingInUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
00846     }
00847 
00848     /* Now the next one */
00849     if (NextSize)
00850     {
00851         /* Adjust size of this free entry and insert it */
00852         NextFreeEntry->Flags = 0;
00853         NextFreeEntry->PreviousSize = 0;
00854         NextFreeEntry->SegmentOffset = Segment->Entry.SegmentOffset;
00855         NextFreeEntry->Size = (USHORT)NextSize;
00856 
00857         ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)NextFreeEntry + NextSize))->PreviousSize = (USHORT)NextSize;
00858 
00859         Heap->TotalFreeSize += NextSize;
00860         RtlpInsertFreeBlockHelper(Heap, NextFreeEntry, NextSize, FALSE);
00861     }
00862     else if (NextInUseEntry)
00863     {
00864         NextInUseEntry->PreviousSize = 0;
00865     }
00866 }
00867 
00868 NTSTATUS
00869 NTAPI
00870 RtlpInitializeHeapSegment(IN OUT PHEAP Heap,
00871                           OUT PHEAP_SEGMENT Segment,
00872                           IN UCHAR SegmentIndex,
00873                           IN ULONG SegmentFlags,
00874                           IN SIZE_T SegmentReserve,
00875                           IN SIZE_T SegmentCommit)
00876 {
00877     PHEAP_ENTRY HeapEntry;
00878 
00879     /* Preconditions */
00880     ASSERT(Heap != NULL);
00881     ASSERT(Segment != NULL);
00882     ASSERT(SegmentCommit >= PAGE_SIZE);
00883     ASSERT(ROUND_DOWN(SegmentCommit, PAGE_SIZE) == SegmentCommit);
00884     ASSERT(SegmentReserve >= SegmentCommit);
00885     ASSERT(ROUND_DOWN(SegmentReserve, PAGE_SIZE) == SegmentReserve);
00886 
00887     DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %lx %lx)\n", Heap, Segment, SegmentIndex, SegmentFlags, SegmentReserve, SegmentCommit);
00888 
00889     /* Initialise the Heap Entry header if this is not the first Heap Segment */
00890     if ((PHEAP_SEGMENT) (Heap) != Segment)
00891     {
00892         Segment->Entry.Size = ROUND_UP(sizeof(HEAP_SEGMENT), sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT;
00893         Segment->Entry.Flags = HEAP_ENTRY_BUSY;
00894         Segment->Entry.SmallTagIndex = LOBYTE(Segment->Entry.Size) ^ HIBYTE(Segment->Entry.Size) ^ Segment->Entry.Flags;
00895         Segment->Entry.PreviousSize = 0;
00896         Segment->Entry.SegmentOffset = SegmentIndex;
00897         Segment->Entry.UnusedBytes = 0;
00898     }
00899 
00900     /* Sanity check */
00901     ASSERT((Segment->Entry.Size << HEAP_ENTRY_SHIFT) <= PAGE_SIZE);
00902 
00903     /* Initialise the Heap Segment header */
00904     Segment->SegmentSignature = HEAP_SEGMENT_SIGNATURE;
00905     Segment->SegmentFlags = SegmentFlags;
00906     Segment->Heap = Heap;
00907     Heap->Segments[SegmentIndex] = Segment;
00908 
00909     /* Initialise the Heap Segment location information */
00910     Segment->BaseAddress = Segment;
00911     Segment->NumberOfPages = (ULONG)(SegmentReserve >> PAGE_SHIFT);
00912 
00913     /* Initialise the Heap Entries contained within the Heap Segment */
00914     Segment->FirstEntry = &Segment->Entry + Segment->Entry.Size;
00915     Segment->LastValidEntry = (PHEAP_ENTRY)((ULONG_PTR)Segment + SegmentReserve);
00916 
00917     if (((SIZE_T)Segment->Entry.Size << HEAP_ENTRY_SHIFT) < SegmentCommit)
00918     {
00919         HeapEntry = Segment->FirstEntry;
00920 
00921         /* Prepare a Free Heap Entry header */
00922         HeapEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
00923         HeapEntry->PreviousSize = Segment->Entry.Size;
00924         HeapEntry->SegmentOffset = SegmentIndex;
00925 
00926         /* Register the Free Heap Entry */
00927         RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY) HeapEntry, (SegmentCommit >> HEAP_ENTRY_SHIFT) - Segment->Entry.Size);
00928     }
00929 
00930     /* Initialise the Heap Segment UnCommitted Range information */
00931     Segment->NumberOfUnCommittedPages = (ULONG)((SegmentReserve - SegmentCommit) >> PAGE_SHIFT);
00932     Segment->NumberOfUnCommittedRanges = 0;
00933     InitializeListHead(&Segment->UCRSegmentList);
00934 
00935     /* Register the UnCommitted Range of the Heap Segment */
00936     if (Segment->NumberOfUnCommittedPages != 0)
00937         RtlpInsertUnCommittedPages(Segment, (ULONG_PTR) (Segment) + SegmentCommit, SegmentReserve - SegmentCommit);
00938 
00939     return STATUS_SUCCESS;
00940 }
00941 
00942 VOID NTAPI
00943 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment)
00944 {
00945     NTSTATUS Status;
00946     PVOID BaseAddress;
00947     SIZE_T Size = 0;
00948 
00949     /* Make sure it's not user allocated */
00950     if (Segment->SegmentFlags & HEAP_USER_ALLOCATED) return;
00951 
00952     BaseAddress = Segment->BaseAddress;
00953     DPRINT("Destroying segment %p, BA %p\n", Segment, BaseAddress);
00954 
00955     /* Release virtual memory */
00956     Status = ZwFreeVirtualMemory(NtCurrentProcess(),
00957                                  &BaseAddress,
00958                                  &Size,
00959                                  MEM_RELEASE);
00960 
00961     if (!NT_SUCCESS(Status))
00962     {
00963         DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status);
00964     }
00965 }
00966 
00967 PHEAP_FREE_ENTRY NTAPI
00968 RtlpCoalesceHeap(PHEAP Heap)
00969 {
00970     UNIMPLEMENTED;
00971     return NULL;
00972 }
00973 
00974 PHEAP_FREE_ENTRY NTAPI
00975 RtlpCoalesceFreeBlocks (PHEAP Heap,
00976                         PHEAP_FREE_ENTRY FreeEntry,
00977                         PSIZE_T FreeSize,
00978                         BOOLEAN Remove)
00979 {
00980     PHEAP_FREE_ENTRY CurrentEntry, NextEntry;
00981 
00982     /* Get the previous entry */
00983     CurrentEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize);
00984 
00985     /* Check it */
00986     if (CurrentEntry != FreeEntry &&
00987         !(CurrentEntry->Flags & HEAP_ENTRY_BUSY) &&
00988         (*FreeSize + CurrentEntry->Size) <= HEAP_MAX_BLOCK_SIZE)
00989     {
00990         ASSERT(FreeEntry->PreviousSize == CurrentEntry->Size);
00991 
00992         /* Remove it if asked for */
00993         if (Remove)
00994         {
00995             RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
00996             Heap->TotalFreeSize -= FreeEntry->Size;
00997 
00998             /* Remove it only once! */
00999             Remove = FALSE;
01000         }
01001 
01002         /* Remove previous entry too */
01003         RtlpRemoveFreeBlock(Heap, CurrentEntry, FALSE, FALSE);
01004 
01005         /* Copy flags */
01006         CurrentEntry->Flags = FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
01007 
01008         /* Advance FreeEntry and update sizes */
01009         FreeEntry = CurrentEntry;
01010         *FreeSize = *FreeSize + CurrentEntry->Size;
01011         Heap->TotalFreeSize -= CurrentEntry->Size;
01012         FreeEntry->Size = (USHORT)(*FreeSize);
01013 
01014         /* Also update previous size if needed */
01015         if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
01016         {
01017             ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize);
01018         }
01019     }
01020 
01021     /* Check the next block if it exists */
01022     if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
01023     {
01024         NextEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + *FreeSize);
01025 
01026         if (!(NextEntry->Flags & HEAP_ENTRY_BUSY) &&
01027             NextEntry->Size + *FreeSize <= HEAP_MAX_BLOCK_SIZE)
01028         {
01029             ASSERT(*FreeSize == NextEntry->PreviousSize);
01030 
01031             /* Remove it if asked for */
01032             if (Remove)
01033             {
01034                 RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
01035                 Heap->TotalFreeSize -= FreeEntry->Size;
01036             }
01037 
01038             /* Copy flags */
01039             FreeEntry->Flags = NextEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
01040 
01041             /* Remove next entry now */
01042             RtlpRemoveFreeBlock(Heap, NextEntry, FALSE, FALSE);
01043 
01044             /* Update sizes */
01045             *FreeSize = *FreeSize + NextEntry->Size;
01046             Heap->TotalFreeSize -= NextEntry->Size;
01047             FreeEntry->Size = (USHORT)(*FreeSize);
01048 
01049             /* Also update previous size if needed */
01050             if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
01051             {
01052                 ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize);
01053             }
01054         }
01055     }
01056     return FreeEntry;
01057 }
01058 
01059 PHEAP_FREE_ENTRY NTAPI
01060 RtlpExtendHeap(PHEAP Heap,
01061                SIZE_T Size)
01062 {
01063     ULONG Pages;
01064     UCHAR Index, EmptyIndex;
01065     SIZE_T FreeSize, CommitSize, ReserveSize;
01066     PHEAP_SEGMENT Segment;
01067     PHEAP_FREE_ENTRY FreeEntry;
01068     NTSTATUS Status;
01069 
01070     DPRINT("RtlpExtendHeap(%p %x)\n", Heap, Size);
01071 
01072     /* Calculate amount in pages */
01073     Pages = (ULONG)((Size + PAGE_SIZE - 1) / PAGE_SIZE);
01074     FreeSize = Pages * PAGE_SIZE;
01075     DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages, FreeSize);
01076 
01077     /* Find an empty segment */
01078     EmptyIndex = HEAP_SEGMENTS;
01079     for (Index = 0; Index < HEAP_SEGMENTS; Index++)
01080     {
01081         Segment = Heap->Segments[Index];
01082 
01083         if (Segment) DPRINT("Segment[%u] %p with NOUCP %x\n", Index, Segment, Segment->NumberOfUnCommittedPages);
01084 
01085         /* Check if its size suits us */
01086         if (Segment &&
01087             Pages <= Segment->NumberOfUnCommittedPages)
01088         {
01089             DPRINT("This segment is suitable\n");
01090 
01091             /* Commit needed amount */
01092             FreeEntry = RtlpFindAndCommitPages(Heap, Segment, &FreeSize, NULL);
01093 
01094             /* Coalesce it with adjacent entries */
01095             if (FreeEntry)
01096             {
01097                 FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
01098                 FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
01099                 RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
01100                 return FreeEntry;
01101             }
01102         }
01103         else if (!Segment &&
01104                  EmptyIndex == HEAP_SEGMENTS)
01105         {
01106             /* Remember the first unused segment index */
01107             EmptyIndex = Index;
01108         }
01109     }
01110 
01111     /* No luck, need to grow the heap */
01112     if ((Heap->Flags & HEAP_GROWABLE) &&
01113         (EmptyIndex != HEAP_SEGMENTS))
01114     {
01115         Segment = NULL;
01116 
01117         /* Reserve the memory */
01118         if ((Size + PAGE_SIZE) <= Heap->SegmentReserve)
01119             ReserveSize = Heap->SegmentReserve;
01120         else
01121             ReserveSize = Size + PAGE_SIZE;
01122 
01123         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
01124                                          (PVOID)&Segment,
01125                                          0,
01126                                          &ReserveSize,
01127                                          MEM_RESERVE,
01128                                          PAGE_READWRITE);
01129 
01130         /* If it failed, retry again with a half division algorithm */
01131         while (!NT_SUCCESS(Status) &&
01132             ReserveSize != Size + PAGE_SIZE)
01133         {
01134             ReserveSize /= 2;
01135 
01136             if (ReserveSize < (Size + PAGE_SIZE))
01137                 ReserveSize = Size + PAGE_SIZE;
01138 
01139             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
01140                                              (PVOID)&Segment,
01141                                              0,
01142                                              &ReserveSize,
01143                                              MEM_RESERVE,
01144                                              PAGE_READWRITE);
01145         }
01146 
01147         /* Proceed only if it's success */
01148         if (NT_SUCCESS(Status))
01149         {
01150             Heap->SegmentReserve += ReserveSize;
01151 
01152             /* Now commit the memory */
01153             if ((Size + PAGE_SIZE) <= Heap->SegmentCommit)
01154                 CommitSize = Heap->SegmentCommit;
01155             else
01156                 CommitSize = Size + PAGE_SIZE;
01157 
01158             Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
01159                                              (PVOID)&Segment,
01160                                              0,
01161                                              &CommitSize,
01162                                              MEM_COMMIT,
01163                                              PAGE_READWRITE);
01164 
01165             DPRINT("Committed %lu bytes at base %p\n", CommitSize, Segment);
01166 
01167             /* Initialize heap segment if commit was successful */
01168             if (NT_SUCCESS(Status))
01169                 Status = RtlpInitializeHeapSegment(Heap, Segment, EmptyIndex, 0, ReserveSize, CommitSize);
01170 
01171             /* If everything worked - cool */
01172             if (NT_SUCCESS(Status)) return (PHEAP_FREE_ENTRY)Segment->FirstEntry;
01173 
01174             DPRINT1("Committing failed with status 0x%08X\n", Status);
01175 
01176             /* Nope, we failed. Free memory */
01177             ZwFreeVirtualMemory(NtCurrentProcess(),
01178                                 (PVOID)&Segment,
01179                                 &ReserveSize,
01180                                 MEM_RELEASE);
01181         }
01182         else
01183         {
01184             DPRINT1("Reserving failed with status 0x%08X\n", Status);
01185         }
01186     }
01187 
01188     if (RtlpGetMode() == UserMode)
01189     {
01190         /* If coalescing on free is disabled in usermode, then do it here */
01191         if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)
01192         {
01193             FreeEntry = RtlpCoalesceHeap(Heap);
01194 
01195             /* If it's a suitable one - return it */
01196             if (FreeEntry &&
01197                 FreeEntry->Size >= Size)
01198             {
01199                 return FreeEntry;
01200             }
01201         }
01202     }
01203 
01204     return NULL;
01205 }
01206 
01207 /***********************************************************************
01208  *           RtlCreateHeap
01209  * RETURNS
01210  * Handle of heap: Success
01211  * NULL: Failure
01212  *
01213  * @implemented
01214  */
01215 HANDLE NTAPI
01216 RtlCreateHeap(ULONG Flags,
01217               PVOID Addr,
01218               SIZE_T TotalSize,
01219               SIZE_T CommitSize,
01220               PVOID Lock,
01221               PRTL_HEAP_PARAMETERS Parameters)
01222 {
01223     PVOID CommittedAddress = NULL, UncommittedAddress = NULL;
01224     PHEAP Heap = NULL;
01225     RTL_HEAP_PARAMETERS SafeParams = {0};
01226     PPEB Peb;
01227     ULONG_PTR MaximumUserModeAddress;
01228     SYSTEM_BASIC_INFORMATION SystemInformation;
01229     MEMORY_BASIC_INFORMATION MemoryInfo;
01230     ULONG NtGlobalFlags = RtlGetNtGlobalFlags();
01231     ULONG HeapSegmentFlags = 0;
01232     NTSTATUS Status;
01233     ULONG MaxBlockSize;
01234 
01235     /* Check for a special heap */
01236     if (RtlpPageHeapEnabled && !Addr && !Lock)
01237     {
01238         Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
01239         if (Heap) return Heap;
01240 
01241         /* Reset a special Parameters == -1 hack */
01242         if ((ULONG_PTR)Parameters == (ULONG_PTR)-1)
01243             Parameters = NULL;
01244         else
01245             DPRINT1("Enabling page heap failed\n");
01246     }
01247 
01248     /* Check validation flags */
01249     if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS) && (Flags & ~HEAP_CREATE_VALID_MASK))
01250     {
01251         DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags);
01252         Flags &= HEAP_CREATE_VALID_MASK;
01253     }
01254 
01255     /* TODO: Capture parameters, once we decide to use SEH */
01256     if (!Parameters) Parameters = &SafeParams;
01257 
01258     /* Check global flags */
01259     if (NtGlobalFlags & FLG_HEAP_DISABLE_COALESCING)
01260         Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
01261 
01262     if (NtGlobalFlags & FLG_HEAP_ENABLE_FREE_CHECK)
01263         Flags |= HEAP_FREE_CHECKING_ENABLED;
01264 
01265     if (NtGlobalFlags & FLG_HEAP_ENABLE_TAIL_CHECK)
01266         Flags |= HEAP_TAIL_CHECKING_ENABLED;
01267 
01268     if (RtlpGetMode() == UserMode)
01269     {
01270         /* Also check these flags if in usermode */
01271         if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL)
01272             Flags |= HEAP_VALIDATE_ALL_ENABLED;
01273 
01274         if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS)
01275             Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
01276 
01277         if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB)
01278             Flags |= HEAP_CAPTURE_STACK_BACKTRACES;
01279 
01280         /* Get PEB */
01281         Peb = RtlGetCurrentPeb();
01282 
01283         /* Apply defaults for non-set parameters */
01284         if (!Parameters->SegmentCommit) Parameters->SegmentCommit = Peb->HeapSegmentCommit;
01285         if (!Parameters->SegmentReserve) Parameters->SegmentReserve = Peb->HeapSegmentReserve;
01286         if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = Peb->HeapDeCommitFreeBlockThreshold;
01287         if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = Peb->HeapDeCommitTotalFreeThreshold;
01288     }
01289     else
01290     {
01291         /* Apply defaults for non-set parameters */
01292 #if 0
01293         if (!Parameters->SegmentCommit) Parameters->SegmentCommit = MmHeapSegmentCommit;
01294         if (!Parameters->SegmentReserve) Parameters->SegmentReserve = MmHeapSegmentReserve;
01295         if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
01296         if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
01297 #endif
01298     }
01299 
01300     // FIXME: Move to memory manager
01301         if (!Parameters->SegmentCommit) Parameters->SegmentCommit = PAGE_SIZE * 2;
01302         if (!Parameters->SegmentReserve) Parameters->SegmentReserve = 1048576;
01303         if (!Parameters->DeCommitFreeBlockThreshold) Parameters->DeCommitFreeBlockThreshold = PAGE_SIZE;
01304         if (!Parameters->DeCommitTotalFreeThreshold) Parameters->DeCommitTotalFreeThreshold = 65536;
01305 
01306     /* Get the max um address */
01307     Status = ZwQuerySystemInformation(SystemBasicInformation,
01308                                       &SystemInformation,
01309                                       sizeof(SystemInformation),
01310                                       NULL);
01311 
01312     if (!NT_SUCCESS(Status))
01313     {
01314         DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status);
01315         return NULL;
01316     }
01317 
01318     MaximumUserModeAddress = SystemInformation.MaximumUserModeAddress;
01319 
01320     /* Calculate max alloc size */
01321     if (!Parameters->MaximumAllocationSize)
01322         Parameters->MaximumAllocationSize = MaximumUserModeAddress - (ULONG_PTR)0x10000 - PAGE_SIZE;
01323 
01324     MaxBlockSize = 0x80000 - PAGE_SIZE;
01325 
01326     if (!Parameters->VirtualMemoryThreshold ||
01327         Parameters->VirtualMemoryThreshold > MaxBlockSize)
01328     {
01329         Parameters->VirtualMemoryThreshold = MaxBlockSize;
01330     }
01331 
01332     /* Check reserve/commit sizes and set default values */
01333     if (!CommitSize)
01334     {
01335         CommitSize = PAGE_SIZE;
01336         if (TotalSize)
01337             TotalSize = ROUND_UP(TotalSize, PAGE_SIZE);
01338         else
01339             TotalSize = 64 * PAGE_SIZE;
01340     }
01341     else
01342     {
01343         /* Round up the commit size to be at least the page size */
01344         CommitSize = ROUND_UP(CommitSize, PAGE_SIZE);
01345 
01346         if (TotalSize)
01347             TotalSize = ROUND_UP(TotalSize, PAGE_SIZE);
01348         else
01349             TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE);
01350     }
01351 
01352     /* Call special heap */
01353     if (RtlpHeapIsSpecial(Flags))
01354         return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
01355 
01356     /* Without serialization, a lock makes no sense */
01357     if ((Flags & HEAP_NO_SERIALIZE) && (Lock != NULL))
01358         return NULL;
01359 
01360     /* See if we are already provided with an address for the heap */
01361     if (Addr)
01362     {
01363         if (Parameters->CommitRoutine)
01364         {
01365             /* There is a commit routine, so no problem here, check params */
01366             if ((Flags & HEAP_GROWABLE) ||
01367                 !Parameters->InitialCommit ||
01368                 !Parameters->InitialReserve ||
01369                 (Parameters->InitialCommit > Parameters->InitialReserve))
01370             {
01371                 /* Fail */
01372                 return NULL;
01373             }
01374 
01375             /* Calculate committed and uncommitted addresses */
01376             CommittedAddress = Addr;
01377             UncommittedAddress = (PCHAR)Addr + Parameters->InitialCommit;
01378             TotalSize = Parameters->InitialReserve;
01379 
01380             /* Zero the initial page ourselves */
01381             RtlZeroMemory(CommittedAddress, PAGE_SIZE);
01382         }
01383         else
01384         {
01385             /* Commit routine is absent, so query how much memory caller reserved */
01386             Status = ZwQueryVirtualMemory(NtCurrentProcess(),
01387                                           Addr,
01388                                           MemoryBasicInformation,
01389                                           &MemoryInfo,
01390                                           sizeof(MemoryInfo),
01391                                           NULL);
01392 
01393             if (!NT_SUCCESS(Status))
01394             {
01395                 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status);
01396                 return NULL;
01397             }
01398 
01399             /* Validate it */
01400             if (MemoryInfo.BaseAddress != Addr ||
01401                 MemoryInfo.State == MEM_FREE)
01402             {
01403                 return NULL;
01404             }
01405 
01406             /* Validation checks passed, set committed/uncommitted addresses */
01407             CommittedAddress = Addr;
01408 
01409             /* Check if it's committed or not */
01410             if (MemoryInfo.State == MEM_COMMIT)
01411             {
01412                 /* Zero it out because it's already committed */
01413                 RtlZeroMemory(CommittedAddress, PAGE_SIZE);
01414 
01415                 /* Calculate uncommitted address value */
01416                 CommitSize = MemoryInfo.RegionSize;
01417                 TotalSize = CommitSize;
01418                 UncommittedAddress = (PCHAR)Addr + CommitSize;
01419 
01420                 /* Check if uncommitted address is reserved */
01421                 Status = ZwQueryVirtualMemory(NtCurrentProcess(),
01422                                               UncommittedAddress,
01423                                               MemoryBasicInformation,
01424                                               &MemoryInfo,
01425                                               sizeof(MemoryInfo),
01426                                               NULL);
01427 
01428                 if (NT_SUCCESS(Status) &&
01429                     MemoryInfo.State == MEM_RESERVE)
01430                 {
01431                     /* It is, so add it up to the reserve size */
01432                     TotalSize += MemoryInfo.RegionSize;
01433                 }
01434             }
01435             else
01436             {
01437                 /* It's not committed, inform following code that a commit is necessary */
01438                 CommitSize = PAGE_SIZE;
01439                 UncommittedAddress = Addr;
01440             }
01441         }
01442 
01443         /* Mark this as a user-committed mem */
01444         HeapSegmentFlags = HEAP_USER_ALLOCATED;
01445         Heap = (PHEAP)Addr;
01446     }
01447     else
01448     {
01449         /* Check commit routine */
01450         if (Parameters->CommitRoutine) return NULL;
01451 
01452         /* Reserve memory */
01453         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
01454                                          (PVOID *)&Heap,
01455                                          0,
01456                                          &TotalSize,
01457                                          MEM_RESERVE,
01458                                          PAGE_READWRITE);
01459 
01460         if (!NT_SUCCESS(Status))
01461         {
01462             DPRINT1("Failed to reserve memory with status 0x%08x\n", Status);
01463             return NULL;
01464         }
01465 
01466         /* Set base addresses */
01467         CommittedAddress = Heap;
01468         UncommittedAddress = Heap;
01469     }
01470 
01471     /* Check if we need to commit something */
01472     if (CommittedAddress == UncommittedAddress)
01473     {
01474         /* Commit the required size */
01475         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
01476                                          &CommittedAddress,
01477                                          0,
01478                                          &CommitSize,
01479                                          MEM_COMMIT,
01480                                          PAGE_READWRITE);
01481 
01482         DPRINT("Committed %Iu bytes at base %p\n", CommitSize, CommittedAddress);
01483 
01484         if (!NT_SUCCESS(Status))
01485         {
01486             DPRINT1("Failure, Status 0x%08X\n", Status);
01487 
01488             /* Release memory if it was reserved */
01489             if (!Addr) ZwFreeVirtualMemory(NtCurrentProcess(),
01490                                            (PVOID *)&Heap,
01491                                            &TotalSize,
01492                                            MEM_RELEASE);
01493 
01494             return NULL;
01495         }
01496 
01497         /* Calculate new uncommitted address */
01498         UncommittedAddress = (PCHAR)UncommittedAddress + CommitSize;
01499     }
01500 
01501     /* Initialize the heap */
01502     Status = RtlpInitializeHeap(Heap, Flags, Lock, Parameters);
01503     if (!NT_SUCCESS(Status))
01504     {
01505         DPRINT1("Failed to initialize heap (%x)\n", Status);
01506         return NULL;
01507     }
01508 
01509     /* Initialize heap's first segment */
01510     Status = RtlpInitializeHeapSegment(Heap, (PHEAP_SEGMENT) (Heap), 0, HeapSegmentFlags, TotalSize, CommitSize);
01511     if (!NT_SUCCESS(Status))
01512     {
01513         DPRINT1("Failed to initialize heap segment (%x)\n", Status);
01514         return NULL;
01515     }
01516 
01517     DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap, CommitSize, TotalSize);
01518 
01519     /* Add heap to process list in case of usermode heap */
01520     if (RtlpGetMode() == UserMode)
01521     {
01522         RtlpAddHeapToProcessList(Heap);
01523 
01524         // FIXME: What about lookasides?
01525     }
01526 
01527     return Heap;
01528 }
01529 
01530 /***********************************************************************
01531  *           RtlDestroyHeap
01532  * RETURNS
01533  * TRUE: Success
01534  * FALSE: Failure
01535  *
01536  * @implemented
01537  *
01538  * RETURNS
01539  *  Success: A NULL HANDLE, if heap is NULL or it was destroyed
01540  *  Failure: The Heap handle, if heap is the process heap.
01541  */
01542 HANDLE NTAPI
01543 RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */
01544 {
01545     PHEAP Heap = (PHEAP)HeapPtr;
01546     PLIST_ENTRY Current;
01547     PHEAP_UCR_SEGMENT UcrSegment;
01548     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
01549     PVOID BaseAddress;
01550     SIZE_T Size;
01551     LONG i;
01552     PHEAP_SEGMENT Segment;
01553 
01554     if (!HeapPtr) return NULL;
01555 
01556     /* Call page heap routine if required */
01557     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) return RtlpPageHeapDestroy(HeapPtr);
01558 
01559     /* Call special heap */
01560     if (RtlpHeapIsSpecial(Heap->Flags))
01561     {
01562         if (!RtlDebugDestroyHeap(Heap)) return HeapPtr;
01563     }
01564 
01565     /* Check for a process heap */
01566     if (RtlpGetMode() == UserMode &&
01567         HeapPtr == NtCurrentPeb()->ProcessHeap) return HeapPtr;
01568 
01569     /* Free up all big allocations */
01570     Current = Heap->VirtualAllocdBlocks.Flink;
01571     while (Current != &Heap->VirtualAllocdBlocks)
01572     {
01573         VirtualEntry = CONTAINING_RECORD(Current, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
01574         BaseAddress = (PVOID)VirtualEntry;
01575         Current = Current->Flink;
01576         Size = 0;
01577         ZwFreeVirtualMemory(NtCurrentProcess(),
01578                             &BaseAddress,
01579                             &Size,
01580                             MEM_RELEASE);
01581     }
01582 
01583     /* Delete tags and remove heap from the process heaps list in user mode */
01584     if (RtlpGetMode() == UserMode)
01585     {
01586         // FIXME DestroyTags
01587         RtlpRemoveHeapFromProcessList(Heap);
01588     }
01589 
01590     /* Delete the heap lock */
01591     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
01592     {
01593         /* Delete it if it wasn't user allocated */
01594         if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED))
01595             RtlDeleteHeapLock(Heap->LockVariable);
01596 
01597         /* Clear out the lock variable */
01598         Heap->LockVariable = NULL;
01599     }
01600 
01601     /* Free UCR segments if any were created */
01602     Current = Heap->UCRSegments.Flink;
01603     while (Current != &Heap->UCRSegments)
01604     {
01605         UcrSegment = CONTAINING_RECORD(Current, HEAP_UCR_SEGMENT, ListEntry);
01606 
01607         /* Advance to the next descriptor */
01608         Current = Current->Flink;
01609 
01610         BaseAddress = (PVOID)UcrSegment;
01611         Size = 0;
01612 
01613         /* Release that memory */
01614         ZwFreeVirtualMemory(NtCurrentProcess(),
01615                             &BaseAddress,
01616                             &Size,
01617                             MEM_RELEASE);
01618     }
01619 
01620     /* Go through segments and destroy them */
01621     for (i = HEAP_SEGMENTS - 1; i >= 0; i--)
01622     {
01623         Segment = Heap->Segments[i];
01624         if (Segment) RtlpDestroyHeapSegment(Segment);
01625     }
01626 
01627     return NULL;
01628 }
01629 
01630 PHEAP_ENTRY NTAPI
01631 RtlpSplitEntry(PHEAP Heap,
01632                ULONG Flags,
01633                PHEAP_FREE_ENTRY FreeBlock,
01634                SIZE_T AllocationSize,
01635                SIZE_T Index,
01636                SIZE_T Size)
01637 {
01638     PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
01639     UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY;
01640     PHEAP_ENTRY InUseEntry;
01641     SIZE_T FreeSize;
01642 
01643     /* Add extra flags in case of settable user value feature is requested,
01644        or there is a tag (small or normal) or there is a request to
01645        capture stack backtraces */
01646     if ((Flags & HEAP_EXTRA_FLAGS_MASK) ||
01647         Heap->PseudoTagEntries)
01648     {
01649         /* Add flag which means that the entry will have extra stuff attached */
01650         EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
01651 
01652         /* NB! AllocationSize is already adjusted by RtlAllocateHeap */
01653     }
01654 
01655     /* Add settable user flags, if any */
01656     EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
01657 
01658     /* Save flags, update total free size */
01659     FreeFlags = FreeBlock->Flags;
01660     Heap->TotalFreeSize -= FreeBlock->Size;
01661 
01662     /* Make this block an in-use one */
01663     InUseEntry = (PHEAP_ENTRY)FreeBlock;
01664     InUseEntry->Flags = EntryFlags;
01665     InUseEntry->SmallTagIndex = 0;
01666 
01667     /* Calculate the extra amount */
01668     FreeSize = InUseEntry->Size - Index;
01669 
01670     /* Update it's size fields (we don't need their data anymore) */
01671     InUseEntry->Size = (USHORT)Index;
01672     InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
01673 
01674     /* If there is something to split - do the split */
01675     if (FreeSize != 0)
01676     {
01677         /* Don't split if resulting entry can't contain any payload data
01678         (i.e. being just HEAP_ENTRY_SIZE) */
01679         if (FreeSize == 1)
01680         {
01681             /* Increase sizes of the in-use entry */
01682             InUseEntry->Size++;
01683             InUseEntry->UnusedBytes += sizeof(HEAP_ENTRY);
01684         }
01685         else
01686         {
01687             /* Calculate a pointer to the new entry */
01688             SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
01689 
01690             /* Initialize it */
01691             SplitBlock->Flags = FreeFlags;
01692             SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
01693             SplitBlock->Size = (USHORT)FreeSize;
01694             SplitBlock->PreviousSize = (USHORT)Index;
01695 
01696             /* Check if it's the last entry */
01697             if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
01698             {
01699                 /* Insert it to the free list if it's the last entry */
01700                 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
01701                 Heap->TotalFreeSize += FreeSize;
01702             }
01703             else
01704             {
01705                 /* Not so easy - need to update next's previous size too */
01706                 SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
01707 
01708                 if (SplitBlock2->Flags & HEAP_ENTRY_BUSY)
01709                 {
01710                     SplitBlock2->PreviousSize = (USHORT)FreeSize;
01711                     RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
01712                     Heap->TotalFreeSize += FreeSize;
01713                 }
01714                 else
01715                 {
01716                     /* Even more complex - the next entry is free, so we can merge them into one! */
01717                     SplitBlock->Flags = SplitBlock2->Flags;
01718 
01719                     /* Remove that next entry */
01720                     RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE, FALSE);
01721 
01722                     /* Update sizes */
01723                     FreeSize += SplitBlock2->Size;
01724                     Heap->TotalFreeSize -= SplitBlock2->Size;
01725 
01726                     if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
01727                     {
01728                         /* Insert it back */
01729                         SplitBlock->Size = (USHORT)FreeSize;
01730 
01731                         /* Don't forget to update previous size of the next entry! */
01732                         if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY))
01733                         {
01734                             ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
01735                         }
01736 
01737                         /* Actually insert it */
01738                         RtlpInsertFreeBlockHelper(Heap, SplitBlock, (USHORT)FreeSize, FALSE);
01739 
01740                         /* Update total size */
01741                         Heap->TotalFreeSize += FreeSize;
01742                     }
01743                     else
01744                     {
01745                         /* Resulting block is quite big */
01746                         RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
01747                     }
01748                 }
01749             }
01750 
01751             /* Reset flags of the free entry */
01752             FreeFlags = 0;
01753         }
01754     }
01755 
01756     /* Set last entry flag */
01757     if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
01758         InUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
01759 
01760     return InUseEntry;
01761 }
01762 
01763 PVOID NTAPI
01764 RtlpAllocateNonDedicated(PHEAP Heap,
01765                          ULONG Flags,
01766                          SIZE_T Size,
01767                          SIZE_T AllocationSize,
01768                          SIZE_T Index,
01769                          BOOLEAN HeapLocked)
01770 {
01771     PLIST_ENTRY FreeListHead, Next;
01772     PHEAP_FREE_ENTRY FreeBlock;
01773     PHEAP_ENTRY InUseEntry;
01774     PHEAP_ENTRY_EXTRA Extra;
01775     EXCEPTION_RECORD ExceptionRecord;
01776 
01777     /* Go through the zero list to find a place where to insert the new entry */
01778     FreeListHead = &Heap->FreeLists[0];
01779 
01780     /* Start from the largest block to reduce time */
01781     Next = FreeListHead->Blink;
01782     if (FreeListHead != Next)
01783     {
01784         FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
01785 
01786         if (FreeBlock->Size >= Index)
01787         {
01788             /* Our request is smaller than the largest entry in the zero list */
01789 
01790             /* Go through the list to find insertion place */
01791             Next = FreeListHead->Flink;
01792             while (FreeListHead != Next)
01793             {
01794                 FreeBlock = CONTAINING_RECORD(Next, HEAP_FREE_ENTRY, FreeList);
01795 
01796                 if (FreeBlock->Size >= Index)
01797                 {
01798                     /* Found minimally fitting entry. Proceed to either using it as it is
01799                     or splitting it to two entries */
01800                     RemoveEntryList(&FreeBlock->FreeList);
01801 
01802                     /* Split it */
01803                     InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
01804 
01805                     /* Release the lock */
01806                     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
01807 
01808                     /* Zero memory if that was requested */
01809                     if (Flags & HEAP_ZERO_MEMORY)
01810                         RtlZeroMemory(InUseEntry + 1, Size);
01811                     else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
01812                     {
01813                         /* Fill this block with a special pattern */
01814                         RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
01815                     }
01816 
01817                     /* Fill tail of the block with a special pattern too if requested */
01818                     if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
01819                     {
01820                         RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
01821                         InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
01822                     }
01823 
01824                     /* Prepare extra if it's present */
01825                     if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
01826                     {
01827                         Extra = RtlpGetExtraStuffPointer(InUseEntry);
01828                         RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
01829 
01830                         // TODO: Tagging
01831                     }
01832 
01833                     /* Return pointer to the */
01834                     return InUseEntry + 1;
01835                 }
01836 
01837                 /* Advance to the next entry */
01838                 Next = Next->Flink;
01839             }
01840         }
01841     }
01842 
01843     /* Extend the heap, 0 list didn't have anything suitable */
01844     FreeBlock = RtlpExtendHeap(Heap, AllocationSize);
01845 
01846     /* Use the new biggest entry we've got */
01847     if (FreeBlock)
01848     {
01849         RemoveEntryList(&FreeBlock->FreeList);
01850 
01851         /* Split it */
01852         InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
01853 
01854         /* Release the lock */
01855         if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
01856 
01857         /* Zero memory if that was requested */
01858         if (Flags & HEAP_ZERO_MEMORY)
01859             RtlZeroMemory(InUseEntry + 1, Size);
01860         else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
01861         {
01862             /* Fill this block with a special pattern */
01863             RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
01864         }
01865 
01866         /* Fill tail of the block with a special pattern too if requested */
01867         if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
01868         {
01869             RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
01870             InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
01871         }
01872 
01873         /* Prepare extra if it's present */
01874         if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
01875         {
01876             Extra = RtlpGetExtraStuffPointer(InUseEntry);
01877             RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
01878 
01879             // TODO: Tagging
01880         }
01881 
01882         /* Return pointer to the */
01883         return InUseEntry + 1;
01884     }
01885 
01886     /* Really unfortunate, out of memory condition */
01887     RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
01888 
01889     /* Generate an exception */
01890     if (Flags & HEAP_GENERATE_EXCEPTIONS)
01891     {
01892         ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
01893         ExceptionRecord.ExceptionRecord = NULL;
01894         ExceptionRecord.NumberParameters = 1;
01895         ExceptionRecord.ExceptionFlags = 0;
01896         ExceptionRecord.ExceptionInformation[0] = AllocationSize;
01897 
01898         RtlRaiseException(&ExceptionRecord);
01899     }
01900 
01901     /* Release the lock */
01902     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
01903     DPRINT1("HEAP: Allocation failed!\n");
01904     DPRINT1("Flags %x\n", Heap->Flags);
01905     return NULL;
01906 }
01907 
01908 /***********************************************************************
01909  *           HeapAlloc   (KERNEL32.334)
01910  * RETURNS
01911  * Pointer to allocated memory block
01912  * NULL: Failure
01913  * 0x7d030f60--invalid flags in RtlHeapAllocate
01914  * @implemented
01915  */
01916 PVOID NTAPI
01917 RtlAllocateHeap(IN PVOID HeapPtr,
01918                 IN ULONG Flags,
01919                 IN SIZE_T Size)
01920 {
01921     PHEAP Heap = (PHEAP)HeapPtr;
01922     PULONG FreeListsInUse;
01923     ULONG FreeListsInUseUlong;
01924     SIZE_T AllocationSize;
01925     SIZE_T Index, InUseIndex, i;
01926     PLIST_ENTRY FreeListHead;
01927     PHEAP_ENTRY InUseEntry;
01928     PHEAP_FREE_ENTRY FreeBlock;
01929     UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY;
01930     EXCEPTION_RECORD ExceptionRecord;
01931     BOOLEAN HeapLocked = FALSE;
01932     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock = NULL;
01933     PHEAP_ENTRY_EXTRA Extra;
01934     NTSTATUS Status;
01935 
01936     /* Force flags */
01937     Flags |= Heap->ForceFlags;
01938 
01939     /* Call special heap */
01940     if (RtlpHeapIsSpecial(Flags))
01941         return RtlDebugAllocateHeap(Heap, Flags, Size);
01942 
01943     /* Check for the maximum size */
01944     if (Size >= 0x80000000)
01945     {
01946         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
01947         DPRINT1("HEAP: Allocation failed!\n");
01948         return NULL;
01949     }
01950 
01951     if (Flags & (HEAP_CREATE_ENABLE_TRACING |
01952                  HEAP_CREATE_ALIGN_16))
01953     {
01954         DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags);
01955     }
01956 
01957     //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
01958 
01959     /* Calculate allocation size and index */
01960     if (Size)
01961         AllocationSize = Size;
01962     else
01963         AllocationSize = 1;
01964     AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
01965 
01966     /* Add extra flags in case of settable user value feature is requested,
01967        or there is a tag (small or normal) or there is a request to
01968        capture stack backtraces */
01969     if ((Flags & HEAP_EXTRA_FLAGS_MASK) ||
01970         Heap->PseudoTagEntries)
01971     {
01972         /* Add flag which means that the entry will have extra stuff attached */
01973         EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
01974 
01975         /* Account for extra stuff size */
01976         AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
01977     }
01978 
01979     /* Add settable user flags, if any */
01980     EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
01981 
01982     Index = AllocationSize >>  HEAP_ENTRY_SHIFT;
01983 
01984     /* Acquire the lock if necessary */
01985     if (!(Flags & HEAP_NO_SERIALIZE))
01986     {
01987         RtlEnterHeapLock(Heap->LockVariable, TRUE);
01988         HeapLocked = TRUE;
01989     }
01990 
01991     /* Depending on the size, the allocation is going to be done from dedicated,
01992        non-dedicated lists or a virtual block of memory */
01993     if (Index < HEAP_FREELISTS)
01994     {
01995         FreeListHead = &Heap->FreeLists[Index];
01996 
01997         if (!IsListEmpty(FreeListHead))
01998         {
01999             /* There is a free entry in this list */
02000             FreeBlock = CONTAINING_RECORD(FreeListHead->Blink,
02001                                           HEAP_FREE_ENTRY,
02002                                           FreeList);
02003 
02004             /* Save flags and remove the free entry */
02005             FreeFlags = FreeBlock->Flags;
02006             RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE, FALSE);
02007 
02008             /* Update the total free size of the heap */
02009             Heap->TotalFreeSize -= Index;
02010 
02011             /* Initialize this block */
02012             InUseEntry = (PHEAP_ENTRY)FreeBlock;
02013             InUseEntry->Flags = EntryFlags | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
02014             InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
02015             InUseEntry->SmallTagIndex = 0;
02016         }
02017         else
02018         {
02019             /* Find smallest free block which this request could fit in */
02020             InUseIndex = Index >> 5;
02021             FreeListsInUse = &Heap->u.FreeListsInUseUlong[InUseIndex];
02022 
02023             /* This bit magic disables all sizes which are less than the requested allocation size */
02024             FreeListsInUseUlong = *FreeListsInUse++ & ~((1 << ((ULONG)Index & 0x1f)) - 1);
02025 
02026             /* If size is definitily more than our lists - go directly to the non-dedicated one */
02027             if (InUseIndex > 3)
02028                 return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
02029 
02030             /* Go through the list */
02031             for (i = InUseIndex; i < 4; i++)
02032             {
02033                 if (FreeListsInUseUlong)
02034                 {
02035                     FreeListHead = &Heap->FreeLists[i * 32];
02036                     break;
02037                 }
02038 
02039                 if (i < 3) FreeListsInUseUlong = *FreeListsInUse++;
02040             }
02041 
02042             /* Nothing found, search in the non-dedicated list */
02043             if (i == 4)
02044                 return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
02045 
02046             /* That list is found, now calculate exact block  */
02047             FreeListHead += RtlpFindLeastSetBit(FreeListsInUseUlong);
02048 
02049             /* Take this entry and remove it from the list of free blocks */
02050             FreeBlock = CONTAINING_RECORD(FreeListHead->Blink,
02051                                           HEAP_FREE_ENTRY,
02052                                           FreeList);
02053             RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE, FALSE);
02054 
02055             /* Split it */
02056             InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
02057         }
02058 
02059         /* Release the lock */
02060         if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
02061 
02062         /* Zero memory if that was requested */
02063         if (Flags & HEAP_ZERO_MEMORY)
02064             RtlZeroMemory(InUseEntry + 1, Size);
02065         else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
02066         {
02067             /* Fill this block with a special pattern */
02068             RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER);
02069         }
02070 
02071         /* Fill tail of the block with a special pattern too if requested */
02072         if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
02073         {
02074             RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL);
02075             InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN;
02076         }
02077 
02078         /* Prepare extra if it's present */
02079         if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
02080         {
02081             Extra = RtlpGetExtraStuffPointer(InUseEntry);
02082             RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
02083 
02084             // TODO: Tagging
02085         }
02086 
02087         /* User data starts right after the entry's header */
02088         return InUseEntry + 1;
02089     }
02090     else if (Index <= Heap->VirtualMemoryThreshold)
02091     {
02092         /* The block is too large for dedicated lists, but fine for a non-dedicated one */
02093         return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
02094     }
02095     else if (Heap->Flags & HEAP_GROWABLE)
02096     {
02097         /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */
02098         AllocationSize += sizeof(HEAP_VIRTUAL_ALLOC_ENTRY) - sizeof(HEAP_ENTRY);
02099 
02100         Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
02101                                          (PVOID *)&VirtualBlock,
02102                                          0,
02103                                          &AllocationSize,
02104                                          MEM_COMMIT,
02105                                          PAGE_READWRITE);
02106 
02107         if (!NT_SUCCESS(Status))
02108         {
02109             // Set STATUS!
02110             /* Release the lock */
02111             if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
02112             DPRINT1("HEAP: Allocation failed!\n");
02113             return NULL;
02114         }
02115 
02116         /* Initialize the newly allocated block */
02117         VirtualBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
02118         VirtualBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT;
02119         VirtualBlock->CommitSize = AllocationSize;
02120         VirtualBlock->ReserveSize = AllocationSize;
02121 
02122         /* Insert it into the list of virtual allocations */
02123         InsertTailList(&Heap->VirtualAllocdBlocks, &VirtualBlock->Entry);
02124 
02125         /* Release the lock */
02126         if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
02127 
02128         /* Return pointer to user data */
02129         return VirtualBlock + 1;
02130     }
02131 
02132     /* Generate an exception */
02133     if (Flags & HEAP_GENERATE_EXCEPTIONS)
02134     {
02135         ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
02136         ExceptionRecord.ExceptionRecord = NULL;
02137         ExceptionRecord.NumberParameters = 1;
02138         ExceptionRecord.ExceptionFlags = 0;
02139         ExceptionRecord.ExceptionInformation[0] = AllocationSize;
02140 
02141         RtlRaiseException(&ExceptionRecord);
02142     }
02143 
02144     RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL);
02145 
02146     /* Release the lock */
02147     if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
02148     DPRINT1("HEAP: Allocation failed!\n");
02149     return NULL;
02150 }
02151 
02152 
02153 /***********************************************************************
02154  *           HeapFree   (KERNEL32.338)
02155  * RETURNS
02156  * TRUE: Success
02157  * FALSE: Failure
02158  *
02159  * @implemented
02160  */
02161 BOOLEAN NTAPI RtlFreeHeap(
02162    HANDLE HeapPtr, /* [in] Handle of heap */
02163    ULONG Flags,   /* [in] Heap freeing flags */
02164    PVOID Ptr     /* [in] Address of memory to free */
02165 )
02166 {
02167     PHEAP Heap;
02168     PHEAP_ENTRY HeapEntry;
02169     USHORT TagIndex = 0;
02170     SIZE_T BlockSize;
02171     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
02172     BOOLEAN Locked = FALSE;
02173     NTSTATUS Status;
02174 
02175     /* Freeing NULL pointer is a legal operation */
02176     if (!Ptr) return TRUE;
02177 
02178     /* Get pointer to the heap and force flags */
02179     Heap = (PHEAP)HeapPtr;
02180     Flags |= Heap->ForceFlags;
02181 
02182     /* Call special heap */
02183     if (RtlpHeapIsSpecial(Flags))
02184         return RtlDebugFreeHeap(Heap, Flags, Ptr);
02185 
02186     /* Lock if necessary */
02187     if (!(Flags & HEAP_NO_SERIALIZE))
02188     {
02189         RtlEnterHeapLock(Heap->LockVariable, TRUE);
02190         Locked = TRUE;
02191     }
02192 
02193     /* Get pointer to the heap entry */
02194     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
02195 
02196     /* Check this entry, fail if it's invalid */
02197     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY) ||
02198         (((ULONG_PTR)Ptr & 0x7) != 0) ||
02199         (HeapEntry->SegmentOffset >= HEAP_SEGMENTS))
02200     {
02201         /* This is an invalid block */
02202         DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr);
02203         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
02204 
02205         /* Release the heap lock */
02206         if (Locked) RtlLeaveHeapLock(Heap->LockVariable);
02207         return FALSE;
02208     }
02209 
02210     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
02211     {
02212         /* Big allocation */
02213         VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
02214 
02215         /* Remove it from the list */
02216         RemoveEntryList(&VirtualEntry->Entry);
02217 
02218         // TODO: Tagging
02219 
02220         BlockSize = 0;
02221         Status = ZwFreeVirtualMemory(NtCurrentProcess(),
02222                                      (PVOID *)&VirtualEntry,
02223                                      &BlockSize,
02224                                      MEM_RELEASE);
02225 
02226         if (!NT_SUCCESS(Status))
02227         {
02228             DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n",
02229                 Status, Heap, Ptr, VirtualEntry);
02230             RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status);
02231         }
02232     }
02233     else
02234     {
02235         /* Normal allocation */
02236         BlockSize = HeapEntry->Size;
02237 
02238         // TODO: Tagging
02239 
02240         /* Coalesce in kernel mode, and in usermode if it's not disabled */
02241         if (RtlpGetMode() == KernelMode ||
02242             (RtlpGetMode() == UserMode && !(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)))
02243         {
02244             HeapEntry = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks(Heap,
02245                                                            (PHEAP_FREE_ENTRY)HeapEntry,
02246                                                            &BlockSize,
02247                                                            FALSE);
02248         }
02249 
02250         /* If there is no need to decommit the block - put it into a free list */
02251         if (BlockSize < Heap->DeCommitFreeBlockThreshold ||
02252             (Heap->TotalFreeSize + BlockSize < Heap->DeCommitTotalFreeThreshold))
02253         {
02254             /* Check if it needs to go to a 0 list */
02255             if (BlockSize > HEAP_MAX_BLOCK_SIZE)
02256             {
02257                 /* General-purpose 0 list */
02258                 RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
02259             }
02260             else
02261             {
02262                 /* Usual free list */
02263                 RtlpInsertFreeBlockHelper(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize, FALSE);
02264 
02265                 /* Assert sizes are consistent */
02266                 if (!(HeapEntry->Flags & HEAP_ENTRY_LAST_ENTRY))
02267                 {
02268                     ASSERT((HeapEntry + BlockSize)->PreviousSize == BlockSize);
02269                 }
02270 
02271                 /* Increase the free size */
02272                 Heap->TotalFreeSize += BlockSize;
02273             }
02274 
02275 
02276             if (RtlpGetMode() == UserMode &&
02277                 TagIndex != 0)
02278             {
02279                 // FIXME: Tagging
02280                 UNIMPLEMENTED;
02281             }
02282         }
02283         else
02284         {
02285             /* Decommit this block */
02286             RtlpDeCommitFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
02287         }
02288     }
02289 
02290     /* Release the heap lock */
02291     if (Locked) RtlLeaveHeapLock(Heap->LockVariable);
02292 
02293     return TRUE;
02294 }
02295 
02296 BOOLEAN NTAPI
02297 RtlpGrowBlockInPlace (IN PHEAP Heap,
02298                       IN ULONG Flags,
02299                       IN PHEAP_ENTRY InUseEntry,
02300                       IN SIZE_T Size,
02301                       IN SIZE_T Index)
02302 {
02303     UCHAR EntryFlags, RememberFlags;
02304     PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry;
02305     SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0;
02306     PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
02307 
02308     /* We can't grow beyond specified threshold */
02309     if (Index > Heap->VirtualMemoryThreshold)
02310         return FALSE;
02311 
02312     /* Get entry flags */
02313     EntryFlags = InUseEntry->Flags;
02314 
02315     /* Get the next free entry */
02316     FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size);
02317 
02318     if (EntryFlags & HEAP_ENTRY_LAST_ENTRY)
02319     {
02320         /* There is no next block, just uncommitted space. Calculate how much is needed */
02321         FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT;
02322         FreeSize = ROUND_UP(FreeSize, PAGE_SIZE);
02323 
02324         /* Find and commit those pages */
02325         FreeEntry = RtlpFindAndCommitPages(Heap,
02326                                            Heap->Segments[InUseEntry->SegmentOffset],
02327                                            &FreeSize,
02328                                            FreeEntry);
02329 
02330         /* Fail if it failed... */
02331         if (!FreeEntry) return FALSE;
02332 
02333         /* It was successful, perform coalescing */
02334         FreeSize = FreeSize >> HEAP_ENTRY_SHIFT;
02335         FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
02336 
02337         /* Check if it's enough */
02338         if (FreeSize + InUseEntry->Size < Index)
02339         {
02340             /* Still not enough */
02341             RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize);
02342             Heap->TotalFreeSize += FreeSize;
02343             return FALSE;
02344         }
02345 
02346         /* Remember flags of this free entry */
02347         RememberFlags = FreeEntry->Flags;
02348 
02349         /* Sum up sizes */
02350         FreeSize += InUseEntry->Size;
02351     }
02352     else
02353     {
02354         /* The next block indeed exists. Check if it's free or in use */
02355         if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE;
02356 
02357         /* Next entry is free, check if it can fit the block we need */
02358         FreeSize = InUseEntry->Size + FreeEntry->Size;
02359         if (FreeSize < Index) return FALSE;
02360 
02361         /* Remember flags of this free entry */
02362         RememberFlags = FreeEntry->Flags;
02363 
02364         /* Remove this block from the free list */
02365         RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE, FALSE);
02366         Heap->TotalFreeSize -= FreeEntry->Size;
02367     }
02368 
02369     PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
02370     FreeSize -= Index;
02371 
02372     /* Don't produce too small blocks */
02373     if (FreeSize <= 2)
02374     {
02375         Index += FreeSize;
02376         FreeSize = 0;
02377     }
02378 
02379     /* Process extra stuff */
02380     if (RememberFlags & HEAP_ENTRY_EXTRA_PRESENT)
02381     {
02382         /* Calculate pointers */
02383         OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
02384         NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
02385 
02386         /* Copy contents */
02387         *NewExtra = *OldExtra;
02388 
02389         // FIXME Tagging
02390     }
02391 
02392     /* Update sizes */
02393     InUseEntry->Size = (USHORT)Index;
02394     InUseEntry->UnusedBytes = (UCHAR)((Index << HEAP_ENTRY_SHIFT) - Size);
02395 
02396     /* Check if there is a free space remaining after merging those blocks */
02397     if (!FreeSize)
02398     {
02399         /* Update flags and sizes */
02400         InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY;
02401 
02402         /* Either update previous size of the next entry or mark it as a last
02403            entry in the segment*/
02404         if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY))
02405             (InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size;
02406     }
02407     else
02408     {
02409         /* Complex case, we need to split the block to give unused free space
02410            back to the heap */
02411         UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
02412         UnusedEntry->PreviousSize = (USHORT)Index;
02413         UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset;
02414 
02415         /* Update the following block or set the last entry in the segment */
02416         if (RememberFlags & HEAP_ENTRY_LAST_ENTRY)
02417         {
02418             /* Set flags and size */
02419             UnusedEntry->Flags = RememberFlags;
02420             UnusedEntry->Size = (USHORT)FreeSize;
02421 
02422             /* Insert it to the heap and update total size  */
02423             RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
02424             Heap->TotalFreeSize += FreeSize;
02425         }
02426         else
02427         {
02428             /* There is a block after this one  */
02429             FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize);
02430 
02431             if (FollowingEntry->Flags & HEAP_ENTRY_BUSY)
02432             {
02433                 /* Update flags and set size of the unused space entry */
02434                 UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY);
02435                 UnusedEntry->Size = (USHORT)FreeSize;
02436 
02437                 /* Update previous size of the following entry */
02438                 FollowingEntry->PreviousSize = (USHORT)FreeSize;
02439 
02440                 /* Insert it to the heap and update total free size */
02441                 RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
02442                 Heap->TotalFreeSize += FreeSize;
02443             }
02444             else
02445             {
02446                 /* That following entry is also free, what a fortune! */
02447                 RememberFlags = FollowingEntry->Flags;
02448 
02449                 /* Remove it */
02450                 RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE, FALSE);
02451                 Heap->TotalFreeSize -= FollowingEntry->Size;
02452 
02453                 /* And make up a new combined block */
02454                 FreeSize += FollowingEntry->Size;
02455                 UnusedEntry->Flags = RememberFlags;
02456 
02457                 /* Check where to put it */
02458                 if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
02459                 {
02460                     /* Fine for a dedicated list */
02461                     UnusedEntry->Size = (USHORT)FreeSize;
02462 
02463                     if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY))
02464                         ((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = (USHORT)FreeSize;
02465 
02466                     /* Insert it back and update total size */
02467                     RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE);
02468                     Heap->TotalFreeSize += FreeSize;
02469                 }
02470                 else
02471                 {
02472                     /* The block is very large, leave all the hassle to the insertion routine */
02473                     RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize);
02474                 }
02475             }
02476         }
02477     }
02478 
02479     /* Properly "zero out" (and fill!) the space */
02480     if (Flags & HEAP_ZERO_MEMORY)
02481     {
02482         RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize);
02483     }
02484     else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
02485     {
02486         /* Calculate tail part which we need to fill */
02487         TailPart = PrevSize & (sizeof(ULONG) - 1);
02488 
02489         /* "Invert" it as usual */
02490         if (TailPart) TailPart = 4 - TailPart;
02491 
02492         if (Size > (PrevSize + TailPart))
02493             AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1);
02494 
02495         if (AddedSize)
02496         {
02497             RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart,
02498                                AddedSize,
02499                                ARENA_INUSE_FILLER);
02500         }
02501     }
02502 
02503     /* Fill the new tail */
02504     if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
02505     {
02506         RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
02507                       HEAP_ENTRY_SIZE,
02508                       HEAP_TAIL_FILL);
02509     }
02510 
02511     /* Copy user settable flags */
02512     InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
02513     InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
02514 
02515     /* Return success */
02516     return TRUE;
02517 }
02518 
02519 PHEAP_ENTRY_EXTRA NTAPI
02520 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry)
02521 {
02522     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
02523 
02524     /* Check if it's a big block */
02525     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
02526     {
02527         VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
02528 
02529         /* Return a pointer to the extra stuff*/
02530         return &VirtualEntry->ExtraStuff;
02531     }
02532     else
02533     {
02534         /* This is a usual entry, which means extra stuff follows this block */
02535         return (PHEAP_ENTRY_EXTRA)(HeapEntry + HeapEntry->Size - 1);
02536     }
02537 }
02538 
02539 
02540 /***********************************************************************
02541  *           RtlReAllocateHeap
02542  * PARAMS
02543  *   Heap   [in] Handle of heap block
02544  *   Flags    [in] Heap reallocation flags
02545  *   Ptr,    [in] Address of memory to reallocate
02546  *   Size     [in] Number of bytes to reallocate
02547  *
02548  * RETURNS
02549  * Pointer to reallocated memory block
02550  * NULL: Failure
02551  * 0x7d030f60--invalid flags in RtlHeapAllocate
02552  * @implemented
02553  */
02554 PVOID NTAPI
02555 RtlReAllocateHeap(HANDLE HeapPtr,
02556                   ULONG Flags,
02557                   PVOID Ptr,
02558                   SIZE_T Size)
02559 {
02560     PHEAP Heap = (PHEAP)HeapPtr;
02561     PHEAP_ENTRY InUseEntry, NewInUseEntry;
02562     PHEAP_ENTRY_EXTRA OldExtra, NewExtra;
02563     SIZE_T AllocationSize, FreeSize, DecommitSize;
02564     BOOLEAN HeapLocked = FALSE;
02565     PVOID NewBaseAddress;
02566     PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
02567     SIZE_T OldSize, Index, OldIndex;
02568     UCHAR FreeFlags;
02569     NTSTATUS Status;
02570     PVOID DecommitBase;
02571     SIZE_T RemainderBytes, ExtraSize;
02572     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
02573     EXCEPTION_RECORD ExceptionRecord;
02574 
02575     /* Return success in case of a null pointer */
02576     if (!Ptr)
02577     {
02578         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS);
02579         return NULL;
02580     }
02581 
02582     /* Force heap flags */
02583     Flags |= Heap->ForceFlags;
02584 
02585     /* Call special heap */
02586     if (RtlpHeapIsSpecial(Flags))
02587         return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size);
02588 
02589     /* Make sure size is valid */
02590     if (Size >= 0x80000000)
02591     {
02592         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
02593         return NULL;
02594     }
02595 
02596     /* Calculate allocation size and index */
02597     if (Size)
02598         AllocationSize = Size;
02599     else
02600         AllocationSize = 1;
02601     AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
02602 
02603     /* Add up extra stuff, if it is present anywhere */
02604     if (((((PHEAP_ENTRY)Ptr)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT) ||
02605         (Flags & HEAP_EXTRA_FLAGS_MASK) ||
02606         Heap->PseudoTagEntries)
02607     {
02608         AllocationSize += sizeof(HEAP_ENTRY_EXTRA);
02609     }
02610 
02611     /* Acquire the lock if necessary */
02612     if (!(Flags & HEAP_NO_SERIALIZE))
02613     {
02614         RtlEnterHeapLock(Heap->LockVariable, TRUE);
02615         HeapLocked = TRUE;
02616         Flags &= ~HEAP_NO_SERIALIZE;
02617     }
02618 
02619     /* Get the pointer to the in-use entry */
02620     InUseEntry = (PHEAP_ENTRY)Ptr - 1;
02621 
02622     /* If that entry is not really in-use, we have a problem */
02623     if (!(InUseEntry->Flags & HEAP_ENTRY_BUSY))
02624     {
02625         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
02626 
02627         /* Release the lock and return */
02628         if (HeapLocked)
02629             RtlLeaveHeapLock(Heap->LockVariable);
02630         return Ptr;
02631     }
02632 
02633     if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
02634     {
02635         /* This is a virtually allocated block. Get its size */
02636         OldSize = RtlpGetSizeOfBigBlock(InUseEntry);
02637 
02638         /* Convert it to an index */
02639         OldIndex = (OldSize + InUseEntry->Size) >> HEAP_ENTRY_SHIFT;
02640 
02641         /* Calculate new allocation size and round it to the page size */
02642         AllocationSize += FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
02643         AllocationSize = ROUND_UP(AllocationSize, PAGE_SIZE);
02644     }
02645     else
02646     {
02647         /* Usual entry */
02648         OldIndex = InUseEntry->Size;
02649 
02650         OldSize = (OldIndex << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes;
02651     }
02652 
02653     /* Calculate new index */
02654     Index = AllocationSize >> HEAP_ENTRY_SHIFT;
02655 
02656     /* Check for 4 different scenarios (old size, new size, old index, new index) */
02657     if (Index <= OldIndex)
02658     {
02659         /* Difference must be greater than 1, adjust if it's not so */
02660         if (Index + 1 == OldIndex)
02661         {
02662             Index++;
02663             AllocationSize += sizeof(HEAP_ENTRY);
02664         }
02665 
02666         /* Calculate new size */
02667         if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
02668         {
02669             /* Simple in case of a virtual alloc - just an unused size */
02670             InUseEntry->Size = (USHORT)((AllocationSize - Size) >> HEAP_ENTRY_SHIFT);
02671         }
02672         else if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
02673         {
02674             /* There is extra stuff, take it into account */
02675             OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1);
02676             NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
02677             *NewExtra = *OldExtra;
02678 
02679             // FIXME Tagging, TagIndex
02680 
02681             /* Update unused bytes count */
02682             InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
02683         }
02684         else
02685         {
02686             // FIXME Tagging, SmallTagIndex
02687             InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
02688         }
02689 
02690         /* If new size is bigger than the old size */
02691         if (Size > OldSize)
02692         {
02693             /* Zero out that additional space if required */
02694             if (Flags & HEAP_ZERO_MEMORY)
02695             {
02696                 RtlZeroMemory((PCHAR)Ptr + OldSize, Size - OldSize);
02697             }
02698             else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED)
02699             {
02700                 /* Fill it on free if required */
02701                 RemainderBytes = OldSize & (sizeof(ULONG) - 1);
02702 
02703                 if (RemainderBytes)
02704                     RemainderBytes = 4 - RemainderBytes;
02705 
02706                 if (Size > (OldSize + RemainderBytes))
02707                 {
02708                     /* Calculate actual amount of extra bytes to fill */
02709                     ExtraSize = (Size - (OldSize + RemainderBytes)) & ~(sizeof(ULONG) - 1);
02710 
02711                     /* Fill them if there are any */
02712                     if (ExtraSize != 0)
02713                     {
02714                         RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + OldSize + RemainderBytes,
02715                                            ExtraSize,
02716                                            ARENA_INUSE_FILLER);
02717                     }
02718                 }
02719             }
02720         }
02721 
02722         /* Fill tail of the heap entry if required */
02723         if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED)
02724         {
02725             RtlFillMemory((PCHAR)(InUseEntry + 1) + Size,
02726                           HEAP_ENTRY_SIZE,
02727                           HEAP_TAIL_FILL);
02728         }
02729 
02730         /* Check if the difference is significant or not */
02731         if (Index != OldIndex)
02732         {
02733             /* Save flags */
02734             FreeFlags = InUseEntry->Flags & ~HEAP_ENTRY_BUSY;
02735 
02736             if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC)
02737             {
02738                 /* This is a virtual block allocation */
02739                 VirtualAllocBlock = CONTAINING_RECORD(InUseEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
02740 
02741                 // FIXME Tagging!
02742 
02743                 DecommitBase = (PCHAR)VirtualAllocBlock + AllocationSize;
02744                 DecommitSize = (OldIndex << HEAP_ENTRY_SHIFT) - AllocationSize;
02745 
02746                 /* Release the memory */
02747                 Status = ZwFreeVirtualMemory(NtCurrentProcess(),
02748                                              (PVOID *)&DecommitBase,
02749                                              &DecommitSize,
02750                                              MEM_RELEASE);
02751 
02752                 if (!NT_SUCCESS(Status))
02753                 {
02754                     DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase, DecommitSize, Status);
02755                 }
02756                 else
02757                 {
02758                     /* Otherwise reduce the commit size */
02759                     VirtualAllocBlock->CommitSize -= DecommitSize;
02760                 }
02761             }
02762             else
02763             {
02764                 /* Reduce size of the block and possibly split it */
02765                 SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
02766 
02767                 /* Initialize this entry */
02768                 SplitBlock->Flags = FreeFlags;
02769                 SplitBlock->PreviousSize = (USHORT)Index;
02770                 SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
02771 
02772                 /* Remember free size */
02773                 FreeSize = InUseEntry->Size - Index;
02774 
02775                 /* Set new size */
02776                 InUseEntry->Size = (USHORT)Index;
02777                 InUseEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
02778 
02779                 /* Is that the last entry */
02780                 if (FreeFlags & HEAP_ENTRY_LAST_ENTRY)
02781                 {
02782                     /* Set its size and insert it to the list */
02783                     SplitBlock->Size = (USHORT)FreeSize;
02784                     RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
02785 
02786                     /* Update total free size */
02787                     Heap->TotalFreeSize += FreeSize;
02788                 }
02789                 else
02790                 {
02791                     /* Get the block after that one */
02792                     SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
02793 
02794                     if (SplitBlock2->Flags & HEAP_ENTRY_BUSY)
02795                     {
02796                         /* It's in use, add it here*/
02797                         SplitBlock->Size = (USHORT)FreeSize;
02798 
02799                         /* Update previous size of the next entry */
02800                         ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
02801 
02802                         /* Insert it to the list */
02803                         RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
02804 
02805                         /* Update total size */
02806                         Heap->TotalFreeSize += FreeSize;
02807                     }
02808                     else
02809                     {
02810                         /* Next entry is free, so merge with it */
02811                         SplitBlock->Flags = SplitBlock2->Flags;
02812 
02813                         /* Remove it, update total size */
02814                         RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE, FALSE);
02815                         Heap->TotalFreeSize -= SplitBlock2->Size;
02816 
02817                         /* Calculate total free size */
02818                         FreeSize += SplitBlock2->Size;
02819 
02820                         if (FreeSize <= HEAP_MAX_BLOCK_SIZE)
02821                         {
02822                             SplitBlock->Size = (USHORT)FreeSize;
02823 
02824                             if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY))
02825                             {
02826                                 /* Update previous size of the next entry */
02827                                 ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
02828                             }
02829 
02830                             /* Insert the new one back and update total size */
02831                             RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
02832                             Heap->TotalFreeSize += FreeSize;
02833                         }
02834                         else
02835                         {
02836                             /* Just add it */
02837                             RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize);
02838                         }
02839                     }
02840                 }
02841             }
02842         }
02843     }
02844     else
02845     {
02846         /* We're growing the block */
02847         if ((InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
02848             !RtlpGrowBlockInPlace(Heap, Flags, InUseEntry, Size, Index))
02849         {
02850             /* Growing in place failed, so growing out of place */
02851             if (Flags & HEAP_REALLOC_IN_PLACE_ONLY)
02852             {
02853                 DPRINT1("Realloc in place failed, but it was the only option\n");
02854                 Ptr = NULL;
02855             }
02856             else
02857             {
02858                 /* Clear tag bits */
02859                 Flags &= ~HEAP_TAG_MASK;
02860 
02861                 /* Process extra stuff */
02862                 if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
02863                 {
02864                     /* Preserve user settable flags */
02865                     Flags &= ~HEAP_SETTABLE_USER_FLAGS;
02866 
02867                     Flags |= HEAP_SETTABLE_USER_VALUE | ((InUseEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
02868 
02869                     /* Get pointer to the old extra data */
02870                     OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
02871 
02872                     /* Save tag index if it was set */
02873                     if (OldExtra->TagIndex &&
02874                         !(OldExtra->TagIndex & HEAP_PSEUDO_TAG_FLAG))
02875                     {
02876                         Flags |= OldExtra->TagIndex << HEAP_TAG_SHIFT;
02877                     }
02878                 }
02879                 else if (InUseEntry->SmallTagIndex)
02880                 {
02881                     /* Take small tag index into account */
02882                     Flags |= InUseEntry->SmallTagIndex << HEAP_TAG_SHIFT;
02883                 }
02884 
02885                 /* Allocate new block from the heap */
02886                 NewBaseAddress = RtlAllocateHeap(HeapPtr,
02887                                                  Flags & ~HEAP_ZERO_MEMORY,
02888                                                  Size);
02889 
02890                 /* Proceed if it didn't fail */
02891                 if (NewBaseAddress)
02892                 {
02893                     /* Get new entry pointer */
02894                     NewInUseEntry = (PHEAP_ENTRY)NewBaseAddress - 1;
02895 
02896                     /* Process extra stuff if it exists */
02897                     if (NewInUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
02898                     {
02899                         NewExtra = RtlpGetExtraStuffPointer(NewInUseEntry);
02900 
02901                         if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
02902                         {
02903                             OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
02904                             NewExtra->Settable = OldExtra->Settable;
02905                         }
02906                         else
02907                         {
02908                             RtlZeroMemory(NewExtra, sizeof(*NewExtra));
02909                         }
02910                     }
02911 
02912                     /* Copy actual user bits */
02913                     if (Size < OldSize)
02914                         RtlMoveMemory(NewBaseAddress, Ptr, Size);
02915                     else
02916                         RtlMoveMemory(NewBaseAddress, Ptr, OldSize);
02917 
02918                     /* Zero remaining part if required */
02919                     if (Size > OldSize &&
02920                         (Flags & HEAP_ZERO_MEMORY))
02921                     {
02922                         RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize);
02923                     }
02924 
02925                     /* Free the old block */
02926                     RtlFreeHeap(HeapPtr, Flags, Ptr);
02927                 }
02928 
02929                 Ptr = NewBaseAddress;
02930             }
02931         }
02932     }
02933 
02934     /* Did resizing fail? */
02935     if (!Ptr && (Flags & HEAP_GENERATE_EXCEPTIONS))
02936     {
02937         /* Generate an exception if required */
02938         ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
02939         ExceptionRecord.ExceptionRecord = NULL;
02940         ExceptionRecord.NumberParameters = 1;
02941         ExceptionRecord.ExceptionFlags = 0;
02942         ExceptionRecord.ExceptionInformation[0] = AllocationSize;
02943 
02944         RtlRaiseException(&ExceptionRecord);
02945     }
02946 
02947     /* Release the heap lock if it was acquired */
02948     if (HeapLocked)
02949         RtlLeaveHeapLock(Heap->LockVariable);
02950 
02951     return Ptr;
02952 }
02953 
02954 
02955 /***********************************************************************
02956  *           RtlCompactHeap
02957  *
02958  * @unimplemented
02959  */
02960 ULONG NTAPI
02961 RtlCompactHeap(HANDLE Heap,
02962         ULONG Flags)
02963 {
02964    UNIMPLEMENTED;
02965    return 0;
02966 }
02967 
02968 
02969 /***********************************************************************
02970  *           RtlLockHeap
02971  * Attempts to acquire the critical section object for a specified heap.
02972  *
02973  * PARAMS
02974  *   Heap  [in] Handle of heap to lock for exclusive access
02975  *
02976  * RETURNS
02977  * TRUE: Success
02978  * FALSE: Failure
02979  *
02980  * @implemented
02981  */
02982 BOOLEAN NTAPI
02983 RtlLockHeap(IN HANDLE HeapPtr)
02984 {
02985     PHEAP Heap = (PHEAP)HeapPtr;
02986 
02987     // FIXME Check for special heap
02988 
02989     /* Check if it's really a heap */
02990     if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
02991 
02992     /* Lock if it's lockable */
02993     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
02994     {
02995         RtlEnterHeapLock(Heap->LockVariable, TRUE);
02996     }
02997 
02998     return TRUE;
02999 }
03000 
03001 
03002 /***********************************************************************
03003  *           RtlUnlockHeap
03004  * Releases ownership of the critical section object.
03005  *
03006  * PARAMS
03007  *   Heap  [in] Handle to the heap to unlock
03008  *
03009  * RETURNS
03010  * TRUE: Success
03011  * FALSE: Failure
03012  *
03013  * @implemented
03014  */
03015 BOOLEAN NTAPI
03016 RtlUnlockHeap(HANDLE HeapPtr)
03017 {
03018     PHEAP Heap = (PHEAP)HeapPtr;
03019 
03020     // FIXME Check for special heap
03021 
03022     /* Check if it's really a heap */
03023     if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
03024 
03025     /* Unlock if it's lockable */
03026     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
03027     {
03028         RtlLeaveHeapLock(Heap->LockVariable);
03029     }
03030 
03031     return TRUE;
03032 }
03033 
03034 
03035 /***********************************************************************
03036  *           RtlSizeHeap
03037  * PARAMS
03038  *   Heap  [in] Handle of heap
03039  *   Flags   [in] Heap size control flags
03040  *   Ptr     [in] Address of memory to return size for
03041  *
03042  * RETURNS
03043  * Size in bytes of allocated memory
03044  * 0xffffffff: Failure
03045  *
03046  * @implemented
03047  */
03048 SIZE_T NTAPI
03049 RtlSizeHeap(
03050    HANDLE HeapPtr,
03051    ULONG Flags,
03052    PVOID Ptr
03053 )
03054 {
03055     PHEAP Heap = (PHEAP)HeapPtr;
03056     PHEAP_ENTRY HeapEntry;
03057     SIZE_T EntrySize;
03058 
03059     // FIXME This is a hack around missing SEH support!
03060     if (!Heap)
03061     {
03062         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE);
03063         return (SIZE_T)-1;
03064     }
03065 
03066     /* Force flags */
03067     Flags |= Heap->ForceFlags;
03068 
03069     /* Call special heap */
03070     if (RtlpHeapIsSpecial(Flags))
03071         return RtlDebugSizeHeap(Heap, Flags, Ptr);
03072 
03073     /* Get the heap entry pointer */
03074     HeapEntry = (PHEAP_ENTRY)Ptr - 1;
03075 
03076     /* Return -1 if that entry is free */
03077     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
03078     {
03079         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
03080         return (SIZE_T)-1;
03081     }
03082 
03083     /* Get size of this block depending if it's a usual or a big one */
03084     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
03085     {
03086         EntrySize = RtlpGetSizeOfBigBlock(HeapEntry);
03087     }
03088     else
03089     {
03090         /* Calculate it */
03091         EntrySize = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
03092     }
03093 
03094     /* Return calculated size */
03095     return EntrySize;
03096 }
03097 
03098 BOOLEAN NTAPI
03099 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry)
03100 {
03101     SIZE_T Size, Result;
03102     PCHAR TailPart;
03103 
03104     /* Calculate size */
03105     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)
03106         Size = RtlpGetSizeOfBigBlock(HeapEntry);
03107     else
03108         Size = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
03109 
03110     /* Calculate pointer to the tail part of the block */
03111     TailPart = (PCHAR)(HeapEntry + 1) + Size;
03112 
03113     /* Compare tail pattern */
03114     Result = RtlCompareMemory(TailPart,
03115                               FillPattern,
03116                               HEAP_ENTRY_SIZE);
03117 
03118     if (Result != HEAP_ENTRY_SIZE)
03119     {
03120         DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size, HeapEntry, TailPart + Result);
03121         return FALSE;
03122     }
03123 
03124     /* All is fine */
03125     return TRUE;
03126 }
03127 
03128 BOOLEAN NTAPI
03129 RtlpValidateHeapHeaders(
03130     PHEAP Heap,
03131     BOOLEAN Recalculate)
03132 {
03133     // We skip header validation for now
03134     return TRUE;
03135 }
03136 
03137 BOOLEAN NTAPI
03138 RtlpValidateHeapEntry(
03139     PHEAP Heap,
03140     PHEAP_ENTRY HeapEntry)
03141 {
03142     BOOLEAN BigAllocation, EntryFound = FALSE;
03143     PHEAP_SEGMENT Segment;
03144     ULONG SegmentOffset;
03145 
03146     /* Perform various consistency checks of this entry */
03147     if (!HeapEntry) goto invalid_entry;
03148     if ((ULONG_PTR)HeapEntry & (HEAP_ENTRY_SIZE - 1)) goto invalid_entry;
03149     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) goto invalid_entry;
03150 
03151     BigAllocation = HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC;
03152     Segment = Heap->Segments[HeapEntry->SegmentOffset];
03153 
03154     if (BigAllocation &&
03155         (((ULONG_PTR)HeapEntry & (PAGE_SIZE - 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock)))
03156          goto invalid_entry;
03157 
03158     if (!BigAllocation && (HeapEntry->SegmentOffset >= HEAP_SEGMENTS ||
03159         !Segment ||
03160         HeapEntry < Segment->FirstEntry ||
03161         HeapEntry >= Segment->LastValidEntry))
03162         goto invalid_entry;
03163 
03164     if ((HeapEntry->Flags & HEAP_ENTRY_FILL_PATTERN) &&
03165         !RtlpCheckInUsePattern(HeapEntry))
03166         goto invalid_entry;
03167 
03168     /* Checks are done, if this is a virtual entry, that's all */
03169     if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) return TRUE;
03170 
03171     /* Go through segments and check if this entry fits into any of them */
03172     for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
03173     {
03174         Segment = Heap->Segments[SegmentOffset];
03175         if (!Segment) continue;
03176 
03177         if ((HeapEntry >= Segment->FirstEntry) &&
03178             (HeapEntry < Segment->LastValidEntry))
03179         {
03180             /* Got it */
03181             EntryFound = TRUE;
03182             break;
03183         }
03184     }
03185 
03186     /* Return our result of finding entry in the segments */
03187     return EntryFound;
03188 
03189 invalid_entry:
03190     DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry, Heap);
03191     return FALSE;
03192 }
03193 
03194 BOOLEAN NTAPI
03195 RtlpValidateHeapSegment(
03196     PHEAP Heap,
03197     PHEAP_SEGMENT Segment,
03198     UCHAR SegmentOffset,
03199     PULONG FreeEntriesCount,
03200     PSIZE_T TotalFreeSize,
03201     PSIZE_T TagEntries,
03202     PSIZE_T PseudoTagEntries)
03203 {
03204     PHEAP_UCR_DESCRIPTOR UcrDescriptor;
03205     PLIST_ENTRY UcrEntry;
03206     SIZE_T ByteSize, Size, Result;
03207     PHEAP_ENTRY CurrentEntry;
03208     ULONG UnCommittedPages;
03209     ULONG UnCommittedRanges;
03210     ULONG PreviousSize;
03211 
03212     UnCommittedPages = 0;
03213     UnCommittedRanges = 0;
03214 
03215     if (IsListEmpty(&Segment->UCRSegmentList))
03216     {
03217         UcrEntry = NULL;
03218         UcrDescriptor = NULL;
03219     }
03220     else
03221     {
03222         UcrEntry = Segment->UCRSegmentList.Flink;
03223         UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
03224     }
03225 
03226     if (Segment->BaseAddress == Heap)
03227         CurrentEntry = &Heap->Entry;
03228     else
03229         CurrentEntry = &Segment->Entry;
03230 
03231     while (CurrentEntry < Segment->LastValidEntry)
03232     {
03233         if (UcrDescriptor &&
03234             ((PVOID)CurrentEntry >= UcrDescriptor->Address))
03235         {
03236             DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n",
03237                     CurrentEntry, UcrDescriptor->Address,
03238                     (PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
03239 
03240             return FALSE;
03241         }
03242 
03243         PreviousSize = 0;
03244 
03245         while (CurrentEntry < Segment->LastValidEntry)
03246         {
03247             if (PreviousSize != CurrentEntry->PreviousSize)
03248             {
03249                 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n",
03250                     CurrentEntry, CurrentEntry->PreviousSize, PreviousSize);
03251 
03252                 return FALSE;
03253             }
03254 
03255             PreviousSize = CurrentEntry->Size;
03256             Size = CurrentEntry->Size << HEAP_ENTRY_SHIFT;
03257 
03258             if (CurrentEntry->Flags & HEAP_ENTRY_BUSY)
03259             {
03260                 if (TagEntries)
03261                 {
03262                     UNIMPLEMENTED;
03263                 }
03264 
03265                 /* Check fill pattern */
03266                 if (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN)
03267                 {
03268                     if (!RtlpCheckInUsePattern(CurrentEntry))
03269                         return FALSE;
03270                 }
03271             }
03272             else
03273             {
03274                 /* The entry is free, increase free entries count and total free size */
03275                 *FreeEntriesCount = *FreeEntriesCount + 1;
03276                 *TotalFreeSize += CurrentEntry->Size;
03277 
03278                 if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) &&
03279                     (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN))
03280                 {
03281                     ByteSize = Size - sizeof(HEAP_FREE_ENTRY);
03282 
03283                     if ((CurrentEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) &&
03284                         (ByteSize > sizeof(HEAP_FREE_ENTRY_EXTRA)))
03285                     {
03286                         ByteSize -= sizeof(HEAP_FREE_ENTRY_EXTRA);
03287                     }
03288 
03289                     Result = RtlCompareMemoryUlong((PCHAR)((PHEAP_FREE_ENTRY)CurrentEntry + 1),
03290                                                     ByteSize,
03291                                                     ARENA_FREE_FILLER);
03292 
03293                     if (Result != ByteSize)
03294                     {
03295                         DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n",
03296                             CurrentEntry,
03297                             (PCHAR)(CurrentEntry + 1) + Result);
03298 
03299                         return FALSE;
03300                     }
03301                 }
03302             }
03303 
03304             if (CurrentEntry->SegmentOffset != SegmentOffset)
03305             {
03306                 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n",
03307                         CurrentEntry, SegmentOffset, CurrentEntry->SegmentOffset);
03308                 return FALSE;
03309             }
03310 
03311             /* Check if it's the last entry */
03312             if (CurrentEntry->Flags & HEAP_ENTRY_LAST_ENTRY)
03313             {
03314                 CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
03315 
03316                 if (!UcrDescriptor)
03317                 {
03318                     /* Check if it's not really the last one */
03319                     if (CurrentEntry != Segment->LastValidEntry)
03320                     {
03321                         DPRINT1("HEAP: Heap entry %p is not last block in segment (%p)\n",
03322                                 CurrentEntry, Segment->LastValidEntry);
03323                         return FALSE;
03324                     }
03325                 }
03326                 else if (CurrentEntry != UcrDescriptor->Address)
03327                 {
03328                     DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n",
03329                         CurrentEntry, UcrDescriptor->Address);
03330 
03331                     return FALSE;
03332                 }
03333                 else
03334                 {
03335                     UnCommittedPages += (ULONG)(UcrDescriptor->Size / PAGE_SIZE);
03336                     UnCommittedRanges++;
03337 
03338                     CurrentEntry = (PHEAP_ENTRY)((PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
03339 
03340                     /* Go to the next UCR descriptor */
03341                     UcrEntry = UcrEntry->Flink;
03342                     if (UcrEntry == &Segment->UCRSegmentList)
03343                     {
03344                         UcrEntry = NULL;
03345                         UcrDescriptor = NULL;
03346                     }
03347                     else
03348                     {
03349                         UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry);
03350                     }
03351                 }
03352 
03353                 break;
03354             }
03355 
03356             /* Advance to the next entry */
03357             CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
03358         }
03359     }
03360 
03361     /* Check total numbers of UCP and UCR */
03362     if (Segment->NumberOfUnCommittedPages != UnCommittedPages)
03363     {
03364         DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n",
03365             Segment, Segment->NumberOfUnCommittedPages, UnCommittedPages);
03366 
03367         return FALSE;
03368     }
03369 
03370     if (Segment->NumberOfUnCommittedRanges != UnCommittedRanges)
03371     {
03372         DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n",
03373             Segment, Segment->NumberOfUnCommittedRanges, UnCommittedRanges);
03374 
03375         return FALSE;
03376     }
03377 
03378     return TRUE;
03379 }
03380 
03381 BOOLEAN NTAPI
03382 RtlpValidateHeap(PHEAP Heap,
03383                  BOOLEAN ForceValidation)
03384 {
03385     PHEAP_SEGMENT Segment;
03386     BOOLEAN EmptyList;
03387     UCHAR SegmentOffset;
03388     SIZE_T Size, TotalFreeSize;
03389     ULONG PreviousSize;
03390     PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
03391     PLIST_ENTRY ListHead, NextEntry;
03392     PHEAP_FREE_ENTRY FreeEntry;
03393     ULONG FreeBlocksCount, FreeListEntriesCount;
03394 
03395     /* Check headers */
03396     if (!RtlpValidateHeapHeaders(Heap, FALSE))
03397         return FALSE;
03398 
03399     /* Skip validation if it's not needed */
03400     if (!ForceValidation && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED))
03401         return TRUE;
03402 
03403     /* Check free lists bitmaps */
03404     FreeListEntriesCount = 0;
03405     ListHead = &Heap->FreeLists[0];
03406 
03407     for (Size = 0; Size < HEAP_FREELISTS; Size++)
03408     {
03409         if (Size)
03410         {
03411             /* This is a dedicated list. Check if it's empty */
03412             EmptyList = IsListEmpty(ListHead);
03413 
03414             if (Heap->u.FreeListsInUseBytes[Size >> 3] & (1 << (Size & 7)))
03415             {
03416                 if (EmptyList)
03417                 {
03418                     DPRINT1("HEAP: Empty %x-free list marked as non-empty\n", Size);
03419                     return FALSE;
03420                 }
03421             }
03422             else
03423             {
03424                 if (!EmptyList)
03425                 {
03426                     DPRINT1("HEAP: Non-empty %x-free list marked as empty\n", Size);
03427                     return FALSE;
03428                 }
03429             }
03430         }
03431 
03432         /* Now check this list entries */
03433         NextEntry = ListHead->Flink;
03434         PreviousSize = 0;
03435 
03436         while (ListHead != NextEntry)
03437         {
03438             FreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList);
03439             NextEntry = NextEntry->Flink;
03440 
03441             /* If there is an in-use entry in a free list - that's quite a big problem */
03442             if (FreeEntry->Flags & HEAP_ENTRY_BUSY)
03443             {
03444                 DPRINT1("HEAP: %Ix-dedicated list free element %p is marked in-use\n", Size, FreeEntry);
03445                 return FALSE;
03446             }
03447 
03448             /* Check sizes according to that specific list's size */
03449             if ((Size == 0) && (FreeEntry->Size < HEAP_FREELISTS))
03450             {
03451                 DPRINT1("HEAP: Non dedicated list free element %p has size %x which would fit a dedicated list\n", FreeEntry, FreeEntry->Size);
03452                 return FALSE;
03453             }
03454             else if (Size && (FreeEntry->Size != Size))
03455             {
03456                 DPRINT1("HEAP: %Ix-dedicated list free element %p has incorrect size %x\n", Size, FreeEntry, FreeEntry->Size);
03457                 return FALSE;
03458             }
03459             else if ((Size == 0) && (FreeEntry->Size < PreviousSize))
03460             {
03461                 DPRINT1("HEAP: Non dedicated list free element %p is not put in order\n", FreeEntry);
03462                 return FALSE;
03463             }
03464 
03465             /* Remember previous size*/
03466             PreviousSize = FreeEntry->Size;
03467 
03468             /* Add up to the total amount of free entries */
03469             FreeListEntriesCount++;
03470         }
03471 
03472         /* Go to the head of the next free list */
03473         ListHead++;
03474     }
03475 
03476     /* Check big allocations */
03477     ListHead = &Heap->VirtualAllocdBlocks;
03478     NextEntry = ListHead->Flink;
03479 
03480     while (ListHead != NextEntry)
03481     {
03482         VirtualAllocBlock = CONTAINING_RECORD(NextEntry, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
03483 
03484         /* We can only check the fill pattern */
03485         if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN)
03486         {
03487             if (!RtlpCheckInUsePattern(&VirtualAllocBlock->BusyBlock))
03488                 return FALSE;
03489         }
03490 
03491         NextEntry = NextEntry->Flink;
03492     }
03493 
03494     /* Check all segments */
03495     FreeBlocksCount = 0;
03496     TotalFreeSize = 0;
03497 
03498     for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++)
03499     {
03500         Segment = Heap->Segments[SegmentOffset];
03501 
03502         /* Go to the next one if there is no segment */
03503         if (!Segment) continue;
03504 
03505         if (!RtlpValidateHeapSegment(Heap,
03506                                      Segment,
03507                                      SegmentOffset,
03508                                      &FreeBlocksCount,
03509                                      &TotalFreeSize,
03510                                      NULL,
03511                                      NULL))
03512         {
03513             return FALSE;
03514         }
03515     }
03516 
03517     if (FreeListEntriesCount != FreeBlocksCount)
03518     {
03519         DPRINT1("HEAP: Free blocks count in arena (%lu) does not match free blocks number in the free lists (%lu)\n", FreeBlocksCount, FreeListEntriesCount);
03520         return FALSE;
03521     }
03522 
03523     if (Heap->TotalFreeSize != TotalFreeSize)
03524     {
03525         DPRINT1("HEAP: Total size of free blocks in arena (%Iu) does not equal to the one in heap header (%Iu)\n", TotalFreeSize, Heap->TotalFreeSize);
03526         return FALSE;
03527     }
03528 
03529     return TRUE;
03530 }
03531 
03532 /***********************************************************************
03533  *           RtlValidateHeap
03534  * Validates a specified heap.
03535  *
03536  * PARAMS
03537  *   Heap  [in] Handle to the heap
03538  *   Flags   [in] Bit flags that control access during operation
03539  *   Block  [in] Optional pointer to memory block to validate
03540  *
03541  * NOTES
03542  * Flags is ignored.
03543  *
03544  * RETURNS
03545  * TRUE: Success
03546  * FALSE: Failure
03547  *
03548  * @implemented
03549  */
03550 BOOLEAN NTAPI RtlValidateHeap(
03551    HANDLE HeapPtr,
03552    ULONG Flags,
03553    PVOID Block
03554 )
03555 {
03556     PHEAP Heap = (PHEAP)HeapPtr;
03557     BOOLEAN HeapLocked = FALSE;
03558     BOOLEAN HeapValid;
03559 
03560     /* Check for page heap */
03561     if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS)
03562         return RtlpDebugPageHeapValidate(HeapPtr, Flags, Block);
03563 
03564     /* Check signature */
03565     if (Heap->Signature != HEAP_SIGNATURE)
03566     {
03567         DPRINT1("HEAP: Signature %lx is invalid for heap %p\n", Heap->Signature, Heap);
03568         return FALSE;
03569     }
03570 
03571     /* Force flags */
03572     Flags = Heap->ForceFlags;
03573 
03574     /* Acquire the lock if necessary */
03575     if (!(Flags & HEAP_NO_SERIALIZE))
03576     {
03577         RtlEnterHeapLock(Heap->LockVariable, TRUE);
03578         HeapLocked = TRUE;
03579     }
03580 
03581     /* Either validate whole heap or just one entry */
03582     if (!Block)
03583         HeapValid = RtlpValidateHeap(Heap, TRUE);
03584     else
03585         HeapValid = RtlpValidateHeapEntry(Heap, (PHEAP_ENTRY)Block - 1);
03586 
03587     /* Unlock if it's lockable */
03588     if (HeapLocked)
03589     {
03590         RtlLeaveHeapLock(Heap->LockVariable);
03591     }
03592 
03593     return HeapValid;
03594 }
03595 
03596 /*
03597  * @implemented
03598  */
03599 NTSTATUS NTAPI
03600 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine,
03601                     PVOID lParam)
03602 {
03603     UNIMPLEMENTED;
03604     return STATUS_NOT_IMPLEMENTED;
03605 }
03606 
03607 
03608 /*
03609  * @implemented
03610  */
03611 ULONG NTAPI
03612 RtlGetProcessHeaps(ULONG count,
03613                    HANDLE *heaps)
03614 {
03615     UNIMPLEMENTED;
03616     return 0;
03617 }
03618 
03619 
03620 /*
03621  * @implemented
03622  */
03623 BOOLEAN NTAPI
03624 RtlValidateProcessHeaps(VOID)
03625 {
03626     UNIMPLEMENTED;
03627     return TRUE;
03628 }
03629 
03630 
03631 /*
03632  * @unimplemented
03633  */
03634 BOOLEAN NTAPI
03635 RtlZeroHeap(
03636     IN PVOID HeapHandle,
03637     IN ULONG Flags
03638     )
03639 {
03640     UNIMPLEMENTED;
03641     return FALSE;
03642 }
03643 
03644 /*
03645  * @implemented
03646  */
03647 BOOLEAN
03648 NTAPI
03649 RtlSetUserValueHeap(IN PVOID HeapHandle,
03650                     IN ULONG Flags,
03651                     IN PVOID BaseAddress,
03652                     IN PVOID UserValue)
03653 {
03654     PHEAP Heap = (PHEAP)HeapHandle;
03655     PHEAP_ENTRY HeapEntry;
03656     PHEAP_ENTRY_EXTRA Extra;
03657     BOOLEAN HeapLocked = FALSE, ValueSet = FALSE;
03658 
03659     /* Force flags */
03660     Flags |= Heap->Flags;
03661 
03662     /* Call special heap */
03663     if (RtlpHeapIsSpecial(Flags))
03664         return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue);
03665 
03666     /* Lock if it's lockable */
03667     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
03668     {
03669         RtlEnterHeapLock(Heap->LockVariable, TRUE);
03670         HeapLocked = TRUE;
03671     }
03672 
03673     /* Get a pointer to the entry */
03674     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
03675 
03676     /* If it's a free entry - return error */
03677     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
03678     {
03679         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
03680 
03681         /* Release the heap lock if it was acquired */
03682         if (HeapLocked)
03683             RtlLeaveHeapLock(Heap->LockVariable);
03684 
03685         return FALSE;
03686     }
03687 
03688     /* Check if this entry has an extra stuff associated with it */
03689     if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
03690     {
03691         /* Use extra to store the value */
03692         Extra = RtlpGetExtraStuffPointer(HeapEntry);
03693         Extra->Settable = (ULONG_PTR)UserValue;
03694 
03695         /* Indicate that value was set */
03696         ValueSet = TRUE;
03697     }
03698 
03699     /* Release the heap lock if it was acquired */
03700     if (HeapLocked)
03701         RtlLeaveHeapLock(Heap->LockVariable);
03702 
03703     return ValueSet;
03704 }
03705 
03706 /*
03707  * @implemented
03708  */
03709 BOOLEAN
03710 NTAPI
03711 RtlSetUserFlagsHeap(IN PVOID HeapHandle,
03712                     IN ULONG Flags,
03713                     IN PVOID BaseAddress,
03714                     IN ULONG UserFlagsReset,
03715                     IN ULONG UserFlagsSet)
03716 {
03717     PHEAP Heap = (PHEAP)HeapHandle;
03718     PHEAP_ENTRY HeapEntry;
03719     BOOLEAN HeapLocked = FALSE;
03720 
03721     /* Force flags */
03722     Flags |= Heap->Flags;
03723 
03724     /* Call special heap */
03725     if (RtlpHeapIsSpecial(Flags))
03726         return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
03727 
03728     /* Lock if it's lockable */
03729     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
03730     {
03731         RtlEnterHeapLock(Heap->LockVariable, TRUE);
03732         HeapLocked = TRUE;
03733     }
03734 
03735     /* Get a pointer to the entry */
03736     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
03737 
03738     /* If it's a free entry - return error */
03739     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
03740     {
03741         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
03742 
03743         /* Release the heap lock if it was acquired */
03744         if (HeapLocked)
03745             RtlLeaveHeapLock(Heap->LockVariable);
03746 
03747         return FALSE;
03748     }
03749 
03750     /* Set / reset flags */
03751     HeapEntry->Flags &= ~(UserFlagsReset >> 4);
03752     HeapEntry->Flags |= (UserFlagsSet >> 4);
03753 
03754     /* Release the heap lock if it was acquired */
03755     if (HeapLocked)
03756         RtlLeaveHeapLock(Heap->LockVariable);
03757 
03758     return TRUE;
03759 }
03760 
03761 /*
03762  * @implemented
03763  */
03764 BOOLEAN
03765 NTAPI
03766 RtlGetUserInfoHeap(IN PVOID HeapHandle,
03767                    IN ULONG Flags,
03768                    IN PVOID BaseAddress,
03769                    OUT PVOID *UserValue,
03770                    OUT PULONG UserFlags)
03771 {
03772     PHEAP Heap = (PHEAP)HeapHandle;
03773     PHEAP_ENTRY HeapEntry;
03774     PHEAP_ENTRY_EXTRA Extra;
03775     BOOLEAN HeapLocked = FALSE;
03776 
03777     /* Force flags */
03778     Flags |= Heap->Flags;
03779 
03780     /* Call special heap */
03781     if (RtlpHeapIsSpecial(Flags))
03782         return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags);
03783 
03784     /* Lock if it's lockable */
03785     if (!(Heap->Flags & HEAP_NO_SERIALIZE))
03786     {
03787         RtlEnterHeapLock(Heap->LockVariable, TRUE);
03788         HeapLocked = TRUE;
03789     }
03790 
03791     /* Get a pointer to the entry */
03792     HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
03793 
03794     /* If it's a free entry - return error */
03795     if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY))
03796     {
03797         RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
03798 
03799         /* Release the heap lock if it was acquired */
03800         if (HeapLocked)
03801             RtlLeaveHeapLock(Heap->LockVariable);
03802 
03803         return FALSE;
03804     }
03805 
03806     /* Check if this entry has an extra stuff associated with it */
03807     if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT)
03808     {
03809         /* Get pointer to extra data */
03810         Extra = RtlpGetExtraStuffPointer(HeapEntry);
03811 
03812         /* Pass user value */
03813         if (UserValue)
03814             *UserValue = (PVOID)Extra->Settable;
03815     }
03816 
03817     /* Decode and return user flags */
03818     if (UserFlags)
03819         *UserFlags = (HeapEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
03820 
03821     /* Release the heap lock if it was acquired */
03822     if (HeapLocked)
03823         RtlLeaveHeapLock(Heap->LockVariable);
03824 
03825     return TRUE;
03826 }
03827 
03828 /*
03829  * @unimplemented
03830  */
03831 NTSTATUS
03832 NTAPI
03833 RtlUsageHeap(IN HANDLE Heap,
03834              IN ULONG Flags,
03835              OUT PRTL_HEAP_USAGE Usage)
03836 {
03837     /* TODO */
03838     UNIMPLEMENTED;
03839     return STATUS_NOT_IMPLEMENTED;
03840 }
03841 
03842 PWSTR
03843 NTAPI
03844 RtlQueryTagHeap(IN PVOID HeapHandle,
03845                 IN ULONG Flags,
03846                 IN USHORT TagIndex,
03847                 IN BOOLEAN ResetCounters,
03848                 OUT PRTL_HEAP_TAG_INFO HeapTagInfo)
03849 {
03850     /* TODO */
03851     UNIMPLEMENTED;
03852     return NULL;
03853 }
03854 
03855 ULONG
03856 NTAPI
03857 RtlExtendHeap(IN HANDLE Heap,
03858               IN ULONG Flags,
03859               IN PVOID P,
03860               IN SIZE_T Size)
03861 {
03862     /* TODO */
03863     UNIMPLEMENTED;
03864     return 0;
03865 }
03866 
03867 ULONG
03868 NTAPI
03869 RtlCreateTagHeap(IN HANDLE HeapHandle,
03870                  IN ULONG Flags,
03871                  IN PWSTR TagName,
03872                  IN PWSTR TagSubName)
03873 {
03874     /* TODO */
03875     UNIMPLEMENTED;
03876     return 0;
03877 }
03878 
03879 NTSTATUS
03880 NTAPI
03881 RtlWalkHeap(IN HANDLE HeapHandle,
03882             IN PVOID HeapEntry)
03883 {
03884     UNIMPLEMENTED;
03885     return STATUS_NOT_IMPLEMENTED;
03886 }
03887 
03888 PVOID
03889 NTAPI
03890 RtlProtectHeap(IN PVOID HeapHandle,
03891                IN BOOLEAN ReadOnly)
03892 {
03893     UNIMPLEMENTED;
03894     return NULL;
03895 }
03896 
03897 NTSTATUS
03898 NTAPI
03899 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL,
03900                       IN HEAP_INFORMATION_CLASS HeapInformationClass,
03901                       IN PVOID HeapInformation,
03902                       IN SIZE_T HeapInformationLength)
03903 {
03904     /* Setting heap information is not really supported except for enabling LFH */
03905     if (HeapInformationClass == HeapCompatibilityInformation)
03906     {
03907         /* Check buffer length */
03908         if (HeapInformationLength < sizeof(ULONG))
03909         {
03910             /* The provided buffer is too small */
03911             return STATUS_BUFFER_TOO_SMALL;
03912         }
03913 
03914         /* Check for a special magic value for enabling LFH */
03915         if (*(PULONG)HeapInformation != 2)
03916         {
03917             return STATUS_UNSUCCESSFUL;
03918         }
03919 
03920         DPRINT1("RtlSetHeapInformation() needs to enable LFH\n");
03921         return STATUS_SUCCESS;
03922     }
03923 
03924     return STATUS_SUCCESS;
03925 }
03926 
03927 NTSTATUS
03928 NTAPI
03929 RtlQueryHeapInformation(HANDLE HeapHandle,
03930                         HEAP_INFORMATION_CLASS HeapInformationClass,
03931                         PVOID HeapInformation,
03932                         SIZE_T HeapInformationLength,
03933                         PSIZE_T ReturnLength OPTIONAL)
03934 {
03935     PHEAP Heap = (PHEAP)HeapHandle;
03936 
03937     /* Only HeapCompatibilityInformation is supported */
03938     if (HeapInformationClass == HeapCompatibilityInformation)
03939     {
03940         /* Set result length */
03941         if (ReturnLength)
03942             *ReturnLength = sizeof(ULONG);
03943 
03944         /* Check buffer length */
03945         if (HeapInformationLength < sizeof(ULONG))
03946         {
03947             /* It's too small, return needed length */
03948             return STATUS_BUFFER_TOO_SMALL;
03949         }
03950 
03951         /* Return front end heap type */
03952         *(PULONG)HeapInformation = Heap->FrontEndHeapType;
03953 
03954         return STATUS_SUCCESS;
03955     }
03956 
03957     return STATUS_UNSUCCESSFUL;
03958 }
03959 
03960 NTSTATUS
03961 NTAPI
03962 RtlMultipleAllocateHeap(IN PVOID HeapHandle,
03963                         IN ULONG Flags,
03964                         IN SIZE_T Size,
03965                         IN ULONG Count,
03966                         OUT PVOID *Array)
03967 {
03968     UNIMPLEMENTED;
03969     return 0;
03970 }
03971 
03972 NTSTATUS
03973 NTAPI
03974 RtlMultipleFreeHeap(IN PVOID HeapHandle,
03975                     IN ULONG Flags,
03976                     IN ULONG Count,
03977                     OUT PVOID *Array)
03978 {
03979     UNIMPLEMENTED;
03980     return 0;
03981 }
03982 
03983 /* EOF */