From f6f72373630d901f331df719a0fb55e8f1143c4f Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Wed, 7 Feb 2024 15:43:10 -0500 Subject: [PATCH 10/17] SecurityPkg: DxeTpmMeasureBootLib: SECURITY PATCH 4118 - CVE 2022-36764 RH-Author: Jon Maloy RH-MergeRequest: 44: edk2: heap buffer overflow in Tcg2MeasureGptTable() RH-Jira: RHEL-21154 RHEL-21156 RH-Acked-by: Laszlo Ersek RH-Commit: [10/13] 5ed702e16f390c79d1abb0ec0b04d886e0094c0b (jmaloy/jons_fork) JIRA: https://issues.redhat.com/browse/RHEL-21156 CVE: CVE-2022-36764 Upstream: Merged Conflicts: We get function definiton clash for the following three functions: - SanitizePeImageEventSize() This is defined both in - SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitazion.c and - SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitazion.c Closer investigation reveals that they are identical in functionality (although not in comment style). I chose to leave them as is now, meaning that this package will be unbuildable until I add a commit renaming these symbols later in this series. commit 0d341c01eeabe0ab5e76693b36e728b8f538a40e Author: Douglas Flick [MSFT] 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 Signed-off-by: Doug Flick [MSFT] Reviewed-by: Jiewen Yao Signed-off-by: Jon Maloy --- .../DxeTpmMeasureBootLib.c | 17 ++-- .../DxeTpmMeasureBootLibSanitization.c | 44 +++++++++ .../DxeTpmMeasureBootLibSanitization.h | 23 +++++ .../DxeTpmMeasureBootLibSanitizationTest.c | 98 +++++++++++++++++-- 4 files changed, 170 insertions(+), 12 deletions(-) diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c index d44422dee8..1598015176 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.
SPDX-License-Identifier: BSD-2-Clause-Patent +Copyright (c) Microsoft Corporation.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -338,19 +339,23 @@ TcgMeasurePeImage ( ImageLoad = NULL; SectionHeader = NULL; Sha1Ctx = NULL; - FilePathSize = (UINT32) GetDevicePathSize (FilePath); + 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; - ImageLoad = (EFI_IMAGE_LOAD_EVENT *) TcgEvent->Event; + TcgEvent->EventSize = EventSize - sizeof (TCG_PCR_EVENT_HDR); + ImageLoad = (EFI_IMAGE_LOAD_EVENT *)TcgEvent->Event; switch (ImageType) { case EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION: diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c index 37cd3ed0ea..bcf8c6de6f 100644 --- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c @@ -240,3 +240,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.
-SPDX-License-Identifier: BSD-2-Clause-Patent + Copyright (c) Microsoft Corporation.
+ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include @@ -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.41.0