From: Laszlo Ersek <lersek@redhat.com>
To: Jian J Wang <jian.j.wang@intel.com>, edk2-devel@lists.01.org
Cc: Michael D Kinney <michael.d.kinney@intel.com>,
Ruiyu Ni <ruiyu.ni@intel.com>, Jiewen Yao <jiewen.yao@intel.com>,
Star Zeng <star.zeng@intel.com>
Subject: Re: [PATCH v2 3/5] MdeModulePkg/Core: fix a lock issue in GCD memory map dump
Date: Tue, 23 Oct 2018 20:26:13 +0200 [thread overview]
Message-ID: <71e87828-fcc6-ba1e-cac0-31e4050f795c@redhat.com> (raw)
In-Reply-To: <20181023145331.5768-4-jian.j.wang@intel.com>
On 10/23/18 16:53, Jian J Wang wrote:
>> v2 changes:
>> a. Update implementation of CoreGetMemorySpaceMap() and
>> CoreGetIoSpaceMap() to avoid lock failure. Drop the code to
>> detect debug print level used to achieve the same effect.
>
> This issue is hidden in current code but exposed by introduction
> of freed-memory guard feature due to the fact that the feature
> will turn all pool allocation to page allocation.
>
> The solution is move the memory allocation in CoreGetMemorySpaceMap()
> and CoreGetIoSpaceMap() to be out of the GCD memory map lock.
>
> CoreDumpGcdMemorySpaceMap()
> => CoreGetMemorySpaceMap()
> => CoreAcquireGcdMemoryLock () *
> AllocatePool()
> => InternalAllocatePool()
> => CoreAllocatePool()
> => CoreAllocatePoolI()
> => CoreAllocatePoolPagesI()
> => CoreAllocatePoolPages()
> => FindFreePages()
> => PromoteMemoryResource()
> => CoreAcquireGcdMemoryLock() **
>
> Cc: Star Zeng <star.zeng@intel.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Ruiyu Ni <ruiyu.ni@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
> ---
> MdeModulePkg/Core/Dxe/Gcd/Gcd.c | 140 ++++++++++++++++++++++++----------------
> 1 file changed, 86 insertions(+), 54 deletions(-)
>
> diff --git a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c
> index d9c65a8045..133c3dcd87 100644
> --- a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c
> +++ b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c
> @@ -1691,10 +1691,10 @@ CoreGetMemorySpaceMap (
> OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR **MemorySpaceMap
> )
> {
> - EFI_STATUS Status;
> LIST_ENTRY *Link;
> EFI_GCD_MAP_ENTRY *Entry;
> EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor;
> + UINTN Number;
>
> //
> // Make sure parameters are valid
> @@ -1706,38 +1706,54 @@ CoreGetMemorySpaceMap (
> return EFI_INVALID_PARAMETER;
> }
>
> - CoreAcquireGcdMemoryLock ();
> -
> //
> // Count the number of descriptors
> //
> - *NumberOfDescriptors = CoreCountGcdMapEntry (&mGcdMemorySpaceMap);
> + Number = 0;
> + *NumberOfDescriptors = 0;
> + *MemorySpaceMap = NULL;
> + while (TRUE) {
> + //
> + // Allocate the MemorySpaceMap
> + //
> + if (Number != 0) {
> + if (*MemorySpaceMap != NULL) {
> + FreePool (*MemorySpaceMap);
> + }
>
> - //
> - // Allocate the MemorySpaceMap
> - //
> - *MemorySpaceMap = AllocatePool (*NumberOfDescriptors * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR));
> - if (*MemorySpaceMap == NULL) {
> - Status = EFI_OUT_OF_RESOURCES;
> - goto Done;
> - }
> + *MemorySpaceMap = AllocatePool (Number * sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR));
(1) Side comment: you dropped the space character after "sizeof".
> + if (*MemorySpaceMap == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
>
> - //
> - // Fill in the MemorySpaceMap
> - //
> - Descriptor = *MemorySpaceMap;
> - Link = mGcdMemorySpaceMap.ForwardLink;
> - while (Link != &mGcdMemorySpaceMap) {
> - Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
> - BuildMemoryDescriptor (Descriptor, Entry);
> - Descriptor++;
> - Link = Link->ForwardLink;
> + *NumberOfDescriptors = Number;
> + }
> +
> + CoreAcquireGcdMemoryLock ();
> +
> + Number = CoreCountGcdMapEntry (&mGcdMemorySpaceMap);
> + if (Number == *NumberOfDescriptors) {
> + //
> + // Fill in the MemorySpaceMap
> + //
> + Descriptor = *MemorySpaceMap;
> + Link = mGcdMemorySpaceMap.ForwardLink;
> + while (Link != &mGcdMemorySpaceMap && Number != 0) {
> + Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
> + BuildMemoryDescriptor (Descriptor, Entry);
> + Descriptor++;
> + Number--;
> + Link = Link->ForwardLink;
> + }
> +
> + CoreReleaseGcdMemoryLock ();
> + break;
> + }
> +
> + CoreReleaseGcdMemoryLock ();
> }
> - Status = EFI_SUCCESS;
>
> -Done:
> - CoreReleaseGcdMemoryLock ();
> - return Status;
> + return EFI_SUCCESS;
> }
I think this loop can be improved. Here's the facts that I don't like
about it:
(2) The "Number" variable name is bad. It should be "DescriptorCount".
(3) The ways the outer "if"s are formulated in the loop body are hard to
read. In particular, I think what bothers me the most is that the
loop body starts with the memory allocation, and not with the
CoreCountGcdMapEntry() call. I think we can do better.
(4) We have one location in the code that acquires the lock, but two
locations that release it. While it is technically correct, it's
hard to read as well.
(5) Decreasing "Number" in the inner "while" loop, and checking it in
the inner loop condition, seems strange. The GCD memory space map
will have at least one entry at all times (minimally there is a
NonExistent entry that covers the entire address space, according to
the address width from the CPU HOB). In addition, I don't see when
it could validly occur that Number doesn't match the length of the
list.
How about the following instead (I'm quoting the full function, untested
/ uncompiled):
> EFI_STATUS
> EFIAPI
> CoreGetMemorySpaceMap (
> OUT UINTN *NumberOfDescriptors,
> OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR **MemorySpaceMap
> )
> {
> LIST_ENTRY *Link;
> EFI_GCD_MAP_ENTRY *Entry;
> EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor;
> UINTN DescriptorCount;
>
> //
> // Make sure parameters are valid
> //
> if (NumberOfDescriptors == NULL) {
> return EFI_INVALID_PARAMETER;
> }
> if (MemorySpaceMap == NULL) {
> return EFI_INVALID_PARAMETER;
> }
>
> //
> // No candidate map allocated yet.
> //
> *NumberOfDescriptors = 0;
> *MemorySpaceMap = NULL;
>
> //
> // Take the lock, for entering the loop with the lock held.
> //
> CoreAcquireGcdMemoryLock ();
> while (TRUE) {
> //
> // Count the descriptors.
> //
> DescriptorCount = CoreCountGcdMapEntry (&mGcdMemorySpaceMap);
>
> if (DescriptorCount == *NumberOfDescriptors) {
> //
> // Fill in the MemorySpaceMap
> //
> Descriptor = *MemorySpaceMap;
> Link = mGcdMemorySpaceMap.ForwardLink;
> while (Link != &mGcdMemorySpaceMap) {
> Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
> BuildMemoryDescriptor (Descriptor, Entry);
> Descriptor++;
> Link = Link->ForwardLink;
> }
>
> //
> // We're done; exit the loop with the lock held.
> //
> break;
> }
>
> CoreReleaseGcdMemoryLock ();
>
> //
> // Free previous allocation, with the lock released.
> //
> if (*MemorySpaceMap != NULL) {
> FreePool (*MemorySpaceMap);
> }
>
> //
> // Allocate the MemorySpaceMap, with the lock released.
> //
> *MemorySpaceMap = AllocatePool (
> (DescriptorCount *
> sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR))
> );
> if (*MemorySpaceMap == NULL) {
> *NumberOfDescriptors = 0;
> return EFI_OUT_OF_RESOURCES;
> }
> *NumberOfDescriptors = DescriptorCount;
>
> //
> // Re-acquire the lock, for the next iteration.
> //
> CoreAcquireGcdMemoryLock ();
> }
>
> //
> // We exited the loop with the lock held, release it.
> //
> CoreReleaseGcdMemoryLock ();
>
> return EFI_SUCCESS;
> }
>
I don't insist on this variant, of course, it's just an idea to discuss!
If others find your variant easier to read, I'm fine with it as well.
(Still, I think points (1), (2) and (5) should be fixed.)
(6) I've snipped the CoreGetIoSpaceMap() changes because, AFAICS, they
mirror the CoreGetMemorySpaceMap() changes. However: do we *really* need
to update CoreGetIoSpaceMap()? Because, allocating pool memory, even if
it ends up allocating page memory, should be independent of the *IO Port
space* in GCD.
If you fix CoreGetMemorySpaceMap() but don't touch CoreGetIoSpaceMap(),
can you actually trigger a deadlock (with or without enabling the UAF
guard)?
Thanks,
Laszlo
next prev parent reply other threads:[~2018-10-23 18:26 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-10-23 14:53 [PATCH v2 0/5] Add freed-memory guard feature Jian J Wang
2018-10-23 14:53 ` [PATCH v2 1/5] MdeModulePkg/MdeModulePkg.dec: update PCD description for new feature Jian J Wang
2018-10-23 16:09 ` Laszlo Ersek
2018-10-24 0:45 ` Wang, Jian J
2018-10-23 14:53 ` [PATCH v2 2/5] UefiCpuPkg/CpuDxe: fix an infinite loop issue Jian J Wang
2018-10-23 16:41 ` Laszlo Ersek
2018-10-23 14:53 ` [PATCH v2 3/5] MdeModulePkg/Core: fix a lock issue in GCD memory map dump Jian J Wang
2018-10-23 18:26 ` Laszlo Ersek [this message]
2018-10-23 14:53 ` [PATCH v2 4/5] MdeModulePkg/Core: add freed-memory guard feature Jian J Wang
2018-10-23 18:29 ` Laszlo Ersek
2018-10-23 14:53 ` [PATCH v2 5/5] MdeModulePkg/Core: fix-up for changes introduced by freed-memory guard Jian J Wang
2018-10-23 17:16 ` Laszlo Ersek
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=71e87828-fcc6-ba1e-cac0-31e4050f795c@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