Skip to content

Commit

Permalink
[CHERRY-PICK]MdeModulePkg: HeapGuard: Don't Assume Pool Head Allocate…
Browse files Browse the repository at this point in the history
…d In First Page (tianocore#567)

## Description

Currently, HeapGuard, when in the GuardAlignedToTail mode, assumes that
the pool head has been allocated in the first page of memory that was
allocated. This is not the case for ARM64 platforms when allocating
runtime pools, as RUNTIME_PAGE_ALLOCATION_GRANULARITY is 64k, unlike
X64, which has RUNTIME_PAGE_ALLOCATION_GRANULARITY as 4k.

When a runtime pool is allocated on ARM64, the minimum number of pages
allocated is 16, to match the runtime granularity. When a small pool is
allocated and GuardAlignedToTail is true, HeapGuard instructs the pool
head to be placed as (MemoryAllocated + EFI_PAGES_TO_SIZE(Number of
Pages)
- SizeRequiredForPool).

This gives this scenario:

|Head Guard|Large Free Number of Pages|PoolHead|TailGuard|

When this pool goes to be freed, HeapGuard instructs the pool code to
free from (PoolHead & ~EFI_PAGE_MASK). However, this assumes that the
PoolHead is in the first page allocated, which as shown above is not
true in this case. For the 4k granularity case (i.e. where the correct
number of pages are allocated for this pool), this logic does work.

In this failing case, HeapGuard then instructs the pool code to free 16
(or more depending) pages from the page the pool head was allocated on,
which as seen above means we overrun the pool and attempt to free memory
far past the pool. We end up running into the tail guard and getting an
access flag fault.

This causes ArmVirtQemu to fail to boot with an access flag fault when
GuardAlignedToTail is set to true (and pool guard enabled for runtime
memory). It should also cause all ARM64 platforms to fail in this
configuration, for exactly the same reason, as this is core code making
the assumption.

This patch removes HeapGuard's assumption that the pool head is
allocated on the first page and instead undoes the same logic that
HeapGuard did when allocating the pool head in the first place.

With this patch in place, ArmVirtQemu boots with GuardAlignedToTail set
to true (and when it is false, also).

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4521
Github PR: tianocore#4731

Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Dandan Bi <dandan.bi@intel.com>


Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Leif Lindholm <quic_llindhol@quicinc.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>

For each item, place an "x" in between `[` and `]` if true. Example:
`[x]`.
_(you can also check items in the GitHub UI)_

- [x] Impacts functionality?
- **Functionality** - Does the change ultimately impact how firmware
functions?
- Examples: Add a new library, publish a new PPI, update an algorithm,
...
- [ ] Impacts security?
- **Security** - Does the change have a direct security impact on an
application,
    flow, or firmware?
  - Examples: Crypto algorithm change, buffer overflow fix, parameter
    validation improvement, ...
- [ ] Breaking change?
- **Breaking change** - Will anyone consuming this change experience a
break
    in build or boot behavior?
- Examples: Add a new library class, move a module to a different repo,
call
    a function in a new library class in a pre-existing module, ...
- [ ] Includes tests?
  - **Tests** - Does the change include any explicit test code?
  - Examples: Unit tests, integration tests, robot tests, ...
- [ ] Includes documentation?
- **Documentation** - Does the change contain explicit documentation
additions
    outside direct code modifications (and comments)?
- Examples: Update readme file, add feature readme file, link to
documentation
    on an a separate Web page, ...

## How This Was Tested

Tested on ArmVirtQemu on edk2.

## Integration Instructions

N/A.

Signed-off-by: Oliver Smith-Denny <osde@linux.microsoft.com>
Co-authored-by: Oliver Smith-Denny <osde@linux.microsoft.com>
  • Loading branch information
os-d and Oliver Smith-Denny committed Sep 20, 2023
1 parent fcf730f commit 1674208
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 5 deletions.
14 changes: 11 additions & 3 deletions MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
Expand Up @@ -1068,12 +1068,17 @@ AdjustPoolHeadA (
Get the page base address according to pool head address.
@param[in] Memory Head address of pool to free.
@param[in] NoPages Number of pages actually allocated.
@param[in] Size Size of memory requested.
(plus pool head/tail overhead)
@return Address of pool head.
**/
VOID *
AdjustPoolHeadF (
IN EFI_PHYSICAL_ADDRESS Memory
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NoPages,
IN UINTN Size
)
{
// MU_CHANGE START Update to use memory protection settings HOB
Expand All @@ -1087,9 +1092,12 @@ AdjustPoolHeadF (
}

//
// Pool head is put near the tail Guard
// Pool head is put near the tail Guard. We need to exactly undo the addition done in AdjustPoolHeadA
// because we may not have allocated the pool head on the first allocated page, since we are aligned to
// the tail and on some architectures, the runtime page allocation granularity is > one page. So we allocate
// more pages than we need and put the pool head somewhere past the first page.
//
return (VOID *)(UINTN)(Memory & ~EFI_PAGE_MASK);
return (VOID *)(UINTN)(Memory + Size - EFI_PAGES_TO_SIZE (NoPages));
}

/**
Expand Down
7 changes: 6 additions & 1 deletion MdeModulePkg/Core/Dxe/Mem/HeapGuard.h
Expand Up @@ -378,12 +378,17 @@ AdjustPoolHeadA (
Get the page base address according to pool head address.
@param[in] Memory Head address of pool to free.
@param[in] NoPages Number of pages actually allocated.
@param[in] Size Size of memory requested.
(plus pool head/tail overhead)
@return Address of pool head.
**/
VOID *
AdjustPoolHeadF (
IN EFI_PHYSICAL_ADDRESS Memory
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NoPages,
IN UINTN Size
);

/**
Expand Down
2 changes: 1 addition & 1 deletion MdeModulePkg/Core/Dxe/Mem/Pool.c
Expand Up @@ -794,7 +794,7 @@ CoreFreePoolI (
NoPages = EFI_SIZE_TO_PAGES (Size) + EFI_SIZE_TO_PAGES (Granularity) - 1;
NoPages &= ~(UINTN)(EFI_SIZE_TO_PAGES (Granularity) - 1);
if (IsGuarded) {
Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head);
Head = AdjustPoolHeadF ((EFI_PHYSICAL_ADDRESS)(UINTN)Head, NoPages, Size);
CoreFreePoolPagesWithGuard (
Pool->MemoryType,
(EFI_PHYSICAL_ADDRESS)(UINTN)Head,
Expand Down

0 comments on commit 1674208

Please sign in to comment.