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