From c5580cd68acf14c9e8660f6ee2842654479089ae Mon Sep 17 00:00:00 2001
From: Jon Maloy <jmaloy@redhat.com>
Date: Wed, 7 Feb 2024 15:43:10 -0500
Subject: [PATCH 2/9] SecurityPkg: DxeTpmMeasureBootLib: SECURITY PATCH 4118 -
 CVE 2022-36764

RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 53: SecurityPkg: DxeTpm2MeasureBootLib: SECURITY PATCH 4118 - CVE 2022-36764
RH-Jira: RHEL-21157
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
RH-Acked-by: Gerd Hoffmann <None>
RH-Commit: [2/5] 3945cfd0838c822a3b2cc4b4e315c39a779a7344

JIRA: https://issues.redhat.com/browse/RHEL-21157
CVE: CVE-2022-36764
Upstream: Merged

commit 0d341c01eeabe0ab5e76693b36e728b8f538a40e
Author: Douglas Flick [MSFT] <doug.edk2@gmail.com>
Date:   Fri Jan 12 02:16:05 2024 +0800

    SecurityPkg: DxeTpmMeasureBootLib: SECURITY PATCH 4118 - CVE 2022-36764

    This commit contains the patch files and tests for DxeTpmMeasureBootLib
    CVE 2022-36764.

    Cc: Jiewen Yao <jiewen.yao@intel.com>

    Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com>
    Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>

Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
 .../DxeTpmMeasureBootLib.c                    | 13 ++-
 .../DxeTpmMeasureBootLibSanitization.c        | 44 +++++++++
 .../DxeTpmMeasureBootLibSanitization.h        | 23 +++++
 .../DxeTpmMeasureBootLibSanitizationTest.c    | 98 +++++++++++++++++--
 4 files changed, 168 insertions(+), 10 deletions(-)

diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c
index 669ab19134..a9fc440a09 100644
--- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c
+++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c
@@ -17,6 +17,7 @@
 
 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
+Copyright (c) Microsoft Corporation.<BR>
 
 Copyright (c) Microsoft Corporation.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -345,18 +346,22 @@ TcgMeasurePeImage (
   ImageLoad     = NULL;
   SectionHeader = NULL;
   Sha1Ctx       = NULL;
+  TcgEvent      = NULL;
   FilePathSize  = (UINT32)GetDevicePathSize (FilePath);
 
-  //
   // Determine destination PCR by BootPolicy
   //
-  EventSize = sizeof (*ImageLoad) - sizeof (ImageLoad->DevicePath) + FilePathSize;
-  TcgEvent  = AllocateZeroPool (EventSize + sizeof (TCG_PCR_EVENT));
+  Status = SanitizePeImageEventSize (FilePathSize, &EventSize);
+  if (EFI_ERROR (Status)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  TcgEvent = AllocateZeroPool (EventSize);
   if (TcgEvent == NULL) {
     return EFI_OUT_OF_RESOURCES;
   }
 
-  TcgEvent->EventSize = EventSize;
+  TcgEvent->EventSize = EventSize - sizeof (TCG_PCR_EVENT_HDR);
   ImageLoad           = (EFI_IMAGE_LOAD_EVENT *)TcgEvent->Event;
 
   switch (ImageType) {
diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c
index a3fa46f5e6..c989851cec 100644
--- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c
+++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c
@@ -239,3 +239,47 @@ SanitizePrimaryHeaderGptEventSize (
 
   return EFI_SUCCESS;
 }
+
+/**
+  This function will validate that the PeImage Event Size from the loaded image is sane
+  It will check the following:
+    - EventSize does not overflow
+
+  @param[in] FilePathSize - Size of the file path.
+  @param[out] EventSize - Pointer to the event size.
+
+  @retval EFI_SUCCESS
+    The event size is valid.
+
+  @retval EFI_OUT_OF_RESOURCES
+    Overflow would have occurred.
+
+  @retval EFI_INVALID_PARAMETER
+    One of the passed parameters was invalid.
+**/
+EFI_STATUS
+SanitizePeImageEventSize (
+  IN  UINT32  FilePathSize,
+  OUT UINT32  *EventSize
+  )
+{
+  EFI_STATUS  Status;
+
+  // Replacing logic:
+  // sizeof (*ImageLoad) - sizeof (ImageLoad->DevicePath) + FilePathSize;
+  Status = SafeUint32Add (OFFSET_OF (EFI_IMAGE_LOAD_EVENT, DevicePath), FilePathSize, EventSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EventSize would overflow!\n"));
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // Replacing logic:
+  // EventSize + sizeof (TCG_PCR_EVENT_HDR)
+  Status = SafeUint32Add (*EventSize, sizeof (TCG_PCR_EVENT_HDR), EventSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EventSize would overflow!\n"));
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h
index 0d9d00c281..2248495813 100644
--- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h
+++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h
@@ -111,4 +111,27 @@ SanitizePrimaryHeaderGptEventSize (
   OUT UINT32                            *EventSize
   );
 
+/**
+  This function will validate that the PeImage Event Size from the loaded image is sane
+  It will check the following:
+    - EventSize does not overflow
+
+  @param[in] FilePathSize - Size of the file path.
+  @param[out] EventSize - Pointer to the event size.
+
+  @retval EFI_SUCCESS
+    The event size is valid.
+
+  @retval EFI_OUT_OF_RESOURCES
+    Overflow would have occurred.
+
+  @retval EFI_INVALID_PARAMETER
+    One of the passed parameters was invalid.
+**/
+EFI_STATUS
+SanitizePeImageEventSize (
+  IN  UINT32  FilePathSize,
+  OUT UINT32  *EventSize
+  );
+
 #endif // DXE_TPM_MEASURE_BOOT_LIB_VALIDATION_
diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c
index eeb928cdb0..c41498be45 100644
--- a/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c
+++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c
@@ -1,8 +1,8 @@
 /** @file
-This file includes the unit test cases for the DxeTpmMeasureBootLibSanitizationTest.c.
+  This file includes the unit test cases for the DxeTpmMeasureBootLibSanitizationTest.c.
 
-Copyright (c) Microsoft Corporation.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
+  Copyright (c) Microsoft Corporation.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
 #include <Uefi.h>
@@ -186,9 +186,6 @@ TestSanitizePrimaryHeaderGptEventSize (
   EFI_STATUS                  Status;
   EFI_PARTITION_TABLE_HEADER  PrimaryHeader;
   UINTN                       NumberOfPartition;
-  EFI_GPT_DATA                *GptData;
-
-  GptData = NULL;
 
   // Test that a normal PrimaryHeader passes validation
   PrimaryHeader.NumberOfPartitionEntries = 5;
@@ -222,6 +219,94 @@ TestSanitizePrimaryHeaderGptEventSize (
   return UNIT_TEST_PASSED;
 }
 
+/**
+  This function tests the SanitizePeImageEventSize function.
+  It's intent is to test that the untrusted input from a file path for an
+  EFI_IMAGE_LOAD_EVENT structure will not cause an overflow when calculating
+  the event size when allocating space.
+
+  @param[in] Context  The unit test context.
+
+  @retval UNIT_TEST_PASSED  The test passed.
+  @retval UNIT_TEST_ERROR_TEST_FAILED  The test failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestSanitizePeImageEventSize (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  UINT32                    EventSize;
+  UINTN                     ExistingLogicEventSize;
+  UINT32                    FilePathSize;
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  DevicePath;
+  EFI_IMAGE_LOAD_EVENT      *ImageLoadEvent;
+  UNIT_TEST_STATUS          TestStatus;
+
+  TestStatus = UNIT_TEST_ERROR_TEST_FAILED;
+
+  // Generate EFI_DEVICE_PATH_PROTOCOL test data
+  DevicePath.Type      = 0;
+  DevicePath.SubType   = 0;
+  DevicePath.Length[0] = 0;
+  DevicePath.Length[1] = 0;
+
+  // Generate EFI_IMAGE_LOAD_EVENT test data
+  ImageLoadEvent = AllocateZeroPool (sizeof (EFI_IMAGE_LOAD_EVENT) + sizeof (EFI_DEVICE_PATH_PROTOCOL));
+  if (ImageLoadEvent == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: AllocateZeroPool failed\n", __func__));
+    goto Exit;
+  }
+
+  // Populate EFI_IMAGE_LOAD_EVENT54 test data
+  ImageLoadEvent->ImageLocationInMemory = (EFI_PHYSICAL_ADDRESS)0x12345678;
+  ImageLoadEvent->ImageLengthInMemory   = 0x1000;
+  ImageLoadEvent->ImageLinkTimeAddress  = (UINTN)ImageLoadEvent;
+  ImageLoadEvent->LengthOfDevicePath    = sizeof (EFI_DEVICE_PATH_PROTOCOL);
+  CopyMem (ImageLoadEvent->DevicePath, &DevicePath, sizeof (EFI_DEVICE_PATH_PROTOCOL));
+
+  FilePathSize = 255;
+
+  // Test that a normal PE image passes validation
+  Status = SanitizePeImageEventSize (FilePathSize, &EventSize);
+  if (EFI_ERROR (Status)) {
+    UT_LOG_ERROR ("SanitizePeImageEventSize failed with %r\n", Status);
+    goto Exit;
+  }
+
+  // Test that the event size is correct compared to the existing logic
+  ExistingLogicEventSize  = OFFSET_OF (EFI_IMAGE_LOAD_EVENT, DevicePath) + FilePathSize;
+  ExistingLogicEventSize += sizeof (TCG_PCR_EVENT_HDR);
+
+  if (EventSize != ExistingLogicEventSize) {
+    UT_LOG_ERROR ("SanitizePeImageEventSize returned an incorrect event size. Expected %u, got %u\n", ExistingLogicEventSize, EventSize);
+    goto Exit;
+  }
+
+  // Test that the event size may not overflow
+  Status = SanitizePeImageEventSize (MAX_UINT32, &EventSize);
+  if (Status != EFI_BAD_BUFFER_SIZE) {
+    UT_LOG_ERROR ("SanitizePeImageEventSize succeded when it was supposed to fail with %r\n", Status);
+    goto Exit;
+  }
+
+  TestStatus = UNIT_TEST_PASSED;
+Exit:
+
+  if (ImageLoadEvent != NULL) {
+    FreePool (ImageLoadEvent);
+  }
+
+  if (TestStatus == UNIT_TEST_ERROR_TEST_FAILED) {
+    DEBUG ((DEBUG_ERROR, "%a: Test failed\n", __func__));
+  } else {
+    DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
+  }
+
+  return TestStatus;
+}
+
 // *--------------------------------------------------------------------*
 // *  Unit Test Code Main Function
 // *--------------------------------------------------------------------*
@@ -265,6 +350,7 @@ UefiTestMain (
   AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests Validating EFI Partition Table", "Common.TcgMeasureBootLibValidation", TestSanitizeEfiPartitionTableHeader, NULL, NULL, NULL);
   AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests Primary header gpt event checks for overflow", "Common.TcgMeasureBootLibValidation", TestSanitizePrimaryHeaderAllocationSize, NULL, NULL, NULL);
   AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests Primary header allocation size checks for overflow", "Common.TcgMeasureBootLibValidation", TestSanitizePrimaryHeaderGptEventSize, NULL, NULL, NULL);
+  AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests PE Image and FileSize checks for overflow", "Common.TcgMeasureBootLibValidation", TestSanitizePeImageEventSize, NULL, NULL, NULL);
 
   Status = RunAllTestSuites (Framework);
 
-- 
2.39.3