public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v2 0/2] fix DXE memory free issue in SMM mode
@ 2018-06-13  5:34 Jian J Wang
  2018-06-13  5:35 ` [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table " Jian J Wang
  2018-06-13  5:35 ` [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection Jian J Wang
  0 siblings, 2 replies; 9+ messages in thread
From: Jian J Wang @ 2018-06-13  5:34 UTC (permalink / raw)
  To: edk2-devel

> v2:
>   a. add more specific explanations in commit message
>   b. add more comments in code
>   c. remove redundant logic in code
>   d. fix logic hole in code
>   e. replace meanless constant macro with meaning ones
>   f. remove irrelated changes

This patch series try to fix an issue caused by trying to free memory
allocated in DXE but freed in SMM mode. This happens only if Heap
Guard feature is enabled, which needs to update page table. The root
cause is that DXE and SMM don't share the same paging configuration
but CpuDxe driver still tries to access page table through current
processor registers (CR3) in SMM mode, during memory free opration in
DXE core. This will cause DXE core got the incorrect paging attributes
of memory to be freed, and then fail the free operation.

The solution is let CpuDxe store the paging configuration in a global
variable and use it to access DXE page table if in SMM mode.

Jian J Wang (2):
  UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  MdeModulePkg/Core: remove SMM check for Heap Guard feature detection

 MdeModulePkg/Core/Dxe/Mem/HeapGuard.c |  10 ---
 UefiCpuPkg/CpuDxe/CpuDxe.inf          |   1 +
 UefiCpuPkg/CpuDxe/CpuPageTable.c      | 159 ++++++++++++++++++++++++++--------
 3 files changed, 123 insertions(+), 47 deletions(-)

-- 
2.16.2.windows.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  2018-06-13  5:34 [PATCH v2 0/2] fix DXE memory free issue in SMM mode Jian J Wang
@ 2018-06-13  5:35 ` Jian J Wang
  2018-06-13 15:10   ` Laszlo Ersek
  2018-06-13 15:14   ` Andrew Fish
  2018-06-13  5:35 ` [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection Jian J Wang
  1 sibling, 2 replies; 9+ messages in thread
From: Jian J Wang @ 2018-06-13  5:35 UTC (permalink / raw)
  To: edk2-devel; +Cc: Eric Dong, Laszlo Ersek, Jiewen Yao, Ruiyu Ni

> v2:
>   a. add more specific explanations in commit message
>   b. add more comments in code
>   c. remove redundant logic in IsInSmm()
>   d. fix a logic hole in GetCurrentPagingContext()
>   e. replace meanless constant macro with meaning ones

The MdePkg/Library/SmmMemoryAllocationLib, used only by DXE_SMM_DRIVER,
allows to free memory allocated in DXE (before EndOfDxe). This is done
by checking the memory range and calling gBS services to do real
operation if the memory to free is out of SMRAM. If some memory related
features, like Heap Guard, are enabled, gBS interface will turn to
EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes(), provided by
DXE driver UefiCpuPkg/CpuDxe, to change memory paging attributes. This
means we have part of DXE code running in SMM mode in certain
circumstances.

Because page table in SMM mode is different from DXE mode and CpuDxe
always uses current registers (CR0, CR3, etc.) to get memory paging
attributes, it cannot get the correct attributes of DXE memory in SMM
mode from SMM page table. This will cause incorrect memory manipulations,
like fail the releasing of Guard pages if Heap Guard is enabled.

The solution in this patch is to store the DXE page table information
(e.g. value of CR0, CR3 registers, etc.) in a global variable of CpuDxe
driver. If CpuDxe detects it's in SMM mode, it will use this global
variable to access page table instead of current processor registers.
This can avoid retrieving wrong DXE memory paging attributes and changing
SMM page table attributes unexpectedly.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
---
 UefiCpuPkg/CpuDxe/CpuDxe.inf     |   1 +
 UefiCpuPkg/CpuDxe/CpuPageTable.c | 159 ++++++++++++++++++++++++++++++---------
 2 files changed, 123 insertions(+), 37 deletions(-)

diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
index 3c938cee53..ce2bd3627c 100644
--- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
+++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
@@ -66,6 +66,7 @@
 [Protocols]
   gEfiCpuArchProtocolGuid                       ## PRODUCES
   gEfiMpServiceProtocolGuid                     ## PRODUCES
+  gEfiSmmBase2ProtocolGuid                      ## CONSUMES
 
 [Guids]
   gIdleLoopEventGuid                            ## CONSUMES           ## Event
diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c b/UefiCpuPkg/CpuDxe/CpuPageTable.c
index e2595b4d89..b7e75922b6 100644
--- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
+++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
@@ -23,10 +23,21 @@
 #include <Library/DebugLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Protocol/MpService.h>
+#include <Protocol/SmmBase2.h>
+#include <Register/Cpuid.h>
+#include <Register/Msr.h>
 
 #include "CpuDxe.h"
 #include "CpuPageTable.h"
 
+///
+/// Paging registers
+///
+#define CR0_WP                      BIT16
+#define CR0_PG                      BIT31
+#define CR4_PSE                     BIT4
+#define CR4_PAE                     BIT5
+
 ///
 /// Page Table Entry
 ///
@@ -87,7 +98,46 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
   {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
 };
 
-PAGE_TABLE_POOL   *mPageTablePool = NULL;
+PAGE_TABLE_POOL                   *mPageTablePool = NULL;
+PAGE_TABLE_LIB_PAGING_CONTEXT     mPagingContext;
+EFI_SMM_BASE2_PROTOCOL            *mSmmBase2 = NULL;
+
+/**
+ Check if current execution environment is in SMM mode or not, via
+ EFI_SMM_BASE2_PROTOCOL.
+
+ This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib
+ supports to free memory outside SMRAM. The library will call gBS->FreePool() or
+ gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change
+ memory paging attributes during free operation, if some memory related features
+ are enabled (like Heap Guard).
+
+ This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This
+ will cause incorrect result because SMM mode always loads its own page tables,
+ which are usually different from DXE. This function can be used to detect such
+ situation and help to avoid further misoperations.
+
+  @retval TRUE    In SMM mode.
+  @retval FALSE   Not in SMM mode.
+**/
+BOOLEAN
+IsInSmm (
+  VOID
+  )
+{
+  BOOLEAN                 InSmm;
+
+  InSmm = FALSE;
+  if (mSmmBase2 == NULL) {
+    gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2);
+  }
+
+  if (mSmmBase2 != NULL) {
+    mSmmBase2->InSmm (mSmmBase2, &InSmm);
+  }
+
+  return InSmm;
+}
 
 /**
   Return current paging context.
@@ -99,45 +149,61 @@ GetCurrentPagingContext (
   IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext
   )
 {
-  UINT32                         RegEax;
-  UINT32                         RegEdx;
+  UINT32                          RegEax;
+  CPUID_EXTENDED_CPU_SIG_EDX      RegEdx;
+  MSR_IA32_EFER_REGISTER          MsrEfer;
 
-  ZeroMem(PagingContext, sizeof(*PagingContext));
-  if (sizeof(UINTN) == sizeof(UINT64)) {
-    PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;
-  } else {
-    PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;
-  }
-  if ((AsmReadCr0 () & BIT31) != 0) {
-    PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
-  } else {
-    PagingContext->ContextData.X64.PageTableBase = 0;
-  }
+  //
+  // Don't retrieve current paging context from processor if in SMM mode.
+  //
+  if (!IsInSmm ()) {
+    ZeroMem (&mPagingContext, sizeof(mPagingContext));
 
-  if ((AsmReadCr4 () & BIT4) != 0) {
-    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
-  }
-  if ((AsmReadCr4 () & BIT5) != 0) {
-    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
-  }
-  if ((AsmReadCr0 () & BIT16) != 0) {
-    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
-  }
+    if (sizeof(UINTN) == sizeof(UINT64)) {
+      mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;
+    } else {
+      mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;
+    }
+    if ((AsmReadCr0 () & CR0_PG) != 0) {
+      mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
+    } else {
+      mPagingContext.ContextData.X64.PageTableBase = 0;
+    }
 
-  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
-  if (RegEax > 0x80000000) {
-    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
-    if ((RegEdx & BIT20) != 0) {
-      // XD supported
-      if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {
-        // XD activated
-        PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
-      }
+    if ((AsmReadCr4 () & CR4_PSE) != 0) {
+      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
+    }
+    if ((AsmReadCr4 () & CR4_PAE) != 0) {
+      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
+    }
+    if ((AsmReadCr0 () & CR0_WP) != 0) {
+      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
     }
-    if ((RegEdx & BIT26) != 0) {
-      PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
+
+    AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
+    if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
+      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, (UINT32 *)&RegEdx);
+
+      if (RegEdx.Bits.NX != 0) {
+        // XD supported
+        MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);
+        if (MsrEfer.Bits.NXE != 0) {
+          // XD activated
+          mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
+        }
+      }
+
+      if (RegEdx.Bits.Page1GB != 0) {
+        mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
+      }
     }
   }
+
+  //
+  // This can avoid getting SMM paging context if in SMM mode. We cannot assume
+  // SMM mode shares the same paging context as DXE.
+  //
+  CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));
 }
 
 /**
@@ -507,7 +573,14 @@ IsReadOnlyPageWriteProtected (
   VOID
   )
 {
-  return ((AsmReadCr0 () & BIT16) != 0);
+  //
+  // To avoid unforseen consequences, don't touch paging settings in SMM mode
+  // in this driver.
+  //
+  if (!IsInSmm ()) {
+    return ((AsmReadCr0 () & CR0_WP) != 0);
+  }
+  return FALSE;
 }
 
 /**
@@ -518,7 +591,13 @@ DisableReadOnlyPageWriteProtect (
   VOID
   )
 {
-  AsmWriteCr0 (AsmReadCr0() & ~BIT16);
+  //
+  // To avoid unforseen consequences, don't touch paging settings in SMM mode
+  // in this driver.
+  //
+  if (!IsInSmm ()) {
+    AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
+  }
 }
 
 /**
@@ -529,7 +608,13 @@ EnableReadOnlyPageWriteProtect (
   VOID
   )
 {
-  AsmWriteCr0 (AsmReadCr0() | BIT16);
+  //
+  // To avoid unforseen consequences, don't touch paging settings in SMM mode
+  // in this driver.
+  //
+  if (!IsInSmm ()) {
+    AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
+  }
 }
 
 /**
-- 
2.16.2.windows.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection
  2018-06-13  5:34 [PATCH v2 0/2] fix DXE memory free issue in SMM mode Jian J Wang
  2018-06-13  5:35 ` [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table " Jian J Wang
@ 2018-06-13  5:35 ` Jian J Wang
  2018-06-14  0:58   ` Zeng, Star
  1 sibling, 1 reply; 9+ messages in thread
From: Jian J Wang @ 2018-06-13  5:35 UTC (permalink / raw)
  To: edk2-devel; +Cc: Star Zeng, Eric Dong, Jiewen Yao, Ruiyu Ni

CpuDxe driver is updated to be able to access DXE page table in SMM mode,
which means Heap Guard can get correct memory paging attributes in what
environment. It's not necessary to exclude SMM from detecting Heap Guard
feature support.

Cc: Star Zeng <star.zeng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
---
 MdeModulePkg/Core/Dxe/Mem/HeapGuard.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
index 9d765c98f6..447c56bb11 100644
--- a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
+++ b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
@@ -667,21 +667,11 @@ IsMemoryTypeToGuard (
 {
   UINT64 TestBit;
   UINT64 ConfigBit;
-  BOOLEAN     InSmm;
 
   if (AllocateType == AllocateAddress) {
     return FALSE;
   }
 
-  InSmm = FALSE;
-  if (gSmmBase2 != NULL) {
-    gSmmBase2->InSmm (gSmmBase2, &InSmm);
-  }
-
-  if (InSmm) {
-    return FALSE;
-  }
-
   if ((PcdGet8 (PcdHeapGuardPropertyMask) & PageOrPool) == 0) {
     return FALSE;
   }
-- 
2.16.2.windows.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  2018-06-13  5:35 ` [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table " Jian J Wang
@ 2018-06-13 15:10   ` Laszlo Ersek
  2018-06-14  0:46     ` Wang, Jian J
  2018-06-13 15:14   ` Andrew Fish
  1 sibling, 1 reply; 9+ messages in thread
From: Laszlo Ersek @ 2018-06-13 15:10 UTC (permalink / raw)
  To: Jian J Wang, edk2-devel; +Cc: Ruiyu Ni, Jiewen Yao, Eric Dong

Hi Jian,

I have three comments:

On 06/13/18 07:35, Jian J Wang wrote:
>> v2:
>>   a. add more specific explanations in commit message
>>   b. add more comments in code
>>   c. remove redundant logic in IsInSmm()
>>   d. fix a logic hole in GetCurrentPagingContext()
>>   e. replace meanless constant macro with meaning ones
> 
> The MdePkg/Library/SmmMemoryAllocationLib, used only by DXE_SMM_DRIVER,
> allows to free memory allocated in DXE (before EndOfDxe). This is done
> by checking the memory range and calling gBS services to do real
> operation if the memory to free is out of SMRAM. If some memory related
> features, like Heap Guard, are enabled, gBS interface will turn to
> EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes(), provided by
> DXE driver UefiCpuPkg/CpuDxe, to change memory paging attributes. This
> means we have part of DXE code running in SMM mode in certain
> circumstances.
> 
> Because page table in SMM mode is different from DXE mode and CpuDxe
> always uses current registers (CR0, CR3, etc.) to get memory paging
> attributes, it cannot get the correct attributes of DXE memory in SMM
> mode from SMM page table. This will cause incorrect memory manipulations,
> like fail the releasing of Guard pages if Heap Guard is enabled.
> 
> The solution in this patch is to store the DXE page table information
> (e.g. value of CR0, CR3 registers, etc.) in a global variable of CpuDxe
> driver. If CpuDxe detects it's in SMM mode, it will use this global
> variable to access page table instead of current processor registers.
> This can avoid retrieving wrong DXE memory paging attributes and changing
> SMM page table attributes unexpectedly.
> 
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Ruiyu Ni <ruiyu.ni@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
> ---
>  UefiCpuPkg/CpuDxe/CpuDxe.inf     |   1 +
>  UefiCpuPkg/CpuDxe/CpuPageTable.c | 159 ++++++++++++++++++++++++++++++---------
>  2 files changed, 123 insertions(+), 37 deletions(-)
> 
> diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> index 3c938cee53..ce2bd3627c 100644
> --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
> +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> @@ -66,6 +66,7 @@
>  [Protocols]
>    gEfiCpuArchProtocolGuid                       ## PRODUCES
>    gEfiMpServiceProtocolGuid                     ## PRODUCES
> +  gEfiSmmBase2ProtocolGuid                      ## CONSUMES

(1) In my v1 review, I suggested "SOMETIMES_CONSUMES" for this hint.

And that's because CpuDxe can be included in platform builds that don't
include the SMM driver stack at all; in that case,
gEfiSmmBase2ProtocolGuid will not be available and will not be consumed.
In other words, we cannot say that the protocol is always consumed.

>  
>  [Guids]
>    gIdleLoopEventGuid                            ## CONSUMES           ## Event
> diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> index e2595b4d89..b7e75922b6 100644
> --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> @@ -23,10 +23,21 @@
>  #include <Library/DebugLib.h>
>  #include <Library/UefiBootServicesTableLib.h>
>  #include <Protocol/MpService.h>
> +#include <Protocol/SmmBase2.h>
> +#include <Register/Cpuid.h>
> +#include <Register/Msr.h>
>  
>  #include "CpuDxe.h"
>  #include "CpuPageTable.h"
>  
> +///
> +/// Paging registers
> +///
> +#define CR0_WP                      BIT16
> +#define CR0_PG                      BIT31
> +#define CR4_PSE                     BIT4
> +#define CR4_PAE                     BIT5

(2a) I think the BITxx -> CRx_xx macro replacements should have been
split to a separate patch.

> +
>  ///
>  /// Page Table Entry
>  ///
> @@ -87,7 +98,46 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
>    {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
>  };
>  
> -PAGE_TABLE_POOL   *mPageTablePool = NULL;
> +PAGE_TABLE_POOL                   *mPageTablePool = NULL;
> +PAGE_TABLE_LIB_PAGING_CONTEXT     mPagingContext;
> +EFI_SMM_BASE2_PROTOCOL            *mSmmBase2 = NULL;
> +
> +/**
> + Check if current execution environment is in SMM mode or not, via
> + EFI_SMM_BASE2_PROTOCOL.
> +
> + This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib
> + supports to free memory outside SMRAM. The library will call gBS->FreePool() or
> + gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change
> + memory paging attributes during free operation, if some memory related features
> + are enabled (like Heap Guard).
> +
> + This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This
> + will cause incorrect result because SMM mode always loads its own page tables,
> + which are usually different from DXE. This function can be used to detect such
> + situation and help to avoid further misoperations.
> +
> +  @retval TRUE    In SMM mode.
> +  @retval FALSE   Not in SMM mode.
> +**/
> +BOOLEAN
> +IsInSmm (
> +  VOID
> +  )
> +{
> +  BOOLEAN                 InSmm;
> +
> +  InSmm = FALSE;
> +  if (mSmmBase2 == NULL) {
> +    gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2);
> +  }
> +
> +  if (mSmmBase2 != NULL) {
> +    mSmmBase2->InSmm (mSmmBase2, &InSmm);
> +  }
> +
> +  return InSmm;
> +}
>  
>  /**
>    Return current paging context.
> @@ -99,45 +149,61 @@ GetCurrentPagingContext (
>    IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext
>    )
>  {
> -  UINT32                         RegEax;
> -  UINT32                         RegEdx;
> +  UINT32                          RegEax;
> +  CPUID_EXTENDED_CPU_SIG_EDX      RegEdx;
> +  MSR_IA32_EFER_REGISTER          MsrEfer;
>  
> -  ZeroMem(PagingContext, sizeof(*PagingContext));
> -  if (sizeof(UINTN) == sizeof(UINT64)) {
> -    PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;
> -  } else {
> -    PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;
> -  }
> -  if ((AsmReadCr0 () & BIT31) != 0) {
> -    PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
> -  } else {
> -    PagingContext->ContextData.X64.PageTableBase = 0;
> -  }
> +  //
> +  // Don't retrieve current paging context from processor if in SMM mode.
> +  //
> +  if (!IsInSmm ()) {
> +    ZeroMem (&mPagingContext, sizeof(mPagingContext));
>  
> -  if ((AsmReadCr4 () & BIT4) != 0) {
> -    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> -  }
> -  if ((AsmReadCr4 () & BIT5) != 0) {
> -    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> -  }
> -  if ((AsmReadCr0 () & BIT16) != 0) {
> -    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> -  }
> +    if (sizeof(UINTN) == sizeof(UINT64)) {
> +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;
> +    } else {
> +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;
> +    }
> +    if ((AsmReadCr0 () & CR0_PG) != 0) {
> +      mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
> +    } else {
> +      mPagingContext.ContextData.X64.PageTableBase = 0;
> +    }
>  
> -  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> -  if (RegEax > 0x80000000) {
> -    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> -    if ((RegEdx & BIT20) != 0) {
> -      // XD supported
> -      if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {
> -        // XD activated
> -        PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> -      }
> +    if ((AsmReadCr4 () & CR4_PSE) != 0) {
> +      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> +    }
> +    if ((AsmReadCr4 () & CR4_PAE) != 0) {
> +      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> +    }
> +    if ((AsmReadCr0 () & CR0_WP) != 0) {
> +      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
>      }
> -    if ((RegEdx & BIT26) != 0) {
> -      PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
> +
> +    AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
> +    if (RegEax >= CPUID_EXTENDED_CPU_SIG) {

(2b) Similarly to (2a), this cleanup (which is correct, and welcome)
should have been split to a separate patch.

> +      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, (UINT32 *)&RegEdx);

(3) I think it would be more idiomatic to pass &RegEdx.Uint32 as the
last argument. However, this method works as well.


I don't think that posting a v3 is necessary just because of these
remarks. Please fix (1) though, just before you push the patch.

* For this patch, with (1) updated:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

* For the series:

Regression-tested-by: Laszlo Ersek <lersek@redhat.com>

For the regression testing, I used OVMF built both with and without SMM.
Also, for patch #2, please remember that OVMF does not enable the page
guard feature.

Thanks!
Laszlo


> +
> +      if (RegEdx.Bits.NX != 0) {
> +        // XD supported
> +        MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);
> +        if (MsrEfer.Bits.NXE != 0) {
> +          // XD activated
> +          mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> +        }
> +      }
> +
> +      if (RegEdx.Bits.Page1GB != 0) {
> +        mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
> +      }
>      }
>    }
> +
> +  //
> +  // This can avoid getting SMM paging context if in SMM mode. We cannot assume
> +  // SMM mode shares the same paging context as DXE.
> +  //
> +  CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));
>  }
>  
>  /**
> @@ -507,7 +573,14 @@ IsReadOnlyPageWriteProtected (
>    VOID
>    )
>  {
> -  return ((AsmReadCr0 () & BIT16) != 0);
> +  //
> +  // To avoid unforseen consequences, don't touch paging settings in SMM mode
> +  // in this driver.
> +  //
> +  if (!IsInSmm ()) {
> +    return ((AsmReadCr0 () & CR0_WP) != 0);
> +  }
> +  return FALSE;
>  }
>  
>  /**
> @@ -518,7 +591,13 @@ DisableReadOnlyPageWriteProtect (
>    VOID
>    )
>  {
> -  AsmWriteCr0 (AsmReadCr0() & ~BIT16);
> +  //
> +  // To avoid unforseen consequences, don't touch paging settings in SMM mode
> +  // in this driver.
> +  //
> +  if (!IsInSmm ()) {
> +    AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
> +  }
>  }
>  
>  /**
> @@ -529,7 +608,13 @@ EnableReadOnlyPageWriteProtect (
>    VOID
>    )
>  {
> -  AsmWriteCr0 (AsmReadCr0() | BIT16);
> +  //
> +  // To avoid unforseen consequences, don't touch paging settings in SMM mode
> +  // in this driver.
> +  //
> +  if (!IsInSmm ()) {
> +    AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
> +  }
>  }
>  
>  /**
> 



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  2018-06-13  5:35 ` [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table " Jian J Wang
  2018-06-13 15:10   ` Laszlo Ersek
@ 2018-06-13 15:14   ` Andrew Fish
  2018-06-13 19:54     ` Laszlo Ersek
  1 sibling, 1 reply; 9+ messages in thread
From: Andrew Fish @ 2018-06-13 15:14 UTC (permalink / raw)
  To: Jian J Wang; +Cc: edk2-devel, Ruiyu Ni, Jiewen Yao, Laszlo Ersek, Eric Dong



> On Jun 12, 2018, at 10:35 PM, Jian J Wang <jian.j.wang@intel.com> wrote:
> 
>> v2:
>>  a. add more specific explanations in commit message
>>  b. add more comments in code
>>  c. remove redundant logic in IsInSmm()
>>  d. fix a logic hole in GetCurrentPagingContext()
>>  e. replace meanless constant macro with meaning ones
> 
> The MdePkg/Library/SmmMemoryAllocationLib, used only by DXE_SMM_DRIVER,
> allows to free memory allocated in DXE (before EndOfDxe). This is done
> by checking the memory range and calling gBS services to do real
> operation if the memory to free is out of SMRAM. If some memory related
> features, like Heap Guard, are enabled, gBS interface will turn to
> EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes(), provided by
> DXE driver UefiCpuPkg/CpuDxe, to change memory paging attributes. This
> means we have part of DXE code running in SMM mode in certain
> circumstances.
> 
> Because page table in SMM mode is different from DXE mode and CpuDxe
> always uses current registers (CR0, CR3, etc.) to get memory paging
> attributes, it cannot get the correct attributes of DXE memory in SMM
> mode from SMM page table. This will cause incorrect memory manipulations,
> like fail the releasing of Guard pages if Heap Guard is enabled.
> 
> The solution in this patch is to store the DXE page table information
> (e.g. value of CR0, CR3 registers, etc.) in a global variable of CpuDxe
> driver. If CpuDxe detects it's in SMM mode, it will use this global
> variable to access page table instead of current processor registers.
> This can avoid retrieving wrong DXE memory paging attributes and changing
> SMM page table attributes unexpectedly.
> 

Are there any security implications having SMM depend on attacker controlled data (DXE page tables)?

Thanks,

Andrew Fish

> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Ruiyu Ni <ruiyu.ni@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
> ---
> UefiCpuPkg/CpuDxe/CpuDxe.inf     |   1 +
> UefiCpuPkg/CpuDxe/CpuPageTable.c | 159 ++++++++++++++++++++++++++++++---------
> 2 files changed, 123 insertions(+), 37 deletions(-)
> 
> diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> index 3c938cee53..ce2bd3627c 100644
> --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
> +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> @@ -66,6 +66,7 @@
> [Protocols]
>   gEfiCpuArchProtocolGuid                       ## PRODUCES
>   gEfiMpServiceProtocolGuid                     ## PRODUCES
> +  gEfiSmmBase2ProtocolGuid                      ## CONSUMES
> 
> [Guids]
>   gIdleLoopEventGuid                            ## CONSUMES           ## Event
> diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> index e2595b4d89..b7e75922b6 100644
> --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> @@ -23,10 +23,21 @@
> #include <Library/DebugLib.h>
> #include <Library/UefiBootServicesTableLib.h>
> #include <Protocol/MpService.h>
> +#include <Protocol/SmmBase2.h>
> +#include <Register/Cpuid.h>
> +#include <Register/Msr.h>
> 
> #include "CpuDxe.h"
> #include "CpuPageTable.h"
> 
> +///
> +/// Paging registers
> +///
> +#define CR0_WP                      BIT16
> +#define CR0_PG                      BIT31
> +#define CR4_PSE                     BIT4
> +#define CR4_PAE                     BIT5
> +
> ///
> /// Page Table Entry
> ///
> @@ -87,7 +98,46 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
>   {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
> };
> 
> -PAGE_TABLE_POOL   *mPageTablePool = NULL;
> +PAGE_TABLE_POOL                   *mPageTablePool = NULL;
> +PAGE_TABLE_LIB_PAGING_CONTEXT     mPagingContext;
> +EFI_SMM_BASE2_PROTOCOL            *mSmmBase2 = NULL;
> +
> +/**
> + Check if current execution environment is in SMM mode or not, via
> + EFI_SMM_BASE2_PROTOCOL.
> +
> + This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib
> + supports to free memory outside SMRAM. The library will call gBS->FreePool() or
> + gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change
> + memory paging attributes during free operation, if some memory related features
> + are enabled (like Heap Guard).
> +
> + This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This
> + will cause incorrect result because SMM mode always loads its own page tables,
> + which are usually different from DXE. This function can be used to detect such
> + situation and help to avoid further misoperations.
> +
> +  @retval TRUE    In SMM mode.
> +  @retval FALSE   Not in SMM mode.
> +**/
> +BOOLEAN
> +IsInSmm (
> +  VOID
> +  )
> +{
> +  BOOLEAN                 InSmm;
> +
> +  InSmm = FALSE;
> +  if (mSmmBase2 == NULL) {
> +    gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2);
> +  }
> +
> +  if (mSmmBase2 != NULL) {
> +    mSmmBase2->InSmm (mSmmBase2, &InSmm);
> +  }
> +
> +  return InSmm;
> +}
> 
> /**
>   Return current paging context.
> @@ -99,45 +149,61 @@ GetCurrentPagingContext (
>   IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext
>   )
> {
> -  UINT32                         RegEax;
> -  UINT32                         RegEdx;
> +  UINT32                          RegEax;
> +  CPUID_EXTENDED_CPU_SIG_EDX      RegEdx;
> +  MSR_IA32_EFER_REGISTER          MsrEfer;
> 
> -  ZeroMem(PagingContext, sizeof(*PagingContext));
> -  if (sizeof(UINTN) == sizeof(UINT64)) {
> -    PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;
> -  } else {
> -    PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;
> -  }
> -  if ((AsmReadCr0 () & BIT31) != 0) {
> -    PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
> -  } else {
> -    PagingContext->ContextData.X64.PageTableBase = 0;
> -  }
> +  //
> +  // Don't retrieve current paging context from processor if in SMM mode.
> +  //
> +  if (!IsInSmm ()) {
> +    ZeroMem (&mPagingContext, sizeof(mPagingContext));
> 
> -  if ((AsmReadCr4 () & BIT4) != 0) {
> -    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> -  }
> -  if ((AsmReadCr4 () & BIT5) != 0) {
> -    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> -  }
> -  if ((AsmReadCr0 () & BIT16) != 0) {
> -    PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> -  }
> +    if (sizeof(UINTN) == sizeof(UINT64)) {
> +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;
> +    } else {
> +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;
> +    }
> +    if ((AsmReadCr0 () & CR0_PG) != 0) {
> +      mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
> +    } else {
> +      mPagingContext.ContextData.X64.PageTableBase = 0;
> +    }
> 
> -  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> -  if (RegEax > 0x80000000) {
> -    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> -    if ((RegEdx & BIT20) != 0) {
> -      // XD supported
> -      if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {
> -        // XD activated
> -        PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> -      }
> +    if ((AsmReadCr4 () & CR4_PSE) != 0) {
> +      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> +    }
> +    if ((AsmReadCr4 () & CR4_PAE) != 0) {
> +      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> +    }
> +    if ((AsmReadCr0 () & CR0_WP) != 0) {
> +      mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
>     }
> -    if ((RegEdx & BIT26) != 0) {
> -      PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
> +
> +    AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
> +    if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
> +      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, (UINT32 *)&RegEdx);
> +
> +      if (RegEdx.Bits.NX != 0) {
> +        // XD supported
> +        MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);
> +        if (MsrEfer.Bits.NXE != 0) {
> +          // XD activated
> +          mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> +        }
> +      }
> +
> +      if (RegEdx.Bits.Page1GB != 0) {
> +        mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;
> +      }
>     }
>   }
> +
> +  //
> +  // This can avoid getting SMM paging context if in SMM mode. We cannot assume
> +  // SMM mode shares the same paging context as DXE.
> +  //
> +  CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));
> }
> 
> /**
> @@ -507,7 +573,14 @@ IsReadOnlyPageWriteProtected (
>   VOID
>   )
> {
> -  return ((AsmReadCr0 () & BIT16) != 0);
> +  //
> +  // To avoid unforseen consequences, don't touch paging settings in SMM mode
> +  // in this driver.
> +  //
> +  if (!IsInSmm ()) {
> +    return ((AsmReadCr0 () & CR0_WP) != 0);
> +  }
> +  return FALSE;
> }
> 
> /**
> @@ -518,7 +591,13 @@ DisableReadOnlyPageWriteProtect (
>   VOID
>   )
> {
> -  AsmWriteCr0 (AsmReadCr0() & ~BIT16);
> +  //
> +  // To avoid unforseen consequences, don't touch paging settings in SMM mode
> +  // in this driver.
> +  //
> +  if (!IsInSmm ()) {
> +    AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
> +  }
> }
> 
> /**
> @@ -529,7 +608,13 @@ EnableReadOnlyPageWriteProtect (
>   VOID
>   )
> {
> -  AsmWriteCr0 (AsmReadCr0() | BIT16);
> +  //
> +  // To avoid unforseen consequences, don't touch paging settings in SMM mode
> +  // in this driver.
> +  //
> +  if (!IsInSmm ()) {
> +    AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
> +  }
> }
> 
> /**
> -- 
> 2.16.2.windows.1
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  2018-06-13 15:14   ` Andrew Fish
@ 2018-06-13 19:54     ` Laszlo Ersek
  0 siblings, 0 replies; 9+ messages in thread
From: Laszlo Ersek @ 2018-06-13 19:54 UTC (permalink / raw)
  To: Andrew Fish, Jian J Wang; +Cc: edk2-devel, Ruiyu Ni, Jiewen Yao, Eric Dong

On 06/13/18 17:14, Andrew Fish wrote:
> 
> 
>> On Jun 12, 2018, at 10:35 PM, Jian J Wang <jian.j.wang@intel.com> wrote:
>>
>>> v2:
>>>  a. add more specific explanations in commit message
>>>  b. add more comments in code
>>>  c. remove redundant logic in IsInSmm()
>>>  d. fix a logic hole in GetCurrentPagingContext()
>>>  e. replace meanless constant macro with meaning ones
>>
>> The MdePkg/Library/SmmMemoryAllocationLib, used only by DXE_SMM_DRIVER,
>> allows to free memory allocated in DXE (before EndOfDxe). This is done
>> by checking the memory range and calling gBS services to do real
>> operation if the memory to free is out of SMRAM. If some memory related
>> features, like Heap Guard, are enabled, gBS interface will turn to
>> EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes(), provided by
>> DXE driver UefiCpuPkg/CpuDxe, to change memory paging attributes. This
>> means we have part of DXE code running in SMM mode in certain
>> circumstances.
>>
>> Because page table in SMM mode is different from DXE mode and CpuDxe
>> always uses current registers (CR0, CR3, etc.) to get memory paging
>> attributes, it cannot get the correct attributes of DXE memory in SMM
>> mode from SMM page table. This will cause incorrect memory manipulations,
>> like fail the releasing of Guard pages if Heap Guard is enabled.
>>
>> The solution in this patch is to store the DXE page table information
>> (e.g. value of CR0, CR3 registers, etc.) in a global variable of CpuDxe
>> driver. If CpuDxe detects it's in SMM mode, it will use this global
>> variable to access page table instead of current processor registers.
>> This can avoid retrieving wrong DXE memory paging attributes and changing
>> SMM page table attributes unexpectedly.
>>
> 
> Are there any security implications having SMM depend on attacker controlled data (DXE page tables)?

This thought crossed my mind as well, and a part of the questions that I
raised earlier are related.

The important thing to consider here is the call chain; in other words,
the circumstances under which we can reach CpuDxe code *at all* while
the processor is in SMM.

The call chain starts when an SMM driver (a traditional SMM driver I
reckon) calls FreePool() or FreePages(), and the SmmMemoryAllocationLib
instance determines that the SMM driver is freeing *DXE* pool (or pages)
memory, and not SMRAM. In this case, SmmMemoryAllocationLib forwards the
call out to gBS->FreePool() or gBS->FreePages(). And, it is *from there*
that we gradually reach CpuDxe (the provider of the CpuArch protocol) to
mess with DXE page table entries -- because the gBS->FreePool() and
gBS->FreePages() services do that in edk2, regardless of SMM, when the
heap guard is enabled.

Obviously, gBS points into attacker controlled memory, so as soon as
SmmMemoryAllocationLib calls gBS->anything, the calling SMM driver has
been compromised immediately, way before we reach CpuDxe or anything
else in DXE. So why isn't SmmMemoryAllocationLib already broken?

Because, I think, a traditional SMM driver *must not* call FreePool()
and FreePages(), for potentially releasing DXE memory, outside its entry
point function anyway. That is, such FreePool / FreePages calls are
permitted only under the exact same circumstances where the driver is
allowed to utilize any other DXE protocols and services anyway; despite
potentially executing in SMM.

Once all DXE and SMM drivers have been dispatched and we reach BDS,
SMRAM is locked down (by platform BDS), and SMM drivers aren't allowed
to touch *anything* DXE any longer.

In other words, it is safe to rely on CpuDxe global variables in SMM
because we only do that when calling gBS->anything is still safe as well
-- that is, before any 3rd party code gets a chance to run.

In my previous review at

http://mid.mail-archive.com/aada511c-bdb9-d833-caa5-bee56cc47d27@redhat.com

this topic was the subject of my question (9a):

> So basically what I'd like to see here is evidence that
>
> (9a) the IsInSmm() helper function is never called outside of the
>      entry point of an MM driver, due to the restrictions on
>      gBS->LocateProtocol() and mSmmBase2->InSmm()

I didn't get a clear answer to that, but I managed to convince myself,
with the above argument. If any of the new code added to CpuDxe is
reached in SMM *after* 3rd party code was launched, then at least one
SMM driver messed up (calling FreePool / FreePages on DXE memory at the
wrong time), and then the vulnerability exists in that driver without
the CpuDxe changes already.

Thus, I believe this series does not introduce a vulnerability or new
risks, it just adds more logic to the end of a call chain that is *already*
- either broken (if an SMM driver calls FreePool / FreePages on DXE
  memory at the wrong time),
- or safe (if all SMM drivers call FreePool / FreePages on DXE memory
  only in their entry point functions).


More generally, to be honest: I find it terribly risky that
SmmMemoryAllocationLib contains convenience code like this. In my
opinion, SmmMemoryAllocationLib should never ever silently call out to
gBS->anything; if it determines that the memory to release is outside of
SMRAM, it should call CpuDeadLoop(). SMM drivers that want to release
DXE memory should always call gBS services directly, not through
MemoryAllocationLib APIs. Such a clear distinction would have also made
the verification of this patch a lot easier -- the argument would have
started with, "CpuDxe code is reached in SMM when SMM drivers call
gBS->FreeXxx()". And the restrictions on gBS->FreeXxx() (namely, that
they are restricted to the entry point function) make for a simpler
argument.

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  2018-06-13 15:10   ` Laszlo Ersek
@ 2018-06-14  0:46     ` Wang, Jian J
  2018-06-14  2:01       ` Dong, Eric
  0 siblings, 1 reply; 9+ messages in thread
From: Wang, Jian J @ 2018-06-14  0:46 UTC (permalink / raw)
  To: Laszlo Ersek, edk2-devel@lists.01.org; +Cc: Ni, Ruiyu, Yao, Jiewen, Dong, Eric

Hi Laszlo,

Regards,
Jian

> -----Original Message-----
> From: Laszlo Ersek [mailto:lersek@redhat.com]
> Sent: Wednesday, June 13, 2018 11:10 PM
> To: Wang, Jian J <jian.j.wang@intel.com>; edk2-devel@lists.01.org
> Cc: Ni, Ruiyu <ruiyu.ni@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>; Dong,
> Eric <eric.dong@intel.com>
> Subject: Re: [edk2] [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE)
> page table in SMM mode
> 
> Hi Jian,
> 
> I have three comments:
> 
> On 06/13/18 07:35, Jian J Wang wrote:
> >> v2:
> >>   a. add more specific explanations in commit message
> >>   b. add more comments in code
> >>   c. remove redundant logic in IsInSmm()
> >>   d. fix a logic hole in GetCurrentPagingContext()
> >>   e. replace meanless constant macro with meaning ones
> >
> > The MdePkg/Library/SmmMemoryAllocationLib, used only by
> DXE_SMM_DRIVER,
> > allows to free memory allocated in DXE (before EndOfDxe). This is done
> > by checking the memory range and calling gBS services to do real
> > operation if the memory to free is out of SMRAM. If some memory related
> > features, like Heap Guard, are enabled, gBS interface will turn to
> > EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes(), provided by
> > DXE driver UefiCpuPkg/CpuDxe, to change memory paging attributes. This
> > means we have part of DXE code running in SMM mode in certain
> > circumstances.
> >
> > Because page table in SMM mode is different from DXE mode and CpuDxe
> > always uses current registers (CR0, CR3, etc.) to get memory paging
> > attributes, it cannot get the correct attributes of DXE memory in SMM
> > mode from SMM page table. This will cause incorrect memory manipulations,
> > like fail the releasing of Guard pages if Heap Guard is enabled.
> >
> > The solution in this patch is to store the DXE page table information
> > (e.g. value of CR0, CR3 registers, etc.) in a global variable of CpuDxe
> > driver. If CpuDxe detects it's in SMM mode, it will use this global
> > variable to access page table instead of current processor registers.
> > This can avoid retrieving wrong DXE memory paging attributes and changing
> > SMM page table attributes unexpectedly.
> >
> > Cc: Eric Dong <eric.dong@intel.com>
> > Cc: Laszlo Ersek <lersek@redhat.com>
> > Cc: Jiewen Yao <jiewen.yao@intel.com>
> > Cc: Ruiyu Ni <ruiyu.ni@intel.com>
> > Contributed-under: TianoCore Contribution Agreement 1.1
> > Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
> > ---
> >  UefiCpuPkg/CpuDxe/CpuDxe.inf     |   1 +
> >  UefiCpuPkg/CpuDxe/CpuPageTable.c | 159
> ++++++++++++++++++++++++++++++---------
> >  2 files changed, 123 insertions(+), 37 deletions(-)
> >
> > diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> > index 3c938cee53..ce2bd3627c 100644
> > --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
> > +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> > @@ -66,6 +66,7 @@
> >  [Protocols]
> >    gEfiCpuArchProtocolGuid                       ## PRODUCES
> >    gEfiMpServiceProtocolGuid                     ## PRODUCES
> > +  gEfiSmmBase2ProtocolGuid                      ## CONSUMES
> 
> (1) In my v1 review, I suggested "SOMETIMES_CONSUMES" for this hint.
> 
> And that's because CpuDxe can be included in platform builds that don't
> include the SMM driver stack at all; in that case,
> gEfiSmmBase2ProtocolGuid will not be available and will not be consumed.
> In other words, we cannot say that the protocol is always consumed.
> 

Make sense.

> >
> >  [Guids]
> >    gIdleLoopEventGuid                            ## CONSUMES           ## Event
> > diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > index e2595b4d89..b7e75922b6 100644
> > --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > @@ -23,10 +23,21 @@
> >  #include <Library/DebugLib.h>
> >  #include <Library/UefiBootServicesTableLib.h>
> >  #include <Protocol/MpService.h>
> > +#include <Protocol/SmmBase2.h>
> > +#include <Register/Cpuid.h>
> > +#include <Register/Msr.h>
> >
> >  #include "CpuDxe.h"
> >  #include "CpuPageTable.h"
> >
> > +///
> > +/// Paging registers
> > +///
> > +#define CR0_WP                      BIT16
> > +#define CR0_PG                      BIT31
> > +#define CR4_PSE                     BIT4
> > +#define CR4_PAE                     BIT5
> 
> (2a) I think the BITxx -> CRx_xx macro replacements should have been
> split to a separate patch.
> 

Sure.

> > +
> >  ///
> >  /// Page Table Entry
> >  ///
> > @@ -87,7 +98,46 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
> >    {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},
> >  };
> >
> > -PAGE_TABLE_POOL   *mPageTablePool = NULL;
> > +PAGE_TABLE_POOL                   *mPageTablePool = NULL;
> > +PAGE_TABLE_LIB_PAGING_CONTEXT     mPagingContext;
> > +EFI_SMM_BASE2_PROTOCOL            *mSmmBase2 = NULL;
> > +
> > +/**
> > + Check if current execution environment is in SMM mode or not, via
> > + EFI_SMM_BASE2_PROTOCOL.
> > +
> > + This is necessary because of the fact that
> MdePkg\Library\SmmMemoryAllocationLib
> > + supports to free memory outside SMRAM. The library will call gBS->FreePool()
> or
> > + gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to
> change
> > + memory paging attributes during free operation, if some memory related
> features
> > + are enabled (like Heap Guard).
> > +
> > + This means that SetMemorySpaceAttributes() has chance to run in SMM
> mode. This
> > + will cause incorrect result because SMM mode always loads its own page
> tables,
> > + which are usually different from DXE. This function can be used to detect
> such
> > + situation and help to avoid further misoperations.
> > +
> > +  @retval TRUE    In SMM mode.
> > +  @retval FALSE   Not in SMM mode.
> > +**/
> > +BOOLEAN
> > +IsInSmm (
> > +  VOID
> > +  )
> > +{
> > +  BOOLEAN                 InSmm;
> > +
> > +  InSmm = FALSE;
> > +  if (mSmmBase2 == NULL) {
> > +    gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID
> **)&mSmmBase2);
> > +  }
> > +
> > +  if (mSmmBase2 != NULL) {
> > +    mSmmBase2->InSmm (mSmmBase2, &InSmm);
> > +  }
> > +
> > +  return InSmm;
> > +}
> >
> >  /**
> >    Return current paging context.
> > @@ -99,45 +149,61 @@ GetCurrentPagingContext (
> >    IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext
> >    )
> >  {
> > -  UINT32                         RegEax;
> > -  UINT32                         RegEdx;
> > +  UINT32                          RegEax;
> > +  CPUID_EXTENDED_CPU_SIG_EDX      RegEdx;
> > +  MSR_IA32_EFER_REGISTER          MsrEfer;
> >
> > -  ZeroMem(PagingContext, sizeof(*PagingContext));
> > -  if (sizeof(UINTN) == sizeof(UINT64)) {
> > -    PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;
> > -  } else {
> > -    PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;
> > -  }
> > -  if ((AsmReadCr0 () & BIT31) != 0) {
> > -    PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () &
> PAGING_4K_ADDRESS_MASK_64);
> > -  } else {
> > -    PagingContext->ContextData.X64.PageTableBase = 0;
> > -  }
> > +  //
> > +  // Don't retrieve current paging context from processor if in SMM mode.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    ZeroMem (&mPagingContext, sizeof(mPagingContext));
> >
> > -  if ((AsmReadCr4 () & BIT4) != 0) {
> > -    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> > -  }
> > -  if ((AsmReadCr4 () & BIT5) != 0) {
> > -    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> > -  }
> > -  if ((AsmReadCr0 () & BIT16) != 0) {
> > -    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> > -  }
> > +    if (sizeof(UINTN) == sizeof(UINT64)) {
> > +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;
> > +    } else {
> > +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;
> > +    }
> > +    if ((AsmReadCr0 () & CR0_PG) != 0) {
> > +      mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () &
> PAGING_4K_ADDRESS_MASK_64);
> > +    } else {
> > +      mPagingContext.ContextData.X64.PageTableBase = 0;
> > +    }
> >
> > -  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> > -  if (RegEax > 0x80000000) {
> > -    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> > -    if ((RegEdx & BIT20) != 0) {
> > -      // XD supported
> > -      if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {
> > -        // XD activated
> > -        PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> > -      }
> > +    if ((AsmReadCr4 () & CR4_PSE) != 0) {
> > +      mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> > +    }
> > +    if ((AsmReadCr4 () & CR4_PAE) != 0) {
> > +      mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> > +    }
> > +    if ((AsmReadCr0 () & CR0_WP) != 0) {
> > +      mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> >      }
> > -    if ((RegEdx & BIT26) != 0) {
> > -      PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPO
> RT;
> > +
> > +    AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
> > +    if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
> 
> (2b) Similarly to (2a), this cleanup (which is correct, and welcome)
> should have been split to a separate patch.
> 


Ok.

> > +      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, (UINT32
> *)&RegEdx);
> 
> (3) I think it would be more idiomatic to pass &RegEdx.Uint32 as the
> last argument. However, this method works as well.
> 

Your way is better. At least it saves a type cast.

> 
> I don't think that posting a v3 is necessary just because of these
> remarks. Please fix (1) though, just before you push the patch.
> 
> * For this patch, with (1) updated:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> 
> * For the series:
> 
> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
> 
> For the regression testing, I used OVMF built both with and without SMM.
> Also, for patch #2, please remember that OVMF does not enable the page
> guard feature.
> 

Thank you very much for the test.

> Thanks!
> Laszlo
> 
> 
> > +
> > +      if (RegEdx.Bits.NX != 0) {
> > +        // XD supported
> > +        MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);
> > +        if (MsrEfer.Bits.NXE != 0) {
> > +          // XD activated
> > +          mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> > +        }
> > +      }
> > +
> > +      if (RegEdx.Bits.Page1GB != 0) {
> > +        mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPO
> RT;
> > +      }
> >      }
> >    }
> > +
> > +  //
> > +  // This can avoid getting SMM paging context if in SMM mode. We cannot
> assume
> > +  // SMM mode shares the same paging context as DXE.
> > +  //
> > +  CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));
> >  }
> >
> >  /**
> > @@ -507,7 +573,14 @@ IsReadOnlyPageWriteProtected (
> >    VOID
> >    )
> >  {
> > -  return ((AsmReadCr0 () & BIT16) != 0);
> > +  //
> > +  // To avoid unforseen consequences, don't touch paging settings in SMM
> mode
> > +  // in this driver.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    return ((AsmReadCr0 () & CR0_WP) != 0);
> > +  }
> > +  return FALSE;
> >  }
> >
> >  /**
> > @@ -518,7 +591,13 @@ DisableReadOnlyPageWriteProtect (
> >    VOID
> >    )
> >  {
> > -  AsmWriteCr0 (AsmReadCr0() & ~BIT16);
> > +  //
> > +  // To avoid unforseen consequences, don't touch paging settings in SMM
> mode
> > +  // in this driver.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
> > +  }
> >  }
> >
> >  /**
> > @@ -529,7 +608,13 @@ EnableReadOnlyPageWriteProtect (
> >    VOID
> >    )
> >  {
> > -  AsmWriteCr0 (AsmReadCr0() | BIT16);
> > +  //
> > +  // To avoid unforseen consequences, don't touch paging settings in SMM
> mode
> > +  // in this driver.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
> > +  }
> >  }
> >
> >  /**
> >


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection
  2018-06-13  5:35 ` [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection Jian J Wang
@ 2018-06-14  0:58   ` Zeng, Star
  0 siblings, 0 replies; 9+ messages in thread
From: Zeng, Star @ 2018-06-14  0:58 UTC (permalink / raw)
  To: Wang, Jian J, edk2-devel@lists.01.org
  Cc: Dong, Eric, Yao, Jiewen, Ni, Ruiyu, Zeng, Star

Reviewed-by: Star Zeng <star.zeng@intel.com>

-----Original Message-----
From: Wang, Jian J 
Sent: Wednesday, June 13, 2018 1:35 PM
To: edk2-devel@lists.01.org
Cc: Zeng, Star <star.zeng@intel.com>; Dong, Eric <eric.dong@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>; Ni, Ruiyu <ruiyu.ni@intel.com>
Subject: [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection

CpuDxe driver is updated to be able to access DXE page table in SMM mode, which means Heap Guard can get correct memory paging attributes in what environment. It's not necessary to exclude SMM from detecting Heap Guard feature support.

Cc: Star Zeng <star.zeng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
---
 MdeModulePkg/Core/Dxe/Mem/HeapGuard.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
index 9d765c98f6..447c56bb11 100644
--- a/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
+++ b/MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
@@ -667,21 +667,11 @@ IsMemoryTypeToGuard (  {
   UINT64 TestBit;
   UINT64 ConfigBit;
-  BOOLEAN     InSmm;
 
   if (AllocateType == AllocateAddress) {
     return FALSE;
   }
 
-  InSmm = FALSE;
-  if (gSmmBase2 != NULL) {
-    gSmmBase2->InSmm (gSmmBase2, &InSmm);
-  }
-
-  if (InSmm) {
-    return FALSE;
-  }
-
   if ((PcdGet8 (PcdHeapGuardPropertyMask) & PageOrPool) == 0) {
     return FALSE;
   }
--
2.16.2.windows.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode
  2018-06-14  0:46     ` Wang, Jian J
@ 2018-06-14  2:01       ` Dong, Eric
  0 siblings, 0 replies; 9+ messages in thread
From: Dong, Eric @ 2018-06-14  2:01 UTC (permalink / raw)
  To: Wang, Jian J, Laszlo Ersek, edk2-devel@lists.01.org
  Cc: Ni, Ruiyu, Yao, Jiewen

Reviewed-by: Eric Dong <eric.dong@intel.com>

-----Original Message-----
From: Wang, Jian J 
Sent: Thursday, June 14, 2018 8:47 AM
To: Laszlo Ersek <lersek@redhat.com>; edk2-devel@lists.01.org
Cc: Ni, Ruiyu <ruiyu.ni@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>; Dong, Eric <eric.dong@intel.com>
Subject: RE: [edk2] [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table in SMM mode

Hi Laszlo,

Regards,
Jian

> -----Original Message-----
> From: Laszlo Ersek [mailto:lersek@redhat.com]
> Sent: Wednesday, June 13, 2018 11:10 PM
> To: Wang, Jian J <jian.j.wang@intel.com>; edk2-devel@lists.01.org
> Cc: Ni, Ruiyu <ruiyu.ni@intel.com>; Yao, Jiewen 
> <jiewen.yao@intel.com>; Dong, Eric <eric.dong@intel.com>
> Subject: Re: [edk2] [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing 
> (DXE) page table in SMM mode
> 
> Hi Jian,
> 
> I have three comments:
> 
> On 06/13/18 07:35, Jian J Wang wrote:
> >> v2:
> >>   a. add more specific explanations in commit message
> >>   b. add more comments in code
> >>   c. remove redundant logic in IsInSmm()
> >>   d. fix a logic hole in GetCurrentPagingContext()
> >>   e. replace meanless constant macro with meaning ones
> >
> > The MdePkg/Library/SmmMemoryAllocationLib, used only by
> DXE_SMM_DRIVER,
> > allows to free memory allocated in DXE (before EndOfDxe). This is 
> > done by checking the memory range and calling gBS services to do 
> > real operation if the memory to free is out of SMRAM. If some memory 
> > related features, like Heap Guard, are enabled, gBS interface will 
> > turn to EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes(), provided by DXE 
> > driver UefiCpuPkg/CpuDxe, to change memory paging attributes. This 
> > means we have part of DXE code running in SMM mode in certain 
> > circumstances.
> >
> > Because page table in SMM mode is different from DXE mode and CpuDxe 
> > always uses current registers (CR0, CR3, etc.) to get memory paging 
> > attributes, it cannot get the correct attributes of DXE memory in 
> > SMM mode from SMM page table. This will cause incorrect memory 
> > manipulations, like fail the releasing of Guard pages if Heap Guard is enabled.
> >
> > The solution in this patch is to store the DXE page table 
> > information (e.g. value of CR0, CR3 registers, etc.) in a global 
> > variable of CpuDxe driver. If CpuDxe detects it's in SMM mode, it 
> > will use this global variable to access page table instead of current processor registers.
> > This can avoid retrieving wrong DXE memory paging attributes and 
> > changing SMM page table attributes unexpectedly.
> >
> > Cc: Eric Dong <eric.dong@intel.com>
> > Cc: Laszlo Ersek <lersek@redhat.com>
> > Cc: Jiewen Yao <jiewen.yao@intel.com>
> > Cc: Ruiyu Ni <ruiyu.ni@intel.com>
> > Contributed-under: TianoCore Contribution Agreement 1.1
> > Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
> > ---
> >  UefiCpuPkg/CpuDxe/CpuDxe.inf     |   1 +
> >  UefiCpuPkg/CpuDxe/CpuPageTable.c | 159
> ++++++++++++++++++++++++++++++---------
> >  2 files changed, 123 insertions(+), 37 deletions(-)
> >
> > diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf 
> > b/UefiCpuPkg/CpuDxe/CpuDxe.inf index 3c938cee53..ce2bd3627c 100644
> > --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
> > +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> > @@ -66,6 +66,7 @@
> >  [Protocols]
> >    gEfiCpuArchProtocolGuid                       ## PRODUCES
> >    gEfiMpServiceProtocolGuid                     ## PRODUCES
> > +  gEfiSmmBase2ProtocolGuid                      ## CONSUMES
> 
> (1) In my v1 review, I suggested "SOMETIMES_CONSUMES" for this hint.
> 
> And that's because CpuDxe can be included in platform builds that 
> don't include the SMM driver stack at all; in that case, 
> gEfiSmmBase2ProtocolGuid will not be available and will not be consumed.
> In other words, we cannot say that the protocol is always consumed.
> 

Make sense.

> >
> >  [Guids]
> >    gIdleLoopEventGuid                            ## CONSUMES           ## Event
> > diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > index e2595b4d89..b7e75922b6 100644
> > --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > @@ -23,10 +23,21 @@
> >  #include <Library/DebugLib.h>
> >  #include <Library/UefiBootServicesTableLib.h>
> >  #include <Protocol/MpService.h>
> > +#include <Protocol/SmmBase2.h>
> > +#include <Register/Cpuid.h>
> > +#include <Register/Msr.h>
> >
> >  #include "CpuDxe.h"
> >  #include "CpuPageTable.h"
> >
> > +///
> > +/// Paging registers
> > +///
> > +#define CR0_WP                      BIT16
> > +#define CR0_PG                      BIT31
> > +#define CR4_PSE                     BIT4
> > +#define CR4_PAE                     BIT5
> 
> (2a) I think the BITxx -> CRx_xx macro replacements should have been 
> split to a separate patch.
> 

Sure.

> > +
> >  ///
> >  /// Page Table Entry
> >  ///
> > @@ -87,7 +98,46 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
> >    {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},  };
> >
> > -PAGE_TABLE_POOL   *mPageTablePool = NULL;
> > +PAGE_TABLE_POOL                   *mPageTablePool = NULL;
> > +PAGE_TABLE_LIB_PAGING_CONTEXT     mPagingContext;
> > +EFI_SMM_BASE2_PROTOCOL            *mSmmBase2 = NULL;
> > +
> > +/**
> > + Check if current execution environment is in SMM mode or not, via  
> > +EFI_SMM_BASE2_PROTOCOL.
> > +
> > + This is necessary because of the fact that
> MdePkg\Library\SmmMemoryAllocationLib
> > + supports to free memory outside SMRAM. The library will call 
> > + gBS->FreePool()
> or
> > + gBS->FreePages() and then SetMemorySpaceAttributes interface in 
> > + gBS->turn to
> change
> > + memory paging attributes during free operation, if some memory 
> > + related
> features
> > + are enabled (like Heap Guard).
> > +
> > + This means that SetMemorySpaceAttributes() has chance to run in 
> > + SMM
> mode. This
> > + will cause incorrect result because SMM mode always loads its own 
> > + page
> tables,
> > + which are usually different from DXE. This function can be used to 
> > + detect
> such
> > + situation and help to avoid further misoperations.
> > +
> > +  @retval TRUE    In SMM mode.
> > +  @retval FALSE   Not in SMM mode.
> > +**/
> > +BOOLEAN
> > +IsInSmm (
> > +  VOID
> > +  )
> > +{
> > +  BOOLEAN                 InSmm;
> > +
> > +  InSmm = FALSE;
> > +  if (mSmmBase2 == NULL) {
> > +    gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID
> **)&mSmmBase2);
> > +  }
> > +
> > +  if (mSmmBase2 != NULL) {
> > +    mSmmBase2->InSmm (mSmmBase2, &InSmm);  }
> > +
> > +  return InSmm;
> > +}
> >
> >  /**
> >    Return current paging context.
> > @@ -99,45 +149,61 @@ GetCurrentPagingContext (
> >    IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT     *PagingContext
> >    )
> >  {
> > -  UINT32                         RegEax;
> > -  UINT32                         RegEdx;
> > +  UINT32                          RegEax;
> > +  CPUID_EXTENDED_CPU_SIG_EDX      RegEdx;
> > +  MSR_IA32_EFER_REGISTER          MsrEfer;
> >
> > -  ZeroMem(PagingContext, sizeof(*PagingContext));
> > -  if (sizeof(UINTN) == sizeof(UINT64)) {
> > -    PagingContext->MachineType = IMAGE_FILE_MACHINE_X64;
> > -  } else {
> > -    PagingContext->MachineType = IMAGE_FILE_MACHINE_I386;
> > -  }
> > -  if ((AsmReadCr0 () & BIT31) != 0) {
> > -    PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () &
> PAGING_4K_ADDRESS_MASK_64);
> > -  } else {
> > -    PagingContext->ContextData.X64.PageTableBase = 0;
> > -  }
> > +  //
> > +  // Don't retrieve current paging context from processor if in SMM mode.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    ZeroMem (&mPagingContext, sizeof(mPagingContext));
> >
> > -  if ((AsmReadCr4 () & BIT4) != 0) {
> > -    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> > -  }
> > -  if ((AsmReadCr4 () & BIT5) != 0) {
> > -    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> > -  }
> > -  if ((AsmReadCr0 () & BIT16) != 0) {
> > -    PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> > -  }
> > +    if (sizeof(UINTN) == sizeof(UINT64)) {
> > +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;
> > +    } else {
> > +      mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;
> > +    }
> > +    if ((AsmReadCr0 () & CR0_PG) != 0) {
> > +      mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () 
> > + &
> PAGING_4K_ADDRESS_MASK_64);
> > +    } else {
> > +      mPagingContext.ContextData.X64.PageTableBase = 0;
> > +    }
> >
> > -  AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
> > -  if (RegEax > 0x80000000) {
> > -    AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
> > -    if ((RegEdx & BIT20) != 0) {
> > -      // XD supported
> > -      if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) {
> > -        // XD activated
> > -        PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> > -      }
> > +    if ((AsmReadCr4 () & CR4_PSE) != 0) {
> > +      mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;
> > +    }
> > +    if ((AsmReadCr4 () & CR4_PAE) != 0) {
> > +      mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;
> > +    }
> > +    if ((AsmReadCr0 () & CR0_WP) != 0) {
> > +      mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;
> >      }
> > -    if ((RegEdx & BIT26) != 0) {
> > -      PagingContext->ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPO
> RT;
> > +
> > +    AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
> > +    if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
> 
> (2b) Similarly to (2a), this cleanup (which is correct, and welcome) 
> should have been split to a separate patch.
> 


Ok.

> > +      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, (UINT32
> *)&RegEdx);
> 
> (3) I think it would be more idiomatic to pass &RegEdx.Uint32 as the 
> last argument. However, this method works as well.
> 

Your way is better. At least it saves a type cast.

> 
> I don't think that posting a v3 is necessary just because of these 
> remarks. Please fix (1) though, just before you push the patch.
> 
> * For this patch, with (1) updated:
> 
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> 
> * For the series:
> 
> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
> 
> For the regression testing, I used OVMF built both with and without SMM.
> Also, for patch #2, please remember that OVMF does not enable the page 
> guard feature.
> 

Thank you very much for the test.

> Thanks!
> Laszlo
> 
> 
> > +
> > +      if (RegEdx.Bits.NX != 0) {
> > +        // XD supported
> > +        MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER);
> > +        if (MsrEfer.Bits.NXE != 0) {
> > +          // XD activated
> > +          mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;
> > +        }
> > +      }
> > +
> > +      if (RegEdx.Bits.Page1GB != 0) {
> > +        mPagingContext.ContextData.Ia32.Attributes |=
> PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPO
> RT;
> > +      }
> >      }
> >    }
> > +
> > +  //
> > +  // This can avoid getting SMM paging context if in SMM mode. We 
> > + cannot
> assume
> > +  // SMM mode shares the same paging context as DXE.
> > +  //
> > +  CopyMem (PagingContext, &mPagingContext, sizeof 
> > + (mPagingContext));
> >  }
> >
> >  /**
> > @@ -507,7 +573,14 @@ IsReadOnlyPageWriteProtected (
> >    VOID
> >    )
> >  {
> > -  return ((AsmReadCr0 () & BIT16) != 0);
> > +  //
> > +  // To avoid unforseen consequences, don't touch paging settings 
> > + in SMM
> mode
> > +  // in this driver.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    return ((AsmReadCr0 () & CR0_WP) != 0);  }  return FALSE;
> >  }
> >
> >  /**
> > @@ -518,7 +591,13 @@ DisableReadOnlyPageWriteProtect (
> >    VOID
> >    )
> >  {
> > -  AsmWriteCr0 (AsmReadCr0() & ~BIT16);
> > +  //
> > +  // To avoid unforseen consequences, don't touch paging settings 
> > + in SMM
> mode
> > +  // in this driver.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);  }
> >  }
> >
> >  /**
> > @@ -529,7 +608,13 @@ EnableReadOnlyPageWriteProtect (
> >    VOID
> >    )
> >  {
> > -  AsmWriteCr0 (AsmReadCr0() | BIT16);
> > +  //
> > +  // To avoid unforseen consequences, don't touch paging settings 
> > + in SMM
> mode
> > +  // in this driver.
> > +  //
> > +  if (!IsInSmm ()) {
> > +    AsmWriteCr0 (AsmReadCr0 () | CR0_WP);  }
> >  }
> >
> >  /**
> >


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2018-06-14  2:02 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-13  5:34 [PATCH v2 0/2] fix DXE memory free issue in SMM mode Jian J Wang
2018-06-13  5:35 ` [PATCH v2 1/2] UefiCpuPkg/CpuDxe: allow accessing (DXE) page table " Jian J Wang
2018-06-13 15:10   ` Laszlo Ersek
2018-06-14  0:46     ` Wang, Jian J
2018-06-14  2:01       ` Dong, Eric
2018-06-13 15:14   ` Andrew Fish
2018-06-13 19:54     ` Laszlo Ersek
2018-06-13  5:35 ` [PATCH v2 2/2] MdeModulePkg/Core: remove SMM check for Heap Guard feature detection Jian J Wang
2018-06-14  0:58   ` Zeng, Star

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox