MdeModulePkg/DxeCore: Implement heap guard feature for UEFI
This feature makes use of paging mechanism to add a hidden (not present) page just before and after the allocated memory block. If the code tries to access memory outside of the allocated part, page fault exception will be triggered. This feature is controlled by three PCDs: gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPageType BIT0 and BIT1 of PcdHeapGuardPropertyMask can be used to enable or disable memory guard for page and pool respectively. PcdHeapGuardPoolType and/or PcdHeapGuardPageType are used to enable or disable guard for specific type of memory. For example, we can turn on guard only for EfiBootServicesData and EfiRuntimeServicesData by setting the PCD with value 0x50. Pool memory is not ususally integer multiple of one page, and is more likely less than a page. There's no way to monitor the overflow at both top and bottom of pool memory. BIT7 of PcdHeapGuardPropertyMask is used to control how to position the head of pool memory so that it's easier to catch memory overflow in memory growing direction or in decreasing direction. Note1: Turning on heap guard, especially pool guard, will introduce too many memory fragments. Windows 10 has a limitation in its boot loader, which accepts at most 512 memory descriptors passed from BIOS. This will prevent Windows 10 from booting if heap guard is enabled. The latest Linux distribution with grub boot loader has no such issue. Normally it's not recommended to enable this feature in production build of BIOS. Note2: Don't enable this feature for NT32 emulation platform which doesn't support paging. Cc: Star Zeng <star.zeng@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Suggested-by: Ayellet Wolman <ayellet.wolman@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Jian J Wang <jian.j.wang@intel.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
@@ -14,6 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
|
||||
#include "DxeMain.h"
|
||||
#include "Imem.h"
|
||||
#include "HeapGuard.h"
|
||||
|
||||
STATIC EFI_LOCK mPoolMemoryLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
|
||||
|
||||
@@ -169,7 +170,7 @@ LookupPoolHead (
|
||||
}
|
||||
}
|
||||
|
||||
Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL));
|
||||
Pool = CoreAllocatePoolI (EfiBootServicesData, sizeof (POOL), FALSE);
|
||||
if (Pool == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -214,7 +215,8 @@ CoreInternalAllocatePool (
|
||||
OUT VOID **Buffer
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_STATUS Status;
|
||||
BOOLEAN NeedGuard;
|
||||
|
||||
//
|
||||
// If it's not a valid type, fail it
|
||||
@@ -238,6 +240,8 @@ CoreInternalAllocatePool (
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
NeedGuard = IsPoolTypeToGuard (PoolType) && !mOnGuarding;
|
||||
|
||||
//
|
||||
// Acquire the memory lock and make the allocation
|
||||
//
|
||||
@@ -246,7 +250,7 @@ CoreInternalAllocatePool (
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
*Buffer = CoreAllocatePoolI (PoolType, Size);
|
||||
*Buffer = CoreAllocatePoolI (PoolType, Size, NeedGuard);
|
||||
CoreReleaseLock (&mPoolMemoryLock);
|
||||
return (*Buffer != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
@@ -298,6 +302,7 @@ CoreAllocatePool (
|
||||
@param PoolType The type of memory for the new pool pages
|
||||
@param NoPages No of pages to allocate
|
||||
@param Granularity Bits to align.
|
||||
@param NeedGuard Flag to indicate Guard page is needed or not
|
||||
|
||||
@return The allocated memory, or NULL
|
||||
|
||||
@@ -307,7 +312,8 @@ VOID *
|
||||
CoreAllocatePoolPagesI (
|
||||
IN EFI_MEMORY_TYPE PoolType,
|
||||
IN UINTN NoPages,
|
||||
IN UINTN Granularity
|
||||
IN UINTN Granularity,
|
||||
IN BOOLEAN NeedGuard
|
||||
)
|
||||
{
|
||||
VOID *Buffer;
|
||||
@@ -318,11 +324,14 @@ CoreAllocatePoolPagesI (
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity);
|
||||
Buffer = CoreAllocatePoolPages (PoolType, NoPages, Granularity, NeedGuard);
|
||||
CoreReleaseMemoryLock ();
|
||||
|
||||
if (Buffer != NULL) {
|
||||
ApplyMemoryProtectionPolicy (EfiConventionalMemory, PoolType,
|
||||
if (NeedGuard) {
|
||||
SetGuardForMemory ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, NoPages);
|
||||
}
|
||||
ApplyMemoryProtectionPolicy(EfiConventionalMemory, PoolType,
|
||||
(EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (NoPages));
|
||||
}
|
||||
return Buffer;
|
||||
@@ -334,6 +343,7 @@ CoreAllocatePoolPagesI (
|
||||
|
||||
@param PoolType Type of pool to allocate
|
||||
@param Size The amount of pool to allocate
|
||||
@param NeedGuard Flag to indicate Guard page is needed or not
|
||||
|
||||
@return The allocate pool, or NULL
|
||||
|
||||
@@ -341,7 +351,8 @@ CoreAllocatePoolPagesI (
|
||||
VOID *
|
||||
CoreAllocatePoolI (
|
||||
IN EFI_MEMORY_TYPE PoolType,
|
||||
IN UINTN Size
|
||||
IN UINTN Size,
|
||||
IN BOOLEAN NeedGuard
|
||||
)
|
||||
{
|
||||
POOL *Pool;
|
||||
@@ -355,6 +366,7 @@ CoreAllocatePoolI (
|
||||
UINTN Offset, MaxOffset;
|
||||
UINTN NoPages;
|
||||
UINTN Granularity;
|
||||
BOOLEAN HasPoolTail;
|
||||
|
||||
ASSERT_LOCKED (&mPoolMemoryLock);
|
||||
|
||||
@@ -372,6 +384,9 @@ CoreAllocatePoolI (
|
||||
// Adjust the size by the pool header & tail overhead
|
||||
//
|
||||
|
||||
HasPoolTail = !(NeedGuard &&
|
||||
((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
|
||||
|
||||
//
|
||||
// Adjusting the Size to be of proper alignment so that
|
||||
// we don't get an unaligned access fault later when
|
||||
@@ -391,10 +406,16 @@ CoreAllocatePoolI (
|
||||
// If allocation is over max size, just allocate pages for the request
|
||||
// (slow)
|
||||
//
|
||||
if (Index >= SIZE_TO_LIST (Granularity)) {
|
||||
NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
|
||||
if (Index >= SIZE_TO_LIST (Granularity) || NeedGuard) {
|
||||
if (!HasPoolTail) {
|
||||
Size -= sizeof (POOL_TAIL);
|
||||
}
|
||||
NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
|
||||
NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
|
||||
Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity);
|
||||
Head = CoreAllocatePoolPagesI (PoolType, NoPages, Granularity, NeedGuard);
|
||||
if (NeedGuard) {
|
||||
Head = AdjustPoolHeadA ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
|
||||
}
|
||||
goto Done;
|
||||
}
|
||||
|
||||
@@ -422,7 +443,8 @@ CoreAllocatePoolI (
|
||||
//
|
||||
// Get another page
|
||||
//
|
||||
NewPage = CoreAllocatePoolPagesI (PoolType, EFI_SIZE_TO_PAGES (Granularity), Granularity);
|
||||
NewPage = CoreAllocatePoolPagesI (PoolType, EFI_SIZE_TO_PAGES (Granularity),
|
||||
Granularity, NeedGuard);
|
||||
if (NewPage == NULL) {
|
||||
goto Done;
|
||||
}
|
||||
@@ -468,30 +490,39 @@ Done:
|
||||
|
||||
if (Head != NULL) {
|
||||
|
||||
//
|
||||
// Account the allocation
|
||||
//
|
||||
Pool->Used += Size;
|
||||
|
||||
//
|
||||
// If we have a pool buffer, fill in the header & tail info
|
||||
//
|
||||
Head->Signature = POOL_HEAD_SIGNATURE;
|
||||
Head->Size = Size;
|
||||
Head->Type = (EFI_MEMORY_TYPE) PoolType;
|
||||
Tail = HEAD_TO_TAIL (Head);
|
||||
Tail->Signature = POOL_TAIL_SIGNATURE;
|
||||
Tail->Size = Size;
|
||||
Buffer = Head->Data;
|
||||
DEBUG_CLEAR_MEMORY (Buffer, Size - POOL_OVERHEAD);
|
||||
|
||||
if (HasPoolTail) {
|
||||
Tail = HEAD_TO_TAIL (Head);
|
||||
Tail->Signature = POOL_TAIL_SIGNATURE;
|
||||
Tail->Size = Size;
|
||||
|
||||
Size -= POOL_OVERHEAD;
|
||||
} else {
|
||||
Size -= SIZE_OF_POOL_HEAD;
|
||||
}
|
||||
|
||||
DEBUG_CLEAR_MEMORY (Buffer, Size);
|
||||
|
||||
DEBUG ((
|
||||
DEBUG_POOL,
|
||||
"AllocatePoolI: Type %x, Addr %p (len %lx) %,ld\n", PoolType,
|
||||
Buffer,
|
||||
(UINT64)(Size - POOL_OVERHEAD),
|
||||
(UINT64)Size,
|
||||
(UINT64) Pool->Used
|
||||
));
|
||||
|
||||
//
|
||||
// Account the allocation
|
||||
//
|
||||
Pool->Used += Size;
|
||||
|
||||
} else {
|
||||
DEBUG ((DEBUG_ERROR | DEBUG_POOL, "AllocatePool: failed to allocate %ld bytes\n", (UINT64) Size));
|
||||
@@ -588,6 +619,34 @@ CoreFreePoolPagesI (
|
||||
(EFI_PHYSICAL_ADDRESS)(UINTN)Memory, EFI_PAGES_TO_SIZE (NoPages));
|
||||
}
|
||||
|
||||
/**
|
||||
Internal function. Frees guarded pool pages.
|
||||
|
||||
@param PoolType The type of memory for the pool pages
|
||||
@param Memory The base address to free
|
||||
@param NoPages The number of pages to free
|
||||
|
||||
**/
|
||||
STATIC
|
||||
VOID
|
||||
CoreFreePoolPagesWithGuard (
|
||||
IN EFI_MEMORY_TYPE PoolType,
|
||||
IN EFI_PHYSICAL_ADDRESS Memory,
|
||||
IN UINTN NoPages
|
||||
)
|
||||
{
|
||||
EFI_PHYSICAL_ADDRESS MemoryGuarded;
|
||||
UINTN NoPagesGuarded;
|
||||
|
||||
MemoryGuarded = Memory;
|
||||
NoPagesGuarded = NoPages;
|
||||
|
||||
AdjustMemoryF (&Memory, &NoPages);
|
||||
CoreFreePoolPagesI (PoolType, Memory, NoPages);
|
||||
|
||||
UnsetGuardForMemory (MemoryGuarded, NoPagesGuarded);
|
||||
}
|
||||
|
||||
/**
|
||||
Internal function to free a pool entry.
|
||||
Caller must have the memory lock held
|
||||
@@ -616,6 +675,8 @@ CoreFreePoolI (
|
||||
UINTN Offset;
|
||||
BOOLEAN AllFree;
|
||||
UINTN Granularity;
|
||||
BOOLEAN IsGuarded;
|
||||
BOOLEAN HasPoolTail;
|
||||
|
||||
ASSERT(Buffer != NULL);
|
||||
//
|
||||
@@ -628,24 +689,32 @@ CoreFreePoolI (
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Tail = HEAD_TO_TAIL (Head);
|
||||
ASSERT(Tail != NULL);
|
||||
IsGuarded = IsPoolTypeToGuard (Head->Type) &&
|
||||
IsMemoryGuarded ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
|
||||
HasPoolTail = !(IsGuarded &&
|
||||
((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0));
|
||||
|
||||
//
|
||||
// Debug
|
||||
//
|
||||
ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
|
||||
ASSERT (Head->Size == Tail->Size);
|
||||
ASSERT_LOCKED (&mPoolMemoryLock);
|
||||
if (HasPoolTail) {
|
||||
Tail = HEAD_TO_TAIL (Head);
|
||||
ASSERT (Tail != NULL);
|
||||
|
||||
if (Tail->Signature != POOL_TAIL_SIGNATURE) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (Head->Size != Tail->Size) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
//
|
||||
// Debug
|
||||
//
|
||||
ASSERT (Tail->Signature == POOL_TAIL_SIGNATURE);
|
||||
ASSERT (Head->Size == Tail->Size);
|
||||
|
||||
if (Tail->Signature != POOL_TAIL_SIGNATURE) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (Head->Size != Tail->Size) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_LOCKED (&mPoolMemoryLock);
|
||||
|
||||
//
|
||||
// Determine the pool type and account for it
|
||||
//
|
||||
@@ -680,14 +749,27 @@ CoreFreePoolI (
|
||||
//
|
||||
// If it's not on the list, it must be pool pages
|
||||
//
|
||||
if (Index >= SIZE_TO_LIST (Granularity)) {
|
||||
if (Index >= SIZE_TO_LIST (Granularity) || IsGuarded) {
|
||||
|
||||
//
|
||||
// Return the memory pages back to free memory
|
||||
//
|
||||
NoPages = EFI_SIZE_TO_PAGES(Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
|
||||
NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
|
||||
NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
|
||||
CoreFreePoolPagesI (Pool->MemoryType, (EFI_PHYSICAL_ADDRESS) (UINTN) Head, NoPages);
|
||||
if (IsGuarded) {
|
||||
Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
|
||||
CoreFreePoolPagesWithGuard (
|
||||
Pool->MemoryType,
|
||||
(EFI_PHYSICAL_ADDRESS)(UINTN)Head,
|
||||
NoPages
|
||||
);
|
||||
} else {
|
||||
CoreFreePoolPagesI (
|
||||
Pool->MemoryType,
|
||||
(EFI_PHYSICAL_ADDRESS)(UINTN)Head,
|
||||
NoPages
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
Reference in New Issue
Block a user