From: "Laszlo Ersek" <lersek@redhat.com>
To: devel@edk2.groups.io, jbrasen@nvidia.com
Cc: dandan.bi@intel.com, gaoliming@byosoft.com.cn,
"Ard Biesheuvel (ARM address)" <ard.biesheuvel@arm.com>,
"Leif Lindholm (Nuvia address)" <leif@nuviainc.com>
Subject: Re: [edk2-devel] [PATCH v3 1/1] MdeModulePkg/Gcd: Check memory allocation when initializing memory
Date: Tue, 3 Nov 2020 15:01:21 +0100 [thread overview]
Message-ID: <11029d65-f4be-b563-ef8d-0f616fccf2f2@redhat.com> (raw)
In-Reply-To: <e8f7e21501207c6d03513186360660698ab0de98.1603906382.git.jbrasen@nvidia.com>
On 10/28/20 18:35, Jeff Brasen wrote:
> CoreInitializeMemoryServices was not checking for any existing memory
> allocation created in the HOB producer phase. If there are memory
> allocations outside of the region covered by the HOB List then Gcd could
> select that region for memory which can result in the memory allocation
> to not be handled and memory overwrites.
>
> Signed-off-by: Jeff Brasen <jbrasen@nvidia.com>
> ---
> MdeModulePkg/Core/Dxe/Gcd/Gcd.c | 58 +++++++++++++++++++++++++++++++++
> 1 file changed, 58 insertions(+)
>
> diff --git a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c
> index 2d8c076f7113..51b082b7e7eb 100644
> --- a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c
> +++ b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c
> @@ -2097,6 +2097,60 @@ CalculateTotalMemoryBinSizeNeeded (
> return TotalSize;
> }
>
> +/**
> + Find the largest region in the specified region that is not covered by an existing memory allocation
> +
> + @param BaseAddress On input start of the region to check.
> + On output start of the largest free region.
> + @param Length On input size of region to check.
> + On output size of the largest free region.
> + @param MemoryHob Hob pointer for the first memory allocation pointer to check
> +**/
> +VOID
> +FindLargestFreeRegion (
> + IN OUT EFI_PHYSICAL_ADDRESS *BaseAddress,
> + IN OUT UINT64 *Length,
> + IN EFI_HOB_MEMORY_ALLOCATION *MemoryHob
> + )
> +{
> + EFI_PHYSICAL_ADDRESS TopAddress;
> + EFI_PHYSICAL_ADDRESS AllocatedTop;
> + EFI_PHYSICAL_ADDRESS LowerBase;
> + UINT64 LowerSize;
> + EFI_PHYSICAL_ADDRESS UpperBase;
> + UINT64 UpperSize;
> +
> + TopAddress = *BaseAddress + *Length;
> + while (MemoryHob != NULL) {
> + AllocatedTop = MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength;
> +
> + if ((MemoryHob->AllocDescriptor.MemoryBaseAddress >= *BaseAddress) &&
> + (AllocatedTop <= TopAddress)) {
> + LowerBase = *BaseAddress;
> + LowerSize = MemoryHob->AllocDescriptor.MemoryBaseAddress - *BaseAddress;
> + UpperBase = AllocatedTop;
> + UpperSize = TopAddress - AllocatedTop;
> +
> + if (LowerSize != 0) {
> + FindLargestFreeRegion (&LowerBase, &LowerSize, (EFI_HOB_MEMORY_ALLOCATION *) GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (MemoryHob)));
> + }
> + if (UpperSize != 0) {
> + FindLargestFreeRegion (&UpperBase, &UpperSize, (EFI_HOB_MEMORY_ALLOCATION *) GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (MemoryHob)));
> + }
> +
> + if (UpperSize >= LowerSize) {
> + *Length = UpperSize;
> + *BaseAddress = UpperBase;
> + } else {
> + *Length = LowerSize;
> + *BaseAddress = LowerBase;
> + }
> + return;
> + }
> + MemoryHob = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (MemoryHob));
> + }
> +}
> +
> /**
> External function. Initializes memory services based on the memory
> descriptor HOBs. This function is responsible for priming the memory
> @@ -2235,6 +2289,7 @@ CoreInitializeMemoryServices (
> Attributes = PhitResourceHob->ResourceAttribute;
> BaseAddress = PageAlignAddress (PhitHob->EfiMemoryTop);
> Length = PageAlignLength (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - BaseAddress);
> + FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION));
> if (Length < MinimalMemorySizeNeeded) {
> //
> // If that range is not large enough to intialize the DXE Core, then
> @@ -2242,6 +2297,7 @@ CoreInitializeMemoryServices (
> //
> BaseAddress = PageAlignAddress (PhitHob->EfiFreeMemoryBottom);
> Length = PageAlignLength (PhitHob->EfiFreeMemoryTop - BaseAddress);
> + //This region is required to have no memory allocation inside it, skip check for entries in HOB List
> if (Length < MinimalMemorySizeNeeded) {
> //
> // If that range is not large enough to intialize the DXE Core, then
> @@ -2249,6 +2305,7 @@ CoreInitializeMemoryServices (
> //
> BaseAddress = PageAlignAddress (ResourceHob->PhysicalStart);
> Length = PageAlignLength ((UINT64)((UINTN)*HobStart - BaseAddress));
> + FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION));
> }
> }
> break;
> @@ -2312,6 +2369,7 @@ CoreInitializeMemoryServices (
> //
> TestedMemoryBaseAddress = PageAlignAddress (ResourceHob->PhysicalStart);
> TestedMemoryLength = PageAlignLength (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - TestedMemoryBaseAddress);
> + FindLargestFreeRegion (&TestedMemoryBaseAddress, &TestedMemoryLength, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION));
> if (TestedMemoryLength < MinimalMemorySizeNeeded) {
> continue;
> }
>
I applied this on top of commit 0166dad49698 ("BaseTools: Update the FV
Space Information to display decimal and Hex", 2020-11-03).
* In my regression testing with ArmVirtQemu (AARCH64 -- 1 scenario), the
only difference seems to be the following, in the firmware log:
> -HOBLIST address in DXE = 0x13B641018
> +HOBLIST address in DXE = 0x13B621018
Plus, a whole lot of subsequent addresses are consistently decreased
by 0x2_0000. I could see or experience no other differences.
* In my regression testing with OVMF, with S3 disabled (IA32 (q35), X64
(i440fx), IA32X64 (q35) -- 3 scenarios):
I saw some addresses in the log grow by 0x280 (IA32) or 0x200 (X64) or
0x240 (IA32X64). In the IA32 and X64 cases, these offsets were
consistent; in the IA32X64 case, I saw some other (less uniform)
address shifts too (for example growth by 0x5_7E80, or decrease by
0x180).
* In my regression testing with OVMF, with S3 enabled (IA32 (q35), X64
(i440fx), IA32X64 (q35) -- 3 scenarios):
Regarding the "cold boot" related parts of the logs, the same applies
as above, except:
- the X64 address shifts were less uniform than in the "S3 disabled"
case,
- the IA32X64 address shifts were more uniform than in the "S3
disabled" case.
Regarding the "S3 resume" related parts of the logs, address
differences did not affect them at all, for all of IA32, X64, and
IA32X64.
* In the 3+3=6 OVMF scenarios above, the patch did not change the
BaseAddress / Length / MinimalMemorySizeNeeded INFO message printed by
CoreInitializeMemoryServices():
- S3 disabled:
- IA32:
BaseAddress - 0x83423000 Length - 0x3EEA000 MinimalMemorySizeNeeded - 0x320000
- X64:
BaseAddress - 0xBBFE2000 Length - 0x3C1E000 MinimalMemorySizeNeeded - 0x320000
- IA32X64:
BaseAddress - 0x7AFE1000 Length - 0x3C1E000 MinimalMemorySizeNeeded - 0x4E5000
- S3 enabled:
- IA32:
BaseAddress - 0x83383000 Length - 0x3EEA000 MinimalMemorySizeNeeded - 0x320000
- X64:
BaseAddress - 0xBBF42000 Length - 0x3ABE000 MinimalMemorySizeNeeded - 0x320000
- IA32X64:
BaseAddress - 0x7AF21000 Length - 0x3CDE000 MinimalMemorySizeNeeded - 0x4E5000
* In the ArmVirtQemu scenario, I couldn't compare the the "BaseAddress /
Length / MinimalMemorySizeNeeded" INFO messages from before/after the
patch, because the firmware logs don't seem to contain this message at
all. This is despite PcdDebugPrintErrorLevel having DEBUG_INFO
(0x00000040) set.
... Oh. This logging issue is similar to the one fixed for OVMF in
commit 91a5b1365075 ("OvmfPkg/PlatformDebugLibIoPort: fix port
detection for use in the DXE Core", 2018-08-06). Namely,
CoreInitializeMemoryServices() calls DEBUG() before the DXE Core calls
library constructors explicitly -- so those DEBUGs are issued against
an un-constructed DebugLib, and also against a -- possibly underlying
-- unconstructed SerialPortLib.
I'll propose a very simple patch for that issue soon. For now, I've
used said patch locally, for capturing BaseAddress and Length under
ArmVirtQemu as well, before and after Jeff's patch. So I could do a
comparison even under ArmVirtQemu.
Result:
> -DxeMain: MemoryBaseAddress=0x40000000 MemoryLength=0xFC020000
> +DxeMain: MemoryBaseAddress=0x40000000 MemoryLength=0xFC000000
So this patch makes an actual difference for ArmVirtQemu!
Where does the difference come from? Let's check the log for
0x40000000+0xFC000000 = 0x13C000000:
> QemuVirtMemInfoPeiLibConstructor: System RAM @ 0x40000000 - 0x13FFFFFFF
> [...]
> PeiInstallPeiMemory MemoryBegin 0x13C000000, MemoryLength 0x4000000
> [...]
> Old Stack size 8160, New stack size 131072
> Stack Hob: BaseAddress=0x13C000000 Length=0x20000
> [...]
> -DxeMain: MemoryBaseAddress=0x40000000 MemoryLength=0xFC020000
> +DxeMain: MemoryBaseAddress=0x40000000 MemoryLength=0xFC000000
> [...]
> -HOBLIST address in DXE = 0x13B641018
> +HOBLIST address in DXE = 0x13B621018
> [...]
> Memory Allocation 0x00000004 0x13C000000 - 0x13C01FFFF
The above happens when the permanent PEI RAM is installed by the
platform, and the PEI Core migrates temp RAM to permanent RAM. The CPU
stack on the permanent PEI RAM is described by the stack HOB, which is
a special EfiBootServicesData memory allocation HOB. It's created by
the PEI Core (PeiDispatcher --> PeiCheckAndSwitchStack -->
BuildStackHob). The HOB is specified in PI v1.7, Volume 3, section
"5.4.2 Boot-Strap Processor (BSP) Stack Memory Allocation HOB".
Without Jeff's patch, the DXE core thinks that the area is free memory
(initially), and then the memalloc (stack) HOB is processed later.
With Jeff's patch, the memalloc (stack) HOB is immediately excluded
from DXE memory. In this particular case, I'm not convinced that the
pre-patch behavior is unsafe or wrong. But the post-patch behavior
also seems to be OK.
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
Thanks
Laszlo
next prev parent reply other threads:[~2020-11-03 14:01 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-28 17:35 [PATCH v3 1/1] MdeModulePkg/Gcd: Check memory allocation when initializing memory Jeff Brasen
2020-10-30 0:59 ` 回复: [edk2-devel] " gaoliming
2020-11-02 17:29 ` Laszlo Ersek
2020-11-03 0:56 ` 回复: " gaoliming
2020-11-03 14:01 ` Laszlo Ersek [this message]
2020-11-05 3:10 ` gaoliming
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=11029d65-f4be-b563-ef8d-0f616fccf2f2@redhat.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox