On Wed, 8 Feb 2023 at 19:32, Marvin Häuser <mhaeu...@posteo.de> wrote:
>
> Thanks!! :) Comments inline.
>
> > On 8. Feb 2023, at 18:58, Ard Biesheuvel <a...@kernel.org> wrote:
> >
> > The DXE core implementation of PcdDxeNxMemoryProtectionPolicy already
> > contains an assertion that EfiConventionalMemory and EfiBootServicesData
> > are subjected to the same policy when it comes to the use of NX
> > permissions. The reason for this is that we may otherwise end up with
> > unbounded recursion in the page table code, given that allocating a page
> > table would then involve a permission attribute change, and this could
> > result in the need for a block entry to be split, which would trigger
> > the allocation of a page table recursively.
> >
> > For the same reason, a shortcut exists in ApplyMemoryProtectionPolicy()
> > where, instead of setting the memory attributes unconditionally, we
> > compare the NX policies and avoid touching the page tables if they are
> > the same for the old and the new memory types. Without this shortcut, we
> > may end up in a situation where, as the CPU arch protocol DXE driver is
> > ramping up, the same unbounded recursion is triggered, due to the fact
> > that the NX policy for EfiConventionalMemory has not been applied yet.
> >
> > To break this cycle, let's remap all EfiConventionalMemory regions
> > according to the NX policy for EfiBootServicesData before exposing the
> > CPU arch protocol to the DXE core and other drivers. This ensures that
> > creating EfiBootServicesData allocations does not result in memory
> > attribute changes, and therefore no recursion.
> >
> > Signed-off-by: Ard Biesheuvel <a...@kernel.org>
> > ---
> > ArmPkg/Drivers/CpuDxe/CpuDxe.c   | 77 ++++++++++++++++++++
> > ArmPkg/Drivers/CpuDxe/CpuDxe.inf |  2 +
> > 2 files changed, 79 insertions(+)
> >
> > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > index d04958e79e52..83fd6fd4e476 100644
> > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > @@ -11,6 +11,8 @@
> >
> > #include <Guid/IdleLoopEvent.h>
> >
> > +#include <Library/MemoryAllocationLib.h>
> > +
> > BOOLEAN  mIsFlushingGCD;
> >
> > /**
> > @@ -227,6 +229,69 @@ InitializeDma (
> >   CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule ();
> > }
> >
> > +STATIC
> > +VOID
> > +RemapUnusedMemoryNx (
> > +  VOID
> > +  )
> > +{
> > +  UINT64                     TestBit;
> > +  UINTN                      MemoryMapSize;
> > +  UINTN                      MapKey;
> > +  UINTN                      DescriptorSize;
> > +  UINT32                     DescriptorVersion;
> > +  EFI_MEMORY_DESCRIPTOR      *MemoryMap;
> > +  EFI_MEMORY_DESCRIPTOR      *MemoryMapEntry;
> > +  EFI_MEMORY_DESCRIPTOR      *MemoryMapEnd;
> > +  EFI_STATUS                 Status;
> > +
> > +  TestBit = LShiftU64 (1, EfiBootServicesData);
> > +  if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) == 0) {
> > +    return;
> > +  }
> > +
> > +  MemoryMapSize = 0;
> > +  MemoryMap     = NULL;
> > +
> > +  Status = gBS->GetMemoryMap (
> > +                  &MemoryMapSize,
> > +                  MemoryMap,
> > +                  &MapKey,
> > +                  &DescriptorSize,
> > +                  &DescriptorVersion
> > +                  );
> > +  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
> > +  do {
> > +    MemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocatePool (MemoryMapSize);
>
> nitpick: *If* there is a V2, maybe drop the cast?
>
> > +    ASSERT (MemoryMap != NULL);
>
> I know ASSERTing for NULL is a common pattern for some reason, but I'd rather 
> not have more code with this added.
>
> > +    Status = gBS->GetMemoryMap (
> > +                    &MemoryMapSize,
> > +                    MemoryMap,
> > +                    &MapKey,
> > +                    &DescriptorSize,
> > +                    &DescriptorVersion
> > +                    );
>
> Another nitpick, isn't it common practice to call the Core* functions 
> directly within *Core? I know there is code that doesn't, but the former 
> should be more efficient.
>
> > +    if (EFI_ERROR (Status)) {
> > +      FreePool (MemoryMap);
> > +    }
> > +  } while (Status == EFI_BUFFER_TOO_SMALL);
>
> Is this guaranteed to terminate? I mean, I get the idea - try again with the 
> larger size and when allocating the bigger buffer, its potential new entry 
> will already be accounted for. However, I saw downstream code that tried 
> something like this (they actually added a constant overhead ahead of time) 
> bounded by something like 5 iterations. Might just have been defensive 
> programming - probably was -, but it's not trivial to verify, I think, is it?
>
> Regarding the added constant, the spec actually says the following, which 
> obviously is just to shortcut a second round of GetMemoryMap(), but still:
> "The actual size of the buffer allocated for the consequent call to 
> GetMemoryMap() should be bigger then the value returned in MemoryMapSize"
>
> It appears the spec did not define a canonical way to call GetMemoryMap(), 
> did it? :(
>

This is all copy-pasted from MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c

> > +
> > +  ASSERT_EFI_ERROR (Status);
> > +
> > +  MemoryMapEntry = MemoryMap;
> > +  MemoryMapEnd   = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + 
> > MemoryMapSize);
> > +  while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
> > +    if (MemoryMapEntry->Type == EfiConventionalMemory) {
> > +      ArmSetMemoryRegionNoExec (
> > +        MemoryMapEntry->PhysicalStart,
> > +        EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages)
> > +        );
> > +    }
> > +
> > +    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, 
> > DescriptorSize);
> > +  }
> > +}
> > +
> > EFI_STATUS
> > CpuDxeInitialize (
> >   IN EFI_HANDLE        ImageHandle,
> > @@ -240,6 +305,18 @@ CpuDxeInitialize (
> >
> >   InitializeDma (&mCpu);
> >
> > +  //
> > +  // Once we install the CPU arch protocol, the DXE core's memory
> > +  // protection routines will invoke them to manage the permissions of page
> > +  // allocations as they are created. Given that this includes pages
> > +  // allocated for page tables by this driver, we must ensure that unused
> > +  // memory is mapped with the same permissions as boot services data
> > +  // regions. Otherwise, we may end up with unbounded recursion, due to the
> > +  // fact that updating permissions on a newly allocated page table may 
> > trigger
> > +  // a block entry split, which triggers a page table allocation, etc etc
> > +  //
> > +  RemapUnusedMemoryNx ();
>
> Hmm. I might be too tired, but why is this not already done by 
> InitializeDxeNxMemoryProtectionPolicy(), assuming BS_Data and ConvMem have 
> the same XP configuration?
>
> This reassures me that the CPU Arch protocol producers should be linked into 
> DxeCore rather than loaded at some arbitrary point in time... Unrelated to 
> the patch, of course.
>

The ordering here is a bit tricky. As soon as the CPU arch protocol is
exposed, every allocation will be remapped individually, resulting in
page table splits and therefore recursion.


>
> > +
> >   Status = gBS->InstallMultipleProtocolInterfaces (
> >                   &mCpuHandle,
> >                   &gEfiCpuArchProtocolGuid,
> > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf 
> > b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > index e732e21cb94a..8fd0f4133088 100644
> > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > @@ -48,6 +48,7 @@ [LibraryClasses]
> >   DefaultExceptionHandlerLib
> >   DxeServicesTableLib
> >   HobLib
> > +  MemoryAllocationLib
> >   PeCoffGetEntryPointLib
> >   UefiDriverEntryPoint
> >   UefiLib
> > @@ -64,6 +65,7 @@ [Guids]
> >
> > [Pcd.common]
> >   gArmTokenSpaceGuid.PcdVFPEnabled
> > +  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy
> >
> > [FeaturePcd.common]
> >   gArmTokenSpaceGuid.PcdDebuggerExceptionSupport
> > --
> > 2.39.1
> >
>
>
>
> 
>
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#99811): https://edk2.groups.io/g/devel/message/99811
Mute This Topic: https://groups.io/mt/96835915/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to