public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: edk2-devel@lists.01.org, afish@apple.com,
	leif.lindholm@linaro.org, michael.d.kinney@intel.com,
	liming.gao@intel.com, jiewen.yao@intel.com
Cc: lersek@redhat.com, feng.tian@intel.com, star.zeng@intel.com,
	Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [PATCH v2 5/5] MdeModulePkg/DxeCore: implement memory protection policy
Date: Fri, 24 Feb 2017 15:04:59 +0000	[thread overview]
Message-ID: <1487948699-3179-6-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1487948699-3179-1-git-send-email-ard.biesheuvel@linaro.org>

This implements a DXE memory protection policy that ensure that regions
that don't require executable permissions are mapped with the non-exec
attribute set.

First of all, it iterates over all entries in the UEFI memory map, and
removes executable permissions according to the configured DXE memory
protection policy, as recorded in PcdDxeMemoryProtectionPolicy.

Secondly, it sets or clears the non-executable attribute when allocating
or freeing pages, both for page based or pool based allocations.

Note that this complements the image protection facility, which applies
strict permissions to BootServicesCode/RuntimeServicesCode regions when
the section alignment allows it. The memory protection configured by this
patch operates on non-code regions only.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 MdeModulePkg/Core/Dxe/DxeMain.inf             |   1 +
 MdeModulePkg/Core/Dxe/Mem/Page.c              | 105 ++++++++++++++++++++
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 104 ++++++++++++++++++-
 3 files changed, 209 insertions(+), 1 deletion(-)

diff --git a/MdeModulePkg/Core/Dxe/DxeMain.inf b/MdeModulePkg/Core/Dxe/DxeMain.inf
index 371e91cb0d7e..871868dbf305 100644
--- a/MdeModulePkg/Core/Dxe/DxeMain.inf
+++ b/MdeModulePkg/Core/Dxe/DxeMain.inf
@@ -191,6 +191,7 @@ [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdMemoryProfileDriverPath                 ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdPropertiesTableEnable                   ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy                   ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeMemoryProtectionPolicy               ## CONSUMES
 
 # [Hob]
 # RESOURCE_DESCRIPTOR   ## CONSUMES
diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c
index 6330d41e7b3b..569238c77f98 100644
--- a/MdeModulePkg/Core/Dxe/Mem/Page.c
+++ b/MdeModulePkg/Core/Dxe/Mem/Page.c
@@ -1305,6 +1305,101 @@ Done:
 }
 
 /**
+  Return the EFI memory permission attribute associated with memory type 'Type'
+  under the configured DXE memory protection policy.
+**/
+STATIC
+UINT64
+EFIAPI
+GetPermissionAttributeForMemoryType (
+  IN  EFI_MEMORY_TYPE     Type
+  )
+{
+  switch (Type) {
+  case EfiBootServicesCode:
+  case EfiRuntimeServicesCode:
+  case EfiLoaderCode:
+    break;
+
+  default:
+    if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT0) != 0) {
+      return EFI_MEMORY_XP;
+    }
+    break;
+  case EfiReservedMemoryType:
+    if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT1) != 0) {
+      return EFI_MEMORY_XP;
+    }
+    break;
+  case EfiACPIMemoryNVS:
+    if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT2) != 0) {
+      return EFI_MEMORY_XP;
+    }
+    break;
+  }
+  return 0;
+}
+
+/**
+  Manage memory permission attributes on a memory range, according to the
+  configured DXE memory protection policy.
+
+  @param  OldType           The old memory type of the range
+  @param  NewType           The new memory type of the range
+  @param  Memory            The base address of the range
+  @param  Length            The size of the range (in bytes)
+
+  @return EFI_SUCCESS       If the the CPU arch protocol is not installed yet
+  @return EFI_SUCCESS       If no DXE memory protection policy has been configured
+  @return EFI_SUCCESS       If OldType and NewType use the same permission attributes
+  @return other             Return value of gCpu->SetMemoryAttributes()
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ApplyMemoryProtection (
+  IN  EFI_MEMORY_TYPE       OldType,
+  IN  EFI_MEMORY_TYPE       NewType,
+  IN  EFI_PHYSICAL_ADDRESS  Memory,
+  IN  UINT64                Length
+  )
+{
+  UINT64  OldAttributes;
+  UINT64  NewAttributes;
+
+  //
+  // If the CPU arch protocol is not installed yet, we cannot manage memory
+  // permission attributes, and it is the job of the driver that installs this
+  // protocol to set the permissions on existing allocations.
+  //
+  if (gCpu == NULL) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Check if a DXE memory protection policy has been configured
+  //
+  if (PcdGet32 (PcdDxeMemoryProtectionPolicy) == 0) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Update the executable permissions according to the DXE memory
+  // protection policy, but only if the policy is different between
+  // the old and the new type.
+  //
+  OldAttributes = GetPermissionAttributeForMemoryType (OldType);
+  NewAttributes = GetPermissionAttributeForMemoryType (NewType);
+
+  if (OldAttributes == NewAttributes) {
+    return EFI_SUCCESS;
+  }
+
+  return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
+}
+
+/**
   Allocates pages from the memory map.
 
   @param  Type                   The type of allocation to perform
@@ -1344,6 +1439,8 @@ CoreAllocatePages (
       NULL
       );
     InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);
+    ApplyMemoryProtection (EfiConventionalMemory, MemoryType, *Memory,
+      EFI_PAGES_TO_SIZE (NumberOfPages));
   }
   return Status;
 }
@@ -1460,6 +1557,8 @@ CoreFreePages (
       NULL
       );
     InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);
+    ApplyMemoryProtection (MemoryType, EfiConventionalMemory, Memory,
+      EFI_PAGES_TO_SIZE (NumberOfPages));
   }
   return Status;
 }
@@ -1856,6 +1955,9 @@ CoreAllocatePoolPages (
     DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", (UINT32)NumberOfPages));
   } else {
     CoreConvertPages (Start, NumberOfPages, PoolType);
+
+    ApplyMemoryProtection (EfiConventionalMemory, PoolType, Start,
+      EFI_PAGES_TO_SIZE (NumberOfPages));
   }
 
   return (VOID *)(UINTN) Start;
@@ -1877,6 +1979,9 @@ CoreFreePoolPages (
   )
 {
   CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);
+
+  ApplyMemoryProtection (PoolType, EfiConventionalMemory, Memory,
+    EFI_PAGES_TO_SIZE (NumberOfPages));
 }
 
 
diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index c36612a1b1f2..1142dcc5a83d 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -639,6 +639,97 @@ UnprotectUefiImage (
 }
 
 /**
+  Remove exec permissions from all regions whose type is identified by
+  PcdDxeMemoryProtectionPolicy
+**/
+STATIC
+VOID
+ApplyDxeMemoryProtectionPolicy (
+  VOID
+  )
+{
+  UINTN                             MemoryMapSize;
+  UINTN                             MapKey;
+  UINTN                             DescriptorSize;
+  UINT32                            DescriptorVersion;
+  EFI_MEMORY_DESCRIPTOR             *MemoryMap;
+  EFI_MEMORY_DESCRIPTOR             *MemoryMapEntry;
+  EFI_MEMORY_DESCRIPTOR             *MemoryMapEnd;
+  EFI_STATUS                        Status;
+
+  //
+  // Get the EFI memory map.
+  //
+  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);
+    ASSERT (MemoryMap != NULL);
+    Status = gBS->GetMemoryMap (
+                    &MemoryMapSize,
+                    MemoryMap,
+                    &MapKey,
+                    &DescriptorSize,
+                    &DescriptorVersion
+                    );
+    if (EFI_ERROR (Status)) {
+      FreePool (MemoryMap);
+    }
+  } while (Status == EFI_BUFFER_TOO_SMALL);
+  ASSERT_EFI_ERROR (Status);
+
+  DEBUG((DEBUG_ERROR, "%a: removing exec permissions from memory regions\n",
+    __FUNCTION__));
+
+  for (MemoryMapEntry = MemoryMap,
+       MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
+       (UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd;
+       MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize)) {
+
+    switch (MemoryMapEntry->Type) {
+    case EfiBootServicesCode:
+    case EfiRuntimeServicesCode:
+    case EfiLoaderCode:
+      continue;
+
+    default:
+      if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT0) == 0) {
+        continue;
+      }
+      break;
+
+    case EfiReservedMemoryType:
+      if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT1) == 0) {
+        continue;
+      }
+      break;
+
+    case EfiACPIMemoryNVS:
+      if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT2) == 0) {
+        continue;
+      }
+      break;
+    }
+
+    SetUefiImageMemoryAttributes (
+      MemoryMapEntry->PhysicalStart,
+      EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages),
+      EFI_MEMORY_XP);
+  }
+  FreePool (MemoryMap);
+}
+
+
+/**
   A notification for CPU_ARCH protocol.
 
   @param[in]  Event                 Event whose notification function is being invoked.
@@ -666,6 +757,17 @@ MemoryProtectionCpuArchProtocolNotify (
     return;
   }
 
+  //
+  // Apply the memory protection policy on non-BScode/RTcode regions.
+  //
+  if (PcdGet32 (PcdDxeMemoryProtectionPolicy) != 0) {
+    ApplyDxeMemoryProtectionPolicy ();
+  }
+
+  if (mImageProtectionPolicy == 0) {
+    return;
+  }
+
   Status = gBS->LocateHandleBuffer (
                   ByProtocol,
                   &gEfiLoadedImageProtocolGuid,
@@ -745,7 +847,7 @@ CoreInitializeMemoryProtection (
 
   mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);
 
-  if (mImageProtectionPolicy != 0) {
+  if (mImageProtectionPolicy != 0 || PcdGet32 (PcdDxeMemoryProtectionPolicy) != 0) {
     Status = CoreCreateEvent (
                EVT_NOTIFY_SIGNAL,
                TPL_CALLBACK,
-- 
2.7.4



  parent reply	other threads:[~2017-02-24 15:05 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-02-24 15:04 [PATCH v2 0/5] RFC: increased memory protection Ard Biesheuvel
2017-02-24 15:04 ` [PATCH v2 1/5] ArmPkg/CpuDxe: ignore attribute changes during SyncCacheConfig() Ard Biesheuvel
2017-02-24 15:04 ` [PATCH v2 2/5] MdeModulePkg/PeiCore: allocate BootServicesCode memory for PE/COFF images Ard Biesheuvel
2017-02-24 15:04 ` [PATCH v2 3/5] MdeModulePkg/DxeCore: pass pool type to CoreFreePoolPages () Ard Biesheuvel
2017-02-24 15:04 ` [PATCH v2 4/5] MdeModulePkg: define PCD for DXE memory protection policy Ard Biesheuvel
2017-02-24 15:04 ` Ard Biesheuvel [this message]
2017-02-25  4:04 ` [PATCH v2 0/5] RFC: increased memory protection Yao, Jiewen
2017-02-26 15:09   ` Ard Biesheuvel
2017-02-27  8:56     ` 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=1487948699-3179-6-git-send-email-ard.biesheuvel@linaro.org \
    --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