]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/Dxe/Mem/HeapGuard.c
MdeModulePkg/Core: allow HeapGuard even before CpuArchProtocol installed
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Mem / HeapGuard.c
index 30a73fc04d15ffbfc64a4395a622845c524d4fb6..f6068c459ca75385c242da9639146a67508c5bba 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   UEFI Heap Guard functions.\r
 \r
-Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017-2018, Intel Corporation. All rights reserved.<BR>\r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions of the BSD License\r
 which accompanies this distribution.  The full text of the license may be found at\r
@@ -225,8 +225,8 @@ FindGuardedMemoryMap (
   //\r
   // Adjust current map table depth according to the address to access\r
   //\r
-  while (mMapLevel < GUARDED_HEAP_MAP_TABLE_DEPTH\r
-         &&\r
+  while (AllocMapUnit &&\r
+         mMapLevel < GUARDED_HEAP_MAP_TABLE_DEPTH &&\r
          RShiftU64 (\r
            Address,\r
            mLevelShift[GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel - 1]\r
@@ -576,6 +576,10 @@ SetGuardPage (
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress\r
   )\r
 {\r
+  if (gCpu == NULL) {\r
+    return;\r
+  }\r
+\r
   //\r
   // Set flag to make sure allocating memory without GUARD for page table\r
   // operation; otherwise infinite loops could be caused.\r
@@ -583,8 +587,7 @@ SetGuardPage (
   mOnGuarding = TRUE;\r
   //\r
   // Note: This might overwrite other attributes needed by other features,\r
-  // such as memory protection (NX). Please make sure they are not enabled\r
-  // at the same time.\r
+  // such as NX memory protection.\r
   //\r
   gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, EFI_MEMORY_RP);\r
   mOnGuarding = FALSE;\r
@@ -605,6 +608,22 @@ UnsetGuardPage (
   IN  EFI_PHYSICAL_ADDRESS      BaseAddress\r
   )\r
 {\r
+  UINT64          Attributes;\r
+\r
+  if (gCpu == NULL) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Once the Guard page is unset, it will be freed back to memory pool. NX\r
+  // memory protection must be restored for this page if NX is enabled for free\r
+  // memory.\r
+  //\r
+  Attributes = 0;\r
+  if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & (1 << EfiConventionalMemory)) != 0) {\r
+    Attributes |= EFI_MEMORY_XP;\r
+  }\r
+\r
   //\r
   // Set flag to make sure allocating memory without GUARD for page table\r
   // operation; otherwise infinite loops could be caused.\r
@@ -615,7 +634,7 @@ UnsetGuardPage (
   // such as memory protection (NX). Please make sure they are not enabled\r
   // at the same time.\r
   //\r
-  gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, 0);\r
+  gCpu->SetMemoryAttributes (gCpu, BaseAddress, EFI_PAGE_SIZE, Attributes);\r
   mOnGuarding = FALSE;\r
 }\r
 \r
@@ -641,7 +660,7 @@ IsMemoryTypeToGuard (
   UINT64 ConfigBit;\r
   BOOLEAN     InSmm;\r
 \r
-  if (gCpu == NULL || AllocateType == AllocateAddress) {\r
+  if (AllocateType == AllocateAddress) {\r
     return FALSE;\r
   }\r
 \r
@@ -717,6 +736,20 @@ IsPageTypeToGuard (
   return IsMemoryTypeToGuard (MemoryType, AllocateType, GUARD_HEAP_TYPE_PAGE);\r
 }\r
 \r
+/**\r
+  Check to see if the heap guard is enabled for page and/or pool allocation.\r
+\r
+  @return TRUE/FALSE.\r
+**/\r
+BOOLEAN\r
+IsHeapGuardEnabled (\r
+  VOID\r
+  )\r
+{\r
+  return IsMemoryTypeToGuard (EfiMaxMemoryType, AllocateAnyPages,\r
+                              GUARD_HEAP_TYPE_POOL|GUARD_HEAP_TYPE_PAGE);\r
+}\r
+\r
 /**\r
   Set head Guard and tail Guard for the given memory range.\r
 \r
@@ -768,6 +801,7 @@ UnsetGuardForMemory (
   )\r
 {\r
   EFI_PHYSICAL_ADDRESS  GuardPage;\r
+  UINT64                GuardBitmap;\r
 \r
   if (NumberOfPages == 0) {\r
     return;\r
@@ -776,16 +810,29 @@ UnsetGuardForMemory (
   //\r
   // Head Guard must be one page before, if any.\r
   //\r
+  //          MSB-> 1     0 <-LSB\r
+  //          -------------------\r
+  //  Head Guard -> 0     1 -> Don't free Head Guard  (shared Guard)\r
+  //  Head Guard -> 0     0 -> Free Head Guard either (not shared Guard)\r
+  //                1     X -> Don't free first page  (need a new Guard)\r
+  //                           (it'll be turned into a Guard page later)\r
+  //          -------------------\r
+  //      Start -> -1    -2\r
+  //\r
   GuardPage = Memory - EFI_PAGES_TO_SIZE (1);\r
-  if (IsHeadGuard (GuardPage)) {\r
-    if (!IsMemoryGuarded (GuardPage - EFI_PAGES_TO_SIZE (1))) {\r
+  GuardBitmap = GetGuardedMemoryBits (Memory - EFI_PAGES_TO_SIZE (2), 2);\r
+  if ((GuardBitmap & BIT1) == 0) {\r
+    //\r
+    // Head Guard exists.\r
+    //\r
+    if ((GuardBitmap & BIT0) == 0) {\r
       //\r
       // If the head Guard is not a tail Guard of adjacent memory block,\r
       // unset it.\r
       //\r
       UnsetGuardPage (GuardPage);\r
     }\r
-  } else if (IsMemoryGuarded (GuardPage)) {\r
+  } else {\r
     //\r
     // Pages before memory to free are still in Guard. It's a partial free\r
     // case. Turn first page of memory block to free into a new Guard.\r
@@ -796,16 +843,29 @@ UnsetGuardForMemory (
   //\r
   // Tail Guard must be the page after this memory block to free, if any.\r
   //\r
+  //   MSB-> 1     0 <-LSB\r
+  //  --------------------\r
+  //         1     0 <- Tail Guard -> Don't free Tail Guard  (shared Guard)\r
+  //         0     0 <- Tail Guard -> Free Tail Guard either (not shared Guard)\r
+  //         X     1               -> Don't free last page   (need a new Guard)\r
+  //                                 (it'll be turned into a Guard page later)\r
+  //  --------------------\r
+  //        +1    +0 <- End\r
+  //\r
   GuardPage = Memory + EFI_PAGES_TO_SIZE (NumberOfPages);\r
-  if (IsTailGuard (GuardPage)) {\r
-    if (!IsMemoryGuarded (GuardPage + EFI_PAGES_TO_SIZE (1))) {\r
+  GuardBitmap = GetGuardedMemoryBits (GuardPage, 2);\r
+  if ((GuardBitmap & BIT0) == 0) {\r
+    //\r
+    // Tail Guard exists.\r
+    //\r
+    if ((GuardBitmap & BIT1) == 0) {\r
       //\r
       // If the tail Guard is not a head Guard of adjacent memory block,\r
       // free it; otherwise, keep it.\r
       //\r
       UnsetGuardPage (GuardPage);\r
     }\r
-  } else if (IsMemoryGuarded (GuardPage)) {\r
+  } else {\r
     //\r
     // Pages after memory to free are still in Guard. It's a partial free\r
     // case. We need to keep one page to be a head Guard.\r
@@ -842,12 +902,18 @@ AdjustMemoryS (
 {\r
   UINT64  Target;\r
 \r
-  Target = Start + Size - SizeRequested;\r
-\r
   //\r
-  // At least one more page needed for Guard page.\r
+  // UEFI spec requires that allocated pool must be 8-byte aligned. If it's\r
+  // indicated to put the pool near the Tail Guard, we need extra bytes to\r
+  // make sure alignment of the returned pool address.\r
   //\r
-  if (Size < (SizeRequested + EFI_PAGES_TO_SIZE (1))) {\r
+  if ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) == 0) {\r
+    SizeRequested = ALIGN_VALUE(SizeRequested, 8);\r
+  }\r
+\r
+  Target = Start + Size - SizeRequested;\r
+  ASSERT (Target >= Start);\r
+  if (Target == 0) {\r
     return 0;\r
   }\r
 \r
@@ -895,6 +961,7 @@ AdjustMemoryF (
   EFI_PHYSICAL_ADDRESS  Start;\r
   EFI_PHYSICAL_ADDRESS  MemoryToTest;\r
   UINTN                 PagesToFree;\r
+  UINT64                GuardBitmap;\r
 \r
   if (Memory == NULL || NumberOfPages == NULL || *NumberOfPages == 0) {\r
     return;\r
@@ -906,9 +973,22 @@ AdjustMemoryF (
   //\r
   // Head Guard must be one page before, if any.\r
   //\r
-  MemoryToTest = Start - EFI_PAGES_TO_SIZE (1);\r
-  if (IsHeadGuard (MemoryToTest)) {\r
-    if (!IsMemoryGuarded (MemoryToTest - EFI_PAGES_TO_SIZE (1))) {\r
+  //          MSB-> 1     0 <-LSB\r
+  //          -------------------\r
+  //  Head Guard -> 0     1 -> Don't free Head Guard  (shared Guard)\r
+  //  Head Guard -> 0     0 -> Free Head Guard either (not shared Guard)\r
+  //                1     X -> Don't free first page  (need a new Guard)\r
+  //                           (it'll be turned into a Guard page later)\r
+  //          -------------------\r
+  //      Start -> -1    -2\r
+  //\r
+  MemoryToTest = Start - EFI_PAGES_TO_SIZE (2);\r
+  GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2);\r
+  if ((GuardBitmap & BIT1) == 0) {\r
+    //\r
+    // Head Guard exists.\r
+    //\r
+    if ((GuardBitmap & BIT0) == 0) {\r
       //\r
       // If the head Guard is not a tail Guard of adjacent memory block,\r
       // free it; otherwise, keep it.\r
@@ -916,10 +996,10 @@ AdjustMemoryF (
       Start       -= EFI_PAGES_TO_SIZE (1);\r
       PagesToFree += 1;\r
     }\r
-  } else if (IsMemoryGuarded (MemoryToTest)) {\r
+  } else {\r
     //\r
-    // Pages before memory to free are still in Guard. It's a partial free\r
-    // case. We need to keep one page to be a tail Guard.\r
+    // No Head Guard, and pages before memory to free are still in Guard. It's a\r
+    // partial free case. We need to keep one page to be a tail Guard.\r
     //\r
     Start       += EFI_PAGES_TO_SIZE (1);\r
     PagesToFree -= 1;\r
@@ -928,19 +1008,32 @@ AdjustMemoryF (
   //\r
   // Tail Guard must be the page after this memory block to free, if any.\r
   //\r
+  //   MSB-> 1     0 <-LSB\r
+  //  --------------------\r
+  //         1     0 <- Tail Guard -> Don't free Tail Guard  (shared Guard)\r
+  //         0     0 <- Tail Guard -> Free Tail Guard either (not shared Guard)\r
+  //         X     1               -> Don't free last page   (need a new Guard)\r
+  //                                 (it'll be turned into a Guard page later)\r
+  //  --------------------\r
+  //        +1    +0 <- End\r
+  //\r
   MemoryToTest = Start + EFI_PAGES_TO_SIZE (PagesToFree);\r
-  if (IsTailGuard (MemoryToTest)) {\r
-    if (!IsMemoryGuarded (MemoryToTest + EFI_PAGES_TO_SIZE (1))) {\r
+  GuardBitmap = GetGuardedMemoryBits (MemoryToTest, 2);\r
+  if ((GuardBitmap & BIT0) == 0) {\r
+    //\r
+    // Tail Guard exists.\r
+    //\r
+    if ((GuardBitmap & BIT1) == 0) {\r
       //\r
       // If the tail Guard is not a head Guard of adjacent memory block,\r
       // free it; otherwise, keep it.\r
       //\r
       PagesToFree += 1;\r
     }\r
-  } else if (IsMemoryGuarded (MemoryToTest)) {\r
+  } else if (PagesToFree > 0) {\r
     //\r
-    // Pages after memory to free are still in Guard. It's a partial free\r
-    // case. We need to keep one page to be a head Guard.\r
+    // No Tail Guard, and pages after memory to free are still in Guard. It's a\r
+    // partial free case. We need to keep one page to be a head Guard.\r
     //\r
     PagesToFree -= 1;\r
   }\r
@@ -998,7 +1091,7 @@ AdjustPoolHeadA (
   IN UINTN                   Size\r
   )\r
 {\r
-  if ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0) {\r
+  if (Memory == 0 || (PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0) {\r
     //\r
     // Pool head is put near the head Guard\r
     //\r
@@ -1008,6 +1101,7 @@ AdjustPoolHeadA (
   //\r
   // Pool head is put near the tail Guard\r
   //\r
+  Size = ALIGN_VALUE (Size, 8);\r
   return (VOID *)(UINTN)(Memory + EFI_PAGES_TO_SIZE (NoPages) - Size);\r
 }\r
 \r
@@ -1023,7 +1117,7 @@ AdjustPoolHeadF (
   IN EFI_PHYSICAL_ADDRESS    Memory\r
   )\r
 {\r
-  if ((PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0) {\r
+  if (Memory == 0 || (PcdGet8 (PcdHeapGuardPropertyMask) & BIT7) != 0) {\r
     //\r
     // Pool head is put near the head Guard\r
     //\r
@@ -1052,13 +1146,152 @@ CoreConvertPagesWithGuard (
   IN EFI_MEMORY_TYPE  NewType\r
   )\r
 {\r
+  UINT64  OldStart;\r
+  UINTN   OldPages;\r
+\r
   if (NewType == EfiConventionalMemory) {\r
+    OldStart = Start;\r
+    OldPages = NumberOfPages;\r
+\r
     AdjustMemoryF (&Start, &NumberOfPages);\r
+    //\r
+    // It's safe to unset Guard page inside memory lock because there should\r
+    // be no memory allocation occurred in updating memory page attribute at\r
+    // this point. And unsetting Guard page before free will prevent Guard\r
+    // page just freed back to pool from being allocated right away before\r
+    // marking it usable (from non-present to present).\r
+    //\r
+    UnsetGuardForMemory (OldStart, OldPages);\r
+    if (NumberOfPages == 0) {\r
+      return EFI_SUCCESS;\r
+    }\r
   } else {\r
     AdjustMemoryA (&Start, &NumberOfPages);\r
   }\r
 \r
-  return CoreConvertPages(Start, NumberOfPages, NewType);\r
+  return CoreConvertPages (Start, NumberOfPages, NewType);\r
+}\r
+\r
+/**\r
+  Set all Guard pages which cannot be set before CPU Arch Protocol installed.\r
+**/\r
+VOID\r
+SetAllGuardPages (\r
+  VOID\r
+  )\r
+{\r
+  UINTN     Entries[GUARDED_HEAP_MAP_TABLE_DEPTH];\r
+  UINTN     Shifts[GUARDED_HEAP_MAP_TABLE_DEPTH];\r
+  UINTN     Indices[GUARDED_HEAP_MAP_TABLE_DEPTH];\r
+  UINT64    Tables[GUARDED_HEAP_MAP_TABLE_DEPTH];\r
+  UINT64    Addresses[GUARDED_HEAP_MAP_TABLE_DEPTH];\r
+  UINT64    TableEntry;\r
+  UINT64    Address;\r
+  UINT64    GuardPage;\r
+  INTN      Level;\r
+  UINTN     Index;\r
+  BOOLEAN   OnGuarding;\r
+\r
+  if (mGuardedMemoryMap == 0 ||\r
+      mMapLevel == 0 ||\r
+      mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH) {\r
+    return;\r
+  }\r
+\r
+  CopyMem (Entries, mLevelMask, sizeof (Entries));\r
+  CopyMem (Shifts, mLevelShift, sizeof (Shifts));\r
+\r
+  SetMem (Tables, sizeof(Tables), 0);\r
+  SetMem (Addresses, sizeof(Addresses), 0);\r
+  SetMem (Indices, sizeof(Indices), 0);\r
+\r
+  Level         = GUARDED_HEAP_MAP_TABLE_DEPTH - mMapLevel;\r
+  Tables[Level] = mGuardedMemoryMap;\r
+  Address       = 0;\r
+  OnGuarding    = FALSE;\r
+\r
+  DEBUG_CODE (\r
+    DumpGuardedMemoryBitmap ();\r
+  );\r
+\r
+  while (TRUE) {\r
+    if (Indices[Level] > Entries[Level]) {\r
+      Tables[Level] = 0;\r
+      Level        -= 1;\r
+    } else {\r
+\r
+      TableEntry  = ((UINT64 *)(UINTN)(Tables[Level]))[Indices[Level]];\r
+      Address     = Addresses[Level];\r
+\r
+      if (TableEntry == 0) {\r
+\r
+        OnGuarding = FALSE;\r
+\r
+      } else if (Level < GUARDED_HEAP_MAP_TABLE_DEPTH - 1) {\r
+\r
+        Level            += 1;\r
+        Tables[Level]     = TableEntry;\r
+        Addresses[Level]  = Address;\r
+        Indices[Level]    = 0;\r
+\r
+        continue;\r
+\r
+      } else {\r
+\r
+        Index = 0;\r
+        while (Index < GUARDED_HEAP_MAP_ENTRY_BITS) {\r
+          if ((TableEntry & 1) == 1) {\r
+            if (OnGuarding) {\r
+              GuardPage = 0;\r
+            } else {\r
+              GuardPage = Address - EFI_PAGE_SIZE;\r
+            }\r
+            OnGuarding = TRUE;\r
+          } else {\r
+            if (OnGuarding) {\r
+              GuardPage = Address;\r
+            } else {\r
+              GuardPage = 0;\r
+            }\r
+            OnGuarding = FALSE;\r
+          }\r
+\r
+          if (GuardPage != 0) {\r
+            SetGuardPage (GuardPage);\r
+          }\r
+\r
+          if (TableEntry == 0) {\r
+            break;\r
+          }\r
+\r
+          TableEntry = RShiftU64 (TableEntry, 1);\r
+          Address   += EFI_PAGE_SIZE;\r
+          Index     += 1;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (Level < (GUARDED_HEAP_MAP_TABLE_DEPTH - (INTN)mMapLevel)) {\r
+      break;\r
+    }\r
+\r
+    Indices[Level] += 1;\r
+    Address = (Level == 0) ? 0 : Addresses[Level - 1];\r
+    Addresses[Level] = Address | LShiftU64(Indices[Level], Shifts[Level]);\r
+\r
+  }\r
+}\r
+\r
+/**\r
+  Notify function used to set all Guard pages before CPU Arch Protocol installed.\r
+**/\r
+VOID\r
+HeapGuardCpuArchProtocolNotify (\r
+  VOID\r
+  )\r
+{\r
+  ASSERT (gCpu != NULL);\r
+  SetAllGuardPages ();\r
 }\r
 \r
 /**\r
@@ -1110,7 +1343,9 @@ DumpGuardedMemoryBitmap (
   CHAR8     *Ruler1;\r
   CHAR8     *Ruler2;\r
 \r
-  if (mGuardedMemoryMap == 0) {\r
+  if (mGuardedMemoryMap == 0 ||\r
+      mMapLevel == 0 ||\r
+      mMapLevel > GUARDED_HEAP_MAP_TABLE_DEPTH) {\r
     return;\r
   }\r
 \r