1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/heap.c 5 * PURPOSE: RTL Heap backend allocator 6 * PROGRAMMERS: Copyright 2010 Aleksey Bragin 7 * Copyright 2020 Katayama Hirofumi MZ 8 */ 9 10 /* Useful references: 11 http://msdn.microsoft.com/en-us/library/ms810466.aspx 12 http://msdn.microsoft.com/en-us/library/ms810603.aspx 13 http://www.securitylab.ru/analytics/216376.php 14 http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry 15 http://www.phreedom.org/research/exploits/asn1-bitstring/ 16 http://illmatics.com/Understanding_the_LFH.pdf 17 http://www.alex-ionescu.com/?p=18 18 */ 19 20 /* INCLUDES *****************************************************************/ 21 22 #include <rtl.h> 23 #include <heap.h> 24 25 #define NDEBUG 26 #include <debug.h> 27 28 /* Bitmaps stuff */ 29 30 /* How many least significant bits are clear */ 31 UCHAR RtlpBitsClearLow[] = 32 { 33 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 34 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 35 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 36 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 37 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 38 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 39 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 40 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 41 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 42 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 43 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 44 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 45 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 46 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 47 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 48 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 49 }; 50 51 FORCEINLINE 52 UCHAR 53 RtlpFindLeastSetBit(ULONG Bits) 54 { 55 if (Bits & 0xFFFF) 56 { 57 if (Bits & 0xFF) 58 return RtlpBitsClearLow[Bits & 0xFF]; /* Lowest byte */ 59 else 60 return RtlpBitsClearLow[(Bits >> 8) & 0xFF] + 8; /* 2nd byte */ 61 } 62 else 63 { 64 if ((Bits >> 16) & 0xFF) 65 return RtlpBitsClearLow[(Bits >> 16) & 0xFF] + 16; /* 3rd byte */ 66 else 67 return RtlpBitsClearLow[(Bits >> 24) & 0xFF] + 24; /* Highest byte */ 68 } 69 } 70 71 /* Maximum size of a tail-filling pattern used for compare operation */ 72 UCHAR FillPattern[HEAP_ENTRY_SIZE] = 73 { 74 HEAP_TAIL_FILL, 75 HEAP_TAIL_FILL, 76 HEAP_TAIL_FILL, 77 HEAP_TAIL_FILL, 78 HEAP_TAIL_FILL, 79 HEAP_TAIL_FILL, 80 HEAP_TAIL_FILL, 81 HEAP_TAIL_FILL 82 }; 83 84 static 85 BOOLEAN 86 RtlpIsLastCommittedEntry(PHEAP_ENTRY Entry) 87 { 88 if (Entry->Flags & HEAP_ENTRY_LAST_ENTRY) 89 return TRUE; 90 91 Entry = Entry + Entry->Size; 92 93 /* 1-sized busy last entry are the committed range guard entries */ 94 if ((Entry->Flags != (HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY)) || (Entry->Size != 1)) 95 return FALSE; 96 97 /* This must be the last or the penultimate entry in the page */ 98 ASSERT(((PVOID)PAGE_ROUND_UP(Entry) == (Entry + 1)) || 99 ((PVOID)PAGE_ROUND_UP(Entry)== (Entry + 2))); 100 return TRUE; 101 } 102 103 /* FUNCTIONS *****************************************************************/ 104 105 NTSTATUS NTAPI 106 RtlpInitializeHeap(OUT PHEAP Heap, 107 IN ULONG Flags, 108 IN PHEAP_LOCK Lock OPTIONAL, 109 IN PRTL_HEAP_PARAMETERS Parameters) 110 { 111 ULONG NumUCRs = 8; 112 ULONG Index; 113 SIZE_T HeaderSize; 114 NTSTATUS Status; 115 PHEAP_UCR_DESCRIPTOR UcrDescriptor; 116 SIZE_T DeCommitFreeBlockThreshold; 117 118 /* Preconditions */ 119 ASSERT(Heap != NULL); 120 ASSERT(Parameters != NULL); 121 ASSERT(!(Flags & HEAP_LOCK_USER_ALLOCATED)); 122 ASSERT(!(Flags & HEAP_NO_SERIALIZE) || (Lock == NULL)); /* HEAP_NO_SERIALIZE => no lock */ 123 124 /* Make sure we're not doing stupid things */ 125 DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_ENTRY_SHIFT; 126 /* Start out with the size of a plain Heap header + our hints of free entries + the bitmap */ 127 HeaderSize = FIELD_OFFSET(HEAP, FreeHints[DeCommitFreeBlockThreshold]) 128 + (ROUND_UP(DeCommitFreeBlockThreshold, RTL_BITS_OF(ULONG)) / RTL_BITS_OF(ULONG)) * sizeof(ULONG); 129 130 /* Check if space needs to be added for the Heap Lock */ 131 if (!(Flags & HEAP_NO_SERIALIZE)) 132 { 133 if (Lock != NULL) 134 /* The user manages the Heap Lock */ 135 Flags |= HEAP_LOCK_USER_ALLOCATED; 136 else 137 if (RtlpGetMode() == UserMode) 138 { 139 /* In user mode, the Heap Lock trails the Heap header */ 140 Lock = (PHEAP_LOCK) ((ULONG_PTR) (Heap) + HeaderSize); 141 HeaderSize += sizeof(HEAP_LOCK); 142 } 143 } 144 145 /* Add space for the initial Heap UnCommitted Range Descriptor list */ 146 UcrDescriptor = (PHEAP_UCR_DESCRIPTOR) ((ULONG_PTR) (Heap) + HeaderSize); 147 HeaderSize += NumUCRs * sizeof(HEAP_UCR_DESCRIPTOR); 148 149 HeaderSize = ROUND_UP(HeaderSize, HEAP_ENTRY_SIZE); 150 /* Sanity check */ 151 ASSERT(HeaderSize <= PAGE_SIZE); 152 153 /* Initialise the Heap Entry header containing the Heap header */ 154 Heap->Entry.Size = (USHORT)(HeaderSize >> HEAP_ENTRY_SHIFT); 155 Heap->Entry.Flags = HEAP_ENTRY_BUSY; 156 Heap->Entry.SmallTagIndex = LOBYTE(Heap->Entry.Size) ^ HIBYTE(Heap->Entry.Size) ^ Heap->Entry.Flags; 157 Heap->Entry.PreviousSize = 0; 158 Heap->Entry.SegmentOffset = 0; 159 Heap->Entry.UnusedBytes = 0; 160 161 /* Initialise the Heap header */ 162 Heap->Signature = HEAP_SIGNATURE; 163 Heap->Flags = Flags; 164 Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE | 165 HEAP_GENERATE_EXCEPTIONS | 166 HEAP_ZERO_MEMORY | 167 HEAP_REALLOC_IN_PLACE_ONLY | 168 HEAP_VALIDATE_PARAMETERS_ENABLED | 169 HEAP_VALIDATE_ALL_ENABLED | 170 HEAP_TAIL_CHECKING_ENABLED | 171 HEAP_CREATE_ALIGN_16 | 172 HEAP_FREE_CHECKING_ENABLED)); 173 174 /* Initialise the Heap parameters */ 175 Heap->VirtualMemoryThreshold = ROUND_UP(Parameters->VirtualMemoryThreshold, sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT; 176 Heap->SegmentReserve = Parameters->SegmentReserve; 177 Heap->SegmentCommit = Parameters->SegmentCommit; 178 Heap->DeCommitFreeBlockThreshold = DeCommitFreeBlockThreshold; 179 Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_ENTRY_SHIFT; 180 Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize; 181 Heap->CommitRoutine = Parameters->CommitRoutine; 182 183 /* Initialise the Heap validation info */ 184 Heap->HeaderValidateCopy = NULL; 185 Heap->HeaderValidateLength = (USHORT)HeaderSize; 186 187 /* Initialise the Heap Lock */ 188 if (!(Flags & HEAP_NO_SERIALIZE) && !(Flags & HEAP_LOCK_USER_ALLOCATED)) 189 { 190 Status = RtlInitializeHeapLock(&Lock); 191 if (!NT_SUCCESS(Status)) 192 return Status; 193 } 194 Heap->LockVariable = Lock; 195 196 /* Initialise the Heap alignment info */ 197 if (Flags & HEAP_CREATE_ALIGN_16) 198 { 199 Heap->AlignMask = (ULONG) ~15; 200 Heap->AlignRound = 15 + sizeof(HEAP_ENTRY); 201 } 202 else 203 { 204 Heap->AlignMask = (ULONG) ~(sizeof(HEAP_ENTRY) - 1); 205 Heap->AlignRound = 2 * sizeof(HEAP_ENTRY) - 1; 206 } 207 208 if (Flags & HEAP_TAIL_CHECKING_ENABLED) 209 Heap->AlignRound += sizeof(HEAP_ENTRY); 210 211 /* Initialise the Heap Segment list */ 212 for (Index = 0; Index < HEAP_SEGMENTS; ++Index) 213 Heap->Segments[Index] = NULL; 214 215 /* Initialise the free entry lists. */ 216 InitializeListHead(&Heap->FreeLists); 217 RtlInitializeBitMap(&Heap->FreeHintBitmap, 218 (PULONG)&Heap->FreeHints[DeCommitFreeBlockThreshold], 219 DeCommitFreeBlockThreshold); 220 RtlClearAllBits(&Heap->FreeHintBitmap); 221 RtlZeroMemory(&Heap->FreeHints[0], sizeof(Heap->FreeHints[0]) * DeCommitFreeBlockThreshold); 222 223 /* Initialise the Heap Virtual Allocated Blocks list */ 224 InitializeListHead(&Heap->VirtualAllocdBlocks); 225 226 /* Initialise the Heap UnCommitted Region lists */ 227 InitializeListHead(&Heap->UCRSegments); 228 InitializeListHead(&Heap->UCRList); 229 230 /* Register the initial Heap UnCommitted Region Descriptors */ 231 for (Index = 0; Index < NumUCRs; ++Index) 232 InsertTailList(&Heap->UCRList, &UcrDescriptor[Index].ListEntry); 233 234 return STATUS_SUCCESS; 235 } 236 237 VOID NTAPI 238 RtlpInsertFreeBlockHelper(PHEAP Heap, 239 PHEAP_FREE_ENTRY FreeEntry, 240 SIZE_T BlockSize, 241 BOOLEAN NoFill) 242 { 243 ULONG HintIndex, NextHintIndex; 244 245 ASSERT(FreeEntry->Size == BlockSize); 246 247 /* Fill if it's not denied */ 248 if (!NoFill) 249 { 250 FreeEntry->Flags &= ~(HEAP_ENTRY_FILL_PATTERN | 251 HEAP_ENTRY_EXTRA_PRESENT | 252 HEAP_ENTRY_BUSY); 253 254 if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) 255 { 256 RtlFillMemoryUlong((PCHAR)(FreeEntry + 1), 257 (BlockSize << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry), 258 ARENA_FREE_FILLER); 259 260 FreeEntry->Flags |= HEAP_ENTRY_FILL_PATTERN; 261 } 262 } 263 else 264 { 265 /* Clear out all flags except the last entry one */ 266 FreeEntry->Flags &= HEAP_ENTRY_LAST_ENTRY; 267 } 268 269 /* See if this should go to the dedicated list */ 270 if (BlockSize > Heap->DeCommitFreeBlockThreshold) 271 { 272 PLIST_ENTRY ListEntry = Heap->FreeHints[0]; 273 274 /* Check if we have a hint there */ 275 if (ListEntry == NULL) 276 { 277 ASSERT(!RtlTestBit(&Heap->FreeHintBitmap, 0)); 278 Heap->FreeHints[0] = &FreeEntry->FreeList; 279 RtlSetBit(&Heap->FreeHintBitmap, 0); 280 InsertTailList(&Heap->FreeLists, &FreeEntry->FreeList); 281 return; 282 } 283 284 ASSERT(RtlTestBit(&Heap->FreeHintBitmap, 0)); 285 286 while (ListEntry != &Heap->FreeLists) 287 { 288 PHEAP_FREE_ENTRY PreviousEntry = CONTAINING_RECORD(ListEntry, 289 HEAP_FREE_ENTRY, 290 FreeList); 291 if (PreviousEntry->Size >= BlockSize) 292 { 293 DPRINT("Inserting size %lu before %lu.\n", BlockSize, PreviousEntry->Size); 294 break; 295 } 296 297 ListEntry = ListEntry->Flink; 298 } 299 300 InsertTailList(ListEntry, &FreeEntry->FreeList); 301 302 /* Update our hint if needed */ 303 if (Heap->FreeHints[0] == ListEntry) 304 Heap->FreeHints[0] = &FreeEntry->FreeList; 305 306 return; 307 } 308 309 ASSERT(BlockSize >= 2); 310 HintIndex = BlockSize - 1; 311 312 if (Heap->FreeHints[HintIndex] != NULL) 313 { 314 ASSERT(RtlTestBit(&Heap->FreeHintBitmap, HintIndex)); 315 316 /* Insert it after our hint. */ 317 InsertHeadList(Heap->FreeHints[HintIndex], &FreeEntry->FreeList); 318 319 return; 320 } 321 322 /* This is the first time we insert such an entry in the list. */ 323 ASSERT(!RtlTestBit(&Heap->FreeHintBitmap, HintIndex)); 324 if (IsListEmpty(&Heap->FreeLists)) 325 { 326 /* First entry inserted in this list ever */ 327 InsertHeadList(&Heap->FreeLists, &FreeEntry->FreeList); 328 RtlSetBit(&Heap->FreeHintBitmap, HintIndex); 329 Heap->FreeHints[HintIndex] = &FreeEntry->FreeList; 330 return; 331 } 332 333 /* Find the closest one */ 334 NextHintIndex = RtlFindSetBits(&Heap->FreeHintBitmap, 1, HintIndex); 335 ASSERT(NextHintIndex != 0xFFFFFFFF); 336 if ((NextHintIndex == 0) || (NextHintIndex > HintIndex)) 337 { 338 /* 339 * We found a larger entry. Insert this one before. 340 * It is guaranteed to be our successor in the list. 341 */ 342 InsertTailList(Heap->FreeHints[NextHintIndex], &FreeEntry->FreeList); 343 } 344 else 345 { 346 /* We only found an entry smaller than us. Then we will be the largest one. */ 347 ASSERT(CONTAINING_RECORD(Heap->FreeLists.Blink, HEAP_FREE_ENTRY, FreeList)->Size < BlockSize); 348 InsertTailList(&Heap->FreeLists, &FreeEntry->FreeList); 349 } 350 351 /* Setup our hint */ 352 RtlSetBit(&Heap->FreeHintBitmap, HintIndex); 353 Heap->FreeHints[HintIndex] = &FreeEntry->FreeList; 354 } 355 356 PHEAP_FREE_ENTRY 357 NTAPI 358 RtlpInsertFreeBlock(PHEAP Heap, 359 PHEAP_FREE_ENTRY FreeEntry, 360 SIZE_T BlockSize) 361 { 362 USHORT Size, PreviousSize; 363 UCHAR SegmentOffset, Flags; 364 PHEAP_SEGMENT Segment; 365 PHEAP_FREE_ENTRY LastEntry; 366 367 DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap, FreeEntry, BlockSize); 368 369 /* Increase the free size counter */ 370 Heap->TotalFreeSize += BlockSize; 371 372 /* Remember certain values */ 373 LastEntry = FreeEntry; 374 Flags = FreeEntry->Flags; 375 PreviousSize = FreeEntry->PreviousSize; 376 SegmentOffset = FreeEntry->SegmentOffset; 377 Segment = Heap->Segments[SegmentOffset]; 378 379 /* Process it */ 380 while (BlockSize) 381 { 382 /* Check for the max size */ 383 if (BlockSize > HEAP_MAX_BLOCK_SIZE) 384 { 385 Size = HEAP_MAX_BLOCK_SIZE; 386 387 /* Special compensation if it goes above limit just by 1 */ 388 if (BlockSize == (HEAP_MAX_BLOCK_SIZE + 1)) 389 Size -= 16; 390 391 FreeEntry->Flags = 0; 392 } 393 else 394 { 395 Size = (USHORT)BlockSize; 396 FreeEntry->Flags = Flags; 397 } 398 399 /* Change its size and insert it into a free list */ 400 FreeEntry->Size = Size; 401 FreeEntry->PreviousSize = PreviousSize; 402 FreeEntry->SegmentOffset = SegmentOffset; 403 404 /* Call a helper to actually insert the block */ 405 RtlpInsertFreeBlockHelper(Heap, FreeEntry, Size, FALSE); 406 407 /* Update sizes */ 408 PreviousSize = Size; 409 BlockSize -= Size; 410 411 /* Go to the next entry */ 412 LastEntry = FreeEntry; 413 FreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size); 414 415 /* Check if that's all */ 416 if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry) 417 { 418 return LastEntry; 419 } 420 } 421 422 /* Update previous size if needed */ 423 if (!(Flags & HEAP_ENTRY_LAST_ENTRY)) 424 FreeEntry->PreviousSize = PreviousSize; 425 426 return LastEntry; 427 } 428 429 static 430 VOID 431 RtlpRemoveFreeBlock(PHEAP Heap, 432 PHEAP_FREE_ENTRY FreeEntry, 433 BOOLEAN NoFill) 434 { 435 SIZE_T Result, RealSize; 436 ULONG HintIndex; 437 438 /* This was a problem before we handled segments > MAXUSHORT. 439 * It may not be needed now, but is left just for safety. */ 440 ASSERT(FreeEntry->Size != 0); 441 442 /* Remove the free block */ 443 if (FreeEntry->Size > Heap->DeCommitFreeBlockThreshold) 444 HintIndex = 0; 445 else 446 HintIndex = FreeEntry->Size - 1; 447 448 ASSERT(RtlTestBit(&Heap->FreeHintBitmap, HintIndex)); 449 450 /* Are we removing the hint entry for this size ? */ 451 if (Heap->FreeHints[HintIndex] == &FreeEntry->FreeList) 452 { 453 PHEAP_FREE_ENTRY NewHintEntry = NULL; 454 if (FreeEntry->FreeList.Flink != &Heap->FreeLists) 455 { 456 NewHintEntry = CONTAINING_RECORD(FreeEntry->FreeList.Flink, 457 HEAP_FREE_ENTRY, 458 FreeList); 459 /* 460 * In non-dedicated list, we just put the next entry as hint. 461 * For the dedicated ones, we take care of putting entries of the right size hint. 462 */ 463 if ((HintIndex != 0) && (NewHintEntry->Size != FreeEntry->Size)) 464 { 465 /* Of course this must be a larger one after us */ 466 ASSERT(NewHintEntry->Size > FreeEntry->Size); 467 NewHintEntry = NULL; 468 } 469 } 470 471 /* Replace the hint, if we can */ 472 if (NewHintEntry != NULL) 473 { 474 Heap->FreeHints[HintIndex] = &NewHintEntry->FreeList; 475 } 476 else 477 { 478 Heap->FreeHints[HintIndex] = NULL; 479 RtlClearBit(&Heap->FreeHintBitmap, HintIndex); 480 } 481 } 482 483 RemoveEntryList(&FreeEntry->FreeList); 484 485 /* Fill with pattern if necessary */ 486 if (!NoFill && 487 (FreeEntry->Flags & HEAP_ENTRY_FILL_PATTERN)) 488 { 489 RealSize = (FreeEntry->Size << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry); 490 491 /* Deduct extra stuff from block's real size */ 492 if (FreeEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT && 493 RealSize > sizeof(HEAP_FREE_ENTRY_EXTRA)) 494 { 495 RealSize -= sizeof(HEAP_FREE_ENTRY_EXTRA); 496 } 497 498 /* Check if the free filler is intact */ 499 Result = RtlCompareMemoryUlong((PCHAR)(FreeEntry + 1), 500 RealSize, 501 ARENA_FREE_FILLER); 502 503 if (Result != RealSize) 504 { 505 DPRINT1("Free heap block %p modified at %p after it was freed\n", 506 FreeEntry, 507 (PCHAR)(FreeEntry + 1) + Result); 508 } 509 } 510 } 511 512 SIZE_T NTAPI 513 RtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry) 514 { 515 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; 516 517 /* Get pointer to the containing record */ 518 VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); 519 ASSERT(VirtualEntry->BusyBlock.Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY)); 520 521 /* Restore the real size */ 522 return VirtualEntry->CommitSize - HeapEntry->Size; 523 } 524 525 PHEAP_UCR_DESCRIPTOR NTAPI 526 RtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment) 527 { 528 PLIST_ENTRY Entry; 529 PHEAP_UCR_DESCRIPTOR UcrDescriptor; 530 PHEAP_UCR_SEGMENT UcrSegment; 531 PHEAP Heap = Segment->Heap; 532 SIZE_T ReserveSize = 16 * PAGE_SIZE; 533 SIZE_T CommitSize = 1 * PAGE_SIZE; 534 NTSTATUS Status; 535 536 DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment); 537 538 /* Check if we have unused UCRs */ 539 if (IsListEmpty(&Heap->UCRList)) 540 { 541 /* Get a pointer to the first UCR segment */ 542 UcrSegment = CONTAINING_RECORD(Heap->UCRSegments.Flink, HEAP_UCR_SEGMENT, ListEntry); 543 544 /* Check the list of UCR segments */ 545 if (IsListEmpty(&Heap->UCRSegments) || 546 UcrSegment->ReservedSize == UcrSegment->CommittedSize) 547 { 548 /* We need to create a new one. Reserve 16 pages for it */ 549 UcrSegment = NULL; 550 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 551 (PVOID *)&UcrSegment, 552 0, 553 &ReserveSize, 554 MEM_RESERVE, 555 PAGE_READWRITE); 556 557 if (!NT_SUCCESS(Status)) return NULL; 558 559 /* Commit one page */ 560 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 561 (PVOID *)&UcrSegment, 562 0, 563 &CommitSize, 564 MEM_COMMIT, 565 PAGE_READWRITE); 566 567 if (!NT_SUCCESS(Status)) 568 { 569 /* Release reserved memory */ 570 ZwFreeVirtualMemory(NtCurrentProcess(), 571 (PVOID *)&UcrSegment, 572 &ReserveSize, 573 MEM_RELEASE); 574 return NULL; 575 } 576 577 /* Set it's data */ 578 UcrSegment->ReservedSize = ReserveSize; 579 UcrSegment->CommittedSize = CommitSize; 580 581 /* Add it to the head of the list */ 582 InsertHeadList(&Heap->UCRSegments, &UcrSegment->ListEntry); 583 584 /* Get a pointer to the first available UCR descriptor */ 585 UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)(UcrSegment + 1); 586 } 587 else 588 { 589 /* It's possible to use existing UCR segment. Commit one more page */ 590 UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)((PCHAR)UcrSegment + UcrSegment->CommittedSize); 591 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 592 (PVOID *)&UcrDescriptor, 593 0, 594 &CommitSize, 595 MEM_COMMIT, 596 PAGE_READWRITE); 597 598 if (!NT_SUCCESS(Status)) return NULL; 599 600 ASSERT((PCHAR)UcrDescriptor == ((PCHAR)UcrSegment + UcrSegment->CommittedSize)); 601 602 /* Update sizes */ 603 UcrSegment->CommittedSize += CommitSize; 604 } 605 606 /* There is a whole bunch of new UCR descriptors. Put them into the unused list */ 607 while ((PCHAR)(UcrDescriptor + 1) <= (PCHAR)UcrSegment + UcrSegment->CommittedSize) 608 { 609 InsertTailList(&Heap->UCRList, &UcrDescriptor->ListEntry); 610 UcrDescriptor++; 611 } 612 } 613 614 /* There are unused UCRs, just get the first one */ 615 Entry = RemoveHeadList(&Heap->UCRList); 616 UcrDescriptor = CONTAINING_RECORD(Entry, HEAP_UCR_DESCRIPTOR, ListEntry); 617 return UcrDescriptor; 618 } 619 620 VOID NTAPI 621 RtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment, 622 PHEAP_UCR_DESCRIPTOR UcrDescriptor) 623 { 624 /* Zero it out */ 625 UcrDescriptor->Address = NULL; 626 UcrDescriptor->Size = 0; 627 628 /* Put it into the heap's list of unused UCRs */ 629 InsertHeadList(&Segment->Heap->UCRList, &UcrDescriptor->ListEntry); 630 } 631 632 VOID NTAPI 633 RtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment, 634 ULONG_PTR Address, 635 SIZE_T Size) 636 { 637 PLIST_ENTRY Current; 638 PHEAP_UCR_DESCRIPTOR UcrDescriptor; 639 640 DPRINT("RtlpInsertUnCommittedPages(%p %08Ix %Ix)\n", Segment, Address, Size); 641 642 /* Go through the list of UCR descriptors, they are sorted from lowest address 643 to the highest */ 644 Current = Segment->UCRSegmentList.Flink; 645 while (Current != &Segment->UCRSegmentList) 646 { 647 UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry); 648 649 if ((ULONG_PTR)UcrDescriptor->Address > Address) 650 { 651 /* Check for a really lucky case */ 652 if ((Address + Size) == (ULONG_PTR)UcrDescriptor->Address) 653 { 654 /* Exact match */ 655 UcrDescriptor->Address = (PVOID)Address; 656 UcrDescriptor->Size += Size; 657 return; 658 } 659 660 /* We found the block before which the new one should go */ 661 break; 662 } 663 else if (((ULONG_PTR)UcrDescriptor->Address + UcrDescriptor->Size) == Address) 664 { 665 /* Modify this entry */ 666 Address = (ULONG_PTR)UcrDescriptor->Address; 667 Size += UcrDescriptor->Size; 668 669 /* Advance to the next descriptor */ 670 Current = Current->Flink; 671 672 /* Remove the current descriptor from the list and destroy it */ 673 RemoveEntryList(&UcrDescriptor->SegmentEntry); 674 RtlpDestroyUnCommittedRange(Segment, UcrDescriptor); 675 676 Segment->NumberOfUnCommittedRanges--; 677 } 678 else 679 { 680 /* Advance to the next descriptor */ 681 Current = Current->Flink; 682 } 683 } 684 685 /* Create a new UCR descriptor */ 686 UcrDescriptor = RtlpCreateUnCommittedRange(Segment); 687 if (!UcrDescriptor) return; 688 689 UcrDescriptor->Address = (PVOID)Address; 690 UcrDescriptor->Size = Size; 691 692 /* "Current" is the descriptor before which our one should go */ 693 InsertTailList(Current, &UcrDescriptor->SegmentEntry); 694 695 DPRINT("Added segment UCR with base %08Ix, size 0x%x\n", Address, Size); 696 697 /* Increase counters */ 698 Segment->NumberOfUnCommittedRanges++; 699 } 700 701 static 702 PHEAP_FREE_ENTRY 703 RtlpFindAndCommitPages(PHEAP Heap, 704 PHEAP_SEGMENT Segment, 705 PSIZE_T Size, 706 PVOID AddressRequested) 707 { 708 PLIST_ENTRY Current; 709 NTSTATUS Status; 710 711 DPRINT("RtlpFindAndCommitPages(%p %p %Ix %p)\n", Heap, Segment, *Size, AddressRequested); 712 713 /* Go through UCRs in a segment */ 714 Current = Segment->UCRSegmentList.Flink; 715 while (Current != &Segment->UCRSegmentList) 716 { 717 PHEAP_UCR_DESCRIPTOR UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry); 718 719 /* Check if we can use that one right away */ 720 if (UcrDescriptor->Size >= *Size && 721 (UcrDescriptor->Address == AddressRequested || !AddressRequested)) 722 { 723 PHEAP_ENTRY GuardEntry, FreeEntry; 724 PVOID Address = UcrDescriptor->Address; 725 726 /* Commit it */ 727 if (Heap->CommitRoutine) 728 { 729 Status = Heap->CommitRoutine(Heap, &Address, Size); 730 } 731 else 732 { 733 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 734 &Address, 735 0, 736 Size, 737 MEM_COMMIT, 738 PAGE_READWRITE); 739 } 740 741 DPRINT("Committed %Iu bytes at base %08Ix, UCR size is %lu\n", *Size, Address, UcrDescriptor->Size); 742 743 /* Fail in unsuccessful case */ 744 if (!NT_SUCCESS(Status)) 745 { 746 DPRINT1("Committing page failed with status 0x%08X\n", Status); 747 return NULL; 748 } 749 750 /* Update tracking numbers */ 751 Segment->NumberOfUnCommittedPages -= (ULONG)(*Size / PAGE_SIZE); 752 753 /* Update UCR descriptor */ 754 UcrDescriptor->Address = (PVOID)((ULONG_PTR)UcrDescriptor->Address + *Size); 755 UcrDescriptor->Size -= *Size; 756 757 /* Grab the previous guard entry */ 758 GuardEntry = (PHEAP_ENTRY)Address - 1; 759 ASSERT(GuardEntry->Flags & HEAP_ENTRY_LAST_ENTRY); 760 ASSERT(GuardEntry->Flags & HEAP_ENTRY_BUSY); 761 ASSERT(GuardEntry->Size == 1); 762 763 /* Did we have a double guard entry ? */ 764 if (GuardEntry->PreviousSize == 1) 765 { 766 /* Use the one before instead */ 767 GuardEntry--; 768 769 ASSERT(GuardEntry->Flags & HEAP_ENTRY_LAST_ENTRY); 770 ASSERT(GuardEntry->Flags & HEAP_ENTRY_BUSY); 771 ASSERT(GuardEntry->Size == 1); 772 773 /* We gain one slot more */ 774 *Size += HEAP_ENTRY_SIZE; 775 } 776 777 /* This will become our returned free entry. 778 * Now we can make it span the whole committed range. 779 * But we keep one slot for a guard entry, if needed. 780 */ 781 FreeEntry = GuardEntry; 782 783 FreeEntry->Flags &= ~(HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY); 784 FreeEntry->Size = (*Size) >> HEAP_ENTRY_SHIFT; 785 786 DPRINT("Updating UcrDescriptor %p, new Address %p, size %lu\n", 787 UcrDescriptor, UcrDescriptor->Address, UcrDescriptor->Size); 788 789 /* Check if anything left in this UCR */ 790 if (UcrDescriptor->Size == 0) 791 { 792 /* It's fully exhausted. Take the guard entry for us */ 793 FreeEntry->Size++; 794 *Size += HEAP_ENTRY_SIZE; 795 796 ASSERT((FreeEntry + FreeEntry->Size) == UcrDescriptor->Address); 797 798 /* Check if this is the end of the segment */ 799 if(UcrDescriptor->Address == Segment->LastValidEntry) 800 { 801 FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY; 802 } 803 else 804 { 805 PHEAP_ENTRY NextEntry = UcrDescriptor->Address; 806 807 /* We should not have a UCR right behind us */ 808 ASSERT((UcrDescriptor->SegmentEntry.Flink == &Segment->UCRSegmentList) 809 || (CONTAINING_RECORD(UcrDescriptor->SegmentEntry.Flink, HEAP_UCR_DESCRIPTOR, SegmentEntry)->Address > UcrDescriptor->Address)); 810 811 ASSERT(NextEntry->PreviousSize == 0); 812 ASSERT(NextEntry == FreeEntry + FreeEntry->Size); 813 NextEntry->PreviousSize = FreeEntry->Size; 814 } 815 816 /* This UCR needs to be removed because it became useless */ 817 RemoveEntryList(&UcrDescriptor->SegmentEntry); 818 819 RtlpDestroyUnCommittedRange(Segment, UcrDescriptor); 820 Segment->NumberOfUnCommittedRanges--; 821 } 822 else 823 { 824 /* Setup a guard entry */ 825 GuardEntry = (PHEAP_ENTRY)UcrDescriptor->Address - 1; 826 ASSERT(GuardEntry == FreeEntry + FreeEntry->Size); 827 GuardEntry->Flags = HEAP_ENTRY_LAST_ENTRY | HEAP_ENTRY_BUSY; 828 GuardEntry->Size = 1; 829 GuardEntry->PreviousSize = FreeEntry->Size; 830 GuardEntry->SegmentOffset = FreeEntry->SegmentOffset; 831 DPRINT("Setting %p as UCR guard entry.\n", GuardEntry); 832 } 833 834 /* We're done */ 835 return (PHEAP_FREE_ENTRY)FreeEntry; 836 } 837 838 /* Advance to the next descriptor */ 839 Current = Current->Flink; 840 } 841 842 return NULL; 843 } 844 845 static 846 VOID 847 RtlpDeCommitFreeBlock(PHEAP Heap, 848 PHEAP_FREE_ENTRY FreeEntry, 849 SIZE_T Size) 850 { 851 PHEAP_SEGMENT Segment; 852 PHEAP_ENTRY NextEntry, GuardEntry; 853 PHEAP_UCR_DESCRIPTOR UcrDescriptor; 854 SIZE_T PrecedingSize, DecommitSize; 855 ULONG_PTR DecommitBase, DecommitEnd; 856 NTSTATUS Status; 857 858 DPRINT("Decommitting %p %p %x\n", Heap, FreeEntry, Size); 859 860 /* We can't decommit if there is a commit routine! */ 861 if (Heap->CommitRoutine) 862 { 863 /* Just add it back the usual way */ 864 RtlpInsertFreeBlock(Heap, FreeEntry, Size); 865 return; 866 } 867 868 /* Get the segment */ 869 Segment = Heap->Segments[FreeEntry->SegmentOffset]; 870 871 /* Get the preceding entry */ 872 DecommitBase = ROUND_UP(FreeEntry, PAGE_SIZE); 873 PrecedingSize = (PHEAP_ENTRY)DecommitBase - (PHEAP_ENTRY)FreeEntry; 874 875 if (PrecedingSize == 0) 876 { 877 /* We need some space in order to insert our guard entry */ 878 DecommitBase += PAGE_SIZE; 879 PrecedingSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT; 880 } 881 882 /* Get the entry after this one. */ 883 884 /* Do we really have a next entry */ 885 if (RtlpIsLastCommittedEntry((PHEAP_ENTRY)FreeEntry)) 886 { 887 /* No, Decommit till the next UCR. */ 888 DecommitEnd = PAGE_ROUND_UP((PHEAP_ENTRY)FreeEntry + FreeEntry->Size); 889 NextEntry = NULL; 890 } 891 else 892 { 893 NextEntry = (PHEAP_ENTRY)FreeEntry + Size; 894 DecommitEnd = PAGE_ROUND_DOWN(NextEntry); 895 896 /* Can we make a free entry out of what's left ? */ 897 if ((NextEntry - (PHEAP_ENTRY)DecommitEnd) == 1) 898 { 899 /* Nope. Let's keep one page before this */ 900 DecommitEnd -= PAGE_SIZE; 901 } 902 } 903 904 if (DecommitEnd <= DecommitBase) 905 { 906 /* There's nothing left to decommit. */ 907 RtlpInsertFreeBlock(Heap, FreeEntry, Size); 908 return; 909 } 910 911 DecommitSize = DecommitEnd - DecommitBase; 912 913 /* A decommit is necessary. Create a UCR descriptor */ 914 UcrDescriptor = RtlpCreateUnCommittedRange(Segment); 915 if (!UcrDescriptor) 916 { 917 DPRINT1("HEAP: Failed to create UCR descriptor\n"); 918 RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize); 919 return; 920 } 921 922 /* Decommit the memory */ 923 Status = ZwFreeVirtualMemory(NtCurrentProcess(), 924 (PVOID *)&DecommitBase, 925 &DecommitSize, 926 MEM_DECOMMIT); 927 ASSERT((DecommitBase + DecommitSize) == DecommitEnd); 928 929 /* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */ 930 RtlpDestroyUnCommittedRange(Segment, UcrDescriptor); 931 932 if (!NT_SUCCESS(Status)) 933 { 934 RtlpInsertFreeBlock(Heap, FreeEntry, Size); 935 return; 936 } 937 938 /* Insert uncommitted pages */ 939 RtlpInsertUnCommittedPages(Segment, DecommitBase, DecommitSize); 940 Segment->NumberOfUnCommittedPages += (ULONG)(DecommitSize / PAGE_SIZE); 941 942 /* Insert our guard entry before this */ 943 GuardEntry = (PHEAP_ENTRY)DecommitBase - 1; 944 GuardEntry->Size = 1; 945 GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; 946 GuardEntry->SegmentOffset = FreeEntry->SegmentOffset; 947 DPRINT("Setting %p as UCR guard entry.\n", GuardEntry); 948 949 /* Now see what's really behind us */ 950 PrecedingSize--; 951 switch (PrecedingSize) 952 { 953 case 1: 954 /* No space left for a free entry. Make this another guard entry */ 955 GuardEntry->PreviousSize = 1; 956 GuardEntry--; 957 GuardEntry->Size = 1; 958 GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; 959 GuardEntry->SegmentOffset = FreeEntry->SegmentOffset; 960 /* Fall-through */ 961 case 0: 962 /* There was just enough space four our guard entry */ 963 ASSERT((PHEAP_ENTRY)FreeEntry == GuardEntry); 964 GuardEntry->PreviousSize = FreeEntry->PreviousSize; 965 break; 966 default: 967 /* We can insert this as a free entry */ 968 GuardEntry->PreviousSize = PrecedingSize; 969 FreeEntry->Size = PrecedingSize; 970 FreeEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY; 971 FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &PrecedingSize, FALSE); 972 RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize); 973 break; 974 } 975 976 /* Now the next one */ 977 if (NextEntry) 978 { 979 ASSERT((PHEAP_ENTRY)DecommitEnd <= NextEntry); 980 981 SIZE_T NextSize = NextEntry - (PHEAP_ENTRY)DecommitEnd; 982 if (NextSize) 983 { 984 PHEAP_FREE_ENTRY NextFreeEntry = (PHEAP_FREE_ENTRY)DecommitEnd; 985 986 /* Make sure this is all valid */ 987 ASSERT((PHEAP_ENTRY)DecommitEnd < Segment->LastValidEntry); 988 ASSERT(NextSize >= 2); 989 990 /* Adjust size of this free entry and insert it */ 991 NextFreeEntry->Flags = 0; 992 NextFreeEntry->PreviousSize = 0; 993 NextFreeEntry->SegmentOffset = Segment->Entry.SegmentOffset; 994 NextFreeEntry->Size = (USHORT)NextSize; 995 996 NextEntry->PreviousSize = NextSize; 997 ASSERT(NextEntry == (PHEAP_ENTRY)NextFreeEntry + NextFreeEntry->Size); 998 999 NextFreeEntry = RtlpCoalesceFreeBlocks(Heap, NextFreeEntry, &NextSize, FALSE); 1000 RtlpInsertFreeBlock(Heap, NextFreeEntry, NextSize); 1001 } 1002 else 1003 { 1004 /* This one must be at the beginning of a page */ 1005 ASSERT(NextEntry == (PHEAP_ENTRY)PAGE_ROUND_DOWN(NextEntry)); 1006 /* And we must have a gap betwwen */ 1007 ASSERT(NextEntry > (PHEAP_ENTRY)DecommitBase); 1008 NextEntry->PreviousSize = 0; 1009 } 1010 } 1011 } 1012 1013 NTSTATUS 1014 NTAPI 1015 RtlpInitializeHeapSegment(IN OUT PHEAP Heap, 1016 OUT PHEAP_SEGMENT Segment, 1017 IN UCHAR SegmentIndex, 1018 IN ULONG SegmentFlags, 1019 IN SIZE_T SegmentReserve, 1020 IN SIZE_T SegmentCommit) 1021 { 1022 /* Preconditions */ 1023 ASSERT(Heap != NULL); 1024 ASSERT(Segment != NULL); 1025 ASSERT(SegmentCommit >= PAGE_SIZE); 1026 ASSERT(ROUND_DOWN(SegmentCommit, PAGE_SIZE) == SegmentCommit); 1027 ASSERT(SegmentReserve >= SegmentCommit); 1028 ASSERT(ROUND_DOWN(SegmentReserve, PAGE_SIZE) == SegmentReserve); 1029 1030 DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %lx %lx)\n", Heap, Segment, SegmentIndex, SegmentFlags, SegmentReserve, SegmentCommit); 1031 1032 /* Initialise the Heap Entry header if this is not the first Heap Segment */ 1033 if ((PHEAP_SEGMENT) (Heap) != Segment) 1034 { 1035 Segment->Entry.Size = ROUND_UP(sizeof(HEAP_SEGMENT), sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT; 1036 Segment->Entry.Flags = HEAP_ENTRY_BUSY; 1037 Segment->Entry.SmallTagIndex = LOBYTE(Segment->Entry.Size) ^ HIBYTE(Segment->Entry.Size) ^ Segment->Entry.Flags; 1038 Segment->Entry.PreviousSize = 0; 1039 Segment->Entry.SegmentOffset = SegmentIndex; 1040 Segment->Entry.UnusedBytes = 0; 1041 } 1042 1043 /* Sanity check */ 1044 ASSERT((Segment->Entry.Size << HEAP_ENTRY_SHIFT) <= PAGE_SIZE); 1045 1046 /* Initialise the Heap Segment header */ 1047 Segment->SegmentSignature = HEAP_SEGMENT_SIGNATURE; 1048 Segment->SegmentFlags = SegmentFlags; 1049 Segment->Heap = Heap; 1050 Heap->Segments[SegmentIndex] = Segment; 1051 1052 /* Initialise the Heap Segment location information */ 1053 Segment->BaseAddress = Segment; 1054 Segment->NumberOfPages = (ULONG)(SegmentReserve >> PAGE_SHIFT); 1055 1056 /* Initialise the Heap Entries contained within the Heap Segment */ 1057 Segment->FirstEntry = &Segment->Entry + Segment->Entry.Size; 1058 Segment->LastValidEntry = (PHEAP_ENTRY)((ULONG_PTR)Segment + SegmentReserve); 1059 1060 /* Initialise the Heap Segment UnCommitted Range information */ 1061 Segment->NumberOfUnCommittedPages = (ULONG)((SegmentReserve - SegmentCommit) >> PAGE_SHIFT); 1062 Segment->NumberOfUnCommittedRanges = 0; 1063 InitializeListHead(&Segment->UCRSegmentList); 1064 1065 /* We must have space for a guard entry ! */ 1066 ASSERT (((SegmentCommit >> HEAP_ENTRY_SHIFT) > Segment->Entry.Size) || (Segment->NumberOfUnCommittedPages == 0)); 1067 1068 if (((SIZE_T)Segment->Entry.Size << HEAP_ENTRY_SHIFT) < SegmentCommit) 1069 { 1070 PHEAP_ENTRY FreeEntry = NULL; 1071 1072 if (Segment->NumberOfUnCommittedPages != 0) 1073 { 1074 /* Ensure we put our guard entry at the end of the last committed page */ 1075 PHEAP_ENTRY GuardEntry = &Segment->Entry + (SegmentCommit >> HEAP_ENTRY_SHIFT) - 1; 1076 SIZE_T PreviousSize; 1077 1078 ASSERT(GuardEntry > &Segment->Entry); 1079 GuardEntry->Size = 1; 1080 GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; 1081 GuardEntry->SegmentOffset = SegmentIndex; 1082 PreviousSize = GuardEntry - Segment->FirstEntry; 1083 1084 /* Check what is left behind us */ 1085 switch (PreviousSize) 1086 { 1087 case 1: 1088 GuardEntry->PreviousSize = PreviousSize; 1089 1090 /* There is not enough space for a free entry. Double the guard entry */ 1091 GuardEntry--; 1092 GuardEntry->Size = 1; 1093 GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; 1094 GuardEntry->SegmentOffset = SegmentIndex; 1095 DPRINT1("Setting %p as UCR guard entry.\n", GuardEntry); 1096 /* Fall through */ 1097 case 0: 1098 ASSERT(GuardEntry == Segment->FirstEntry); 1099 GuardEntry->PreviousSize = Segment->Entry.Size; 1100 break; 1101 default: 1102 /* There will be a free entry between the segment and the guard entry */ 1103 FreeEntry = Segment->FirstEntry; 1104 FreeEntry->PreviousSize = Segment->Entry.Size; 1105 FreeEntry->SegmentOffset = SegmentIndex; 1106 FreeEntry->Size = PreviousSize; 1107 FreeEntry->Flags = 0; 1108 1109 /* Register the Free Heap Entry */ 1110 FreeEntry = (PHEAP_ENTRY)RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)FreeEntry, PreviousSize); 1111 GuardEntry->PreviousSize = FreeEntry->Size; 1112 break; 1113 } 1114 } 1115 else 1116 { 1117 /* Prepare a Free Heap Entry header */ 1118 FreeEntry = Segment->FirstEntry; 1119 FreeEntry->PreviousSize = Segment->Entry.Size; 1120 FreeEntry->SegmentOffset = SegmentIndex; 1121 FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY; 1122 FreeEntry->Size = (SegmentCommit >> HEAP_ENTRY_SHIFT) - Segment->Entry.Size; 1123 1124 /* Register the Free Heap Entry */ 1125 RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)FreeEntry, FreeEntry->Size); 1126 } 1127 } 1128 1129 /* Register the UnCommitted Range of the Heap Segment */ 1130 if (Segment->NumberOfUnCommittedPages != 0) 1131 RtlpInsertUnCommittedPages(Segment, (ULONG_PTR) (Segment) + SegmentCommit, SegmentReserve - SegmentCommit); 1132 1133 return STATUS_SUCCESS; 1134 } 1135 1136 VOID NTAPI 1137 RtlpDestroyHeapSegment(PHEAP_SEGMENT Segment) 1138 { 1139 NTSTATUS Status; 1140 PVOID BaseAddress; 1141 SIZE_T Size = 0; 1142 1143 /* Make sure it's not user allocated */ 1144 if (Segment->SegmentFlags & HEAP_USER_ALLOCATED) return; 1145 1146 BaseAddress = Segment->BaseAddress; 1147 DPRINT("Destroying segment %p, BA %p\n", Segment, BaseAddress); 1148 1149 /* Release virtual memory */ 1150 Status = ZwFreeVirtualMemory(NtCurrentProcess(), 1151 &BaseAddress, 1152 &Size, 1153 MEM_RELEASE); 1154 1155 if (!NT_SUCCESS(Status)) 1156 { 1157 DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status); 1158 } 1159 } 1160 1161 PHEAP_FREE_ENTRY NTAPI 1162 RtlpCoalesceHeap(PHEAP Heap) 1163 { 1164 UNIMPLEMENTED; 1165 return NULL; 1166 } 1167 1168 PHEAP_FREE_ENTRY NTAPI 1169 RtlpCoalesceFreeBlocks (PHEAP Heap, 1170 PHEAP_FREE_ENTRY FreeEntry, 1171 PSIZE_T FreeSize, 1172 BOOLEAN Remove) 1173 { 1174 PHEAP_FREE_ENTRY CurrentEntry, NextEntry; 1175 UCHAR SegmentOffset; 1176 1177 /* Get the previous entry */ 1178 CurrentEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize); 1179 1180 /* Check it */ 1181 if (CurrentEntry != FreeEntry && 1182 !(CurrentEntry->Flags & HEAP_ENTRY_BUSY) && 1183 (*FreeSize + CurrentEntry->Size) <= HEAP_MAX_BLOCK_SIZE) 1184 { 1185 ASSERT(FreeEntry->PreviousSize == CurrentEntry->Size); 1186 1187 /* Remove it if asked for */ 1188 if (Remove) 1189 { 1190 RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); 1191 Heap->TotalFreeSize -= FreeEntry->Size; 1192 1193 /* Remove it only once! */ 1194 Remove = FALSE; 1195 } 1196 1197 /* Remove previous entry too */ 1198 RtlpRemoveFreeBlock(Heap, CurrentEntry, FALSE); 1199 1200 /* Copy flags */ 1201 CurrentEntry->Flags = FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY; 1202 1203 /* Advance FreeEntry and update sizes */ 1204 FreeEntry = CurrentEntry; 1205 *FreeSize = *FreeSize + CurrentEntry->Size; 1206 Heap->TotalFreeSize -= CurrentEntry->Size; 1207 FreeEntry->Size = (USHORT)(*FreeSize); 1208 1209 /* Also update previous size if needed */ 1210 if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) 1211 { 1212 ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize); 1213 } 1214 else 1215 { 1216 SegmentOffset = FreeEntry->SegmentOffset; 1217 ASSERT(SegmentOffset < HEAP_SEGMENTS); 1218 } 1219 } 1220 1221 /* Check the next block if it exists */ 1222 if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) 1223 { 1224 NextEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + *FreeSize); 1225 1226 if (!(NextEntry->Flags & HEAP_ENTRY_BUSY) && 1227 NextEntry->Size + *FreeSize <= HEAP_MAX_BLOCK_SIZE) 1228 { 1229 ASSERT(*FreeSize == NextEntry->PreviousSize); 1230 1231 /* Remove it if asked for */ 1232 if (Remove) 1233 { 1234 RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); 1235 Heap->TotalFreeSize -= FreeEntry->Size; 1236 } 1237 1238 /* Copy flags */ 1239 FreeEntry->Flags = NextEntry->Flags & HEAP_ENTRY_LAST_ENTRY; 1240 1241 /* Remove next entry now */ 1242 RtlpRemoveFreeBlock(Heap, NextEntry, FALSE); 1243 1244 /* Update sizes */ 1245 *FreeSize = *FreeSize + NextEntry->Size; 1246 Heap->TotalFreeSize -= NextEntry->Size; 1247 FreeEntry->Size = (USHORT)(*FreeSize); 1248 1249 /* Also update previous size if needed */ 1250 if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) 1251 { 1252 ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize); 1253 } 1254 else 1255 { 1256 SegmentOffset = FreeEntry->SegmentOffset; 1257 ASSERT(SegmentOffset < HEAP_SEGMENTS); 1258 } 1259 } 1260 } 1261 return FreeEntry; 1262 } 1263 1264 static 1265 PHEAP_FREE_ENTRY 1266 RtlpExtendHeap(PHEAP Heap, 1267 SIZE_T Size) 1268 { 1269 ULONG Pages; 1270 UCHAR Index, EmptyIndex; 1271 SIZE_T FreeSize, CommitSize, ReserveSize; 1272 PHEAP_SEGMENT Segment; 1273 PHEAP_FREE_ENTRY FreeEntry; 1274 NTSTATUS Status; 1275 1276 DPRINT("RtlpExtendHeap(%p %x)\n", Heap, Size); 1277 1278 /* Calculate amount in pages */ 1279 Pages = (ULONG)((Size + PAGE_SIZE - 1) / PAGE_SIZE); 1280 FreeSize = Pages * PAGE_SIZE; 1281 DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages, FreeSize); 1282 1283 /* Find an empty segment */ 1284 EmptyIndex = HEAP_SEGMENTS; 1285 for (Index = 0; Index < HEAP_SEGMENTS; Index++) 1286 { 1287 Segment = Heap->Segments[Index]; 1288 1289 if (Segment) DPRINT("Segment[%u] %p with NOUCP %x\n", Index, Segment, Segment->NumberOfUnCommittedPages); 1290 1291 /* Check if its size suits us */ 1292 if (Segment && 1293 Pages <= Segment->NumberOfUnCommittedPages) 1294 { 1295 DPRINT("This segment is suitable\n"); 1296 1297 /* Commit needed amount */ 1298 FreeEntry = RtlpFindAndCommitPages(Heap, Segment, &FreeSize, NULL); 1299 1300 /* Coalesce it with adjacent entries */ 1301 if (FreeEntry) 1302 { 1303 FreeSize = FreeSize >> HEAP_ENTRY_SHIFT; 1304 FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE); 1305 RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize); 1306 return FreeEntry; 1307 } 1308 } 1309 else if (!Segment && 1310 EmptyIndex == HEAP_SEGMENTS) 1311 { 1312 /* Remember the first unused segment index */ 1313 EmptyIndex = Index; 1314 } 1315 } 1316 1317 /* No luck, need to grow the heap */ 1318 if ((Heap->Flags & HEAP_GROWABLE) && 1319 (EmptyIndex != HEAP_SEGMENTS)) 1320 { 1321 Segment = NULL; 1322 1323 /* Reserve the memory */ 1324 if ((Size + PAGE_SIZE) <= Heap->SegmentReserve) 1325 ReserveSize = Heap->SegmentReserve; 1326 else 1327 ReserveSize = Size + PAGE_SIZE; 1328 1329 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 1330 (PVOID)&Segment, 1331 0, 1332 &ReserveSize, 1333 MEM_RESERVE, 1334 PAGE_READWRITE); 1335 1336 /* If it failed, retry again with a half division algorithm */ 1337 while (!NT_SUCCESS(Status) && 1338 ReserveSize != Size + PAGE_SIZE) 1339 { 1340 ReserveSize /= 2; 1341 1342 if (ReserveSize < (Size + PAGE_SIZE)) 1343 ReserveSize = Size + PAGE_SIZE; 1344 1345 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 1346 (PVOID)&Segment, 1347 0, 1348 &ReserveSize, 1349 MEM_RESERVE, 1350 PAGE_READWRITE); 1351 } 1352 1353 /* Proceed only if it's success */ 1354 if (NT_SUCCESS(Status)) 1355 { 1356 Heap->SegmentReserve += ReserveSize; 1357 1358 /* Now commit the memory */ 1359 if ((Size + PAGE_SIZE) <= Heap->SegmentCommit) 1360 CommitSize = Heap->SegmentCommit; 1361 else 1362 CommitSize = Size + PAGE_SIZE; 1363 1364 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 1365 (PVOID)&Segment, 1366 0, 1367 &CommitSize, 1368 MEM_COMMIT, 1369 PAGE_READWRITE); 1370 1371 DPRINT("Committed %lu bytes at base %p\n", CommitSize, Segment); 1372 1373 /* Initialize heap segment if commit was successful */ 1374 if (NT_SUCCESS(Status)) 1375 Status = RtlpInitializeHeapSegment(Heap, Segment, EmptyIndex, 0, ReserveSize, CommitSize); 1376 1377 /* If everything worked - cool */ 1378 if (NT_SUCCESS(Status)) return (PHEAP_FREE_ENTRY)Segment->FirstEntry; 1379 1380 DPRINT1("Committing failed with status 0x%08X\n", Status); 1381 1382 /* Nope, we failed. Free memory */ 1383 ZwFreeVirtualMemory(NtCurrentProcess(), 1384 (PVOID)&Segment, 1385 &ReserveSize, 1386 MEM_RELEASE); 1387 } 1388 else 1389 { 1390 DPRINT1("Reserving failed with status 0x%08X\n", Status); 1391 } 1392 } 1393 1394 if (RtlpGetMode() == UserMode) 1395 { 1396 /* If coalescing on free is disabled in usermode, then do it here */ 1397 if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE) 1398 { 1399 FreeEntry = RtlpCoalesceHeap(Heap); 1400 1401 /* If it's a suitable one - return it */ 1402 if (FreeEntry && 1403 FreeEntry->Size >= Size) 1404 { 1405 return FreeEntry; 1406 } 1407 } 1408 } 1409 1410 return NULL; 1411 } 1412 1413 /*********************************************************************** 1414 * RtlCreateHeap 1415 * RETURNS 1416 * Handle of heap: Success 1417 * NULL: Failure 1418 * 1419 * @implemented 1420 */ 1421 HANDLE NTAPI 1422 RtlCreateHeap(ULONG Flags, 1423 PVOID Addr, 1424 SIZE_T TotalSize, 1425 SIZE_T CommitSize, 1426 PVOID Lock, 1427 PRTL_HEAP_PARAMETERS Parameters) 1428 { 1429 PVOID CommittedAddress = NULL, UncommittedAddress = NULL; 1430 PHEAP Heap = NULL; 1431 RTL_HEAP_PARAMETERS SafeParams = {0}; 1432 ULONG_PTR MaximumUserModeAddress; 1433 SYSTEM_BASIC_INFORMATION SystemInformation; 1434 MEMORY_BASIC_INFORMATION MemoryInfo; 1435 ULONG NtGlobalFlags = RtlGetNtGlobalFlags(); 1436 ULONG HeapSegmentFlags = 0; 1437 NTSTATUS Status; 1438 ULONG MaxBlockSize; 1439 1440 /* Check for a special heap */ 1441 if (RtlpPageHeapEnabled && !Addr && !Lock) 1442 { 1443 Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); 1444 if (Heap) return Heap; 1445 1446 /* Reset a special Parameters == -1 hack */ 1447 if ((ULONG_PTR)Parameters == (ULONG_PTR)-1) 1448 Parameters = NULL; 1449 else 1450 DPRINT1("Enabling page heap failed\n"); 1451 } 1452 1453 /* Check validation flags */ 1454 if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS) && (Flags & ~HEAP_CREATE_VALID_MASK)) 1455 { 1456 DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags); 1457 Flags &= HEAP_CREATE_VALID_MASK; 1458 } 1459 1460 /* Capture parameters */ 1461 if (Parameters) 1462 { 1463 _SEH2_TRY 1464 { 1465 /* If size of structure correct, then copy it */ 1466 if (Parameters->Length == sizeof(RTL_HEAP_PARAMETERS)) 1467 RtlCopyMemory(&SafeParams, Parameters, sizeof(RTL_HEAP_PARAMETERS)); 1468 } 1469 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1470 { 1471 _SEH2_YIELD(return NULL); 1472 } 1473 _SEH2_END; 1474 } 1475 1476 Parameters = &SafeParams; 1477 1478 /* Check global flags */ 1479 if (NtGlobalFlags & FLG_HEAP_DISABLE_COALESCING) 1480 Flags |= HEAP_DISABLE_COALESCE_ON_FREE; 1481 1482 if (NtGlobalFlags & FLG_HEAP_ENABLE_FREE_CHECK) 1483 Flags |= HEAP_FREE_CHECKING_ENABLED; 1484 1485 if (NtGlobalFlags & FLG_HEAP_ENABLE_TAIL_CHECK) 1486 Flags |= HEAP_TAIL_CHECKING_ENABLED; 1487 1488 if (RtlpGetMode() == UserMode) 1489 { 1490 /* Also check these flags if in usermode */ 1491 if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL) 1492 Flags |= HEAP_VALIDATE_ALL_ENABLED; 1493 1494 if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS) 1495 Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED; 1496 1497 if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB) 1498 Flags |= HEAP_CAPTURE_STACK_BACKTRACES; 1499 } 1500 1501 /* Set tunable parameters */ 1502 RtlpSetHeapParameters(Parameters); 1503 1504 /* Get the max um address */ 1505 Status = ZwQuerySystemInformation(SystemBasicInformation, 1506 &SystemInformation, 1507 sizeof(SystemInformation), 1508 NULL); 1509 1510 if (!NT_SUCCESS(Status)) 1511 { 1512 DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status); 1513 return NULL; 1514 } 1515 1516 MaximumUserModeAddress = SystemInformation.MaximumUserModeAddress; 1517 1518 /* Calculate max alloc size */ 1519 if (!Parameters->MaximumAllocationSize) 1520 Parameters->MaximumAllocationSize = MaximumUserModeAddress - (ULONG_PTR)0x10000 - PAGE_SIZE; 1521 1522 MaxBlockSize = 0x80000 - PAGE_SIZE; 1523 1524 if (!Parameters->VirtualMemoryThreshold || 1525 Parameters->VirtualMemoryThreshold > MaxBlockSize) 1526 { 1527 Parameters->VirtualMemoryThreshold = MaxBlockSize; 1528 } 1529 1530 if (Parameters->DeCommitFreeBlockThreshold != PAGE_SIZE) 1531 { 1532 DPRINT1("WARNING: Ignoring DeCommitFreeBlockThreshold %lx, setting it to PAGE_SIZE.\n", 1533 Parameters->DeCommitFreeBlockThreshold); 1534 Parameters->DeCommitFreeBlockThreshold = PAGE_SIZE; 1535 } 1536 1537 /* Check reserve/commit sizes and set default values */ 1538 if (!CommitSize) 1539 { 1540 CommitSize = PAGE_SIZE; 1541 if (TotalSize) 1542 TotalSize = ROUND_UP(TotalSize, PAGE_SIZE); 1543 else 1544 TotalSize = 64 * PAGE_SIZE; 1545 } 1546 else 1547 { 1548 /* Round up the commit size to be at least the page size */ 1549 CommitSize = ROUND_UP(CommitSize, PAGE_SIZE); 1550 1551 if (TotalSize) 1552 TotalSize = ROUND_UP(TotalSize, PAGE_SIZE); 1553 else 1554 TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE); 1555 } 1556 1557 /* Call special heap */ 1558 if (RtlpHeapIsSpecial(Flags)) 1559 return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); 1560 1561 /* Without serialization, a lock makes no sense */ 1562 if ((Flags & HEAP_NO_SERIALIZE) && (Lock != NULL)) 1563 return NULL; 1564 1565 /* See if we are already provided with an address for the heap */ 1566 if (Addr) 1567 { 1568 if (Parameters->CommitRoutine) 1569 { 1570 /* There is a commit routine, so no problem here, check params */ 1571 if ((Flags & HEAP_GROWABLE) || 1572 !Parameters->InitialCommit || 1573 !Parameters->InitialReserve || 1574 (Parameters->InitialCommit > Parameters->InitialReserve)) 1575 { 1576 /* Fail */ 1577 return NULL; 1578 } 1579 1580 /* Calculate committed and uncommitted addresses */ 1581 CommittedAddress = Addr; 1582 UncommittedAddress = (PCHAR)Addr + Parameters->InitialCommit; 1583 TotalSize = Parameters->InitialReserve; 1584 1585 /* Zero the initial page ourselves */ 1586 RtlZeroMemory(CommittedAddress, PAGE_SIZE); 1587 } 1588 else 1589 { 1590 /* Commit routine is absent, so query how much memory caller reserved */ 1591 Status = ZwQueryVirtualMemory(NtCurrentProcess(), 1592 Addr, 1593 MemoryBasicInformation, 1594 &MemoryInfo, 1595 sizeof(MemoryInfo), 1596 NULL); 1597 1598 if (!NT_SUCCESS(Status)) 1599 { 1600 DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status); 1601 return NULL; 1602 } 1603 1604 /* Validate it */ 1605 if (MemoryInfo.BaseAddress != Addr || 1606 MemoryInfo.State == MEM_FREE) 1607 { 1608 return NULL; 1609 } 1610 1611 /* Validation checks passed, set committed/uncommitted addresses */ 1612 CommittedAddress = Addr; 1613 1614 /* Check if it's committed or not */ 1615 if (MemoryInfo.State == MEM_COMMIT) 1616 { 1617 /* Zero it out because it's already committed */ 1618 RtlZeroMemory(CommittedAddress, PAGE_SIZE); 1619 1620 /* Calculate uncommitted address value */ 1621 CommitSize = MemoryInfo.RegionSize; 1622 TotalSize = CommitSize; 1623 UncommittedAddress = (PCHAR)Addr + CommitSize; 1624 1625 /* Check if uncommitted address is reserved */ 1626 Status = ZwQueryVirtualMemory(NtCurrentProcess(), 1627 UncommittedAddress, 1628 MemoryBasicInformation, 1629 &MemoryInfo, 1630 sizeof(MemoryInfo), 1631 NULL); 1632 1633 if (NT_SUCCESS(Status) && 1634 MemoryInfo.State == MEM_RESERVE) 1635 { 1636 /* It is, so add it up to the reserve size */ 1637 TotalSize += MemoryInfo.RegionSize; 1638 } 1639 } 1640 else 1641 { 1642 /* It's not committed, inform following code that a commit is necessary */ 1643 CommitSize = PAGE_SIZE; 1644 UncommittedAddress = Addr; 1645 } 1646 } 1647 1648 /* Mark this as a user-committed mem */ 1649 HeapSegmentFlags = HEAP_USER_ALLOCATED; 1650 Heap = (PHEAP)Addr; 1651 } 1652 else 1653 { 1654 /* Check commit routine */ 1655 if (Parameters->CommitRoutine) return NULL; 1656 1657 /* Reserve memory */ 1658 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 1659 (PVOID *)&Heap, 1660 0, 1661 &TotalSize, 1662 MEM_RESERVE, 1663 PAGE_READWRITE); 1664 1665 if (!NT_SUCCESS(Status)) 1666 { 1667 DPRINT1("Failed to reserve memory with status 0x%08x\n", Status); 1668 return NULL; 1669 } 1670 1671 /* Set base addresses */ 1672 CommittedAddress = Heap; 1673 UncommittedAddress = Heap; 1674 } 1675 1676 /* Check if we need to commit something */ 1677 if (CommittedAddress == UncommittedAddress) 1678 { 1679 /* Commit the required size */ 1680 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 1681 &CommittedAddress, 1682 0, 1683 &CommitSize, 1684 MEM_COMMIT, 1685 PAGE_READWRITE); 1686 1687 DPRINT("Committed %Iu bytes at base %p\n", CommitSize, CommittedAddress); 1688 1689 if (!NT_SUCCESS(Status)) 1690 { 1691 DPRINT1("Failure, Status 0x%08X\n", Status); 1692 1693 /* Release memory if it was reserved */ 1694 if (!Addr) ZwFreeVirtualMemory(NtCurrentProcess(), 1695 (PVOID *)&Heap, 1696 &TotalSize, 1697 MEM_RELEASE); 1698 1699 return NULL; 1700 } 1701 1702 /* Calculate new uncommitted address */ 1703 UncommittedAddress = (PCHAR)UncommittedAddress + CommitSize; 1704 } 1705 1706 /* Initialize the heap */ 1707 Status = RtlpInitializeHeap(Heap, Flags, Lock, Parameters); 1708 if (!NT_SUCCESS(Status)) 1709 { 1710 DPRINT1("Failed to initialize heap (%x)\n", Status); 1711 return NULL; 1712 } 1713 1714 /* Initialize heap's first segment */ 1715 Status = RtlpInitializeHeapSegment(Heap, (PHEAP_SEGMENT) (Heap), 0, HeapSegmentFlags, TotalSize, CommitSize); 1716 if (!NT_SUCCESS(Status)) 1717 { 1718 DPRINT1("Failed to initialize heap segment (%x)\n", Status); 1719 return NULL; 1720 } 1721 1722 DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap, CommitSize, TotalSize); 1723 1724 /* Add heap to process list in case of usermode heap */ 1725 if (RtlpGetMode() == UserMode) 1726 { 1727 RtlpAddHeapToProcessList(Heap); 1728 1729 // FIXME: What about lookasides? 1730 } 1731 1732 return Heap; 1733 } 1734 1735 /*********************************************************************** 1736 * RtlDestroyHeap 1737 * RETURNS 1738 * TRUE: Success 1739 * FALSE: Failure 1740 * 1741 * @implemented 1742 * 1743 * RETURNS 1744 * Success: A NULL HANDLE, if heap is NULL or it was destroyed 1745 * Failure: The Heap handle, if heap is the process heap. 1746 */ 1747 HANDLE NTAPI 1748 RtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */ 1749 { 1750 PHEAP Heap = (PHEAP)HeapPtr; 1751 PLIST_ENTRY Current; 1752 PHEAP_UCR_SEGMENT UcrSegment; 1753 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; 1754 PVOID BaseAddress; 1755 SIZE_T Size; 1756 LONG i; 1757 PHEAP_SEGMENT Segment; 1758 1759 if (!HeapPtr) return NULL; 1760 1761 /* Call page heap routine if required */ 1762 if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) return RtlpPageHeapDestroy(HeapPtr); 1763 1764 /* Call special heap */ 1765 if (RtlpHeapIsSpecial(Heap->Flags)) 1766 { 1767 if (!RtlDebugDestroyHeap(Heap)) return HeapPtr; 1768 } 1769 1770 /* Check for a process heap */ 1771 if (RtlpGetMode() == UserMode && 1772 HeapPtr == NtCurrentPeb()->ProcessHeap) return HeapPtr; 1773 1774 /* Free up all big allocations */ 1775 Current = Heap->VirtualAllocdBlocks.Flink; 1776 while (Current != &Heap->VirtualAllocdBlocks) 1777 { 1778 VirtualEntry = CONTAINING_RECORD(Current, HEAP_VIRTUAL_ALLOC_ENTRY, Entry); 1779 BaseAddress = (PVOID)VirtualEntry; 1780 Current = Current->Flink; 1781 Size = 0; 1782 ZwFreeVirtualMemory(NtCurrentProcess(), 1783 &BaseAddress, 1784 &Size, 1785 MEM_RELEASE); 1786 } 1787 1788 /* Delete tags and remove heap from the process heaps list in user mode */ 1789 if (RtlpGetMode() == UserMode) 1790 { 1791 // FIXME DestroyTags 1792 RtlpRemoveHeapFromProcessList(Heap); 1793 } 1794 1795 /* Delete the heap lock */ 1796 if (!(Heap->Flags & HEAP_NO_SERIALIZE)) 1797 { 1798 /* Delete it if it wasn't user allocated */ 1799 if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED)) 1800 RtlDeleteHeapLock(Heap->LockVariable); 1801 1802 /* Clear out the lock variable */ 1803 Heap->LockVariable = NULL; 1804 } 1805 1806 /* Free UCR segments if any were created */ 1807 Current = Heap->UCRSegments.Flink; 1808 while (Current != &Heap->UCRSegments) 1809 { 1810 UcrSegment = CONTAINING_RECORD(Current, HEAP_UCR_SEGMENT, ListEntry); 1811 1812 /* Advance to the next descriptor */ 1813 Current = Current->Flink; 1814 1815 BaseAddress = (PVOID)UcrSegment; 1816 Size = 0; 1817 1818 /* Release that memory */ 1819 ZwFreeVirtualMemory(NtCurrentProcess(), 1820 &BaseAddress, 1821 &Size, 1822 MEM_RELEASE); 1823 } 1824 1825 /* Go through segments and destroy them */ 1826 for (i = HEAP_SEGMENTS - 1; i >= 0; i--) 1827 { 1828 Segment = Heap->Segments[i]; 1829 if (Segment) RtlpDestroyHeapSegment(Segment); 1830 } 1831 1832 return NULL; 1833 } 1834 1835 PHEAP_ENTRY NTAPI 1836 RtlpSplitEntry(PHEAP Heap, 1837 ULONG Flags, 1838 PHEAP_FREE_ENTRY FreeBlock, 1839 SIZE_T AllocationSize, 1840 SIZE_T Index, 1841 SIZE_T Size) 1842 { 1843 PHEAP_FREE_ENTRY SplitBlock, SplitBlock2; 1844 UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY; 1845 PHEAP_ENTRY InUseEntry; 1846 SIZE_T FreeSize; 1847 UCHAR SegmentOffset; 1848 1849 /* Add extra flags in case of settable user value feature is requested, 1850 or there is a tag (small or normal) or there is a request to 1851 capture stack backtraces */ 1852 if ((Flags & HEAP_EXTRA_FLAGS_MASK) || 1853 Heap->PseudoTagEntries) 1854 { 1855 /* Add flag which means that the entry will have extra stuff attached */ 1856 EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT; 1857 1858 /* NB! AllocationSize is already adjusted by RtlAllocateHeap */ 1859 } 1860 1861 /* Add settable user flags, if any */ 1862 EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4; 1863 1864 /* Save flags, update total free size */ 1865 FreeFlags = FreeBlock->Flags; 1866 Heap->TotalFreeSize -= FreeBlock->Size; 1867 1868 /* Make this block an in-use one */ 1869 InUseEntry = (PHEAP_ENTRY)FreeBlock; 1870 InUseEntry->Flags = EntryFlags; 1871 InUseEntry->SmallTagIndex = 0; 1872 1873 /* Calculate the extra amount */ 1874 FreeSize = InUseEntry->Size - Index; 1875 1876 /* Update it's size fields (we don't need their data anymore) */ 1877 InUseEntry->Size = (USHORT)Index; 1878 InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size); 1879 1880 /* If there is something to split - do the split */ 1881 if (FreeSize != 0) 1882 { 1883 /* Don't split if resulting entry can't contain any payload data 1884 (i.e. being just HEAP_ENTRY_SIZE) */ 1885 if (FreeSize == 1) 1886 { 1887 /* Increase sizes of the in-use entry */ 1888 InUseEntry->Size++; 1889 InUseEntry->UnusedBytes += sizeof(HEAP_ENTRY); 1890 } 1891 else 1892 { 1893 /* Calculate a pointer to the new entry */ 1894 SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index); 1895 1896 /* Initialize it */ 1897 SplitBlock->Flags = FreeFlags; 1898 SplitBlock->SegmentOffset = InUseEntry->SegmentOffset; 1899 SplitBlock->Size = (USHORT)FreeSize; 1900 SplitBlock->PreviousSize = (USHORT)Index; 1901 1902 /* Check if it's the last entry */ 1903 if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) 1904 { 1905 /* Insert it to the free list if it's the last entry */ 1906 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); 1907 Heap->TotalFreeSize += FreeSize; 1908 } 1909 else 1910 { 1911 /* Not so easy - need to update next's previous size too */ 1912 SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize); 1913 1914 if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) 1915 { 1916 SplitBlock2->PreviousSize = (USHORT)FreeSize; 1917 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); 1918 Heap->TotalFreeSize += FreeSize; 1919 } 1920 else 1921 { 1922 /* Even more complex - the next entry is free, so we can merge them into one! */ 1923 SplitBlock->Flags = SplitBlock2->Flags; 1924 1925 /* Remove that next entry */ 1926 RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE); 1927 1928 /* Update sizes */ 1929 FreeSize += SplitBlock2->Size; 1930 Heap->TotalFreeSize -= SplitBlock2->Size; 1931 1932 if (FreeSize <= HEAP_MAX_BLOCK_SIZE) 1933 { 1934 /* Insert it back */ 1935 SplitBlock->Size = (USHORT)FreeSize; 1936 1937 /* Don't forget to update previous size of the next entry! */ 1938 if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) 1939 { 1940 ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize; 1941 } 1942 1943 /* Actually insert it */ 1944 RtlpInsertFreeBlockHelper(Heap, SplitBlock, (USHORT)FreeSize, FALSE); 1945 1946 /* Update total size */ 1947 Heap->TotalFreeSize += FreeSize; 1948 } 1949 else 1950 { 1951 /* Resulting block is quite big */ 1952 RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize); 1953 } 1954 } 1955 } 1956 1957 /* Reset flags of the free entry */ 1958 FreeFlags = 0; 1959 if (SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY) 1960 { 1961 SegmentOffset = SplitBlock->SegmentOffset; 1962 ASSERT(SegmentOffset < HEAP_SEGMENTS); 1963 } 1964 } 1965 } 1966 1967 /* Set last entry flag */ 1968 if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) 1969 InUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY; 1970 1971 return InUseEntry; 1972 } 1973 1974 static 1975 PVOID 1976 RtlpAllocateNonDedicated(PHEAP Heap, 1977 ULONG Flags, 1978 SIZE_T Size, 1979 SIZE_T AllocationSize, 1980 SIZE_T Index, 1981 BOOLEAN HeapLocked) 1982 { 1983 PHEAP_FREE_ENTRY FreeBlock; 1984 1985 /* The entries in the list must be too small for us */ 1986 ASSERT(IsListEmpty(&Heap->FreeLists) || 1987 (CONTAINING_RECORD(Heap->FreeLists.Blink, HEAP_FREE_ENTRY, FreeList)->Size < Index)); 1988 1989 /* Extend the heap */ 1990 FreeBlock = RtlpExtendHeap(Heap, AllocationSize); 1991 1992 /* Use the new biggest entry we've got */ 1993 if (FreeBlock) 1994 { 1995 PHEAP_ENTRY InUseEntry; 1996 PHEAP_ENTRY_EXTRA Extra; 1997 1998 RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE); 1999 2000 /* Split it */ 2001 InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size); 2002 2003 /* Release the lock */ 2004 if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); 2005 2006 /* Zero memory if that was requested */ 2007 if (Flags & HEAP_ZERO_MEMORY) 2008 RtlZeroMemory(InUseEntry + 1, Size); 2009 else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) 2010 { 2011 /* Fill this block with a special pattern */ 2012 RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER); 2013 } 2014 2015 /* Fill tail of the block with a special pattern too if requested */ 2016 if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) 2017 { 2018 RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL); 2019 InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN; 2020 } 2021 2022 /* Prepare extra if it's present */ 2023 if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 2024 { 2025 Extra = RtlpGetExtraStuffPointer(InUseEntry); 2026 RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA)); 2027 2028 // TODO: Tagging 2029 } 2030 2031 /* Return pointer to the */ 2032 return InUseEntry + 1; 2033 } 2034 2035 /* Really unfortunate, out of memory condition */ 2036 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); 2037 2038 /* Generate an exception */ 2039 if (Flags & HEAP_GENERATE_EXCEPTIONS) 2040 { 2041 EXCEPTION_RECORD ExceptionRecord; 2042 2043 ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; 2044 ExceptionRecord.ExceptionRecord = NULL; 2045 ExceptionRecord.NumberParameters = 1; 2046 ExceptionRecord.ExceptionFlags = 0; 2047 ExceptionRecord.ExceptionInformation[0] = AllocationSize; 2048 2049 RtlRaiseException(&ExceptionRecord); 2050 } 2051 2052 /* Release the lock */ 2053 if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); 2054 DPRINT1("HEAP: Allocation failed!\n"); 2055 DPRINT1("Flags %x\n", Heap->Flags); 2056 return NULL; 2057 } 2058 2059 /*********************************************************************** 2060 * HeapAlloc (KERNEL32.334) 2061 * RETURNS 2062 * Pointer to allocated memory block 2063 * NULL: Failure 2064 * 0x7d030f60--invalid flags in RtlHeapAllocate 2065 * @implemented 2066 */ 2067 PVOID NTAPI 2068 RtlAllocateHeap(IN PVOID HeapPtr, 2069 IN ULONG Flags, 2070 IN SIZE_T Size) 2071 { 2072 PHEAP Heap = (PHEAP)HeapPtr; 2073 SIZE_T AllocationSize; 2074 SIZE_T Index; 2075 UCHAR EntryFlags = HEAP_ENTRY_BUSY; 2076 BOOLEAN HeapLocked = FALSE; 2077 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock = NULL; 2078 PHEAP_ENTRY_EXTRA Extra; 2079 NTSTATUS Status; 2080 2081 /* Force flags */ 2082 Flags |= Heap->ForceFlags; 2083 2084 /* Call special heap */ 2085 if (RtlpHeapIsSpecial(Flags)) 2086 return RtlDebugAllocateHeap(Heap, Flags, Size); 2087 2088 /* Check for the maximum size */ 2089 if (Size >= 0x80000000) 2090 { 2091 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); 2092 DPRINT1("HEAP: Allocation failed!\n"); 2093 return NULL; 2094 } 2095 2096 if (Flags & (HEAP_CREATE_ENABLE_TRACING)) 2097 { 2098 DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags); 2099 } 2100 2101 //DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size); 2102 2103 /* Calculate allocation size and index */ 2104 if (Size) 2105 AllocationSize = Size; 2106 else 2107 AllocationSize = 1; 2108 AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask; 2109 2110 /* Add extra flags in case of settable user value feature is requested, 2111 or there is a tag (small or normal) or there is a request to 2112 capture stack backtraces */ 2113 if ((Flags & HEAP_EXTRA_FLAGS_MASK) || 2114 Heap->PseudoTagEntries) 2115 { 2116 /* Add flag which means that the entry will have extra stuff attached */ 2117 EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT; 2118 2119 /* Account for extra stuff size */ 2120 AllocationSize += sizeof(HEAP_ENTRY_EXTRA); 2121 } 2122 2123 /* Add settable user flags, if any */ 2124 EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4; 2125 2126 Index = AllocationSize >> HEAP_ENTRY_SHIFT; 2127 2128 /* Acquire the lock if necessary */ 2129 if (!(Flags & HEAP_NO_SERIALIZE)) 2130 { 2131 RtlEnterHeapLock(Heap->LockVariable, TRUE); 2132 HeapLocked = TRUE; 2133 } 2134 2135 /* Depending on the size, the allocation is going to be done from dedicated, 2136 non-dedicated lists or a virtual block of memory */ 2137 if (Index <= Heap->VirtualMemoryThreshold) 2138 { 2139 PHEAP_ENTRY InUseEntry; 2140 PHEAP_FREE_ENTRY FreeEntry; 2141 2142 /* First quick check: Anybody here ? */ 2143 if (IsListEmpty(&Heap->FreeLists)) 2144 return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked); 2145 2146 /* Second quick check: Is there someone for us ? */ 2147 FreeEntry = CONTAINING_RECORD(Heap->FreeLists.Blink, HEAP_FREE_ENTRY, FreeList); 2148 if (FreeEntry->Size < Index) 2149 { 2150 /* Largest entry in the list doesnt fit. */ 2151 return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked); 2152 } 2153 2154 if (Index > Heap->DeCommitFreeBlockThreshold) 2155 { 2156 /* Find an entry from the non dedicated list */ 2157 FreeEntry = CONTAINING_RECORD(Heap->FreeHints[0], 2158 HEAP_FREE_ENTRY, 2159 FreeList); 2160 2161 while (FreeEntry->Size < Index) 2162 { 2163 /* We made sure we had the right size available */ 2164 ASSERT(FreeEntry->FreeList.Flink != &Heap->FreeLists); 2165 FreeEntry = CONTAINING_RECORD(FreeEntry->FreeList.Flink, 2166 HEAP_FREE_ENTRY, 2167 FreeList); 2168 } 2169 } 2170 else 2171 { 2172 /* Get the free entry from the hint */ 2173 ULONG HintIndex = RtlFindSetBits(&Heap->FreeHintBitmap, 1, Index - 1); 2174 ASSERT(HintIndex != 0xFFFFFFFF); 2175 ASSERT((HintIndex >= (Index - 1)) || (HintIndex == 0)); 2176 FreeEntry = CONTAINING_RECORD(Heap->FreeHints[HintIndex], 2177 HEAP_FREE_ENTRY, 2178 FreeList); 2179 } 2180 2181 /* Remove the free block, split, profit. */ 2182 RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); 2183 InUseEntry = RtlpSplitEntry(Heap, Flags, FreeEntry, AllocationSize, Index, Size); 2184 2185 /* Release the lock */ 2186 if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); 2187 2188 /* Zero memory if that was requested */ 2189 if (Flags & HEAP_ZERO_MEMORY) 2190 RtlZeroMemory(InUseEntry + 1, Size); 2191 else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) 2192 { 2193 /* Fill this block with a special pattern */ 2194 RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER); 2195 } 2196 2197 /* Fill tail of the block with a special pattern too if requested */ 2198 if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) 2199 { 2200 RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL); 2201 InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN; 2202 } 2203 2204 /* Prepare extra if it's present */ 2205 if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 2206 { 2207 Extra = RtlpGetExtraStuffPointer(InUseEntry); 2208 RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA)); 2209 2210 // TODO: Tagging 2211 } 2212 2213 /* User data starts right after the entry's header */ 2214 return InUseEntry + 1; 2215 } 2216 2217 if (Heap->Flags & HEAP_GROWABLE) 2218 { 2219 /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */ 2220 AllocationSize += sizeof(HEAP_VIRTUAL_ALLOC_ENTRY) - sizeof(HEAP_ENTRY); 2221 2222 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 2223 (PVOID *)&VirtualBlock, 2224 0, 2225 &AllocationSize, 2226 MEM_COMMIT, 2227 PAGE_READWRITE); 2228 2229 if (!NT_SUCCESS(Status)) 2230 { 2231 // Set STATUS! 2232 /* Release the lock */ 2233 if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); 2234 DPRINT1("HEAP: Allocation failed!\n"); 2235 return NULL; 2236 } 2237 2238 /* Initialize the newly allocated block */ 2239 VirtualBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size); 2240 ASSERT(VirtualBlock->BusyBlock.Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY)); 2241 VirtualBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT; 2242 VirtualBlock->CommitSize = AllocationSize; 2243 VirtualBlock->ReserveSize = AllocationSize; 2244 2245 /* Insert it into the list of virtual allocations */ 2246 InsertTailList(&Heap->VirtualAllocdBlocks, &VirtualBlock->Entry); 2247 2248 /* Release the lock */ 2249 if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); 2250 2251 /* Return pointer to user data */ 2252 return VirtualBlock + 1; 2253 } 2254 2255 /* Generate an exception */ 2256 if (Flags & HEAP_GENERATE_EXCEPTIONS) 2257 { 2258 EXCEPTION_RECORD ExceptionRecord; 2259 2260 ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; 2261 ExceptionRecord.ExceptionRecord = NULL; 2262 ExceptionRecord.NumberParameters = 1; 2263 ExceptionRecord.ExceptionFlags = 0; 2264 ExceptionRecord.ExceptionInformation[0] = AllocationSize; 2265 2266 RtlRaiseException(&ExceptionRecord); 2267 } 2268 2269 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL); 2270 2271 /* Release the lock */ 2272 if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); 2273 DPRINT1("HEAP: Allocation failed!\n"); 2274 return NULL; 2275 } 2276 2277 2278 /*********************************************************************** 2279 * HeapFree (KERNEL32.338) 2280 * RETURNS 2281 * TRUE: Success 2282 * FALSE: Failure 2283 * 2284 * @implemented 2285 */ 2286 BOOLEAN NTAPI RtlFreeHeap( 2287 HANDLE HeapPtr, /* [in] Handle of heap */ 2288 ULONG Flags, /* [in] Heap freeing flags */ 2289 PVOID Ptr /* [in] Address of memory to free */ 2290 ) 2291 { 2292 PHEAP Heap; 2293 PHEAP_ENTRY HeapEntry; 2294 USHORT TagIndex = 0; 2295 SIZE_T BlockSize; 2296 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; 2297 BOOLEAN Locked = FALSE; 2298 NTSTATUS Status; 2299 2300 /* Freeing NULL pointer is a legal operation */ 2301 if (!Ptr) return TRUE; 2302 2303 /* Get pointer to the heap and force flags */ 2304 Heap = (PHEAP)HeapPtr; 2305 Flags |= Heap->ForceFlags; 2306 2307 /* Call special heap */ 2308 if (RtlpHeapIsSpecial(Flags)) 2309 return RtlDebugFreeHeap(Heap, Flags, Ptr); 2310 2311 /* Get pointer to the heap entry */ 2312 HeapEntry = (PHEAP_ENTRY)Ptr - 1; 2313 2314 /* Protect with SEH in case the pointer is not valid */ 2315 _SEH2_TRY 2316 { 2317 /* Check this entry, fail if it's invalid */ 2318 if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY) || 2319 (((ULONG_PTR)Ptr & 0x7) != 0) || 2320 (HeapEntry->SegmentOffset >= HEAP_SEGMENTS)) 2321 { 2322 /* This is an invalid block */ 2323 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr); 2324 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 2325 _SEH2_YIELD(return FALSE); 2326 } 2327 } 2328 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2329 { 2330 /* The pointer was invalid */ 2331 DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr); 2332 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 2333 _SEH2_YIELD(return FALSE); 2334 } 2335 _SEH2_END; 2336 2337 /* Lock if necessary */ 2338 if (!(Flags & HEAP_NO_SERIALIZE)) 2339 { 2340 RtlEnterHeapLock(Heap->LockVariable, TRUE); 2341 Locked = TRUE; 2342 } 2343 2344 if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) 2345 { 2346 /* Big allocation */ 2347 VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); 2348 2349 /* Remove it from the list */ 2350 RemoveEntryList(&VirtualEntry->Entry); 2351 2352 // TODO: Tagging 2353 2354 BlockSize = 0; 2355 Status = ZwFreeVirtualMemory(NtCurrentProcess(), 2356 (PVOID *)&VirtualEntry, 2357 &BlockSize, 2358 MEM_RELEASE); 2359 2360 if (!NT_SUCCESS(Status)) 2361 { 2362 DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n", 2363 Status, Heap, Ptr, VirtualEntry); 2364 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status); 2365 } 2366 } 2367 else 2368 { 2369 /* Normal allocation */ 2370 BlockSize = HeapEntry->Size; 2371 2372 // TODO: Tagging 2373 2374 /* Coalesce in kernel mode, and in usermode if it's not disabled */ 2375 if (RtlpGetMode() == KernelMode || 2376 (RtlpGetMode() == UserMode && !(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE))) 2377 { 2378 HeapEntry = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks(Heap, 2379 (PHEAP_FREE_ENTRY)HeapEntry, 2380 &BlockSize, 2381 FALSE); 2382 } 2383 2384 /* See if we should decommit this block */ 2385 if ((BlockSize >= Heap->DeCommitFreeBlockThreshold) || 2386 (Heap->TotalFreeSize + BlockSize >= Heap->DeCommitTotalFreeThreshold)) 2387 { 2388 RtlpDeCommitFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize); 2389 } 2390 else 2391 { 2392 /* Insert into the free list */ 2393 RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize); 2394 2395 if (RtlpGetMode() == UserMode && 2396 TagIndex != 0) 2397 { 2398 // FIXME: Tagging 2399 UNIMPLEMENTED; 2400 } 2401 } 2402 } 2403 2404 /* Release the heap lock */ 2405 if (Locked) RtlLeaveHeapLock(Heap->LockVariable); 2406 2407 return TRUE; 2408 } 2409 2410 BOOLEAN NTAPI 2411 RtlpGrowBlockInPlace (IN PHEAP Heap, 2412 IN ULONG Flags, 2413 IN PHEAP_ENTRY InUseEntry, 2414 IN SIZE_T Size, 2415 IN SIZE_T Index) 2416 { 2417 UCHAR EntryFlags, RememberFlags; 2418 PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry; 2419 SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0; 2420 PHEAP_ENTRY_EXTRA OldExtra, NewExtra; 2421 UCHAR SegmentOffset; 2422 2423 /* We can't grow beyond specified threshold */ 2424 if (Index > Heap->VirtualMemoryThreshold) 2425 return FALSE; 2426 2427 /* Get entry flags */ 2428 EntryFlags = InUseEntry->Flags; 2429 2430 if (RtlpIsLastCommittedEntry(InUseEntry)) 2431 { 2432 /* There is no next block, just uncommitted space. Calculate how much is needed */ 2433 FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT; 2434 FreeSize = ROUND_UP(FreeSize, PAGE_SIZE); 2435 2436 /* Find and commit those pages */ 2437 FreeEntry = RtlpFindAndCommitPages(Heap, 2438 Heap->Segments[InUseEntry->SegmentOffset], 2439 &FreeSize, 2440 (PVOID)PAGE_ROUND_UP(InUseEntry + InUseEntry->Size)); 2441 2442 /* Fail if it failed... */ 2443 if (!FreeEntry) return FALSE; 2444 2445 /* It was successful, perform coalescing */ 2446 FreeSize = FreeSize >> HEAP_ENTRY_SHIFT; 2447 FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE); 2448 2449 /* Check if it's enough */ 2450 if (FreeSize + InUseEntry->Size < Index) 2451 { 2452 /* Still not enough */ 2453 RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize); 2454 Heap->TotalFreeSize += FreeSize; 2455 return FALSE; 2456 } 2457 2458 /* Remember flags of this free entry */ 2459 RememberFlags = FreeEntry->Flags; 2460 2461 /* Sum up sizes */ 2462 FreeSize += InUseEntry->Size; 2463 } 2464 else 2465 { 2466 FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size); 2467 2468 /* The next block indeed exists. Check if it's free or in use */ 2469 if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE; 2470 2471 /* Next entry is free, check if it can fit the block we need */ 2472 FreeSize = InUseEntry->Size + FreeEntry->Size; 2473 if (FreeSize < Index) return FALSE; 2474 2475 /* Remember flags of this free entry */ 2476 RememberFlags = FreeEntry->Flags; 2477 2478 /* Remove this block from the free list */ 2479 RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); 2480 Heap->TotalFreeSize -= FreeEntry->Size; 2481 } 2482 2483 PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes; 2484 FreeSize -= Index; 2485 2486 /* Don't produce too small blocks */ 2487 if (FreeSize <= 2) 2488 { 2489 Index += FreeSize; 2490 FreeSize = 0; 2491 } 2492 2493 /* Process extra stuff */ 2494 if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT) 2495 { 2496 /* Calculate pointers */ 2497 OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1); 2498 NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1); 2499 2500 /* Copy contents */ 2501 *NewExtra = *OldExtra; 2502 2503 // FIXME Tagging 2504 } 2505 2506 /* Update sizes */ 2507 InUseEntry->Size = (USHORT)Index; 2508 InUseEntry->UnusedBytes = (UCHAR)((Index << HEAP_ENTRY_SHIFT) - Size); 2509 2510 /* Check if there is a free space remaining after merging those blocks */ 2511 if (!FreeSize) 2512 { 2513 /* Update flags and sizes */ 2514 InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY; 2515 2516 /* Either update previous size of the next entry or mark it as a last 2517 entry in the segment */ 2518 if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY)) 2519 { 2520 (InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size; 2521 } 2522 else 2523 { 2524 SegmentOffset = InUseEntry->SegmentOffset; 2525 ASSERT(SegmentOffset < HEAP_SEGMENTS); 2526 } 2527 } 2528 else 2529 { 2530 /* Complex case, we need to split the block to give unused free space 2531 back to the heap */ 2532 UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index); 2533 UnusedEntry->PreviousSize = (USHORT)Index; 2534 UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset; 2535 2536 /* Update the following block or set the last entry in the segment */ 2537 if (RememberFlags & HEAP_ENTRY_LAST_ENTRY) 2538 { 2539 SegmentOffset = UnusedEntry->SegmentOffset; 2540 ASSERT(SegmentOffset < HEAP_SEGMENTS); 2541 2542 /* Set flags and size */ 2543 UnusedEntry->Flags = RememberFlags; 2544 UnusedEntry->Size = (USHORT)FreeSize; 2545 2546 /* Insert it to the heap and update total size */ 2547 RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE); 2548 Heap->TotalFreeSize += FreeSize; 2549 } 2550 else 2551 { 2552 /* There is a block after this one */ 2553 FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize); 2554 2555 if (FollowingEntry->Flags & HEAP_ENTRY_BUSY) 2556 { 2557 /* Update flags and set size of the unused space entry */ 2558 UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY); 2559 UnusedEntry->Size = (USHORT)FreeSize; 2560 2561 /* Update previous size of the following entry */ 2562 FollowingEntry->PreviousSize = (USHORT)FreeSize; 2563 2564 /* Insert it to the heap and update total free size */ 2565 RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE); 2566 Heap->TotalFreeSize += FreeSize; 2567 } 2568 else 2569 { 2570 /* That following entry is also free, what a fortune! */ 2571 RememberFlags = FollowingEntry->Flags; 2572 2573 /* Remove it */ 2574 RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE); 2575 Heap->TotalFreeSize -= FollowingEntry->Size; 2576 2577 /* And make up a new combined block */ 2578 FreeSize += FollowingEntry->Size; 2579 UnusedEntry->Flags = RememberFlags; 2580 2581 /* Check where to put it */ 2582 if (FreeSize <= HEAP_MAX_BLOCK_SIZE) 2583 { 2584 /* Fine for a dedicated list */ 2585 UnusedEntry->Size = (USHORT)FreeSize; 2586 2587 if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY)) 2588 { 2589 ((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = (USHORT)FreeSize; 2590 } 2591 else 2592 { 2593 SegmentOffset = UnusedEntry->SegmentOffset; 2594 ASSERT(SegmentOffset < HEAP_SEGMENTS); 2595 } 2596 2597 /* Insert it back and update total size */ 2598 RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE); 2599 Heap->TotalFreeSize += FreeSize; 2600 } 2601 else 2602 { 2603 /* The block is very large, leave all the hassle to the insertion routine */ 2604 RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize); 2605 } 2606 } 2607 } 2608 } 2609 2610 /* Properly "zero out" (and fill!) the space */ 2611 if (Flags & HEAP_ZERO_MEMORY) 2612 { 2613 RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize); 2614 } 2615 else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) 2616 { 2617 /* Calculate tail part which we need to fill */ 2618 TailPart = PrevSize & (sizeof(ULONG) - 1); 2619 2620 /* "Invert" it as usual */ 2621 if (TailPart) TailPart = 4 - TailPart; 2622 2623 if (Size > (PrevSize + TailPart)) 2624 AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1); 2625 2626 if (AddedSize) 2627 { 2628 RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart, 2629 AddedSize, 2630 ARENA_INUSE_FILLER); 2631 } 2632 } 2633 2634 /* Fill the new tail */ 2635 if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) 2636 { 2637 RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, 2638 HEAP_ENTRY_SIZE, 2639 HEAP_TAIL_FILL); 2640 } 2641 2642 /* Copy user settable flags */ 2643 InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS; 2644 InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4); 2645 2646 /* Return success */ 2647 return TRUE; 2648 } 2649 2650 PHEAP_ENTRY_EXTRA NTAPI 2651 RtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry) 2652 { 2653 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; 2654 2655 /* Check if it's a big block */ 2656 if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) 2657 { 2658 VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); 2659 2660 /* Return a pointer to the extra stuff*/ 2661 return &VirtualEntry->ExtraStuff; 2662 } 2663 else 2664 { 2665 /* This is a usual entry, which means extra stuff follows this block */ 2666 return (PHEAP_ENTRY_EXTRA)(HeapEntry + HeapEntry->Size - 1); 2667 } 2668 } 2669 2670 2671 /*********************************************************************** 2672 * RtlReAllocateHeap 2673 * PARAMS 2674 * Heap [in] Handle of heap block 2675 * Flags [in] Heap reallocation flags 2676 * Ptr, [in] Address of memory to reallocate 2677 * Size [in] Number of bytes to reallocate 2678 * 2679 * RETURNS 2680 * Pointer to reallocated memory block 2681 * NULL: Failure 2682 * 0x7d030f60--invalid flags in RtlHeapAllocate 2683 * @implemented 2684 */ 2685 PVOID NTAPI 2686 RtlReAllocateHeap(HANDLE HeapPtr, 2687 ULONG Flags, 2688 PVOID Ptr, 2689 SIZE_T Size) 2690 { 2691 PHEAP Heap = (PHEAP)HeapPtr; 2692 PHEAP_ENTRY InUseEntry, NewInUseEntry; 2693 PHEAP_ENTRY_EXTRA OldExtra, NewExtra; 2694 SIZE_T AllocationSize, FreeSize, DecommitSize; 2695 BOOLEAN HeapLocked = FALSE; 2696 PVOID NewBaseAddress; 2697 PHEAP_FREE_ENTRY SplitBlock, SplitBlock2; 2698 SIZE_T OldSize, Index, OldIndex; 2699 UCHAR FreeFlags; 2700 NTSTATUS Status; 2701 PVOID DecommitBase; 2702 SIZE_T RemainderBytes, ExtraSize; 2703 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock; 2704 EXCEPTION_RECORD ExceptionRecord; 2705 UCHAR SegmentOffset; 2706 2707 /* Return success in case of a null pointer */ 2708 if (!Ptr) 2709 { 2710 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS); 2711 return NULL; 2712 } 2713 2714 /* Force heap flags */ 2715 Flags |= Heap->ForceFlags; 2716 2717 /* Call special heap */ 2718 if (RtlpHeapIsSpecial(Flags)) 2719 return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size); 2720 2721 /* Make sure size is valid */ 2722 if (Size >= 0x80000000) 2723 { 2724 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); 2725 return NULL; 2726 } 2727 2728 /* Calculate allocation size and index */ 2729 if (Size) 2730 AllocationSize = Size; 2731 else 2732 AllocationSize = 1; 2733 AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask; 2734 2735 /* Add up extra stuff, if it is present anywhere */ 2736 if (((((PHEAP_ENTRY)Ptr)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT) || 2737 (Flags & HEAP_EXTRA_FLAGS_MASK) || 2738 Heap->PseudoTagEntries) 2739 { 2740 AllocationSize += sizeof(HEAP_ENTRY_EXTRA); 2741 } 2742 2743 /* Acquire the lock if necessary */ 2744 if (!(Flags & HEAP_NO_SERIALIZE)) 2745 { 2746 RtlEnterHeapLock(Heap->LockVariable, TRUE); 2747 HeapLocked = TRUE; 2748 /* Do not acquire the lock anymore for re-entrant call */ 2749 Flags |= HEAP_NO_SERIALIZE; 2750 } 2751 2752 /* Get the pointer to the in-use entry */ 2753 InUseEntry = (PHEAP_ENTRY)Ptr - 1; 2754 2755 /* If that entry is not really in-use, we have a problem */ 2756 if (!(InUseEntry->Flags & HEAP_ENTRY_BUSY)) 2757 { 2758 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 2759 2760 /* Release the lock and return */ 2761 if (HeapLocked) 2762 RtlLeaveHeapLock(Heap->LockVariable); 2763 return Ptr; 2764 } 2765 2766 if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) 2767 { 2768 /* This is a virtually allocated block. Get its size */ 2769 OldSize = RtlpGetSizeOfBigBlock(InUseEntry); 2770 2771 /* Convert it to an index */ 2772 OldIndex = (OldSize + InUseEntry->Size) >> HEAP_ENTRY_SHIFT; 2773 2774 /* Calculate new allocation size and round it to the page size */ 2775 AllocationSize += FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); 2776 AllocationSize = ROUND_UP(AllocationSize, PAGE_SIZE); 2777 } 2778 else 2779 { 2780 /* Usual entry */ 2781 OldIndex = InUseEntry->Size; 2782 2783 OldSize = (OldIndex << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes; 2784 } 2785 2786 /* Calculate new index */ 2787 Index = AllocationSize >> HEAP_ENTRY_SHIFT; 2788 2789 /* Check for 4 different scenarios (old size, new size, old index, new index) */ 2790 if (Index <= OldIndex) 2791 { 2792 /* Difference must be greater than 1, adjust if it's not so */ 2793 if (Index + 1 == OldIndex) 2794 { 2795 Index++; 2796 AllocationSize += sizeof(HEAP_ENTRY); 2797 } 2798 2799 /* Calculate new size */ 2800 if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) 2801 { 2802 /* Simple in case of a virtual alloc - just an unused size */ 2803 InUseEntry->Size = (USHORT)(AllocationSize - Size); 2804 ASSERT(InUseEntry->Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY)); 2805 } 2806 else if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 2807 { 2808 /* There is extra stuff, take it into account */ 2809 OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1); 2810 NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1); 2811 *NewExtra = *OldExtra; 2812 2813 // FIXME Tagging, TagIndex 2814 2815 /* Update unused bytes count */ 2816 InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size); 2817 } 2818 else 2819 { 2820 // FIXME Tagging, SmallTagIndex 2821 InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size); 2822 } 2823 2824 /* If new size is bigger than the old size */ 2825 if (Size > OldSize) 2826 { 2827 /* Zero out that additional space if required */ 2828 if (Flags & HEAP_ZERO_MEMORY) 2829 { 2830 RtlZeroMemory((PCHAR)Ptr + OldSize, Size - OldSize); 2831 } 2832 else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) 2833 { 2834 /* Fill it on free if required */ 2835 RemainderBytes = OldSize & (sizeof(ULONG) - 1); 2836 2837 if (RemainderBytes) 2838 RemainderBytes = 4 - RemainderBytes; 2839 2840 if (Size > (OldSize + RemainderBytes)) 2841 { 2842 /* Calculate actual amount of extra bytes to fill */ 2843 ExtraSize = (Size - (OldSize + RemainderBytes)) & ~(sizeof(ULONG) - 1); 2844 2845 /* Fill them if there are any */ 2846 if (ExtraSize != 0) 2847 { 2848 RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + OldSize + RemainderBytes, 2849 ExtraSize, 2850 ARENA_INUSE_FILLER); 2851 } 2852 } 2853 } 2854 } 2855 2856 /* Fill tail of the heap entry if required */ 2857 if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) 2858 { 2859 RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, 2860 HEAP_ENTRY_SIZE, 2861 HEAP_TAIL_FILL); 2862 } 2863 2864 /* Check if the difference is significant or not */ 2865 if (Index != OldIndex) 2866 { 2867 /* Save flags */ 2868 FreeFlags = InUseEntry->Flags & ~HEAP_ENTRY_BUSY; 2869 2870 if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC) 2871 { 2872 /* This is a virtual block allocation */ 2873 VirtualAllocBlock = CONTAINING_RECORD(InUseEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); 2874 2875 // FIXME Tagging! 2876 2877 DecommitBase = (PCHAR)VirtualAllocBlock + AllocationSize; 2878 DecommitSize = (OldIndex << HEAP_ENTRY_SHIFT) - AllocationSize; 2879 2880 /* Release the memory */ 2881 Status = ZwFreeVirtualMemory(NtCurrentProcess(), 2882 (PVOID *)&DecommitBase, 2883 &DecommitSize, 2884 MEM_RELEASE); 2885 2886 if (!NT_SUCCESS(Status)) 2887 { 2888 DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase, DecommitSize, Status); 2889 } 2890 else 2891 { 2892 /* Otherwise reduce the commit size */ 2893 VirtualAllocBlock->CommitSize -= DecommitSize; 2894 } 2895 } 2896 else 2897 { 2898 /* Reduce size of the block and possibly split it */ 2899 SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index); 2900 2901 /* Initialize this entry */ 2902 SplitBlock->Flags = FreeFlags; 2903 SplitBlock->PreviousSize = (USHORT)Index; 2904 SplitBlock->SegmentOffset = InUseEntry->SegmentOffset; 2905 2906 /* Remember free size */ 2907 FreeSize = InUseEntry->Size - Index; 2908 2909 /* Set new size */ 2910 InUseEntry->Size = (USHORT)Index; 2911 InUseEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY; 2912 2913 /* Is that the last entry */ 2914 if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) 2915 { 2916 SegmentOffset = SplitBlock->SegmentOffset; 2917 ASSERT(SegmentOffset < HEAP_SEGMENTS); 2918 2919 /* Set its size and insert it to the list */ 2920 SplitBlock->Size = (USHORT)FreeSize; 2921 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); 2922 2923 /* Update total free size */ 2924 Heap->TotalFreeSize += FreeSize; 2925 } 2926 else 2927 { 2928 /* Get the block after that one */ 2929 SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize); 2930 2931 if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) 2932 { 2933 /* It's in use, add it here*/ 2934 SplitBlock->Size = (USHORT)FreeSize; 2935 2936 /* Update previous size of the next entry */ 2937 ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize; 2938 2939 /* Insert it to the list */ 2940 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); 2941 2942 /* Update total size */ 2943 Heap->TotalFreeSize += FreeSize; 2944 } 2945 else 2946 { 2947 /* Next entry is free, so merge with it */ 2948 SplitBlock->Flags = SplitBlock2->Flags; 2949 2950 /* Remove it, update total size */ 2951 RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE); 2952 Heap->TotalFreeSize -= SplitBlock2->Size; 2953 2954 /* Calculate total free size */ 2955 FreeSize += SplitBlock2->Size; 2956 2957 if (FreeSize <= HEAP_MAX_BLOCK_SIZE) 2958 { 2959 SplitBlock->Size = (USHORT)FreeSize; 2960 2961 if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) 2962 { 2963 /* Update previous size of the next entry */ 2964 ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize; 2965 } 2966 else 2967 { 2968 SegmentOffset = SplitBlock->SegmentOffset; 2969 ASSERT(SegmentOffset < HEAP_SEGMENTS); 2970 } 2971 2972 /* Insert the new one back and update total size */ 2973 RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); 2974 Heap->TotalFreeSize += FreeSize; 2975 } 2976 else 2977 { 2978 /* Just add it */ 2979 RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize); 2980 } 2981 } 2982 } 2983 } 2984 } 2985 } 2986 else 2987 { 2988 /* We're growing the block */ 2989 if ((InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) || 2990 !RtlpGrowBlockInPlace(Heap, Flags, InUseEntry, Size, Index)) 2991 { 2992 /* Growing in place failed, so growing out of place */ 2993 if (Flags & HEAP_REALLOC_IN_PLACE_ONLY) 2994 { 2995 DPRINT1("Realloc in place failed, but it was the only option\n"); 2996 Ptr = NULL; 2997 } 2998 else 2999 { 3000 /* Clear tag bits */ 3001 Flags &= ~HEAP_TAG_MASK; 3002 3003 /* Process extra stuff */ 3004 if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 3005 { 3006 /* Preserve user settable flags */ 3007 Flags &= ~HEAP_SETTABLE_USER_FLAGS; 3008 3009 Flags |= HEAP_SETTABLE_USER_VALUE | ((InUseEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4); 3010 3011 /* Get pointer to the old extra data */ 3012 OldExtra = RtlpGetExtraStuffPointer(InUseEntry); 3013 3014 /* Save tag index if it was set */ 3015 if (OldExtra->TagIndex && 3016 !(OldExtra->TagIndex & HEAP_PSEUDO_TAG_FLAG)) 3017 { 3018 Flags |= OldExtra->TagIndex << HEAP_TAG_SHIFT; 3019 } 3020 } 3021 else if (InUseEntry->SmallTagIndex) 3022 { 3023 /* Take small tag index into account */ 3024 Flags |= InUseEntry->SmallTagIndex << HEAP_TAG_SHIFT; 3025 } 3026 3027 /* Allocate new block from the heap */ 3028 NewBaseAddress = RtlAllocateHeap(HeapPtr, 3029 Flags & ~HEAP_ZERO_MEMORY, 3030 Size); 3031 3032 /* Proceed if it didn't fail */ 3033 if (NewBaseAddress) 3034 { 3035 /* Get new entry pointer */ 3036 NewInUseEntry = (PHEAP_ENTRY)NewBaseAddress - 1; 3037 3038 /* Process extra stuff if it exists */ 3039 if (NewInUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 3040 { 3041 NewExtra = RtlpGetExtraStuffPointer(NewInUseEntry); 3042 3043 if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 3044 { 3045 OldExtra = RtlpGetExtraStuffPointer(InUseEntry); 3046 NewExtra->Settable = OldExtra->Settable; 3047 } 3048 else 3049 { 3050 RtlZeroMemory(NewExtra, sizeof(*NewExtra)); 3051 } 3052 } 3053 3054 /* Copy actual user bits */ 3055 if (Size < OldSize) 3056 RtlMoveMemory(NewBaseAddress, Ptr, Size); 3057 else 3058 RtlMoveMemory(NewBaseAddress, Ptr, OldSize); 3059 3060 /* Zero remaining part if required */ 3061 if (Size > OldSize && 3062 (Flags & HEAP_ZERO_MEMORY)) 3063 { 3064 RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize); 3065 } 3066 3067 /* Free the old block */ 3068 RtlFreeHeap(HeapPtr, Flags, Ptr); 3069 } 3070 3071 Ptr = NewBaseAddress; 3072 } 3073 } 3074 } 3075 3076 /* Did resizing fail? */ 3077 if (!Ptr && (Flags & HEAP_GENERATE_EXCEPTIONS)) 3078 { 3079 /* Generate an exception if required */ 3080 ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; 3081 ExceptionRecord.ExceptionRecord = NULL; 3082 ExceptionRecord.NumberParameters = 1; 3083 ExceptionRecord.ExceptionFlags = 0; 3084 ExceptionRecord.ExceptionInformation[0] = AllocationSize; 3085 3086 RtlRaiseException(&ExceptionRecord); 3087 } 3088 3089 /* Release the heap lock if it was acquired */ 3090 if (HeapLocked) 3091 RtlLeaveHeapLock(Heap->LockVariable); 3092 3093 return Ptr; 3094 } 3095 3096 3097 /*********************************************************************** 3098 * RtlCompactHeap 3099 * 3100 * @unimplemented 3101 */ 3102 ULONG NTAPI 3103 RtlCompactHeap(HANDLE Heap, 3104 ULONG Flags) 3105 { 3106 UNIMPLEMENTED; 3107 return 0; 3108 } 3109 3110 3111 /*********************************************************************** 3112 * RtlLockHeap 3113 * Attempts to acquire the critical section object for a specified heap. 3114 * 3115 * PARAMS 3116 * Heap [in] Handle of heap to lock for exclusive access 3117 * 3118 * RETURNS 3119 * TRUE: Success 3120 * FALSE: Failure 3121 * 3122 * @implemented 3123 */ 3124 BOOLEAN NTAPI 3125 RtlLockHeap(IN HANDLE HeapPtr) 3126 { 3127 PHEAP Heap = (PHEAP)HeapPtr; 3128 3129 /* Check for page heap */ 3130 if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) 3131 { 3132 return RtlpPageHeapLock(Heap); 3133 } 3134 3135 /* Check if it's really a heap */ 3136 if (Heap->Signature != HEAP_SIGNATURE) return FALSE; 3137 3138 /* Lock if it's lockable */ 3139 if (!(Heap->Flags & HEAP_NO_SERIALIZE)) 3140 { 3141 RtlEnterHeapLock(Heap->LockVariable, TRUE); 3142 } 3143 3144 return TRUE; 3145 } 3146 3147 3148 /*********************************************************************** 3149 * RtlUnlockHeap 3150 * Releases ownership of the critical section object. 3151 * 3152 * PARAMS 3153 * Heap [in] Handle to the heap to unlock 3154 * 3155 * RETURNS 3156 * TRUE: Success 3157 * FALSE: Failure 3158 * 3159 * @implemented 3160 */ 3161 BOOLEAN NTAPI 3162 RtlUnlockHeap(HANDLE HeapPtr) 3163 { 3164 PHEAP Heap = (PHEAP)HeapPtr; 3165 3166 /* Check for page heap */ 3167 if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) 3168 { 3169 return RtlpPageHeapUnlock(Heap); 3170 } 3171 3172 /* Check if it's really a heap */ 3173 if (Heap->Signature != HEAP_SIGNATURE) return FALSE; 3174 3175 /* Unlock if it's lockable */ 3176 if (!(Heap->Flags & HEAP_NO_SERIALIZE)) 3177 { 3178 RtlLeaveHeapLock(Heap->LockVariable); 3179 } 3180 3181 return TRUE; 3182 } 3183 3184 3185 /*********************************************************************** 3186 * RtlSizeHeap 3187 * PARAMS 3188 * Heap [in] Handle of heap 3189 * Flags [in] Heap size control flags 3190 * Ptr [in] Address of memory to return size for 3191 * 3192 * RETURNS 3193 * Size in bytes of allocated memory 3194 * 0xffffffff: Failure 3195 * 3196 * @implemented 3197 */ 3198 SIZE_T NTAPI 3199 RtlSizeHeap( 3200 HANDLE HeapPtr, 3201 ULONG Flags, 3202 PVOID Ptr 3203 ) 3204 { 3205 PHEAP Heap = (PHEAP)HeapPtr; 3206 PHEAP_ENTRY HeapEntry; 3207 SIZE_T EntrySize; 3208 3209 // FIXME This is a hack around missing SEH support! 3210 if (!Heap) 3211 { 3212 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE); 3213 return (SIZE_T)-1; 3214 } 3215 3216 /* Force flags */ 3217 Flags |= Heap->ForceFlags; 3218 3219 /* Call special heap */ 3220 if (RtlpHeapIsSpecial(Flags)) 3221 return RtlDebugSizeHeap(Heap, Flags, Ptr); 3222 3223 /* Get the heap entry pointer */ 3224 HeapEntry = (PHEAP_ENTRY)Ptr - 1; 3225 3226 /* Return -1 if that entry is free */ 3227 if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) 3228 { 3229 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 3230 return (SIZE_T)-1; 3231 } 3232 3233 /* Get size of this block depending if it's a usual or a big one */ 3234 if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) 3235 { 3236 EntrySize = RtlpGetSizeOfBigBlock(HeapEntry); 3237 } 3238 else 3239 { 3240 /* Calculate it */ 3241 EntrySize = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes; 3242 } 3243 3244 /* Return calculated size */ 3245 return EntrySize; 3246 } 3247 3248 BOOLEAN NTAPI 3249 RtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry) 3250 { 3251 SIZE_T Size, Result; 3252 PCHAR TailPart; 3253 3254 /* Calculate size */ 3255 if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) 3256 Size = RtlpGetSizeOfBigBlock(HeapEntry); 3257 else 3258 Size = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes; 3259 3260 /* Calculate pointer to the tail part of the block */ 3261 TailPart = (PCHAR)(HeapEntry + 1) + Size; 3262 3263 /* Compare tail pattern */ 3264 Result = RtlCompareMemory(TailPart, 3265 FillPattern, 3266 HEAP_ENTRY_SIZE); 3267 3268 if (Result != HEAP_ENTRY_SIZE) 3269 { 3270 DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size, HeapEntry, TailPart + Result); 3271 return FALSE; 3272 } 3273 3274 /* All is fine */ 3275 return TRUE; 3276 } 3277 3278 BOOLEAN NTAPI 3279 RtlpValidateHeapHeaders( 3280 PHEAP Heap, 3281 BOOLEAN Recalculate) 3282 { 3283 // We skip header validation for now 3284 return TRUE; 3285 } 3286 3287 BOOLEAN NTAPI 3288 RtlpValidateHeapEntry( 3289 PHEAP Heap, 3290 PHEAP_ENTRY HeapEntry) 3291 { 3292 BOOLEAN BigAllocation, EntryFound = FALSE; 3293 PHEAP_SEGMENT Segment; 3294 ULONG SegmentOffset; 3295 3296 /* Perform various consistency checks of this entry */ 3297 if (!HeapEntry) goto invalid_entry; 3298 if ((ULONG_PTR)HeapEntry & (HEAP_ENTRY_SIZE - 1)) goto invalid_entry; 3299 if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) goto invalid_entry; 3300 3301 BigAllocation = HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC; 3302 Segment = Heap->Segments[HeapEntry->SegmentOffset]; 3303 3304 if (BigAllocation && 3305 (((ULONG_PTR)HeapEntry & (PAGE_SIZE - 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock))) 3306 goto invalid_entry; 3307 3308 if (!BigAllocation && (HeapEntry->SegmentOffset >= HEAP_SEGMENTS || 3309 !Segment || 3310 HeapEntry < Segment->FirstEntry || 3311 HeapEntry >= Segment->LastValidEntry)) 3312 goto invalid_entry; 3313 3314 if ((HeapEntry->Flags & HEAP_ENTRY_FILL_PATTERN) && 3315 !RtlpCheckInUsePattern(HeapEntry)) 3316 goto invalid_entry; 3317 3318 /* Checks are done, if this is a virtual entry, that's all */ 3319 if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) return TRUE; 3320 3321 /* Go through segments and check if this entry fits into any of them */ 3322 for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++) 3323 { 3324 Segment = Heap->Segments[SegmentOffset]; 3325 if (!Segment) continue; 3326 3327 if ((HeapEntry >= Segment->FirstEntry) && 3328 (HeapEntry < Segment->LastValidEntry)) 3329 { 3330 /* Got it */ 3331 EntryFound = TRUE; 3332 break; 3333 } 3334 } 3335 3336 /* Return our result of finding entry in the segments */ 3337 return EntryFound; 3338 3339 invalid_entry: 3340 DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry, Heap); 3341 return FALSE; 3342 } 3343 3344 BOOLEAN NTAPI 3345 RtlpValidateHeapSegment( 3346 PHEAP Heap, 3347 PHEAP_SEGMENT Segment, 3348 UCHAR SegmentOffset, 3349 PULONG FreeEntriesCount, 3350 PSIZE_T TotalFreeSize, 3351 PSIZE_T TagEntries, 3352 PSIZE_T PseudoTagEntries) 3353 { 3354 PHEAP_UCR_DESCRIPTOR UcrDescriptor; 3355 PLIST_ENTRY UcrEntry; 3356 SIZE_T ByteSize, Size, Result; 3357 PHEAP_ENTRY CurrentEntry; 3358 ULONG UnCommittedPages; 3359 ULONG UnCommittedRanges; 3360 ULONG PreviousSize; 3361 3362 UnCommittedPages = 0; 3363 UnCommittedRanges = 0; 3364 3365 if (IsListEmpty(&Segment->UCRSegmentList)) 3366 { 3367 UcrEntry = NULL; 3368 UcrDescriptor = NULL; 3369 } 3370 else 3371 { 3372 UcrEntry = Segment->UCRSegmentList.Flink; 3373 UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry); 3374 } 3375 3376 if (Segment->BaseAddress == Heap) 3377 CurrentEntry = &Heap->Entry; 3378 else 3379 CurrentEntry = &Segment->Entry; 3380 3381 while (CurrentEntry < Segment->LastValidEntry) 3382 { 3383 if (UcrDescriptor && 3384 ((PVOID)CurrentEntry >= UcrDescriptor->Address)) 3385 { 3386 DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n", 3387 CurrentEntry, UcrDescriptor->Address, 3388 (PCHAR)UcrDescriptor->Address + UcrDescriptor->Size); 3389 3390 return FALSE; 3391 } 3392 3393 PreviousSize = 0; 3394 3395 while (CurrentEntry < Segment->LastValidEntry) 3396 { 3397 if (PreviousSize != CurrentEntry->PreviousSize) 3398 { 3399 DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n", 3400 CurrentEntry, CurrentEntry->PreviousSize, PreviousSize); 3401 3402 return FALSE; 3403 } 3404 3405 PreviousSize = CurrentEntry->Size; 3406 Size = CurrentEntry->Size << HEAP_ENTRY_SHIFT; 3407 3408 if (CurrentEntry->Flags & HEAP_ENTRY_BUSY) 3409 { 3410 if (TagEntries) 3411 { 3412 UNIMPLEMENTED; 3413 } 3414 3415 /* Check fill pattern */ 3416 if (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN) 3417 { 3418 if (!RtlpCheckInUsePattern(CurrentEntry)) 3419 return FALSE; 3420 } 3421 } 3422 else 3423 { 3424 /* The entry is free, increase free entries count and total free size */ 3425 *FreeEntriesCount = *FreeEntriesCount + 1; 3426 *TotalFreeSize += CurrentEntry->Size; 3427 3428 if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) && 3429 (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN)) 3430 { 3431 ByteSize = Size - sizeof(HEAP_FREE_ENTRY); 3432 3433 if ((CurrentEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) && 3434 (ByteSize > sizeof(HEAP_FREE_ENTRY_EXTRA))) 3435 { 3436 ByteSize -= sizeof(HEAP_FREE_ENTRY_EXTRA); 3437 } 3438 3439 Result = RtlCompareMemoryUlong((PCHAR)((PHEAP_FREE_ENTRY)CurrentEntry + 1), 3440 ByteSize, 3441 ARENA_FREE_FILLER); 3442 3443 if (Result != ByteSize) 3444 { 3445 DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n", 3446 CurrentEntry, 3447 (PCHAR)(CurrentEntry + 1) + Result); 3448 3449 return FALSE; 3450 } 3451 } 3452 } 3453 3454 if (CurrentEntry->SegmentOffset != SegmentOffset) 3455 { 3456 DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", 3457 CurrentEntry, SegmentOffset, CurrentEntry->SegmentOffset); 3458 return FALSE; 3459 } 3460 3461 /* Check if it's the last entry */ 3462 if (CurrentEntry->Flags & HEAP_ENTRY_LAST_ENTRY) 3463 { 3464 CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size); 3465 3466 if (!UcrDescriptor) 3467 { 3468 /* Check if it's not really the last one */ 3469 if (CurrentEntry != Segment->LastValidEntry) 3470 { 3471 DPRINT1("HEAP: Heap entry %p is not last block in segment (%p)\n", 3472 CurrentEntry, Segment->LastValidEntry); 3473 return FALSE; 3474 } 3475 } 3476 else if (CurrentEntry != UcrDescriptor->Address) 3477 { 3478 DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n", 3479 CurrentEntry, UcrDescriptor->Address); 3480 3481 return FALSE; 3482 } 3483 else 3484 { 3485 UnCommittedPages += (ULONG)(UcrDescriptor->Size / PAGE_SIZE); 3486 UnCommittedRanges++; 3487 3488 CurrentEntry = (PHEAP_ENTRY)((PCHAR)UcrDescriptor->Address + UcrDescriptor->Size); 3489 3490 /* Go to the next UCR descriptor */ 3491 UcrEntry = UcrEntry->Flink; 3492 if (UcrEntry == &Segment->UCRSegmentList) 3493 { 3494 UcrEntry = NULL; 3495 UcrDescriptor = NULL; 3496 } 3497 else 3498 { 3499 UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry); 3500 } 3501 } 3502 3503 break; 3504 } 3505 3506 /* Advance to the next entry */ 3507 CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size); 3508 } 3509 } 3510 3511 /* Check total numbers of UCP and UCR */ 3512 if (Segment->NumberOfUnCommittedPages != UnCommittedPages) 3513 { 3514 DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n", 3515 Segment, Segment->NumberOfUnCommittedPages, UnCommittedPages); 3516 3517 return FALSE; 3518 } 3519 3520 if (Segment->NumberOfUnCommittedRanges != UnCommittedRanges) 3521 { 3522 DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n", 3523 Segment, Segment->NumberOfUnCommittedRanges, UnCommittedRanges); 3524 3525 return FALSE; 3526 } 3527 3528 return TRUE; 3529 } 3530 3531 BOOLEAN NTAPI 3532 RtlpValidateHeap(PHEAP Heap, 3533 BOOLEAN ForceValidation) 3534 { 3535 UCHAR SegmentOffset; 3536 SIZE_T TotalFreeSize; 3537 PLIST_ENTRY ListHead, NextEntry; 3538 ULONG FreeBlocksCount, FreeListEntriesCount; 3539 ULONG HintIndex; 3540 3541 /* Check headers */ 3542 if (!RtlpValidateHeapHeaders(Heap, FALSE)) 3543 return FALSE; 3544 3545 /* Skip validation if it's not needed */ 3546 if (!ForceValidation && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED)) 3547 return TRUE; 3548 3549 /* Check free list */ 3550 FreeListEntriesCount = 0; 3551 ListHead = &Heap->FreeLists; 3552 NextEntry = ListHead->Flink; 3553 3554 while (NextEntry != ListHead) 3555 { 3556 PHEAP_FREE_ENTRY FreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList); 3557 3558 NextEntry = NextEntry->Flink; 3559 3560 if (NextEntry != ListHead) 3561 { 3562 PHEAP_FREE_ENTRY NextFreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList); 3563 /* Free entries must be sorted */ 3564 if (FreeEntry->Size > NextFreeEntry->Size) 3565 { 3566 DPRINT1("Dedicated free entry %p of size %ld is not put in order.\n", FreeEntry, FreeEntry->Size); 3567 } 3568 } 3569 3570 /* Check that the hint is there */ 3571 if (FreeEntry->Size > Heap->DeCommitFreeBlockThreshold) 3572 { 3573 if (Heap->FreeHints[0] == NULL) 3574 { 3575 DPRINT1("No hint pointing to the non-dedicated list although there is a free entry %p of size %ld.\n", 3576 FreeEntry, FreeEntry->Size); 3577 } 3578 if (!RtlTestBit(&Heap->FreeHintBitmap, 0)) 3579 { 3580 DPRINT1("Hint bit 0 is not set although there is a free entry %p of size %ld.\n", 3581 FreeEntry, FreeEntry->Size); 3582 } 3583 } 3584 else 3585 { 3586 if (Heap->FreeHints[FreeEntry->Size - 1] == NULL) 3587 { 3588 DPRINT1("No hint pointing to the dedicated list although there is a free entry %p of size %ld.\n", 3589 FreeEntry, FreeEntry->Size); 3590 } 3591 if (!RtlTestBit(&Heap->FreeHintBitmap, FreeEntry->Size - 1)) 3592 { 3593 DPRINT1("Hint bit 0 is not set although there is a free entry %p of size %ld.\n", 3594 FreeEntry, FreeEntry->Size); 3595 } 3596 } 3597 3598 /* If there is an in-use entry in a free list - that's quite a big problem */ 3599 if (FreeEntry->Flags & HEAP_ENTRY_BUSY) 3600 { 3601 DPRINT1("HEAP: Free element %p is marked in-use\n", FreeEntry); 3602 return FALSE; 3603 } 3604 3605 /* Add up to the total amount of free entries */ 3606 FreeListEntriesCount++; 3607 } 3608 3609 /* Check free list hints */ 3610 for (HintIndex = 0; HintIndex < Heap->DeCommitFreeBlockThreshold; HintIndex++) 3611 { 3612 if (Heap->FreeHints[HintIndex] != NULL) 3613 { 3614 PHEAP_FREE_ENTRY FreeEntry = CONTAINING_RECORD(Heap->FreeHints[HintIndex], HEAP_FREE_ENTRY, FreeList); 3615 3616 if (!RtlTestBit(&Heap->FreeHintBitmap, HintIndex)) 3617 { 3618 DPRINT1("Hint bitmap bit at %u is not set, but there is a hint entry.\n", HintIndex); 3619 } 3620 3621 if (HintIndex == 0) 3622 { 3623 if (FreeEntry->Size <= Heap->DeCommitFreeBlockThreshold) 3624 { 3625 DPRINT1("There is an entry %p of size %lu, smaller than the decommit threshold %lu in the non-dedicated free list hint.\n", 3626 FreeEntry, FreeEntry->Size, Heap->DeCommitFreeBlockThreshold); 3627 } 3628 } 3629 else 3630 { 3631 if (HintIndex != FreeEntry->Size - 1) 3632 { 3633 DPRINT1("There is an entry %p of size %lu at the position %u in the free entry hint array.\n", 3634 FreeEntry, FreeEntry->Size, HintIndex); 3635 } 3636 3637 if (FreeEntry->FreeList.Blink != &Heap->FreeLists) 3638 { 3639 /* The entry right before the hint must be smaller. */ 3640 PHEAP_FREE_ENTRY PreviousFreeEntry = CONTAINING_RECORD(FreeEntry->FreeList.Blink, 3641 HEAP_FREE_ENTRY, 3642 FreeList); 3643 if (PreviousFreeEntry->Size >= FreeEntry->Size) 3644 { 3645 DPRINT1("Free entry hint %p of size %lu is larger than the entry before it %p, which is of size %lu.\n", 3646 FreeEntry, FreeEntry->Size, PreviousFreeEntry, PreviousFreeEntry->Size); 3647 } 3648 } 3649 } 3650 } 3651 else if (RtlTestBit(&Heap->FreeHintBitmap, HintIndex)) 3652 { 3653 DPRINT1("Hint bitmap bit at %u is set, but there is no hint entry.\n", HintIndex); 3654 } 3655 } 3656 3657 /* Check big allocations */ 3658 ListHead = &Heap->VirtualAllocdBlocks; 3659 NextEntry = ListHead->Flink; 3660 3661 while (ListHead != NextEntry) 3662 { 3663 PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock = CONTAINING_RECORD(NextEntry, HEAP_VIRTUAL_ALLOC_ENTRY, Entry); 3664 3665 /* We can only check the fill pattern */ 3666 if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN) 3667 { 3668 if (!RtlpCheckInUsePattern(&VirtualAllocBlock->BusyBlock)) 3669 return FALSE; 3670 } 3671 3672 NextEntry = NextEntry->Flink; 3673 } 3674 3675 /* Check all segments */ 3676 FreeBlocksCount = 0; 3677 TotalFreeSize = 0; 3678 3679 for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++) 3680 { 3681 PHEAP_SEGMENT Segment = Heap->Segments[SegmentOffset]; 3682 3683 /* Go to the next one if there is no segment */ 3684 if (!Segment) continue; 3685 3686 if (!RtlpValidateHeapSegment(Heap, 3687 Segment, 3688 SegmentOffset, 3689 &FreeBlocksCount, 3690 &TotalFreeSize, 3691 NULL, 3692 NULL)) 3693 { 3694 return FALSE; 3695 } 3696 } 3697 3698 if (FreeListEntriesCount != FreeBlocksCount) 3699 { 3700 DPRINT1("HEAP: Free blocks count in arena (%lu) does not match free blocks number in the free lists (%lu)\n", FreeBlocksCount, FreeListEntriesCount); 3701 return FALSE; 3702 } 3703 3704 if (Heap->TotalFreeSize != TotalFreeSize) 3705 { 3706 DPRINT1("HEAP: Total size of free blocks in arena (%Iu) does not equal to the one in heap header (%Iu)\n", TotalFreeSize, Heap->TotalFreeSize); 3707 return FALSE; 3708 } 3709 3710 return TRUE; 3711 } 3712 3713 /*********************************************************************** 3714 * RtlValidateHeap 3715 * Validates a specified heap. 3716 * 3717 * PARAMS 3718 * Heap [in] Handle to the heap 3719 * Flags [in] Bit flags that control access during operation 3720 * Block [in] Optional pointer to memory block to validate 3721 * 3722 * NOTES 3723 * Flags is ignored. 3724 * 3725 * RETURNS 3726 * TRUE: Success 3727 * FALSE: Failure 3728 * 3729 * @implemented 3730 */ 3731 BOOLEAN NTAPI RtlValidateHeap( 3732 HANDLE HeapPtr, 3733 ULONG Flags, 3734 PVOID Block 3735 ) 3736 { 3737 PHEAP Heap = (PHEAP)HeapPtr; 3738 BOOLEAN HeapLocked = FALSE; 3739 BOOLEAN HeapValid; 3740 3741 /* Check for page heap */ 3742 if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) 3743 return RtlpDebugPageHeapValidate(HeapPtr, Flags, Block); 3744 3745 /* Check signature */ 3746 if (Heap->Signature != HEAP_SIGNATURE) 3747 { 3748 DPRINT1("HEAP: Signature %lx is invalid for heap %p\n", Heap->Signature, Heap); 3749 return FALSE; 3750 } 3751 3752 /* Force flags */ 3753 Flags |= Heap->ForceFlags; 3754 3755 /* Acquire the lock if necessary */ 3756 if (!(Flags & HEAP_NO_SERIALIZE)) 3757 { 3758 RtlEnterHeapLock(Heap->LockVariable, TRUE); 3759 HeapLocked = TRUE; 3760 } 3761 3762 /* Either validate whole heap or just one entry */ 3763 if (!Block) 3764 HeapValid = RtlpValidateHeap(Heap, TRUE); 3765 else 3766 HeapValid = RtlpValidateHeapEntry(Heap, (PHEAP_ENTRY)Block - 1); 3767 3768 /* Unlock if it's lockable */ 3769 if (HeapLocked) 3770 { 3771 RtlLeaveHeapLock(Heap->LockVariable); 3772 } 3773 3774 return HeapValid; 3775 } 3776 3777 /* 3778 * @unimplemented 3779 */ 3780 NTSTATUS NTAPI 3781 RtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine, 3782 PVOID lParam) 3783 { 3784 UNIMPLEMENTED; 3785 return STATUS_NOT_IMPLEMENTED; 3786 } 3787 3788 /* 3789 * @unimplemented 3790 */ 3791 BOOLEAN NTAPI 3792 RtlValidateProcessHeaps(VOID) 3793 { 3794 UNIMPLEMENTED; 3795 return TRUE; 3796 } 3797 3798 3799 /* 3800 * @unimplemented 3801 */ 3802 BOOLEAN NTAPI 3803 RtlZeroHeap( 3804 IN PVOID HeapHandle, 3805 IN ULONG Flags 3806 ) 3807 { 3808 UNIMPLEMENTED; 3809 return FALSE; 3810 } 3811 3812 /* 3813 * @implemented 3814 */ 3815 BOOLEAN 3816 NTAPI 3817 RtlSetUserValueHeap(IN PVOID HeapHandle, 3818 IN ULONG Flags, 3819 IN PVOID BaseAddress, 3820 IN PVOID UserValue) 3821 { 3822 PHEAP Heap = (PHEAP)HeapHandle; 3823 PHEAP_ENTRY HeapEntry; 3824 PHEAP_ENTRY_EXTRA Extra; 3825 BOOLEAN HeapLocked = FALSE, ValueSet = FALSE; 3826 3827 /* Force flags */ 3828 Flags |= Heap->ForceFlags; 3829 3830 /* Call special heap */ 3831 if (RtlpHeapIsSpecial(Flags)) 3832 return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue); 3833 3834 /* Lock if it's lockable */ 3835 if (!(Heap->Flags & HEAP_NO_SERIALIZE)) 3836 { 3837 RtlEnterHeapLock(Heap->LockVariable, TRUE); 3838 HeapLocked = TRUE; 3839 } 3840 3841 /* Get a pointer to the entry */ 3842 HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; 3843 3844 /* If it's a free entry - return error */ 3845 if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) 3846 { 3847 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 3848 3849 /* Release the heap lock if it was acquired */ 3850 if (HeapLocked) 3851 RtlLeaveHeapLock(Heap->LockVariable); 3852 3853 return FALSE; 3854 } 3855 3856 /* Check if this entry has an extra stuff associated with it */ 3857 if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 3858 { 3859 /* Use extra to store the value */ 3860 Extra = RtlpGetExtraStuffPointer(HeapEntry); 3861 Extra->Settable = (ULONG_PTR)UserValue; 3862 3863 /* Indicate that value was set */ 3864 ValueSet = TRUE; 3865 } 3866 3867 /* Release the heap lock if it was acquired */ 3868 if (HeapLocked) 3869 RtlLeaveHeapLock(Heap->LockVariable); 3870 3871 return ValueSet; 3872 } 3873 3874 /* 3875 * @implemented 3876 */ 3877 BOOLEAN 3878 NTAPI 3879 RtlSetUserFlagsHeap(IN PVOID HeapHandle, 3880 IN ULONG Flags, 3881 IN PVOID BaseAddress, 3882 IN ULONG UserFlagsReset, 3883 IN ULONG UserFlagsSet) 3884 { 3885 PHEAP Heap = (PHEAP)HeapHandle; 3886 PHEAP_ENTRY HeapEntry; 3887 BOOLEAN HeapLocked = FALSE; 3888 3889 /* Force flags */ 3890 Flags |= Heap->ForceFlags; 3891 3892 /* Call special heap */ 3893 if (RtlpHeapIsSpecial(Flags)) 3894 return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet); 3895 3896 /* Lock if it's lockable */ 3897 if (!(Flags & HEAP_NO_SERIALIZE)) 3898 { 3899 RtlEnterHeapLock(Heap->LockVariable, TRUE); 3900 HeapLocked = TRUE; 3901 } 3902 3903 /* Get a pointer to the entry */ 3904 HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; 3905 3906 /* If it's a free entry - return error */ 3907 if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) 3908 { 3909 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 3910 3911 /* Release the heap lock if it was acquired */ 3912 if (HeapLocked) 3913 RtlLeaveHeapLock(Heap->LockVariable); 3914 3915 return FALSE; 3916 } 3917 3918 /* Set / reset flags */ 3919 HeapEntry->Flags &= ~(UserFlagsReset >> 4); 3920 HeapEntry->Flags |= (UserFlagsSet >> 4); 3921 3922 /* Release the heap lock if it was acquired */ 3923 if (HeapLocked) 3924 RtlLeaveHeapLock(Heap->LockVariable); 3925 3926 return TRUE; 3927 } 3928 3929 /* 3930 * @implemented 3931 */ 3932 BOOLEAN 3933 NTAPI 3934 RtlGetUserInfoHeap(IN PVOID HeapHandle, 3935 IN ULONG Flags, 3936 IN PVOID BaseAddress, 3937 OUT PVOID *UserValue, 3938 OUT PULONG UserFlags) 3939 { 3940 PHEAP Heap = (PHEAP)HeapHandle; 3941 PHEAP_ENTRY HeapEntry; 3942 PHEAP_ENTRY_EXTRA Extra; 3943 BOOLEAN HeapLocked = FALSE; 3944 3945 /* Force flags */ 3946 Flags |= Heap->ForceFlags; 3947 3948 /* Call special heap */ 3949 if (RtlpHeapIsSpecial(Flags)) 3950 return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags); 3951 3952 /* Lock if it's lockable */ 3953 if (!(Flags & HEAP_NO_SERIALIZE)) 3954 { 3955 RtlEnterHeapLock(Heap->LockVariable, TRUE); 3956 HeapLocked = TRUE; 3957 } 3958 3959 /* Get a pointer to the entry */ 3960 HeapEntry = (PHEAP_ENTRY)BaseAddress - 1; 3961 3962 /* If it's a free entry - return error */ 3963 if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) 3964 { 3965 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 3966 3967 /* Release the heap lock if it was acquired */ 3968 if (HeapLocked) 3969 RtlLeaveHeapLock(Heap->LockVariable); 3970 3971 return FALSE; 3972 } 3973 3974 /* Check if this entry has an extra stuff associated with it */ 3975 if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) 3976 { 3977 /* Get pointer to extra data */ 3978 Extra = RtlpGetExtraStuffPointer(HeapEntry); 3979 3980 /* Pass user value */ 3981 if (UserValue) 3982 *UserValue = (PVOID)Extra->Settable; 3983 } 3984 3985 /* Decode and return user flags */ 3986 if (UserFlags) 3987 *UserFlags = (HeapEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4; 3988 3989 /* Release the heap lock if it was acquired */ 3990 if (HeapLocked) 3991 RtlLeaveHeapLock(Heap->LockVariable); 3992 3993 return TRUE; 3994 } 3995 3996 /* 3997 * @unimplemented 3998 */ 3999 NTSTATUS 4000 NTAPI 4001 RtlUsageHeap(IN HANDLE Heap, 4002 IN ULONG Flags, 4003 OUT PRTL_HEAP_USAGE Usage) 4004 { 4005 /* TODO */ 4006 UNIMPLEMENTED; 4007 return STATUS_NOT_IMPLEMENTED; 4008 } 4009 4010 PWSTR 4011 NTAPI 4012 RtlQueryTagHeap(IN PVOID HeapHandle, 4013 IN ULONG Flags, 4014 IN USHORT TagIndex, 4015 IN BOOLEAN ResetCounters, 4016 OUT PRTL_HEAP_TAG_INFO HeapTagInfo) 4017 { 4018 /* TODO */ 4019 UNIMPLEMENTED; 4020 return NULL; 4021 } 4022 4023 ULONG 4024 NTAPI 4025 RtlExtendHeap(IN HANDLE Heap, 4026 IN ULONG Flags, 4027 IN PVOID P, 4028 IN SIZE_T Size) 4029 { 4030 /* TODO */ 4031 UNIMPLEMENTED; 4032 return 0; 4033 } 4034 4035 ULONG 4036 NTAPI 4037 RtlCreateTagHeap(_In_ HANDLE HeapHandle, 4038 _In_ ULONG Flags, 4039 _In_opt_ PWSTR TagName, 4040 _In_ PWSTR TagSubName) 4041 { 4042 /* TODO */ 4043 UNIMPLEMENTED; 4044 return 0; 4045 } 4046 4047 NTSTATUS 4048 NTAPI 4049 RtlWalkHeap(IN HANDLE HeapHandle, 4050 IN PVOID HeapEntry) 4051 { 4052 UNIMPLEMENTED; 4053 return STATUS_NOT_IMPLEMENTED; 4054 } 4055 4056 PVOID 4057 NTAPI 4058 RtlProtectHeap(IN PVOID HeapHandle, 4059 IN BOOLEAN ReadOnly) 4060 { 4061 UNIMPLEMENTED; 4062 return NULL; 4063 } 4064 4065 NTSTATUS 4066 NTAPI 4067 RtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL, 4068 IN HEAP_INFORMATION_CLASS HeapInformationClass, 4069 IN PVOID HeapInformation, 4070 IN SIZE_T HeapInformationLength) 4071 { 4072 /* Setting heap information is not really supported except for enabling LFH */ 4073 if (HeapInformationClass == HeapCompatibilityInformation) 4074 { 4075 /* Check buffer length */ 4076 if (HeapInformationLength < sizeof(ULONG)) 4077 { 4078 /* The provided buffer is too small */ 4079 return STATUS_BUFFER_TOO_SMALL; 4080 } 4081 4082 /* Check for a special magic value for enabling LFH */ 4083 if (*(PULONG)HeapInformation != 2) 4084 { 4085 return STATUS_UNSUCCESSFUL; 4086 } 4087 4088 DPRINT1("RtlSetHeapInformation() needs to enable LFH\n"); 4089 return STATUS_SUCCESS; 4090 } 4091 4092 return STATUS_SUCCESS; 4093 } 4094 4095 NTSTATUS 4096 NTAPI 4097 RtlQueryHeapInformation(HANDLE HeapHandle, 4098 HEAP_INFORMATION_CLASS HeapInformationClass, 4099 PVOID HeapInformation, 4100 SIZE_T HeapInformationLength, 4101 PSIZE_T ReturnLength OPTIONAL) 4102 { 4103 PHEAP Heap = (PHEAP)HeapHandle; 4104 4105 /* Only HeapCompatibilityInformation is supported */ 4106 if (HeapInformationClass == HeapCompatibilityInformation) 4107 { 4108 /* Set result length */ 4109 if (ReturnLength) 4110 *ReturnLength = sizeof(ULONG); 4111 4112 /* Check buffer length */ 4113 if (HeapInformationLength < sizeof(ULONG)) 4114 { 4115 /* It's too small, return needed length */ 4116 return STATUS_BUFFER_TOO_SMALL; 4117 } 4118 4119 /* Return front end heap type */ 4120 *(PULONG)HeapInformation = Heap->FrontEndHeapType; 4121 4122 return STATUS_SUCCESS; 4123 } 4124 4125 return STATUS_UNSUCCESSFUL; 4126 } 4127 4128 /* @implemented */ 4129 ULONG 4130 NTAPI 4131 RtlMultipleAllocateHeap(IN PVOID HeapHandle, 4132 IN ULONG Flags, 4133 IN SIZE_T Size, 4134 IN ULONG Count, 4135 OUT PVOID *Array) 4136 { 4137 ULONG Index; 4138 EXCEPTION_RECORD ExceptionRecord; 4139 4140 for (Index = 0; Index < Count; ++Index) 4141 { 4142 Array[Index] = RtlAllocateHeap(HeapHandle, Flags, Size); 4143 if (Array[Index] == NULL) 4144 { 4145 /* ERROR_NOT_ENOUGH_MEMORY */ 4146 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); 4147 4148 if (Flags & HEAP_GENERATE_EXCEPTIONS) 4149 { 4150 ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; 4151 ExceptionRecord.ExceptionRecord = NULL; 4152 ExceptionRecord.NumberParameters = 0; 4153 ExceptionRecord.ExceptionFlags = 0; 4154 4155 RtlRaiseException(&ExceptionRecord); 4156 } 4157 break; 4158 } 4159 } 4160 4161 return Index; 4162 } 4163 4164 /* @implemented */ 4165 ULONG 4166 NTAPI 4167 RtlMultipleFreeHeap(IN PVOID HeapHandle, 4168 IN ULONG Flags, 4169 IN ULONG Count, 4170 OUT PVOID *Array) 4171 { 4172 ULONG Index; 4173 4174 for (Index = 0; Index < Count; ++Index) 4175 { 4176 if (Array[Index] == NULL) 4177 continue; 4178 4179 _SEH2_TRY 4180 { 4181 if (!RtlFreeHeap(HeapHandle, Flags, Array[Index])) 4182 { 4183 /* ERROR_INVALID_PARAMETER */ 4184 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 4185 break; 4186 } 4187 } 4188 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4189 { 4190 /* ERROR_INVALID_PARAMETER */ 4191 RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); 4192 break; 4193 } 4194 _SEH2_END; 4195 } 4196 4197 return Index; 4198 } 4199 4200 /* 4201 * Info: 4202 * - https://securityxploded.com/enumheaps.php 4203 * - https://evilcodecave.wordpress.com/2009/04/14/rtlqueryprocessheapinformation-as-anti-dbg-trick/ 4204 */ 4205 struct _DEBUG_BUFFER; 4206 4207 NTSTATUS 4208 NTAPI 4209 RtlQueryProcessHeapInformation( 4210 IN struct _DEBUG_BUFFER *DebugBuffer) 4211 { 4212 UNIMPLEMENTED; 4213 return STATUS_NOT_IMPLEMENTED; 4214 } 4215 4216 /* EOF */ 4217 4218