2 UEFI Heap Guard functions.
4 Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "HeapGuard.h"
14 // Global to avoid infinite reentrance of memory allocation when updating
15 // page table attributes, which may need allocate pages for new PDE/PTE.
17 GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mOnGuarding
= FALSE
;
20 // Pointer to table tracking the Guarded memory with bitmap, in which '1'
21 // is used to indicate memory guarded. '0' might be free memory or Guard
22 // page itself, depending on status of memory adjacent to it.
24 GLOBAL_REMOVE_IF_UNREFERENCED UINT64 mGuardedMemoryMap
= 0;
27 // Current depth level of map table pointed by mGuardedMemoryMap.
28 // mMapLevel must be initialized at least by 1. It will be automatically
29 // updated according to the address of memory just tracked.
31 GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMapLevel
= 1;
34 // Shift and mask for each level of map table
36 GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelShift
[GUARDED_HEAP_MAP_TABLE_DEPTH
]
37 = GUARDED_HEAP_MAP_TABLE_DEPTH_SHIFTS
;
38 GLOBAL_REMOVE_IF_UNREFERENCED UINTN mLevelMask
[GUARDED_HEAP_MAP_TABLE_DEPTH
]
39 = GUARDED_HEAP_MAP_TABLE_DEPTH_MASKS
;
42 // Used for promoting freed but not used pages.
44 GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS mLastPromotedPage
= BASE_4GB
;
47 Set corresponding bits in bitmap table to 1 according to the address.
49 @param[in] Address Start address to set for.
50 @param[in] BitNumber Number of bits to set.
51 @param[in] BitMap Pointer to bitmap which covers the Address.
58 IN EFI_PHYSICAL_ADDRESS Address
,
69 StartBit
= (UINTN
)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address
);
70 EndBit
= (StartBit
+ BitNumber
- 1) % GUARDED_HEAP_MAP_ENTRY_BITS
;
72 if ((StartBit
+ BitNumber
) >= GUARDED_HEAP_MAP_ENTRY_BITS
) {
73 Msbs
= (GUARDED_HEAP_MAP_ENTRY_BITS
- StartBit
) %
74 GUARDED_HEAP_MAP_ENTRY_BITS
;
75 Lsbs
= (EndBit
+ 1) % GUARDED_HEAP_MAP_ENTRY_BITS
;
76 Qwords
= (BitNumber
- Msbs
) / GUARDED_HEAP_MAP_ENTRY_BITS
;
84 *BitMap
|= LShiftU64 (LShiftU64 (1, Msbs
) - 1, StartBit
);
89 SetMem64 ((VOID
*)BitMap
, Qwords
* GUARDED_HEAP_MAP_ENTRY_BYTES
,
95 *BitMap
|= (LShiftU64 (1, Lsbs
) - 1);
100 Set corresponding bits in bitmap table to 0 according to the address.
102 @param[in] Address Start address to set for.
103 @param[in] BitNumber Number of bits to set.
104 @param[in] BitMap Pointer to bitmap which covers the Address.
111 IN EFI_PHYSICAL_ADDRESS Address
,
122 StartBit
= (UINTN
)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address
);
123 EndBit
= (StartBit
+ BitNumber
- 1) % GUARDED_HEAP_MAP_ENTRY_BITS
;
125 if ((StartBit
+ BitNumber
) >= GUARDED_HEAP_MAP_ENTRY_BITS
) {
126 Msbs
= (GUARDED_HEAP_MAP_ENTRY_BITS
- StartBit
) %
127 GUARDED_HEAP_MAP_ENTRY_BITS
;
128 Lsbs
= (EndBit
+ 1) % GUARDED_HEAP_MAP_ENTRY_BITS
;
129 Qwords
= (BitNumber
- Msbs
) / GUARDED_HEAP_MAP_ENTRY_BITS
;
137 *BitMap
&= ~LShiftU64 (LShiftU64 (1, Msbs
) - 1, StartBit
);
142 SetMem64 ((VOID
*)BitMap
, Qwords
* GUARDED_HEAP_MAP_ENTRY_BYTES
, 0);
147 *BitMap
&= ~(LShiftU64 (1, Lsbs
) - 1);
152 Get corresponding bits in bitmap table according to the address.
154 The value of bit 0 corresponds to the status of memory at given Address.
155 No more than 64 bits can be retrieved in one call.
157 @param[in] Address Start address to retrieve bits for.
158 @param[in] BitNumber Number of bits to get.
159 @param[in] BitMap Pointer to bitmap which covers the Address.
161 @return An integer containing the bits information.
166 IN EFI_PHYSICAL_ADDRESS Address
,
177 ASSERT (BitNumber
<= GUARDED_HEAP_MAP_ENTRY_BITS
);
179 StartBit
= (UINTN
)GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address
);
180 EndBit
= (StartBit
+ BitNumber
- 1) % GUARDED_HEAP_MAP_ENTRY_BITS
;
182 if ((StartBit
+ BitNumber
) > GUARDED_HEAP_MAP_ENTRY_BITS
) {
183 Msbs
= GUARDED_HEAP_MAP_ENTRY_BITS
- StartBit
;
184 Lsbs
= (EndBit
+ 1) % GUARDED_HEAP_MAP_ENTRY_BITS
;
190 if (StartBit
== 0 && BitNumber
== GUARDED_HEAP_MAP_ENTRY_BITS
) {
193 Result
= RShiftU64((*BitMap
), StartBit
) & (LShiftU64(1, Msbs
) - 1);
196 Result
|= LShiftU64 ((*BitMap
) & (LShiftU64 (1, Lsbs
) - 1), Msbs
);
204 Locate the pointer of bitmap from the guarded memory bitmap tables, which
205 covers the given Address.
207 @param[in] Address Start address to search the bitmap for.
208 @param[in] AllocMapUnit Flag to indicate memory allocation for the table.
209 @param[out] BitMap Pointer to bitmap which covers the Address.
211 @return The bit number from given Address to the end of current map table.
214 FindGuardedMemoryMap (
215 IN EFI_PHYSICAL_ADDRESS Address
,
216 IN BOOLEAN AllocMapUnit
,
229 // Adjust current map table depth according to the address to access
231 while (AllocMapUnit
&&
232 mMapLevel
< GUARDED_HEAP_MAP_TABLE_DEPTH
&&
235 mLevelShift
[GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
- 1]
238 if (mGuardedMemoryMap
!= 0) {
239 Size
= (mLevelMask
[GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
- 1] + 1)
240 * GUARDED_HEAP_MAP_ENTRY_BYTES
;
241 Status
= CoreInternalAllocatePages (
244 EFI_SIZE_TO_PAGES (Size
),
248 ASSERT_EFI_ERROR (Status
);
249 ASSERT (MapMemory
!= 0);
251 SetMem ((VOID
*)(UINTN
)MapMemory
, Size
, 0);
253 *(UINT64
*)(UINTN
)MapMemory
= mGuardedMemoryMap
;
254 mGuardedMemoryMap
= MapMemory
;
261 GuardMap
= &mGuardedMemoryMap
;
262 for (Level
= GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
;
263 Level
< GUARDED_HEAP_MAP_TABLE_DEPTH
;
266 if (*GuardMap
== 0) {
272 Size
= (mLevelMask
[Level
] + 1) * GUARDED_HEAP_MAP_ENTRY_BYTES
;
273 Status
= CoreInternalAllocatePages (
276 EFI_SIZE_TO_PAGES (Size
),
280 ASSERT_EFI_ERROR (Status
);
281 ASSERT (MapMemory
!= 0);
283 SetMem ((VOID
*)(UINTN
)MapMemory
, Size
, 0);
284 *GuardMap
= MapMemory
;
287 Index
= (UINTN
)RShiftU64 (Address
, mLevelShift
[Level
]);
288 Index
&= mLevelMask
[Level
];
289 GuardMap
= (UINT64
*)(UINTN
)((*GuardMap
) + Index
* sizeof (UINT64
));
293 BitsToUnitEnd
= GUARDED_HEAP_MAP_BITS
- GUARDED_HEAP_MAP_BIT_INDEX (Address
);
296 return BitsToUnitEnd
;
300 Set corresponding bits in bitmap table to 1 according to given memory range.
302 @param[in] Address Memory address to guard from.
303 @param[in] NumberOfPages Number of pages to guard.
309 SetGuardedMemoryBits (
310 IN EFI_PHYSICAL_ADDRESS Address
,
311 IN UINTN NumberOfPages
318 while (NumberOfPages
> 0) {
319 BitsToUnitEnd
= FindGuardedMemoryMap (Address
, TRUE
, &BitMap
);
320 ASSERT (BitMap
!= NULL
);
322 if (NumberOfPages
> BitsToUnitEnd
) {
324 Bits
= BitsToUnitEnd
;
326 Bits
= NumberOfPages
;
329 SetBits (Address
, Bits
, BitMap
);
331 NumberOfPages
-= Bits
;
332 Address
+= EFI_PAGES_TO_SIZE (Bits
);
337 Clear corresponding bits in bitmap table according to given memory range.
339 @param[in] Address Memory address to unset from.
340 @param[in] NumberOfPages Number of pages to unset guard.
346 ClearGuardedMemoryBits (
347 IN EFI_PHYSICAL_ADDRESS Address
,
348 IN UINTN NumberOfPages
355 while (NumberOfPages
> 0) {
356 BitsToUnitEnd
= FindGuardedMemoryMap (Address
, TRUE
, &BitMap
);
357 ASSERT (BitMap
!= NULL
);
359 if (NumberOfPages
> BitsToUnitEnd
) {
361 Bits
= BitsToUnitEnd
;
363 Bits
= NumberOfPages
;
366 ClearBits (Address
, Bits
, BitMap
);
368 NumberOfPages
-= Bits
;
369 Address
+= EFI_PAGES_TO_SIZE (Bits
);
374 Retrieve corresponding bits in bitmap table according to given memory range.
376 @param[in] Address Memory address to retrieve from.
377 @param[in] NumberOfPages Number of pages to retrieve.
379 @return An integer containing the guarded memory bitmap.
382 GetGuardedMemoryBits (
383 IN EFI_PHYSICAL_ADDRESS Address
,
384 IN UINTN NumberOfPages
393 ASSERT (NumberOfPages
<= GUARDED_HEAP_MAP_ENTRY_BITS
);
397 while (NumberOfPages
> 0) {
398 BitsToUnitEnd
= FindGuardedMemoryMap (Address
, FALSE
, &BitMap
);
400 if (NumberOfPages
> BitsToUnitEnd
) {
402 Bits
= BitsToUnitEnd
;
404 Bits
= NumberOfPages
;
407 if (BitMap
!= NULL
) {
408 Result
|= LShiftU64 (GetBits (Address
, Bits
, BitMap
), Shift
);
412 NumberOfPages
-= Bits
;
413 Address
+= EFI_PAGES_TO_SIZE (Bits
);
420 Get bit value in bitmap table for the given address.
422 @param[in] Address The address to retrieve for.
429 IN EFI_PHYSICAL_ADDRESS Address
434 FindGuardedMemoryMap (Address
, FALSE
, &GuardMap
);
435 if (GuardMap
!= NULL
) {
436 if (RShiftU64 (*GuardMap
,
437 GUARDED_HEAP_MAP_ENTRY_BIT_INDEX (Address
)) & 1) {
447 Check to see if the page at the given address is a Guard page or not.
449 @param[in] Address The address to check for.
451 @return TRUE The page at Address is a Guard page.
452 @return FALSE The page at Address is not a Guard page.
457 IN EFI_PHYSICAL_ADDRESS Address
463 // There must be at least one guarded page before and/or after given
464 // address if it's a Guard page. The bitmap pattern should be one of
467 BitMap
= GetGuardedMemoryBits (Address
- EFI_PAGE_SIZE
, 3);
468 return ((BitMap
== BIT0
) || (BitMap
== BIT2
) || (BitMap
== (BIT2
| BIT0
)));
473 Check to see if the page at the given address is guarded or not.
475 @param[in] Address The address to check for.
477 @return TRUE The page at Address is guarded.
478 @return FALSE The page at Address is not guarded.
483 IN EFI_PHYSICAL_ADDRESS Address
486 return (GetGuardMapBit (Address
) == 1);
490 Set the page at the given address to be a Guard page.
492 This is done by changing the page table attribute to be NOT PRSENT.
494 @param[in] BaseAddress Page address to Guard at
501 IN EFI_PHYSICAL_ADDRESS BaseAddress
511 // Set flag to make sure allocating memory without GUARD for page table
512 // operation; otherwise infinite loops could be caused.
516 // Note: This might overwrite other attributes needed by other features,
517 // such as NX memory protection.
519 Status
= gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, EFI_PAGE_SIZE
, EFI_MEMORY_RP
);
520 ASSERT_EFI_ERROR (Status
);
525 Unset the Guard page at the given address to the normal memory.
527 This is done by changing the page table attribute to be PRSENT.
529 @param[in] BaseAddress Page address to Guard at.
536 IN EFI_PHYSICAL_ADDRESS BaseAddress
547 // Once the Guard page is unset, it will be freed back to memory pool. NX
548 // memory protection must be restored for this page if NX is enabled for free
552 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy
) & (1 << EfiConventionalMemory
)) != 0) {
553 Attributes
|= EFI_MEMORY_XP
;
557 // Set flag to make sure allocating memory without GUARD for page table
558 // operation; otherwise infinite loops could be caused.
562 // Note: This might overwrite other attributes needed by other features,
563 // such as memory protection (NX). Please make sure they are not enabled
566 Status
= gCpu
->SetMemoryAttributes (gCpu
, BaseAddress
, EFI_PAGE_SIZE
, Attributes
);
567 ASSERT_EFI_ERROR (Status
);
572 Check to see if the memory at the given address should be guarded or not.
574 @param[in] MemoryType Memory type to check.
575 @param[in] AllocateType Allocation type to check.
576 @param[in] PageOrPool Indicate a page allocation or pool allocation.
579 @return TRUE The given type of memory should be guarded.
580 @return FALSE The given type of memory should not be guarded.
583 IsMemoryTypeToGuard (
584 IN EFI_MEMORY_TYPE MemoryType
,
585 IN EFI_ALLOCATE_TYPE AllocateType
,
592 if (AllocateType
== AllocateAddress
) {
596 if ((PcdGet8 (PcdHeapGuardPropertyMask
) & PageOrPool
) == 0) {
600 if (PageOrPool
== GUARD_HEAP_TYPE_POOL
) {
601 ConfigBit
= PcdGet64 (PcdHeapGuardPoolType
);
602 } else if (PageOrPool
== GUARD_HEAP_TYPE_PAGE
) {
603 ConfigBit
= PcdGet64 (PcdHeapGuardPageType
);
605 ConfigBit
= (UINT64
)-1;
608 if ((UINT32
)MemoryType
>= MEMORY_TYPE_OS_RESERVED_MIN
) {
610 } else if ((UINT32
) MemoryType
>= MEMORY_TYPE_OEM_RESERVED_MIN
) {
612 } else if (MemoryType
< EfiMaxMemoryType
) {
613 TestBit
= LShiftU64 (1, MemoryType
);
614 } else if (MemoryType
== EfiMaxMemoryType
) {
615 TestBit
= (UINT64
)-1;
620 return ((ConfigBit
& TestBit
) != 0);
624 Check to see if the pool at the given address should be guarded or not.
626 @param[in] MemoryType Pool type to check.
629 @return TRUE The given type of pool should be guarded.
630 @return FALSE The given type of pool should not be guarded.
634 IN EFI_MEMORY_TYPE MemoryType
637 return IsMemoryTypeToGuard (MemoryType
, AllocateAnyPages
,
638 GUARD_HEAP_TYPE_POOL
);
642 Check to see if the page at the given address should be guarded or not.
644 @param[in] MemoryType Page type to check.
645 @param[in] AllocateType Allocation type to check.
647 @return TRUE The given type of page should be guarded.
648 @return FALSE The given type of page should not be guarded.
652 IN EFI_MEMORY_TYPE MemoryType
,
653 IN EFI_ALLOCATE_TYPE AllocateType
656 return IsMemoryTypeToGuard (MemoryType
, AllocateType
, GUARD_HEAP_TYPE_PAGE
);
660 Check to see if the heap guard is enabled for page and/or pool allocation.
662 @param[in] GuardType Specify the sub-type(s) of Heap Guard.
671 return IsMemoryTypeToGuard (EfiMaxMemoryType
, AllocateAnyPages
, GuardType
);
675 Set head Guard and tail Guard for the given memory range.
677 @param[in] Memory Base address of memory to set guard for.
678 @param[in] NumberOfPages Memory size in pages.
684 IN EFI_PHYSICAL_ADDRESS Memory
,
685 IN UINTN NumberOfPages
688 EFI_PHYSICAL_ADDRESS GuardPage
;
693 GuardPage
= Memory
+ EFI_PAGES_TO_SIZE (NumberOfPages
);
694 if (!IsGuardPage (GuardPage
)) {
695 SetGuardPage (GuardPage
);
699 GuardPage
= Memory
- EFI_PAGES_TO_SIZE (1);
700 if (!IsGuardPage (GuardPage
)) {
701 SetGuardPage (GuardPage
);
705 // Mark the memory range as Guarded
707 SetGuardedMemoryBits (Memory
, NumberOfPages
);
711 Unset head Guard and tail Guard for the given memory range.
713 @param[in] Memory Base address of memory to unset guard for.
714 @param[in] NumberOfPages Memory size in pages.
719 UnsetGuardForMemory (
720 IN EFI_PHYSICAL_ADDRESS Memory
,
721 IN UINTN NumberOfPages
724 EFI_PHYSICAL_ADDRESS GuardPage
;
727 if (NumberOfPages
== 0) {
732 // Head Guard must be one page before, if any.
735 // -------------------
736 // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard)
737 // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard)
738 // 1 X -> Don't free first page (need a new Guard)
739 // (it'll be turned into a Guard page later)
740 // -------------------
743 GuardPage
= Memory
- EFI_PAGES_TO_SIZE (1);
744 GuardBitmap
= GetGuardedMemoryBits (Memory
- EFI_PAGES_TO_SIZE (2), 2);
745 if ((GuardBitmap
& BIT1
) == 0) {
747 // Head Guard exists.
749 if ((GuardBitmap
& BIT0
) == 0) {
751 // If the head Guard is not a tail Guard of adjacent memory block,
754 UnsetGuardPage (GuardPage
);
758 // Pages before memory to free are still in Guard. It's a partial free
759 // case. Turn first page of memory block to free into a new Guard.
761 SetGuardPage (Memory
);
765 // Tail Guard must be the page after this memory block to free, if any.
768 // --------------------
769 // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard)
770 // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard)
771 // X 1 -> Don't free last page (need a new Guard)
772 // (it'll be turned into a Guard page later)
773 // --------------------
776 GuardPage
= Memory
+ EFI_PAGES_TO_SIZE (NumberOfPages
);
777 GuardBitmap
= GetGuardedMemoryBits (GuardPage
, 2);
778 if ((GuardBitmap
& BIT0
) == 0) {
780 // Tail Guard exists.
782 if ((GuardBitmap
& BIT1
) == 0) {
784 // If the tail Guard is not a head Guard of adjacent memory block,
785 // free it; otherwise, keep it.
787 UnsetGuardPage (GuardPage
);
791 // Pages after memory to free are still in Guard. It's a partial free
792 // case. We need to keep one page to be a head Guard.
794 SetGuardPage (GuardPage
- EFI_PAGES_TO_SIZE (1));
798 // No matter what, we just clear the mark of the Guarded memory.
800 ClearGuardedMemoryBits(Memory
, NumberOfPages
);
804 Adjust address of free memory according to existing and/or required Guard.
806 This function will check if there're existing Guard pages of adjacent
807 memory blocks, and try to use it as the Guard page of the memory to be
810 @param[in] Start Start address of free memory block.
811 @param[in] Size Size of free memory block.
812 @param[in] SizeRequested Size of memory to allocate.
814 @return The end address of memory block found.
815 @return 0 if no enough space for the required size of memory and its Guard.
821 IN UINT64 SizeRequested
827 // UEFI spec requires that allocated pool must be 8-byte aligned. If it's
828 // indicated to put the pool near the Tail Guard, we need extra bytes to
829 // make sure alignment of the returned pool address.
831 if ((PcdGet8 (PcdHeapGuardPropertyMask
) & BIT7
) == 0) {
832 SizeRequested
= ALIGN_VALUE(SizeRequested
, 8);
835 Target
= Start
+ Size
- SizeRequested
;
836 ASSERT (Target
>= Start
);
841 if (!IsGuardPage (Start
+ Size
)) {
842 // No Guard at tail to share. One more page is needed.
843 Target
-= EFI_PAGES_TO_SIZE (1);
847 if (Target
< Start
) {
852 if (Target
== Start
) {
853 if (!IsGuardPage (Target
- EFI_PAGES_TO_SIZE (1))) {
854 // No enough space for a new head Guard if no Guard at head to share.
859 // OK, we have enough pages for memory and its Guards. Return the End of the
861 return Target
+ SizeRequested
- 1;
865 Adjust the start address and number of pages to free according to Guard.
867 The purpose of this function is to keep the shared Guard page with adjacent
868 memory block if it's still in guard, or free it if no more sharing. Another
869 is to reserve pages as Guard pages in partial page free situation.
871 @param[in,out] Memory Base address of memory to free.
872 @param[in,out] NumberOfPages Size of memory to free.
878 IN OUT EFI_PHYSICAL_ADDRESS
*Memory
,
879 IN OUT UINTN
*NumberOfPages
882 EFI_PHYSICAL_ADDRESS Start
;
883 EFI_PHYSICAL_ADDRESS MemoryToTest
;
887 if (Memory
== NULL
|| NumberOfPages
== NULL
|| *NumberOfPages
== 0) {
892 PagesToFree
= *NumberOfPages
;
895 // Head Guard must be one page before, if any.
898 // -------------------
899 // Head Guard -> 0 1 -> Don't free Head Guard (shared Guard)
900 // Head Guard -> 0 0 -> Free Head Guard either (not shared Guard)
901 // 1 X -> Don't free first page (need a new Guard)
902 // (it'll be turned into a Guard page later)
903 // -------------------
906 MemoryToTest
= Start
- EFI_PAGES_TO_SIZE (2);
907 GuardBitmap
= GetGuardedMemoryBits (MemoryToTest
, 2);
908 if ((GuardBitmap
& BIT1
) == 0) {
910 // Head Guard exists.
912 if ((GuardBitmap
& BIT0
) == 0) {
914 // If the head Guard is not a tail Guard of adjacent memory block,
915 // free it; otherwise, keep it.
917 Start
-= EFI_PAGES_TO_SIZE (1);
922 // No Head Guard, and pages before memory to free are still in Guard. It's a
923 // partial free case. We need to keep one page to be a tail Guard.
925 Start
+= EFI_PAGES_TO_SIZE (1);
930 // Tail Guard must be the page after this memory block to free, if any.
933 // --------------------
934 // 1 0 <- Tail Guard -> Don't free Tail Guard (shared Guard)
935 // 0 0 <- Tail Guard -> Free Tail Guard either (not shared Guard)
936 // X 1 -> Don't free last page (need a new Guard)
937 // (it'll be turned into a Guard page later)
938 // --------------------
941 MemoryToTest
= Start
+ EFI_PAGES_TO_SIZE (PagesToFree
);
942 GuardBitmap
= GetGuardedMemoryBits (MemoryToTest
, 2);
943 if ((GuardBitmap
& BIT0
) == 0) {
945 // Tail Guard exists.
947 if ((GuardBitmap
& BIT1
) == 0) {
949 // If the tail Guard is not a head Guard of adjacent memory block,
950 // free it; otherwise, keep it.
954 } else if (PagesToFree
> 0) {
956 // No Tail Guard, and pages after memory to free are still in Guard. It's a
957 // partial free case. We need to keep one page to be a head Guard.
963 *NumberOfPages
= PagesToFree
;
967 Adjust the base and number of pages to really allocate according to Guard.
969 @param[in,out] Memory Base address of free memory.
970 @param[in,out] NumberOfPages Size of memory to allocate.
976 IN OUT EFI_PHYSICAL_ADDRESS
*Memory
,
977 IN OUT UINTN
*NumberOfPages
981 // FindFreePages() has already taken the Guard into account. It's safe to
982 // adjust the start address and/or number of pages here, to make sure that
983 // the Guards are also "allocated".
985 if (!IsGuardPage (*Memory
+ EFI_PAGES_TO_SIZE (*NumberOfPages
))) {
986 // No tail Guard, add one.
990 if (!IsGuardPage (*Memory
- EFI_PAGE_SIZE
)) {
991 // No head Guard, add one.
992 *Memory
-= EFI_PAGE_SIZE
;
998 Adjust the pool head position to make sure the Guard page is adjavent to
999 pool tail or pool head.
1001 @param[in] Memory Base address of memory allocated.
1002 @param[in] NoPages Number of pages actually allocated.
1003 @param[in] Size Size of memory requested.
1004 (plus pool head/tail overhead)
1006 @return Address of pool head.
1010 IN EFI_PHYSICAL_ADDRESS Memory
,
1015 if (Memory
== 0 || (PcdGet8 (PcdHeapGuardPropertyMask
) & BIT7
) != 0) {
1017 // Pool head is put near the head Guard
1019 return (VOID
*)(UINTN
)Memory
;
1023 // Pool head is put near the tail Guard
1025 Size
= ALIGN_VALUE (Size
, 8);
1026 return (VOID
*)(UINTN
)(Memory
+ EFI_PAGES_TO_SIZE (NoPages
) - Size
);
1030 Get the page base address according to pool head address.
1032 @param[in] Memory Head address of pool to free.
1034 @return Address of pool head.
1038 IN EFI_PHYSICAL_ADDRESS Memory
1041 if (Memory
== 0 || (PcdGet8 (PcdHeapGuardPropertyMask
) & BIT7
) != 0) {
1043 // Pool head is put near the head Guard
1045 return (VOID
*)(UINTN
)Memory
;
1049 // Pool head is put near the tail Guard
1051 return (VOID
*)(UINTN
)(Memory
& ~EFI_PAGE_MASK
);
1055 Allocate or free guarded memory.
1057 @param[in] Start Start address of memory to allocate or free.
1058 @param[in] NumberOfPages Memory size in pages.
1059 @param[in] NewType Memory type to convert to.
1064 CoreConvertPagesWithGuard (
1066 IN UINTN NumberOfPages
,
1067 IN EFI_MEMORY_TYPE NewType
1073 if (NewType
== EfiConventionalMemory
) {
1075 OldPages
= NumberOfPages
;
1077 AdjustMemoryF (&Start
, &NumberOfPages
);
1079 // It's safe to unset Guard page inside memory lock because there should
1080 // be no memory allocation occurred in updating memory page attribute at
1081 // this point. And unsetting Guard page before free will prevent Guard
1082 // page just freed back to pool from being allocated right away before
1083 // marking it usable (from non-present to present).
1085 UnsetGuardForMemory (OldStart
, OldPages
);
1086 if (NumberOfPages
== 0) {
1090 AdjustMemoryA (&Start
, &NumberOfPages
);
1093 return CoreConvertPages (Start
, NumberOfPages
, NewType
);
1097 Set all Guard pages which cannot be set before CPU Arch Protocol installed.
1104 UINTN Entries
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1105 UINTN Shifts
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1106 UINTN Indices
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1107 UINT64 Tables
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1108 UINT64 Addresses
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1116 if (mGuardedMemoryMap
== 0 ||
1118 mMapLevel
> GUARDED_HEAP_MAP_TABLE_DEPTH
) {
1122 CopyMem (Entries
, mLevelMask
, sizeof (Entries
));
1123 CopyMem (Shifts
, mLevelShift
, sizeof (Shifts
));
1125 SetMem (Tables
, sizeof(Tables
), 0);
1126 SetMem (Addresses
, sizeof(Addresses
), 0);
1127 SetMem (Indices
, sizeof(Indices
), 0);
1129 Level
= GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
;
1130 Tables
[Level
] = mGuardedMemoryMap
;
1135 DumpGuardedMemoryBitmap ();
1139 if (Indices
[Level
] > Entries
[Level
]) {
1144 TableEntry
= ((UINT64
*)(UINTN
)(Tables
[Level
]))[Indices
[Level
]];
1145 Address
= Addresses
[Level
];
1147 if (TableEntry
== 0) {
1151 } else if (Level
< GUARDED_HEAP_MAP_TABLE_DEPTH
- 1) {
1154 Tables
[Level
] = TableEntry
;
1155 Addresses
[Level
] = Address
;
1163 while (Index
< GUARDED_HEAP_MAP_ENTRY_BITS
) {
1164 if ((TableEntry
& 1) == 1) {
1168 GuardPage
= Address
- EFI_PAGE_SIZE
;
1173 GuardPage
= Address
;
1180 if (GuardPage
!= 0) {
1181 SetGuardPage (GuardPage
);
1184 if (TableEntry
== 0) {
1188 TableEntry
= RShiftU64 (TableEntry
, 1);
1189 Address
+= EFI_PAGE_SIZE
;
1195 if (Level
< (GUARDED_HEAP_MAP_TABLE_DEPTH
- (INTN
)mMapLevel
)) {
1199 Indices
[Level
] += 1;
1200 Address
= (Level
== 0) ? 0 : Addresses
[Level
- 1];
1201 Addresses
[Level
] = Address
| LShiftU64(Indices
[Level
], Shifts
[Level
]);
1207 Find the address of top-most guarded free page.
1209 @param[out] Address Start address of top-most guarded free page.
1214 GetLastGuardedFreePageAddress (
1215 OUT EFI_PHYSICAL_ADDRESS
*Address
1218 EFI_PHYSICAL_ADDRESS AddressGranularity
;
1219 EFI_PHYSICAL_ADDRESS BaseAddress
;
1224 ASSERT (mMapLevel
>= 1);
1227 Map
= mGuardedMemoryMap
;
1228 for (Level
= GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
;
1229 Level
< GUARDED_HEAP_MAP_TABLE_DEPTH
;
1231 AddressGranularity
= LShiftU64 (1, mLevelShift
[Level
]);
1234 // Find the non-NULL entry at largest index.
1236 for (Index
= (INTN
)mLevelMask
[Level
]; Index
>= 0 ; --Index
) {
1237 if (((UINT64
*)(UINTN
)Map
)[Index
] != 0) {
1238 BaseAddress
+= MultU64x32 (AddressGranularity
, (UINT32
)Index
);
1239 Map
= ((UINT64
*)(UINTN
)Map
)[Index
];
1246 // Find the non-zero MSB then get the page address.
1249 Map
= RShiftU64 (Map
, 1);
1250 BaseAddress
+= EFI_PAGES_TO_SIZE (1);
1253 *Address
= BaseAddress
;
1259 @param[in] BaseAddress Base address of just freed pages.
1260 @param[in] Pages Number of freed pages.
1266 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
1270 SetGuardedMemoryBits (BaseAddress
, Pages
);
1274 Record freed pages as well as mark them as not-present.
1276 @param[in] BaseAddress Base address of just freed pages.
1277 @param[in] Pages Number of freed pages.
1284 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
1291 // Legacy memory lower than 1MB might be accessed with no allocation. Leave
1294 if (BaseAddress
< BASE_1MB
) {
1298 MarkFreedPages (BaseAddress
, Pages
);
1301 // Set flag to make sure allocating memory without GUARD for page table
1302 // operation; otherwise infinite loops could be caused.
1306 // Note: This might overwrite other attributes needed by other features,
1307 // such as NX memory protection.
1309 Status
= gCpu
->SetMemoryAttributes (
1312 EFI_PAGES_TO_SIZE (Pages
),
1316 // Normally we should ASSERT the returned Status. But there might be memory
1317 // alloc/free involved in SetMemoryAttributes(), which might fail this
1318 // calling. It's rare case so it's OK to let a few tiny holes be not-guarded.
1320 if (EFI_ERROR (Status
)) {
1321 DEBUG ((DEBUG_WARN
, "Failed to guard freed pages: %p (%lu)\n", BaseAddress
, (UINT64
)Pages
));
1323 mOnGuarding
= FALSE
;
1328 Record freed pages as well as mark them as not-present, if enabled.
1330 @param[in] BaseAddress Base address of just freed pages.
1331 @param[in] Pages Number of freed pages.
1337 GuardFreedPagesChecked (
1338 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
1342 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED
)) {
1343 GuardFreedPages (BaseAddress
, Pages
);
1348 Mark all pages freed before CPU Arch Protocol as not-present.
1352 GuardAllFreedPages (
1356 UINTN Entries
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1357 UINTN Shifts
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1358 UINTN Indices
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1359 UINT64 Tables
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1360 UINT64 Addresses
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1366 UINTN GuardPageNumber
;
1368 if (mGuardedMemoryMap
== 0 ||
1370 mMapLevel
> GUARDED_HEAP_MAP_TABLE_DEPTH
) {
1374 CopyMem (Entries
, mLevelMask
, sizeof (Entries
));
1375 CopyMem (Shifts
, mLevelShift
, sizeof (Shifts
));
1377 SetMem (Tables
, sizeof(Tables
), 0);
1378 SetMem (Addresses
, sizeof(Addresses
), 0);
1379 SetMem (Indices
, sizeof(Indices
), 0);
1381 Level
= GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
;
1382 Tables
[Level
] = mGuardedMemoryMap
;
1384 GuardPage
= (UINT64
)-1;
1385 GuardPageNumber
= 0;
1388 if (Indices
[Level
] > Entries
[Level
]) {
1392 TableEntry
= ((UINT64
*)(UINTN
)(Tables
[Level
]))[Indices
[Level
]];
1393 Address
= Addresses
[Level
];
1395 if (Level
< GUARDED_HEAP_MAP_TABLE_DEPTH
- 1) {
1397 Tables
[Level
] = TableEntry
;
1398 Addresses
[Level
] = Address
;
1404 while (BitIndex
!= 0) {
1405 if ((TableEntry
& BitIndex
) != 0) {
1406 if (GuardPage
== (UINT64
)-1) {
1407 GuardPage
= Address
;
1410 } else if (GuardPageNumber
> 0) {
1411 GuardFreedPages (GuardPage
, GuardPageNumber
);
1412 GuardPageNumber
= 0;
1413 GuardPage
= (UINT64
)-1;
1416 if (TableEntry
== 0) {
1420 Address
+= EFI_PAGES_TO_SIZE (1);
1421 BitIndex
= LShiftU64 (BitIndex
, 1);
1426 if (Level
< (GUARDED_HEAP_MAP_TABLE_DEPTH
- (INTN
)mMapLevel
)) {
1430 Indices
[Level
] += 1;
1431 Address
= (Level
== 0) ? 0 : Addresses
[Level
- 1];
1432 Addresses
[Level
] = Address
| LShiftU64 (Indices
[Level
], Shifts
[Level
]);
1437 // Update the maximum address of freed page which can be used for memory
1438 // promotion upon out-of-memory-space.
1440 GetLastGuardedFreePageAddress (&Address
);
1442 mLastPromotedPage
= Address
;
1447 This function checks to see if the given memory map descriptor in a memory map
1448 can be merged with any guarded free pages.
1450 @param MemoryMapEntry A pointer to a descriptor in MemoryMap.
1451 @param MaxAddress Maximum address to stop the merge.
1458 IN EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
,
1459 IN EFI_PHYSICAL_ADDRESS MaxAddress
1462 EFI_PHYSICAL_ADDRESS EndAddress
;
1466 if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED
) ||
1467 MemoryMapEntry
->Type
>= EfiMemoryMappedIO
) {
1472 Pages
= EFI_SIZE_TO_PAGES ((UINTN
)(MaxAddress
- MemoryMapEntry
->PhysicalStart
));
1473 Pages
-= (INTN
)MemoryMapEntry
->NumberOfPages
;
1476 EndAddress
= MemoryMapEntry
->PhysicalStart
+
1477 EFI_PAGES_TO_SIZE ((UINTN
)MemoryMapEntry
->NumberOfPages
);
1478 Bitmap
= GetGuardedMemoryBits (EndAddress
, GUARDED_HEAP_MAP_ENTRY_BITS
);
1481 if ((Bitmap
& 1) == 0) {
1486 MemoryMapEntry
->NumberOfPages
++;
1487 Bitmap
= RShiftU64 (Bitmap
, 1);
1492 Put part (at most 64 pages a time) guarded free pages back to free page pool.
1494 Freed memory guard is used to detect Use-After-Free (UAF) memory issue, which
1495 makes use of 'Used then throw away' way to detect any illegal access to freed
1496 memory. The thrown-away memory will be marked as not-present so that any access
1497 to those memory (after free) will be caught by page-fault exception.
1499 The problem is that this will consume lots of memory space. Once no memory
1500 left in pool to allocate, we have to restore part of the freed pages to their
1501 normal function. Otherwise the whole system will stop functioning.
1503 @param StartAddress Start address of promoted memory.
1504 @param EndAddress End address of promoted memory.
1506 @return TRUE Succeeded to promote memory.
1507 @return FALSE No free memory found.
1511 PromoteGuardedFreePages (
1512 OUT EFI_PHYSICAL_ADDRESS
*StartAddress
,
1513 OUT EFI_PHYSICAL_ADDRESS
*EndAddress
1517 UINTN AvailablePages
;
1519 EFI_PHYSICAL_ADDRESS Start
;
1521 if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED
)) {
1526 // Similar to memory allocation service, always search the freed pages in
1527 // descending direction.
1529 Start
= mLastPromotedPage
;
1531 while (AvailablePages
== 0) {
1532 Start
-= EFI_PAGES_TO_SIZE (GUARDED_HEAP_MAP_ENTRY_BITS
);
1534 // If the address wraps around, try the really freed pages at top.
1536 if (Start
> mLastPromotedPage
) {
1537 GetLastGuardedFreePageAddress (&Start
);
1538 ASSERT (Start
!= 0);
1539 Start
-= EFI_PAGES_TO_SIZE (GUARDED_HEAP_MAP_ENTRY_BITS
);
1542 Bitmap
= GetGuardedMemoryBits (Start
, GUARDED_HEAP_MAP_ENTRY_BITS
);
1543 while (Bitmap
> 0) {
1544 if ((Bitmap
& 1) != 0) {
1546 } else if (AvailablePages
== 0) {
1547 Start
+= EFI_PAGES_TO_SIZE (1);
1552 Bitmap
= RShiftU64 (Bitmap
, 1);
1556 if (AvailablePages
!= 0) {
1557 DEBUG ((DEBUG_INFO
, "Promoted pages: %lX (%lx)\r\n", Start
, (UINT64
)AvailablePages
));
1558 ClearGuardedMemoryBits (Start
, AvailablePages
);
1562 // Set flag to make sure allocating memory without GUARD for page table
1563 // operation; otherwise infinite loops could be caused.
1566 Status
= gCpu
->SetMemoryAttributes (gCpu
, Start
, EFI_PAGES_TO_SIZE(AvailablePages
), 0);
1567 ASSERT_EFI_ERROR (Status
);
1568 mOnGuarding
= FALSE
;
1571 mLastPromotedPage
= Start
;
1572 *StartAddress
= Start
;
1573 *EndAddress
= Start
+ EFI_PAGES_TO_SIZE (AvailablePages
) - 1;
1581 Notify function used to set all Guard pages before CPU Arch Protocol installed.
1584 HeapGuardCpuArchProtocolNotify (
1588 ASSERT (gCpu
!= NULL
);
1590 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE
|GUARD_HEAP_TYPE_POOL
) &&
1591 IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED
)) {
1592 DEBUG ((DEBUG_ERROR
, "Heap guard and freed memory guard cannot be enabled at the same time.\n"));
1596 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE
|GUARD_HEAP_TYPE_POOL
)) {
1597 SetAllGuardPages ();
1600 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED
)) {
1601 GuardAllFreedPages ();
1606 Helper function to convert a UINT64 value in binary to a string.
1608 @param[in] Value Value of a UINT64 integer.
1609 @param[out] BinString String buffer to contain the conversion result.
1616 OUT CHAR8
*BinString
1621 if (BinString
== NULL
) {
1625 for (Index
= 64; Index
> 0; --Index
) {
1626 BinString
[Index
- 1] = '0' + (Value
& 1);
1627 Value
= RShiftU64 (Value
, 1);
1629 BinString
[64] = '\0';
1633 Dump the guarded memory bit map.
1637 DumpGuardedMemoryBitmap (
1641 UINTN Entries
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1642 UINTN Shifts
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1643 UINTN Indices
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1644 UINT64 Tables
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1645 UINT64 Addresses
[GUARDED_HEAP_MAP_TABLE_DEPTH
];
1650 CHAR8 String
[GUARDED_HEAP_MAP_ENTRY_BITS
+ 1];
1654 if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_ALL
)) {
1658 if (mGuardedMemoryMap
== 0 ||
1660 mMapLevel
> GUARDED_HEAP_MAP_TABLE_DEPTH
) {
1664 Ruler1
= " 3 2 1 0";
1665 Ruler2
= "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210";
1667 DEBUG ((HEAP_GUARD_DEBUG_LEVEL
, "============================="
1668 " Guarded Memory Bitmap "
1669 "==============================\r\n"));
1670 DEBUG ((HEAP_GUARD_DEBUG_LEVEL
, " %a\r\n", Ruler1
));
1671 DEBUG ((HEAP_GUARD_DEBUG_LEVEL
, " %a\r\n", Ruler2
));
1673 CopyMem (Entries
, mLevelMask
, sizeof (Entries
));
1674 CopyMem (Shifts
, mLevelShift
, sizeof (Shifts
));
1676 SetMem (Indices
, sizeof(Indices
), 0);
1677 SetMem (Tables
, sizeof(Tables
), 0);
1678 SetMem (Addresses
, sizeof(Addresses
), 0);
1680 Level
= GUARDED_HEAP_MAP_TABLE_DEPTH
- mMapLevel
;
1681 Tables
[Level
] = mGuardedMemoryMap
;
1686 if (Indices
[Level
] > Entries
[Level
]) {
1693 HEAP_GUARD_DEBUG_LEVEL
,
1694 "========================================="
1695 "=========================================\r\n"
1700 TableEntry
= ((UINT64
*)(UINTN
)Tables
[Level
])[Indices
[Level
]];
1701 Address
= Addresses
[Level
];
1703 if (TableEntry
== 0) {
1705 if (Level
== GUARDED_HEAP_MAP_TABLE_DEPTH
- 1) {
1706 if (RepeatZero
== 0) {
1707 Uint64ToBinString(TableEntry
, String
);
1708 DEBUG ((HEAP_GUARD_DEBUG_LEVEL
, "%016lx: %a\r\n", Address
, String
));
1709 } else if (RepeatZero
== 1) {
1710 DEBUG ((HEAP_GUARD_DEBUG_LEVEL
, "... : ...\r\n"));
1715 } else if (Level
< GUARDED_HEAP_MAP_TABLE_DEPTH
- 1) {
1718 Tables
[Level
] = TableEntry
;
1719 Addresses
[Level
] = Address
;
1728 Uint64ToBinString(TableEntry
, String
);
1729 DEBUG ((HEAP_GUARD_DEBUG_LEVEL
, "%016lx: %a\r\n", Address
, String
));
1734 if (Level
< (GUARDED_HEAP_MAP_TABLE_DEPTH
- (INTN
)mMapLevel
)) {
1738 Indices
[Level
] += 1;
1739 Address
= (Level
== 0) ? 0 : Addresses
[Level
- 1];
1740 Addresses
[Level
] = Address
| LShiftU64(Indices
[Level
], Shifts
[Level
]);