MdeModulePkg/Core/Dxe: limit FwVol encapsulation section recursion

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>
This commit is contained in:
Laszlo Ersek 2020-11-19 11:53:40 +01:00 committed by mergify[bot]
parent b9bdfc7285
commit 47343af304
4 changed files with 44 additions and 2 deletions

View File

@ -185,6 +185,7 @@
gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdFwVolDxeMaxEncapsulationDepth ## CONSUMES
# [Hob] # [Hob]
# RESOURCE_DESCRIPTOR ## CONSUMES # RESOURCE_DESCRIPTOR ## CONSUMES

View File

@ -955,6 +955,9 @@ CreateChildNode (
This is an in/out parameter and it is 1-based, This is an in/out parameter and it is 1-based,
to deal with recursions. to deal with recursions.
@param SectionDefinitionGuid Guid of section definition @param SectionDefinitionGuid Guid of section definition
@param Depth Nesting depth of encapsulation sections.
Callers different from FindChildNode() are
responsible for passing in a zero Depth.
@param FoundChild Output indicating the child node that is found. @param FoundChild Output indicating the child node that is found.
@param FoundStream Output indicating which section stream the child @param FoundStream Output indicating which section stream the child
was found in. If this stream was generated as a was found in. If this stream was generated as a
@ -968,6 +971,9 @@ CreateChildNode (
@retval EFI_NOT_FOUND Requested child node does not exist. @retval EFI_NOT_FOUND Requested child node does not exist.
@retval EFI_PROTOCOL_ERROR a required GUIDED section extraction protocol @retval EFI_PROTOCOL_ERROR a required GUIDED section extraction protocol
does not exist does not exist
@retval EFI_ABORTED Recursion aborted because Depth has been
greater than or equal to
PcdFwVolDxeMaxEncapsulationDepth.
**/ **/
EFI_STATUS EFI_STATUS
@ -976,6 +982,7 @@ FindChildNode (
IN EFI_SECTION_TYPE SearchType, IN EFI_SECTION_TYPE SearchType,
IN OUT UINTN *SectionInstance, IN OUT UINTN *SectionInstance,
IN EFI_GUID *SectionDefinitionGuid, IN EFI_GUID *SectionDefinitionGuid,
IN UINT32 Depth,
OUT CORE_SECTION_CHILD_NODE **FoundChild, OUT CORE_SECTION_CHILD_NODE **FoundChild,
OUT CORE_SECTION_STREAM_NODE **FoundStream, OUT CORE_SECTION_STREAM_NODE **FoundStream,
OUT UINT32 *AuthenticationStatus OUT UINT32 *AuthenticationStatus
@ -990,6 +997,10 @@ FindChildNode (
ASSERT (*SectionInstance > 0); ASSERT (*SectionInstance > 0);
if (Depth >= PcdGet32 (PcdFwVolDxeMaxEncapsulationDepth)) {
return EFI_ABORTED;
}
CurrentChildNode = NULL; CurrentChildNode = NULL;
ErrorStatus = EFI_NOT_FOUND; ErrorStatus = EFI_NOT_FOUND;
@ -1053,6 +1064,7 @@ FindChildNode (
SearchType, SearchType,
SectionInstance, SectionInstance,
SectionDefinitionGuid, SectionDefinitionGuid,
Depth + 1,
&RecursedChildNode, &RecursedChildNode,
&RecursedFoundStream, &RecursedFoundStream,
AuthenticationStatus AuthenticationStatus
@ -1067,9 +1079,17 @@ FindChildNode (
*FoundStream = RecursedFoundStream; *FoundStream = RecursedFoundStream;
return EFI_SUCCESS; return EFI_SUCCESS;
} else { } else {
if (Status == EFI_ABORTED) {
// //
// If the status is not EFI_SUCCESS, just save the error code and // If the recursive call was aborted due to nesting depth, stop
// continue to find the request child node in the rest stream. // looking for the requested child node. The skipped subtree could
// throw off the instance counting.
//
return Status;
}
//
// Save the error code and continue to find the requested child node in
// the rest of the stream.
// //
ErrorStatus = Status; ErrorStatus = Status;
} }
@ -1272,11 +1292,20 @@ GetSection (
*SectionType, *SectionType,
&Instance, &Instance,
SectionDefinitionGuid, SectionDefinitionGuid,
0, // encapsulation depth
&ChildNode, &ChildNode,
&ChildStreamNode, &ChildStreamNode,
&ExtractedAuthenticationStatus &ExtractedAuthenticationStatus
); );
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
if (Status == EFI_ABORTED) {
DEBUG ((DEBUG_ERROR, "%a: recursion aborted due to nesting depth\n",
__FUNCTION__));
//
// Map "aborted" to "not found".
//
Status = EFI_NOT_FOUND;
}
goto GetSection_Done; goto GetSection_Done;
} }

View File

@ -1529,6 +1529,12 @@
# @Prompt Enable Capsule On Disk support. # @Prompt Enable Capsule On Disk support.
gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleOnDiskSupport|FALSE|BOOLEAN|0x0000002d gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleOnDiskSupport|FALSE|BOOLEAN|0x0000002d
## Maximum permitted encapsulation levels of sections in a firmware volume,
# in the DXE phase. Minimum value is 1. Sections nested more deeply are
# rejected.
# @Prompt Maximum permitted FwVol section nesting depth (exclusive).
gEfiMdeModulePkgTokenSpaceGuid.PcdFwVolDxeMaxEncapsulationDepth|0x10|UINT32|0x00000030
[PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx] [PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]
## This PCD defines the Console output row. The default value is 25 according to UEFI spec. ## This PCD defines the Console output row. The default value is 25 according to UEFI spec.
# This PCD could be set to 0 then console output would be at max column and max row. # This PCD could be set to 0 then console output would be at max column and max row.

View File

@ -1160,6 +1160,12 @@
"Note:<BR>" "Note:<BR>"
"If Both Capsule In Ram and Capsule On Disk are provisioned at the same time, the Capsule On Disk will be bypassed." "If Both Capsule In Ram and Capsule On Disk are provisioned at the same time, the Capsule On Disk will be bypassed."
#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdFwVolDxeMaxEncapsulationDepth_PROMPT #language en-US "Maximum permitted FwVol section nesting depth (exclusive)."
#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdFwVolDxeMaxEncapsulationDepth_HELP #language en-US "Maximum permitted encapsulation levels of sections in a firmware volume,<BR>"
"in the DXE phase. Minimum value is 1. Sections nested more deeply are<BR>"
"rejected."
#string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdCapsuleInRamSupport_PROMPT #language en-US "Enable Capsule In Ram support" #string STR_gEfiMdeModulePkgTokenSpaceGuid_PcdCapsuleInRamSupport_PROMPT #language en-US "Enable Capsule In Ram support"
#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>" #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>"