]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg/Core/Dxe: limit FwVol encapsulation section recursion
authorLaszlo Ersek <lersek@redhat.com>
Thu, 19 Nov 2020 10:53:40 +0000 (11:53 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Sat, 21 Nov 2020 01:40:53 +0000 (01:40 +0000)
The DXE Core sets up a protocol notify function in its entry point, for
instances of the Firmware Volume Block2 Protocol:

  DxeMain()           [DxeMain/DxeMain.c]
    FwVolDriverInit() [FwVol/FwVol.c]

Assume that a 3rd party UEFI driver or application installs an FVB
instance, with crafted contents. The notification function runs:

  NotifyFwVolBlock() [FwVol/FwVol.c]

installing an instance of the Firmware Volume 2 Protocol on the handle.

(Alternatively, assume that a 3rd party application calls
gDS->ProcessFirmwareVolume(), which may also produce a Firmware Volume 2
Protocol instance.)

The EFI_FIRMWARE_VOLUME2_PROTOCOL.ReadSection() member performs "a
depth-first, left-to-right search algorithm through all sections found in
the specified file" (quoting the PI spec), as follows:

  FvReadFileSection()   [FwVol/FwVolRead.c]
    GetSection()        [SectionExtraction/CoreSectionExtraction.c]
      FindChildNode()   [SectionExtraction/CoreSectionExtraction.c]
        FindChildNode() // recursive call

FindChildNode() is called recursively for encapsulation sections.

Currently this recursion is not limited. Introduce a new PCD
(fixed-at-build, or patchable-in-module), and make FindChildNode() track
the section nesting depth against that PCD.

Cc: Dandan Bi <dandan.bi@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1743
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20201119105340.16225-3-lersek@redhat.com>

MdeModulePkg/Core/Dxe/DxeMain.inf
MdeModulePkg/Core/Dxe/SectionExtraction/CoreSectionExtraction.c
MdeModulePkg/MdeModulePkg.dec
MdeModulePkg/MdeModulePkg.uni

index 1d4b11dc7318c8ea0cab237f16a48523af6374f5..e4bca895773d957a1900f3d7c634c1759654ffee 100644 (file)
   gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType                       ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask                   ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                           ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFwVolDxeMaxEncapsulationDepth           ## CONSUMES\r
 \r
 # [Hob]\r
 # RESOURCE_DESCRIPTOR   ## CONSUMES\r
index d7f7ef427422cb7044fc775dac4eec5bd7341e1a..908617d1ca5c37e0caf7fb59eb05534f32191fed 100644 (file)
@@ -955,6 +955,9 @@ CreateChildNode (
                                  This is an in/out parameter and it is 1-based,\r
                                  to deal with recursions.\r
   @param  SectionDefinitionGuid  Guid of section definition\r
+  @param  Depth                  Nesting depth of encapsulation sections.\r
+                                 Callers different from FindChildNode() are\r
+                                 responsible for passing in a zero Depth.\r
   @param  FoundChild             Output indicating the child node that is found.\r
   @param  FoundStream            Output indicating which section stream the child\r
                                  was found in.  If this stream was generated as a\r
@@ -968,6 +971,9 @@ CreateChildNode (
   @retval EFI_NOT_FOUND          Requested child node does not exist.\r
   @retval EFI_PROTOCOL_ERROR     a required GUIDED section extraction protocol\r
                                  does not exist\r
+  @retval EFI_ABORTED            Recursion aborted because Depth has been\r
+                                 greater than or equal to\r
+                                 PcdFwVolDxeMaxEncapsulationDepth.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -976,6 +982,7 @@ FindChildNode (
   IN     EFI_SECTION_TYPE                           SearchType,\r
   IN OUT UINTN                                      *SectionInstance,\r
   IN     EFI_GUID                                   *SectionDefinitionGuid,\r
+  IN     UINT32                                     Depth,\r
   OUT    CORE_SECTION_CHILD_NODE                    **FoundChild,\r
   OUT    CORE_SECTION_STREAM_NODE                   **FoundStream,\r
   OUT    UINT32                                     *AuthenticationStatus\r
@@ -990,6 +997,10 @@ FindChildNode (
 \r
   ASSERT (*SectionInstance > 0);\r
 \r
+  if (Depth >= PcdGet32 (PcdFwVolDxeMaxEncapsulationDepth)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
   CurrentChildNode = NULL;\r
   ErrorStatus = EFI_NOT_FOUND;\r
 \r
@@ -1053,6 +1064,7 @@ FindChildNode (
                 SearchType,\r
                 SectionInstance,\r
                 SectionDefinitionGuid,\r
+                Depth + 1,\r
                 &RecursedChildNode,\r
                 &RecursedFoundStream,\r
                 AuthenticationStatus\r
@@ -1067,9 +1079,17 @@ FindChildNode (
         *FoundStream = RecursedFoundStream;\r
         return EFI_SUCCESS;\r
       } else {\r
+        if (Status == EFI_ABORTED) {\r
+          //\r
+          // If the recursive call was aborted due to nesting depth, stop\r
+          // looking for the requested child node. The skipped subtree could\r
+          // throw off the instance counting.\r
+          //\r
+          return Status;\r
+        }\r
         //\r
-        // If the status is not EFI_SUCCESS, just save the error code and\r
-        // continue to find the request child node in the rest stream.\r
+        // Save the error code and continue to find the requested child node in\r
+        // the rest of the stream.\r
         //\r
         ErrorStatus = Status;\r
       }\r
@@ -1272,11 +1292,20 @@ GetSection (
                *SectionType,\r
                &Instance,\r
                SectionDefinitionGuid,\r
+               0,                             // encapsulation depth\r
                &ChildNode,\r
                &ChildStreamNode,\r
                &ExtractedAuthenticationStatus\r
                );\r
     if (EFI_ERROR (Status)) {\r
+      if (Status == EFI_ABORTED) {\r
+        DEBUG ((DEBUG_ERROR, "%a: recursion aborted due to nesting depth\n",\r
+          __FUNCTION__));\r
+        //\r
+        // Map "aborted" to "not found".\r
+        //\r
+        Status = EFI_NOT_FOUND;\r
+      }\r
       goto GetSection_Done;\r
     }\r
 \r
index 00075528198d08d47b058e4515f77acdee9be888..9b52b34494433757ba0ef1ff9a065d8cf4ae6742 100644 (file)
   # @Prompt Enable Capsule On Disk support.\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleOnDiskSupport|FALSE|BOOLEAN|0x0000002d\r
 \r
+  ## Maximum permitted encapsulation levels of sections in a firmware volume,\r
+  #  in the DXE phase. Minimum value is 1. Sections nested more deeply are\r
+  #  rejected.\r
+  # @Prompt Maximum permitted FwVol section nesting depth (exclusive).\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFwVolDxeMaxEncapsulationDepth|0x10|UINT32|0x00000030\r
+\r
 [PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]\r
   ## This PCD defines the Console output row. The default value is 25 according to UEFI spec.\r
   #  This PCD could be set to 0 then console output would be at max column and max row.\r
index 40884c57a46059761e73620a1539faa3bfe2c0dd..1b347a75f68400c1058640fd6574a43947a6614f 100644 (file)
                                                                                            "Note:<BR>"\r
                                                                                            "If Both Capsule In Ram and Capsule On Disk are provisioned at the same time, the Capsule On Disk will be bypassed."\r
 \r
+#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdFwVolDxeMaxEncapsulationDepth_PROMPT #language en-US "Maximum permitted FwVol section nesting depth (exclusive)."\r
+\r
+#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdFwVolDxeMaxEncapsulationDepth_HELP   #language en-US "Maximum permitted encapsulation levels of sections in a firmware volume,<BR>"\r
+                                                                                                   "in the DXE phase. Minimum value is 1. Sections nested more deeply are<BR>"\r
+                                                                                                   "rejected."\r
+\r
 #string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdCapsuleInRamSupport_PROMPT  #language en-US "Enable Capsule In Ram support"\r
 \r
 #string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdCapsuleInRamSupport_HELP  #language en-US   "Capsule In Ram is to use memory to deliver the capsules that will be processed after system reset.<BR><BR>"\r