new file mode 100644
@@ -0,0 +1,985 @@
+From 224446543206450ddb5830e6abd026d61d3c7f4b Mon Sep 17 00:00:00 2001
+From: "Douglas Flick [MSFT]" <doug.edk2@gmail.com>
+Date: Fri, 12 Jan 2024 02:16:01 +0800
+Subject: [PATCH] SecurityPkg: DxeTpm2MeasureBootLib: SECURITY PATCH 4117 - CVE
+ 2022-36763
+
+This commit contains the patch files and tests for DxeTpm2MeasureBootLib
+CVE 2022-36763.
+
+Cc: Jiewen Yao <jiewen.yao@intel.com>
+
+Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com>
+
+CVE: CVE-2022-36763
+
+Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/224446543206450ddb5830e6abd026d61d3c7f4b]
+
+Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
+---
+ .../DxeTpm2MeasureBootLib.c | 69 ++--
+ .../DxeTpm2MeasureBootLib.inf | 4 +-
+ .../DxeTpm2MeasureBootLibSanitization.c | 275 ++++++++++++++++
+ .../DxeTpm2MeasureBootLibSanitization.h | 113 +++++++
+ .../DxeTpm2MeasureBootLibSanitizationTest.c | 303 ++++++++++++++++++
+ ...Tpm2MeasureBootLibSanitizationTestHost.inf | 28 ++
+ SecurityPkg/SecurityPkg.ci.yaml | 1 +
+ 7 files changed, 763 insertions(+), 30 deletions(-)
+ create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c
+ create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h
+ create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c
+ create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf
+
+diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c
+index 36a256a7af..0475103d6e 100644
+--- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c
++++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c
+@@ -20,6 +20,8 @@ Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
++Copyright (c) Microsoft Corporation.<BR>
++SPDX-License-Identifier: BSD-2-Clause-Patent
+ **/
+
+ #include <PiDxe.h>
+@@ -44,6 +46,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
+ #include <Library/HobLib.h>
+ #include <Protocol/CcMeasurement.h>
+
++#include "DxeTpm2MeasureBootLibSanitization.h"
++
+ typedef struct {
+ EFI_TCG2_PROTOCOL *Tcg2Protocol;
+ EFI_CC_MEASUREMENT_PROTOCOL *CcProtocol;
+@@ -144,10 +148,11 @@ Tcg2MeasureGptTable (
+ EFI_TCG2_EVENT *Tcg2Event;
+ EFI_CC_EVENT *CcEvent;
+ EFI_GPT_DATA *GptData;
+- UINT32 EventSize;
++ UINT32 TcgEventSize;
+ EFI_TCG2_PROTOCOL *Tcg2Protocol;
+ EFI_CC_MEASUREMENT_PROTOCOL *CcProtocol;
+ EFI_CC_MR_INDEX MrIndex;
++ UINT32 AllocSize;
+
+ if (mTcg2MeasureGptCount > 0) {
+ return EFI_SUCCESS;
+@@ -195,25 +200,22 @@ Tcg2MeasureGptTable (
+ BlockIo->Media->BlockSize,
+ (UINT8 *)PrimaryHeader
+ );
+- if (EFI_ERROR (Status)) {
+- DEBUG ((DEBUG_ERROR, "Failed to Read Partition Table Header!\n"));
++ if (EFI_ERROR (Status) || EFI_ERROR (SanitizeEfiPartitionTableHeader (PrimaryHeader, BlockIo))) {
++ DEBUG ((DEBUG_ERROR, "Failed to read Partition Table Header or invalid Partition Table Header!\n"));
+ FreePool (PrimaryHeader);
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+- // PrimaryHeader->SizeOfPartitionEntry should not be zero
++ // Read the partition entry.
+ //
+- if (PrimaryHeader->SizeOfPartitionEntry == 0) {
+- DEBUG ((DEBUG_ERROR, "SizeOfPartitionEntry should not be zero!\n"));
++ Status = SanitizePrimaryHeaderAllocationSize (PrimaryHeader, &AllocSize);
++ if (EFI_ERROR (Status)) {
+ FreePool (PrimaryHeader);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+- //
+- // Read the partition entry.
+- //
+- EntryPtr = (UINT8 *)AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry);
++ EntryPtr = (UINT8 *)AllocatePool (AllocSize);
+ if (EntryPtr == NULL) {
+ FreePool (PrimaryHeader);
+ return EFI_OUT_OF_RESOURCES;
+@@ -223,7 +225,7 @@ Tcg2MeasureGptTable (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockIo->Media->BlockSize),
+- PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry,
++ AllocSize,
+ EntryPtr
+ );
+ if (EFI_ERROR (Status)) {
+@@ -248,16 +250,21 @@ Tcg2MeasureGptTable (
+ //
+ // Prepare Data for Measurement (CcProtocol and Tcg2Protocol)
+ //
+- EventSize = (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions)
+- + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry);
+- EventPtr = (UINT8 *)AllocateZeroPool (EventSize + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event));
++ Status = SanitizePrimaryHeaderGptEventSize (PrimaryHeader, NumberOfPartition, &TcgEventSize);
++ if (EFI_ERROR (Status)) {
++ FreePool (PrimaryHeader);
++ FreePool (EntryPtr);
++ return EFI_DEVICE_ERROR;
++ }
++
++ EventPtr = (UINT8 *)AllocateZeroPool (TcgEventSize);
+ if (EventPtr == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ Tcg2Event = (EFI_TCG2_EVENT *)EventPtr;
+- Tcg2Event->Size = EventSize + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event);
++ Tcg2Event->Size = TcgEventSize;
+ Tcg2Event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER);
+ Tcg2Event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION;
+ Tcg2Event->Header.PCRIndex = 5;
+@@ -310,7 +317,7 @@ Tcg2MeasureGptTable (
+ CcProtocol,
+ 0,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)(VOID *)GptData,
+- (UINT64)EventSize,
++ (UINT64)TcgEventSize - OFFSET_OF (EFI_TCG2_EVENT, Event),
+ CcEvent
+ );
+ if (!EFI_ERROR (Status)) {
+@@ -326,7 +333,7 @@ Tcg2MeasureGptTable (
+ Tcg2Protocol,
+ 0,
+ (EFI_PHYSICAL_ADDRESS)(UINTN)(VOID *)GptData,
+- (UINT64)EventSize,
++ (UINT64)TcgEventSize - OFFSET_OF (EFI_TCG2_EVENT, Event),
+ Tcg2Event
+ );
+ if (!EFI_ERROR (Status)) {
+@@ -443,11 +450,13 @@ Tcg2MeasurePeImage (
+ Tcg2Event->Header.PCRIndex = 2;
+ break;
+ default:
+- DEBUG ((
+- DEBUG_ERROR,
+- "Tcg2MeasurePeImage: Unknown subsystem type %d",
+- ImageType
+- ));
++ DEBUG (
++ (
++ DEBUG_ERROR,
++ "Tcg2MeasurePeImage: Unknown subsystem type %d",
++ ImageType
++ )
++ );
+ goto Finish;
+ }
+
+@@ -515,7 +524,7 @@ Finish:
+
+ @param MeasureBootProtocols Pointer to the located measure boot protocol instances.
+
+- @retval EFI_SUCCESS Sucessfully locate the measure boot protocol instances (at least one instance).
++ @retval EFI_SUCCESS Successfully locate the measure boot protocol instances (at least one instance).
+ @retval EFI_UNSUPPORTED Measure boot is not supported.
+ **/
+ EFI_STATUS
+@@ -646,12 +655,14 @@ DxeTpm2MeasureBootHandler (
+ return EFI_SUCCESS;
+ }
+
+- DEBUG ((
+- DEBUG_INFO,
+- "Tcg2Protocol = %p, CcMeasurementProtocol = %p\n",
+- MeasureBootProtocols.Tcg2Protocol,
+- MeasureBootProtocols.CcProtocol
+- ));
++ DEBUG (
++ (
++ DEBUG_INFO,
++ "Tcg2Protocol = %p, CcMeasurementProtocol = %p\n",
++ MeasureBootProtocols.Tcg2Protocol,
++ MeasureBootProtocols.CcProtocol
++ )
++ );
+
+ //
+ // Copy File Device Path
+diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf
+index 6dca79a20c..28995f438d 100644
+--- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf
++++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf
+@@ -37,6 +37,8 @@
+
+ [Sources]
+ DxeTpm2MeasureBootLib.c
++ DxeTpm2MeasureBootLibSanitization.c
++ DxeTpm2MeasureBootLibSanitization.h
+
+ [Packages]
+ MdePkg/MdePkg.dec
+@@ -46,6 +48,7 @@
+
+ [LibraryClasses]
+ BaseMemoryLib
++ SafeIntLib
+ DebugLib
+ MemoryAllocationLib
+ DevicePathLib
+@@ -65,4 +68,3 @@
+ gEfiFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDiskIoProtocolGuid ## SOMETIMES_CONSUMES
+-
+diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c
+new file mode 100644
+index 0000000000..e2309655d3
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c
+@@ -0,0 +1,275 @@
++/** @file
++ The library instance provides security service of TPM2 measure boot and
++ Confidential Computing (CC) measure boot.
++
++ Caution: This file requires additional review when modified.
++ This library will have external input - PE/COFF image and GPT partition.
++ This external input must be validated carefully to avoid security issue like
++ buffer overflow, integer overflow.
++
++ This file will pull out the validation logic from the following functions, in an
++ attempt to validate the untrusted input in the form of unit tests
++
++ These are those functions:
++
++ DxeTpm2MeasureBootLibImageRead() function will make sure the PE/COFF image content
++ read is within the image buffer.
++
++ Tcg2MeasureGptTable() function will receive untrusted GPT partition table, and parse
++ partition data carefully.
++
++ Copyright (c) Microsoft Corporation.<BR>
++ SPDX-License-Identifier: BSD-2-Clause-Patent
++**/
++#include <Uefi.h>
++#include <Uefi/UefiSpec.h>
++#include <Library/SafeIntLib.h>
++#include <Library/UefiLib.h>
++#include <Library/DebugLib.h>
++#include <Library/BaseLib.h>
++#include <IndustryStandard/UefiTcgPlatform.h>
++#include <Protocol/BlockIo.h>
++#include <Library/MemoryAllocationLib.h>
++
++#include "DxeTpm2MeasureBootLibSanitization.h"
++
++#define GPT_HEADER_REVISION_V1 0x00010000
++
++/**
++ This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse
++ However this function will not attempt to verify the validity of the GPT partition
++ It will check the following:
++ - Signature
++ - Revision
++ - AlternateLBA
++ - FirstUsableLBA
++ - LastUsableLBA
++ - PartitionEntryLBA
++ - NumberOfPartitionEntries
++ - SizeOfPartitionEntry
++ - BlockIo
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[in] BlockIo
++ Pointer to the EFI_BLOCK_IO_PROTOCOL structure.
++
++ @retval EFI_SUCCESS
++ The EFI_PARTITION_TABLE_HEADER structure is valid.
++
++ @retval EFI_INVALID_PARAMETER
++ The EFI_PARTITION_TABLE_HEADER structure is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizeEfiPartitionTableHeader (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo
++ )
++{
++ //
++ // Verify that the input parameters are safe to use
++ //
++ if (PrimaryHeader == NULL) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ if ((BlockIo == NULL) || (BlockIo->Media == NULL)) {
++ DEBUG ((DEBUG_ERROR, "Invalid BlockIo!\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ //
++ // The signature must be EFI_PTAB_HEADER_ID ("EFI PART" in ASCII)
++ //
++ if (PrimaryHeader->Header.Signature != EFI_PTAB_HEADER_ID) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // The version must be GPT_HEADER_REVISION_V1 (0x00010000)
++ //
++ if (PrimaryHeader->Header.Revision != GPT_HEADER_REVISION_V1) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header Revision!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // The HeaderSize must be greater than or equal to 92 and must be less than or equal to the logical block size
++ //
++ if ((PrimaryHeader->Header.HeaderSize < sizeof (EFI_PARTITION_TABLE_HEADER)) || (PrimaryHeader->Header.HeaderSize > BlockIo->Media->BlockSize)) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header HeaderSize!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // The partition entries should all be before the first usable block
++ //
++ if (PrimaryHeader->FirstUsableLBA <= PrimaryHeader->PartitionEntryLBA) {
++ DEBUG ((DEBUG_ERROR, "GPT PartitionEntryLBA is not less than FirstUsableLBA!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // Check that the PartitionEntryLBA greater than the Max LBA
++ // This will be used later for multiplication
++ //
++ if (PrimaryHeader->PartitionEntryLBA > DivU64x32 (MAX_UINT64, BlockIo->Media->BlockSize)) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header PartitionEntryLBA!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // Check that the number of partition entries is greater than zero
++ //
++ if (PrimaryHeader->NumberOfPartitionEntries == 0) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // SizeOfPartitionEntry must be 128, 256, 512... improper size may lead to accessing uninitialized memory
++ //
++ if ((PrimaryHeader->SizeOfPartitionEntry < 128) || ((PrimaryHeader->SizeOfPartitionEntry & (PrimaryHeader->SizeOfPartitionEntry - 1)) != 0)) {
++ DEBUG ((DEBUG_ERROR, "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ //
++ // This check is to prevent overflow when calculating the allocation size for the partition entries
++ // This check will be used later for multiplication
++ //
++ if (PrimaryHeader->NumberOfPartitionEntries > DivU64x32 (MAX_UINT64, PrimaryHeader->SizeOfPartitionEntry)) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ return EFI_SUCCESS;
++}
++
++/**
++ This function will validate that the allocation size from the primary header is sane
++ It will check the following:
++ - AllocationSize does not overflow
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[out] AllocationSize
++ Pointer to the allocation size.
++
++ @retval EFI_SUCCESS
++ The allocation size is valid.
++
++ @retval EFI_OUT_OF_RESOURCES
++ The allocation size is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizePrimaryHeaderAllocationSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ OUT UINT32 *AllocationSize
++ )
++{
++ EFI_STATUS Status;
++
++ if (PrimaryHeader == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ if (AllocationSize == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ //
++ // Replacing logic:
++ // PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry;
++ //
++ Status = SafeUint32Mult (PrimaryHeader->NumberOfPartitionEntries, PrimaryHeader->SizeOfPartitionEntry, AllocationSize);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "Allocation Size would have overflowed!\n"));
++ return EFI_BAD_BUFFER_SIZE;
++ }
++
++ return EFI_SUCCESS;
++}
++
++/**
++ This function will validate that the Gpt Event Size calculated from the primary header is sane
++ It will check the following:
++ - EventSize does not overflow
++
++ Important: This function includes the entire length of the allocated space, including
++ (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)) . When hashing the buffer allocated with this
++ size, the caller must subtract the size of the (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event))
++ from the size of the buffer before hashing.
++
++ @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++ @param[in] NumberOfPartition - Number of partitions.
++ @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
++SanitizePrimaryHeaderGptEventSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN UINTN NumberOfPartition,
++ OUT UINT32 *EventSize
++ )
++{
++ EFI_STATUS Status;
++ UINT32 SafeNumberOfPartitions;
++
++ if (PrimaryHeader == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ if (EventSize == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ //
++ // We shouldn't even attempt to perform the multiplication if the number of partitions is greater than the maximum value of UINT32
++ //
++ Status = SafeUintnToUint32 (NumberOfPartition, &SafeNumberOfPartitions);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "NumberOfPartition would have overflowed!\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ //
++ // Replacing logic:
++ // (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry);
++ //
++ Status = SafeUint32Mult (SafeNumberOfPartitions, PrimaryHeader->SizeOfPartitionEntry, EventSize);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "Event Size would have overflowed!\n"));
++ return EFI_BAD_BUFFER_SIZE;
++ }
++
++ //
++ // Replacing logic:
++ // *EventSize + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event);
++ //
++ Status = SafeUint32Add (
++ OFFSET_OF (EFI_TCG2_EVENT, Event) + OFFSET_OF (EFI_GPT_DATA, Partitions),
++ *EventSize,
++ EventSize
++ );
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "Event Size would have overflowed because of GPTData!\n"));
++ return EFI_BAD_BUFFER_SIZE;
++ }
++
++ return EFI_SUCCESS;
++}
+diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h
+new file mode 100644
+index 0000000000..048b738987
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h
+@@ -0,0 +1,113 @@
++/** @file
++ This file includes the function prototypes for the sanitization functions.
++
++ These are those functions:
++
++ DxeTpm2MeasureBootLibImageRead() function will make sure the PE/COFF image content
++ read is within the image buffer.
++
++ Tcg2MeasureGptTable() function will receive untrusted GPT partition table, and parse
++ partition data carefully.
++
++ Copyright (c) Microsoft Corporation.<BR>
++ SPDX-License-Identifier: BSD-2-Clause-Patent
++
++**/
++
++#ifndef DXE_TPM2_MEASURE_BOOT_LIB_SANITATION_
++#define DXE_TPM2_MEASURE_BOOT_LIB_SANITATION_
++
++#include <Uefi.h>
++#include <Uefi/UefiSpec.h>
++#include <Protocol/BlockIo.h>
++#include <IndustryStandard/UefiTcgPlatform.h>
++#include <Protocol/Tcg2Protocol.h>
++
++/**
++ This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse
++ However this function will not attempt to verify the validity of the GPT partition
++ It will check the following:
++ - Signature
++ - Revision
++ - AlternateLBA
++ - FirstUsableLBA
++ - LastUsableLBA
++ - PartitionEntryLBA
++ - NumberOfPartitionEntries
++ - SizeOfPartitionEntry
++ - BlockIo
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[in] BlockIo
++ Pointer to the EFI_BLOCK_IO_PROTOCOL structure.
++
++ @retval EFI_SUCCESS
++ The EFI_PARTITION_TABLE_HEADER structure is valid.
++
++ @retval EFI_INVALID_PARAMETER
++ The EFI_PARTITION_TABLE_HEADER structure is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizeEfiPartitionTableHeader (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo
++ );
++
++/**
++ This function will validate that the allocation size from the primary header is sane
++ It will check the following:
++ - AllocationSize does not overflow
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[out] AllocationSize
++ Pointer to the allocation size.
++
++ @retval EFI_SUCCESS
++ The allocation size is valid.
++
++ @retval EFI_OUT_OF_RESOURCES
++ The allocation size is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizePrimaryHeaderAllocationSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ OUT UINT32 *AllocationSize
++ );
++
++/**
++ This function will validate that the Gpt Event Size calculated from the primary header is sane
++ It will check the following:
++ - EventSize does not overflow
++
++ Important: This function includes the entire length of the allocated space, including
++ (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)) . When hashing the buffer allocated with this
++ size, the caller must subtract the size of the (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event))
++ from the size of the buffer before hashing.
++
++ @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++ @param[in] NumberOfPartition - Number of partitions.
++ @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
++SanitizePrimaryHeaderGptEventSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN UINTN NumberOfPartition,
++ OUT UINT32 *EventSize
++ );
++
++#endif // DXE_TPM2_MEASURE_BOOT_LIB_SANITATION_
+diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c
+new file mode 100644
+index 0000000000..3eb9763e3c
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c
+@@ -0,0 +1,303 @@
++/** @file
++ This file includes the unit test cases for the DxeTpm2MeasureBootLibSanitizationTest.c.
++
++ Copyright (c) Microsoft Corporation.<BR>
++ SPDX-License-Identifier: BSD-2-Clause-Patent
++**/
++
++#include <Uefi.h>
++#include <Library/UefiLib.h>
++#include <Library/DebugLib.h>
++#include <Library/UnitTestLib.h>
++#include <Protocol/BlockIo.h>
++#include <Library/MemoryAllocationLib.h>
++#include <Library/BaseMemoryLib.h>
++#include <IndustryStandard/UefiTcgPlatform.h>
++#include <Protocol/Tcg2Protocol.h>
++
++#include "../DxeTpm2MeasureBootLibSanitization.h"
++
++#define UNIT_TEST_NAME "DxeTpm2MeasureBootLibSanitizationTest"
++#define UNIT_TEST_VERSION "1.0"
++
++#define DEFAULT_PRIMARY_TABLE_HEADER_REVISION 0x00010000
++#define DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES 1
++#define DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY 128
++
++/**
++ This function tests the SanitizeEfiPartitionTableHeader function.
++ It's intent is to test that a malicious EFI_PARTITION_TABLE_HEADER
++ structure will not cause undefined or unexpected behavior.
++
++ In general the TPM should still be able to measure the data, but
++ be the header should be sanitized to prevent any unexpected behavior.
++
++ @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
++TestSanitizeEfiPartitionTableHeader (
++ IN UNIT_TEST_CONTEXT Context
++ )
++{
++ EFI_STATUS Status;
++ EFI_PARTITION_TABLE_HEADER PrimaryHeader;
++ EFI_BLOCK_IO_PROTOCOL BlockIo;
++ EFI_BLOCK_IO_MEDIA BlockMedia;
++
++ // Generate EFI_BLOCK_IO_MEDIA test data
++ BlockMedia.MediaId = 1;
++ BlockMedia.RemovableMedia = FALSE;
++ BlockMedia.MediaPresent = TRUE;
++ BlockMedia.LogicalPartition = FALSE;
++ BlockMedia.ReadOnly = FALSE;
++ BlockMedia.WriteCaching = FALSE;
++ BlockMedia.BlockSize = 512;
++ BlockMedia.IoAlign = 1;
++ BlockMedia.LastBlock = 0;
++
++ // Generate EFI_BLOCK_IO_PROTOCOL test data
++ BlockIo.Revision = 1;
++ BlockIo.Media = &BlockMedia;
++ BlockIo.Reset = NULL;
++ BlockIo.ReadBlocks = NULL;
++ BlockIo.WriteBlocks = NULL;
++ BlockIo.FlushBlocks = NULL;
++
++ // Geneate EFI_PARTITION_TABLE_HEADER test data
++ PrimaryHeader.Header.Signature = EFI_PTAB_HEADER_ID;
++ PrimaryHeader.Header.Revision = DEFAULT_PRIMARY_TABLE_HEADER_REVISION;
++ PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER);
++ PrimaryHeader.MyLBA = 1;
++ PrimaryHeader.AlternateLBA = 2;
++ PrimaryHeader.FirstUsableLBA = 3;
++ PrimaryHeader.LastUsableLBA = 4;
++ PrimaryHeader.PartitionEntryLBA = 5;
++ PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES;
++ PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++ PrimaryHeader.PartitionEntryArrayCRC32 = 0; // Purposely invalid
++
++ // Calculate the CRC32 of the PrimaryHeader
++ PrimaryHeader.Header.CRC32 = CalculateCrc32 ((UINT8 *)&PrimaryHeader, PrimaryHeader.Header.HeaderSize);
++
++ // Test that a normal PrimaryHeader passes validation
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_NOT_EFI_ERROR (Status);
++
++ // Test that when number of partition entries is 0, the function returns EFI_DEVICE_ERROR
++ // Should print "Invalid Partition Table Header NumberOfPartitionEntries!""
++ PrimaryHeader.NumberOfPartitionEntries = 0;
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR);
++ PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++
++ // Test that when the header size is too small, the function returns EFI_DEVICE_ERROR
++ // Should print "Invalid Partition Table Header Size!"
++ PrimaryHeader.Header.HeaderSize = 0;
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR);
++ PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER);
++
++ // Test that when the SizeOfPartitionEntry is too small, the function returns EFI_DEVICE_ERROR
++ // should print: "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!"
++ PrimaryHeader.SizeOfPartitionEntry = 1;
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR);
++
++ DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
++
++ return UNIT_TEST_PASSED;
++}
++
++/**
++ This function tests the SanitizePrimaryHeaderAllocationSize function.
++ It's intent is to test that the untrusted input from a EFI_PARTITION_TABLE_HEADER
++ structure will not cause an overflow when calculating the allocation size.
++
++ @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
++TestSanitizePrimaryHeaderAllocationSize (
++ IN UNIT_TEST_CONTEXT Context
++ )
++{
++ UINT32 AllocationSize;
++
++ EFI_STATUS Status;
++ EFI_PARTITION_TABLE_HEADER PrimaryHeader;
++
++ // Test that a normal PrimaryHeader passes validation
++ PrimaryHeader.NumberOfPartitionEntries = 5;
++ PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_NOT_EFI_ERROR (Status);
++
++ // Test that the allocation size is correct compared to the existing logic
++ UT_ASSERT_EQUAL (AllocationSize, PrimaryHeader.NumberOfPartitionEntries * PrimaryHeader.SizeOfPartitionEntry);
++
++ // Test that an overflow is detected
++ PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32;
++ PrimaryHeader.SizeOfPartitionEntry = 5;
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ // Test the inverse
++ PrimaryHeader.NumberOfPartitionEntries = 5;
++ PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ // Test the worst case scenario
++ PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32;
++ PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
++
++ return UNIT_TEST_PASSED;
++}
++
++/**
++ This function tests the SanitizePrimaryHeaderGptEventSize function.
++ It's intent is to test that the untrusted input from a EFI_GPT_DATA structure
++ will not cause an overflow when calculating the event size.
++
++ @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
++TestSanitizePrimaryHeaderGptEventSize (
++ IN UNIT_TEST_CONTEXT Context
++ )
++{
++ UINT32 EventSize;
++ UINT32 ExistingLogicEventSize;
++ EFI_STATUS Status;
++ EFI_PARTITION_TABLE_HEADER PrimaryHeader;
++ UINTN NumberOfPartition;
++ EFI_GPT_DATA *GptData;
++ EFI_TCG2_EVENT *Tcg2Event;
++
++ Tcg2Event = NULL;
++ GptData = NULL;
++
++ // Test that a normal PrimaryHeader passes validation
++ PrimaryHeader.NumberOfPartitionEntries = 5;
++ PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++
++ // set the number of partitions
++ NumberOfPartition = 13;
++
++ // that the primary event size is correct
++ Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize);
++ UT_ASSERT_NOT_EFI_ERROR (Status);
++
++ // Calculate the existing logic event size
++ ExistingLogicEventSize = (UINT32)(OFFSET_OF (EFI_TCG2_EVENT, Event) + OFFSET_OF (EFI_GPT_DATA, Partitions)
++ + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry);
++
++ // Check that the event size is correct
++ UT_ASSERT_EQUAL (EventSize, ExistingLogicEventSize);
++
++ // Tests that the primary event size may not overflow
++ Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, MAX_UINT32, &EventSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ // Test that the size of partition entries may not overflow
++ PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
++ Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
++
++ return UNIT_TEST_PASSED;
++}
++
++// *--------------------------------------------------------------------*
++// * Unit Test Code Main Function
++// *--------------------------------------------------------------------*
++
++/**
++ This function acts as the entry point for the unit tests.
++
++ @retval UNIT_TEST_PASSED The test passed.
++ @retval UNIT_TEST_ERROR_TEST_FAILED The test failed.
++ @retval others The test failed.
++**/
++EFI_STATUS
++EFIAPI
++UefiTestMain (
++ VOID
++ )
++{
++ EFI_STATUS Status;
++ UNIT_TEST_FRAMEWORK_HANDLE Framework;
++ UNIT_TEST_SUITE_HANDLE Tcg2MeasureBootLibValidationTestSuite;
++
++ Framework = NULL;
++
++ DEBUG ((DEBUG_INFO, "%a: TestMain() - Start\n", UNIT_TEST_NAME));
++
++ Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "%a: Failed in InitUnitTestFramework. Status = %r\n", UNIT_TEST_NAME, Status));
++ goto EXIT;
++ }
++
++ Status = CreateUnitTestSuite (&Tcg2MeasureBootLibValidationTestSuite, Framework, "Tcg2MeasureBootLibValidationTestSuite", "Common.Tcg2MeasureBootLibValidation", NULL, NULL);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "%s: Failed in CreateUnitTestSuite for Tcg2MeasureBootLibValidationTestSuite\n", UNIT_TEST_NAME));
++ Status = EFI_OUT_OF_RESOURCES;
++ goto EXIT;
++ }
++
++ // -----------Suite---------------------------------Description----------------------------Class----------------------------------Test Function------------------------Pre---Clean-Context
++ AddTestCase (Tcg2MeasureBootLibValidationTestSuite, "Tests Validating EFI Partition Table", "Common.Tcg2MeasureBootLibValidation", TestSanitizeEfiPartitionTableHeader, NULL, NULL, NULL);
++ AddTestCase (Tcg2MeasureBootLibValidationTestSuite, "Tests Primary header gpt event checks for overflow", "Common.Tcg2MeasureBootLibValidation", TestSanitizePrimaryHeaderAllocationSize, NULL, NULL, NULL);
++ AddTestCase (Tcg2MeasureBootLibValidationTestSuite, "Tests Primary header allocation size checks for overflow", "Common.Tcg2MeasureBootLibValidation", TestSanitizePrimaryHeaderGptEventSize, NULL, NULL, NULL);
++
++ Status = RunAllTestSuites (Framework);
++
++EXIT:
++ if (Framework != NULL) {
++ FreeUnitTestFramework (Framework);
++ }
++
++ DEBUG ((DEBUG_INFO, "%a: TestMain() - End\n", UNIT_TEST_NAME));
++ return Status;
++}
++
++///
++/// Avoid ECC error for function name that starts with lower case letter
++///
++#define DxeTpm2MeasureBootLibUnitTestMain main
++
++/**
++ Standard POSIX C entry point for host based unit test execution.
++
++ @param[in] Argc Number of arguments
++ @param[in] Argv Array of pointers to arguments
++
++ @retval 0 Success
++ @retval other Error
++**/
++INT32
++DxeTpm2MeasureBootLibUnitTestMain (
++ IN INT32 Argc,
++ IN CHAR8 *Argv[]
++ )
++{
++ return (INT32)UefiTestMain ();
++}
+diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf
+new file mode 100644
+index 0000000000..2999aa2a44
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf
+@@ -0,0 +1,28 @@
++## @file
++# This file builds the unit tests for DxeTpm2MeasureBootLib
++#
++# Copyright (C) Microsoft Corporation.<BR>
++# SPDX-License-Identifier: BSD-2-Clause-Patent
++##
++
++[Defines]
++ INF_VERSION = 0x00010006
++ BASE_NAME = DxeTpm2MeasuredBootLibTest
++ FILE_GUID = 144d757f-d423-484e-9309-a23695fad5bd
++ MODULE_TYPE = HOST_APPLICATION
++ VERSION_STRING = 1.0
++ ENTRY_POINT = main
++
++[Sources]
++ DxeTpm2MeasureBootLibSanitizationTest.c
++ ../DxeTpm2MeasureBootLibSanitization.c
++
++[Packages]
++ MdePkg/MdePkg.dec
++
++[LibraryClasses]
++ BaseLib
++ DebugLib
++ UnitTestLib
++ PrintLib
++ SafeIntLib
+diff --git a/SecurityPkg/SecurityPkg.ci.yaml b/SecurityPkg/SecurityPkg.ci.yaml
+index 7912142398..da811fdf93 100644
+--- a/SecurityPkg/SecurityPkg.ci.yaml
++++ b/SecurityPkg/SecurityPkg.ci.yaml
+@@ -15,6 +15,7 @@
+ ## "<ErrorID>", "<KeyWord>"
+ ## ]
+ "ExceptionList": [
++ "8001", "DxeTpm2MeasureBootLibUnitTestMain",
+ ],
+ ## Both file path and directory path are accepted.
+ "IgnoreFiles": [
+--
+2.40.0
+
new file mode 100644
@@ -0,0 +1,889 @@
+From 4776a1b39ee08fc45c70c1eab5a0195f325000d3 Mon Sep 17 00:00:00 2001
+From: "Douglas Flick [MSFT]" <doug.edk2@gmail.com>
+Date: Fri, 12 Jan 2024 02:16:02 +0800
+Subject: [PATCH] SecurityPkg: DxeTpmMeasureBootLib: SECURITY PATCH 4117 - CVE
+ 2022-36763
+
+This commit contains the patch files and tests for DxeTpmMeasureBootLib
+CVE 2022-36763.
+
+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>
+
+CVE: CVE-2022-36763
+
+Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/4776a1b39ee08fc45c70c1eab5a0195f325000d3]
+
+Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
+---
+ .../DxeTpmMeasureBootLib.c | 40 ++-
+ .../DxeTpmMeasureBootLib.inf | 4 +-
+ .../DxeTpmMeasureBootLibSanitization.c | 241 ++++++++++++++
+ .../DxeTpmMeasureBootLibSanitization.h | 114 +++++++
+ .../DxeTpmMeasureBootLibSanitizationTest.c | 301 ++++++++++++++++++
+ ...eTpmMeasureBootLibSanitizationTestHost.inf | 28 ++
+ SecurityPkg/SecurityPkg.ci.yaml | 1 +
+ 7 files changed, 715 insertions(+), 14 deletions(-)
+ create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c
+ create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h
+ create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c
+ create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf
+
+diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c
+index 220393dd2b..669ab19134 100644
+--- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c
++++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c
+@@ -18,6 +18,8 @@
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
++Copyright (c) Microsoft Corporation.<BR>
++SPDX-License-Identifier: BSD-2-Clause-Patent
+ **/
+
+ #include <PiDxe.h>
+@@ -40,6 +42,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
+ #include <Library/SecurityManagementLib.h>
+ #include <Library/HobLib.h>
+
++#include "DxeTpmMeasureBootLibSanitization.h"
++
+ //
+ // Flag to check GPT partition. It only need be measured once.
+ //
+@@ -136,6 +140,9 @@ TcgMeasureGptTable (
+ UINT32 EventSize;
+ UINT32 EventNumber;
+ EFI_PHYSICAL_ADDRESS EventLogLastEntry;
++ UINT32 AllocSize;
++
++ GptData = NULL;
+
+ if (mMeasureGptCount > 0) {
+ return EFI_SUCCESS;
+@@ -166,8 +173,8 @@ TcgMeasureGptTable (
+ BlockIo->Media->BlockSize,
+ (UINT8 *)PrimaryHeader
+ );
+- if (EFI_ERROR (Status)) {
+- DEBUG ((DEBUG_ERROR, "Failed to Read Partition Table Header!\n"));
++ if (EFI_ERROR (Status) || EFI_ERROR (SanitizeEfiPartitionTableHeader (PrimaryHeader, BlockIo))) {
++ DEBUG ((DEBUG_ERROR, "Failed to read Partition Table Header or invalid Partition Table Header!\n"));
+ FreePool (PrimaryHeader);
+ return EFI_DEVICE_ERROR;
+ }
+@@ -175,7 +182,13 @@ TcgMeasureGptTable (
+ //
+ // Read the partition entry.
+ //
+- EntryPtr = (UINT8 *)AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry);
++ Status = SanitizePrimaryHeaderAllocationSize (PrimaryHeader, &AllocSize);
++ if (EFI_ERROR (Status)) {
++ FreePool (PrimaryHeader);
++ return EFI_DEVICE_ERROR;
++ }
++
++ EntryPtr = (UINT8 *)AllocatePool (AllocSize);
+ if (EntryPtr == NULL) {
+ FreePool (PrimaryHeader);
+ return EFI_OUT_OF_RESOURCES;
+@@ -185,7 +198,7 @@ TcgMeasureGptTable (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockIo->Media->BlockSize),
+- PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry,
++ AllocSize,
+ EntryPtr
+ );
+ if (EFI_ERROR (Status)) {
+@@ -210,9 +223,8 @@ TcgMeasureGptTable (
+ //
+ // Prepare Data for Measurement
+ //
+- EventSize = (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions)
+- + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry);
+- TcgEvent = (TCG_PCR_EVENT *)AllocateZeroPool (EventSize + sizeof (TCG_PCR_EVENT_HDR));
++ Status = SanitizePrimaryHeaderGptEventSize (PrimaryHeader, NumberOfPartition, &EventSize);
++ TcgEvent = (TCG_PCR_EVENT *)AllocateZeroPool (EventSize);
+ if (TcgEvent == NULL) {
+ FreePool (PrimaryHeader);
+ FreePool (EntryPtr);
+@@ -221,7 +233,7 @@ TcgMeasureGptTable (
+
+ TcgEvent->PCRIndex = 5;
+ TcgEvent->EventType = EV_EFI_GPT_EVENT;
+- TcgEvent->EventSize = EventSize;
++ TcgEvent->EventSize = EventSize - sizeof (TCG_PCR_EVENT_HDR);
+ GptData = (EFI_GPT_DATA *)TcgEvent->Event;
+
+ //
+@@ -361,11 +373,13 @@ TcgMeasurePeImage (
+ TcgEvent->PCRIndex = 2;
+ break;
+ default:
+- DEBUG ((
+- DEBUG_ERROR,
+- "TcgMeasurePeImage: Unknown subsystem type %d",
+- ImageType
+- ));
++ DEBUG (
++ (
++ DEBUG_ERROR,
++ "TcgMeasurePeImage: Unknown subsystem type %d",
++ ImageType
++ )
++ );
+ goto Finish;
+ }
+
+diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf
+index ebab6f7c1e..414c654d15 100644
+--- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf
++++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf
+@@ -32,6 +32,8 @@
+
+ [Sources]
+ DxeTpmMeasureBootLib.c
++ DxeTpmMeasureBootLibSanitization.c
++ DxeTpmMeasureBootLibSanitization.h
+
+ [Packages]
+ MdePkg/MdePkg.dec
+@@ -41,6 +43,7 @@
+
+ [LibraryClasses]
+ BaseMemoryLib
++ SafeIntLib
+ DebugLib
+ MemoryAllocationLib
+ DevicePathLib
+@@ -59,4 +62,3 @@
+ gEfiFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDiskIoProtocolGuid ## SOMETIMES_CONSUMES
+-
+diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c
+new file mode 100644
+index 0000000000..a3fa46f5e6
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c
+@@ -0,0 +1,241 @@
++/** @file
++ The library instance provides security service of TPM2 measure boot and
++ Confidential Computing (CC) measure boot.
++
++ Caution: This file requires additional review when modified.
++ This library will have external input - PE/COFF image and GPT partition.
++ This external input must be validated carefully to avoid security issue like
++ buffer overflow, integer overflow.
++
++ This file will pull out the validation logic from the following functions, in an
++ attempt to validate the untrusted input in the form of unit tests
++
++ These are those functions:
++
++ DxeTpmMeasureBootLibImageRead() function will make sure the PE/COFF image content
++ read is within the image buffer.
++
++ Tcg2MeasureGptTable() function will receive untrusted GPT partition table, and parse
++ partition data carefully.
++
++ Copyright (c) Microsoft Corporation.<BR>
++ SPDX-License-Identifier: BSD-2-Clause-Patent
++**/
++#include <Uefi.h>
++#include <Uefi/UefiSpec.h>
++#include <Library/SafeIntLib.h>
++#include <Library/UefiLib.h>
++#include <Library/DebugLib.h>
++#include <Library/BaseLib.h>
++#include <IndustryStandard/UefiTcgPlatform.h>
++#include <Protocol/BlockIo.h>
++#include <Library/MemoryAllocationLib.h>
++
++#include "DxeTpmMeasureBootLibSanitization.h"
++
++#define GPT_HEADER_REVISION_V1 0x00010000
++
++/**
++ This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse
++ However this function will not attempt to verify the validity of the GPT partition
++ It will check the following:
++ - Signature
++ - Revision
++ - AlternateLBA
++ - FirstUsableLBA
++ - LastUsableLBA
++ - PartitionEntryLBA
++ - NumberOfPartitionEntries
++ - SizeOfPartitionEntry
++ - BlockIo
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[in] BlockIo
++ Pointer to the EFI_BLOCK_IO_PROTOCOL structure.
++
++ @retval EFI_SUCCESS
++ The EFI_PARTITION_TABLE_HEADER structure is valid.
++
++ @retval EFI_INVALID_PARAMETER
++ The EFI_PARTITION_TABLE_HEADER structure is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizeEfiPartitionTableHeader (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo
++ )
++{
++ // Verify that the input parameters are safe to use
++ if (PrimaryHeader == NULL) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ if ((BlockIo == NULL) || (BlockIo->Media == NULL)) {
++ DEBUG ((DEBUG_ERROR, "Invalid BlockIo!\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ // The signature must be EFI_PTAB_HEADER_ID ("EFI PART" in ASCII)
++ if (PrimaryHeader->Header.Signature != EFI_PTAB_HEADER_ID) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ // The version must be GPT_HEADER_REVISION_V1 (0x00010000)
++ if (PrimaryHeader->Header.Revision != GPT_HEADER_REVISION_V1) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header Revision!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ // The HeaderSize must be greater than or equal to 92 and must be less than or equal to the logical block size
++ if ((PrimaryHeader->Header.HeaderSize < sizeof (EFI_PARTITION_TABLE_HEADER)) || (PrimaryHeader->Header.HeaderSize > BlockIo->Media->BlockSize)) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header HeaderSize!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ // check that the PartitionEntryLBA greater than the Max LBA
++ // This will be used later for multiplication
++ if (PrimaryHeader->PartitionEntryLBA > DivU64x32 (MAX_UINT64, BlockIo->Media->BlockSize)) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header PartitionEntryLBA!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ // Check that the number of partition entries is greater than zero
++ if (PrimaryHeader->NumberOfPartitionEntries == 0) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ // SizeOfPartitionEntry must be 128, 256, 512... improper size may lead to accessing uninitialized memory
++ if ((PrimaryHeader->SizeOfPartitionEntry < 128) || ((PrimaryHeader->SizeOfPartitionEntry & (PrimaryHeader->SizeOfPartitionEntry - 1)) != 0)) {
++ DEBUG ((DEBUG_ERROR, "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ // This check is to prevent overflow when calculating the allocation size for the partition entries
++ // This check will be used later for multiplication
++ if (PrimaryHeader->NumberOfPartitionEntries > DivU64x32 (MAX_UINT64, PrimaryHeader->SizeOfPartitionEntry)) {
++ DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n"));
++ return EFI_DEVICE_ERROR;
++ }
++
++ return EFI_SUCCESS;
++}
++
++/**
++ This function will validate that the allocation size from the primary header is sane
++ It will check the following:
++ - AllocationSize does not overflow
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[out] AllocationSize
++ Pointer to the allocation size.
++
++ @retval EFI_SUCCESS
++ The allocation size is valid.
++
++ @retval EFI_OUT_OF_RESOURCES
++ The allocation size is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizePrimaryHeaderAllocationSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ OUT UINT32 *AllocationSize
++ )
++{
++ EFI_STATUS Status;
++
++ if (PrimaryHeader == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ if (AllocationSize == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ // Replacing logic:
++ // PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry;
++ Status = SafeUint32Mult (PrimaryHeader->NumberOfPartitionEntries, PrimaryHeader->SizeOfPartitionEntry, AllocationSize);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "Allocation Size would have overflowed!\n"));
++ return EFI_BAD_BUFFER_SIZE;
++ }
++
++ return EFI_SUCCESS;
++}
++
++/**
++ This function will validate that the Gpt Event Size calculated from the primary header is sane
++ It will check the following:
++ - EventSize does not overflow
++
++ Important: This function includes the entire length of the allocated space, including the
++ TCG_PCR_EVENT_HDR. When hashing the buffer allocated with this size, the caller must subtract
++ the size of the TCG_PCR_EVENT_HDR from the size of the buffer before hashing.
++
++ @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++ @param[in] NumberOfPartition - Number of partitions.
++ @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
++SanitizePrimaryHeaderGptEventSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN UINTN NumberOfPartition,
++ OUT UINT32 *EventSize
++ )
++{
++ EFI_STATUS Status;
++ UINT32 SafeNumberOfPartitions;
++
++ if (PrimaryHeader == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ if (EventSize == NULL) {
++ return EFI_INVALID_PARAMETER;
++ }
++
++ // We shouldn't even attempt to perform the multiplication if the number of partitions is greater than the maximum value of UINT32
++ Status = SafeUintnToUint32 (NumberOfPartition, &SafeNumberOfPartitions);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "NumberOfPartition would have overflowed!\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ // Replacing logic:
++ // (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry + sizeof (TCG_PCR_EVENT_HDR));
++ Status = SafeUint32Mult (SafeNumberOfPartitions, PrimaryHeader->SizeOfPartitionEntry, EventSize);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "Event Size would have overflowed!\n"));
++ return EFI_BAD_BUFFER_SIZE;
++ }
++
++ Status = SafeUint32Add (
++ sizeof (TCG_PCR_EVENT_HDR) +
++ OFFSET_OF (EFI_GPT_DATA, Partitions),
++ *EventSize,
++ EventSize
++ );
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "Event Size would have overflowed because of GPTData!\n"));
++ return EFI_BAD_BUFFER_SIZE;
++ }
++
++ return EFI_SUCCESS;
++}
+diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h
+new file mode 100644
+index 0000000000..0d9d00c281
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h
+@@ -0,0 +1,114 @@
++/** @file
++ This file includes the function prototypes for the sanitization functions.
++
++ These are those functions:
++
++ DxeTpmMeasureBootLibImageRead() function will make sure the PE/COFF image content
++ read is within the image buffer.
++
++ TcgMeasurePeImage() function will accept untrusted PE/COFF image and validate its
++ data structure within this image buffer before use.
++
++ TcgMeasureGptTable() function will receive untrusted GPT partition table, and parse
++ partition data carefully.
++
++ Copyright (c) Microsoft Corporation.<BR>
++ SPDX-License-Identifier: BSD-2-Clause-Patent
++
++**/
++
++#ifndef DXE_TPM_MEASURE_BOOT_LIB_VALIDATION_
++#define DXE_TPM_MEASURE_BOOT_LIB_VALIDATION_
++
++#include <Uefi.h>
++#include <Uefi/UefiSpec.h>
++#include <Protocol/BlockIo.h>
++#include <IndustryStandard/UefiTcgPlatform.h>
++
++/**
++ This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse
++ However this function will not attempt to verify the validity of the GPT partition
++ It will check the following:
++ - Signature
++ - Revision
++ - AlternateLBA
++ - FirstUsableLBA
++ - LastUsableLBA
++ - PartitionEntryLBA
++ - NumberOfPartitionEntries
++ - SizeOfPartitionEntry
++ - BlockIo
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[in] BlockIo
++ Pointer to the EFI_BLOCK_IO_PROTOCOL structure.
++
++ @retval EFI_SUCCESS
++ The EFI_PARTITION_TABLE_HEADER structure is valid.
++
++ @retval EFI_INVALID_PARAMETER
++ The EFI_PARTITION_TABLE_HEADER structure is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizeEfiPartitionTableHeader (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo
++ );
++
++/**
++ This function will validate that the allocation size from the primary header is sane
++ It will check the following:
++ - AllocationSize does not overflow
++
++ @param[in] PrimaryHeader
++ Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++
++ @param[out] AllocationSize
++ Pointer to the allocation size.
++
++ @retval EFI_SUCCESS
++ The allocation size is valid.
++
++ @retval EFI_OUT_OF_RESOURCES
++ The allocation size is invalid.
++**/
++EFI_STATUS
++EFIAPI
++SanitizePrimaryHeaderAllocationSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ OUT UINT32 *AllocationSize
++ );
++
++/**
++ This function will validate that the Gpt Event Size calculated from the primary header is sane
++ It will check the following:
++ - EventSize does not overflow
++
++ Important: This function includes the entire length of the allocated space, including the
++ TCG_PCR_EVENT_HDR. When hashing the buffer allocated with this size, the caller must subtract
++ the size of the TCG_PCR_EVENT_HDR from the size of the buffer before hashing.
++
++ @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure.
++ @param[in] NumberOfPartition - Number of partitions.
++ @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
++SanitizePrimaryHeaderGptEventSize (
++ IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader,
++ IN UINTN NumberOfPartition,
++ 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
+new file mode 100644
+index 0000000000..eeb928cdb0
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c
+@@ -0,0 +1,301 @@
++/** @file
++This file includes the unit test cases for the DxeTpmMeasureBootLibSanitizationTest.c.
++
++Copyright (c) Microsoft Corporation.<BR>
++SPDX-License-Identifier: BSD-2-Clause-Patent
++**/
++
++#include <Uefi.h>
++#include <Library/UefiLib.h>
++#include <Library/DebugLib.h>
++#include <Library/UnitTestLib.h>
++#include <Protocol/BlockIo.h>
++#include <Library/MemoryAllocationLib.h>
++#include <Library/BaseMemoryLib.h>
++#include <IndustryStandard/UefiTcgPlatform.h>
++
++#include "../DxeTpmMeasureBootLibSanitization.h"
++
++#define UNIT_TEST_NAME "DxeTpmMeasureBootLibSanitizationTest"
++#define UNIT_TEST_VERSION "1.0"
++
++#define DEFAULT_PRIMARY_TABLE_HEADER_REVISION 0x00010000
++#define DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES 1
++#define DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY 128
++
++/**
++ This function tests the SanitizeEfiPartitionTableHeader function.
++ It's intent is to test that a malicious EFI_PARTITION_TABLE_HEADER
++ structure will not cause undefined or unexpected behavior.
++
++ In general the TPM should still be able to measure the data, but
++ be the header should be sanitized to prevent any unexpected behavior.
++
++ @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
++TestSanitizeEfiPartitionTableHeader (
++ IN UNIT_TEST_CONTEXT Context
++ )
++{
++ EFI_STATUS Status;
++ EFI_PARTITION_TABLE_HEADER PrimaryHeader;
++ EFI_BLOCK_IO_PROTOCOL BlockIo;
++ EFI_BLOCK_IO_MEDIA BlockMedia;
++
++ // Generate EFI_BLOCK_IO_MEDIA test data
++ BlockMedia.MediaId = 1;
++ BlockMedia.RemovableMedia = FALSE;
++ BlockMedia.MediaPresent = TRUE;
++ BlockMedia.LogicalPartition = FALSE;
++ BlockMedia.ReadOnly = FALSE;
++ BlockMedia.WriteCaching = FALSE;
++ BlockMedia.BlockSize = 512;
++ BlockMedia.IoAlign = 1;
++ BlockMedia.LastBlock = 0;
++
++ // Generate EFI_BLOCK_IO_PROTOCOL test data
++ BlockIo.Revision = 1;
++ BlockIo.Media = &BlockMedia;
++ BlockIo.Reset = NULL;
++ BlockIo.ReadBlocks = NULL;
++ BlockIo.WriteBlocks = NULL;
++ BlockIo.FlushBlocks = NULL;
++
++ // Geneate EFI_PARTITION_TABLE_HEADER test data
++ PrimaryHeader.Header.Signature = EFI_PTAB_HEADER_ID;
++ PrimaryHeader.Header.Revision = DEFAULT_PRIMARY_TABLE_HEADER_REVISION;
++ PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER);
++ PrimaryHeader.MyLBA = 1;
++ PrimaryHeader.AlternateLBA = 2;
++ PrimaryHeader.FirstUsableLBA = 3;
++ PrimaryHeader.LastUsableLBA = 4;
++ PrimaryHeader.PartitionEntryLBA = 5;
++ PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES;
++ PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++ PrimaryHeader.PartitionEntryArrayCRC32 = 0; // Purposely invalid
++
++ // Calculate the CRC32 of the PrimaryHeader
++ PrimaryHeader.Header.CRC32 = CalculateCrc32 ((UINT8 *)&PrimaryHeader, PrimaryHeader.Header.HeaderSize);
++
++ // Test that a normal PrimaryHeader passes validation
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_NOT_EFI_ERROR (Status);
++
++ // Test that when number of partition entries is 0, the function returns EFI_DEVICE_ERROR
++ // Should print "Invalid Partition Table Header NumberOfPartitionEntries!""
++ PrimaryHeader.NumberOfPartitionEntries = 0;
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR);
++ PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++
++ // Test that when the header size is too small, the function returns EFI_DEVICE_ERROR
++ // Should print "Invalid Partition Table Header Size!"
++ PrimaryHeader.Header.HeaderSize = 0;
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR);
++ PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER);
++
++ // Test that when the SizeOfPartitionEntry is too small, the function returns EFI_DEVICE_ERROR
++ // should print: "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!"
++ PrimaryHeader.SizeOfPartitionEntry = 1;
++ Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo);
++ UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR);
++
++ DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
++
++ return UNIT_TEST_PASSED;
++}
++
++/**
++ This function tests the SanitizePrimaryHeaderAllocationSize function.
++ It's intent is to test that the untrusted input from a EFI_PARTITION_TABLE_HEADER
++ structure will not cause an overflow when calculating the allocation size.
++
++ @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
++TestSanitizePrimaryHeaderAllocationSize (
++ IN UNIT_TEST_CONTEXT Context
++ )
++{
++ UINT32 AllocationSize;
++
++ EFI_STATUS Status;
++ EFI_PARTITION_TABLE_HEADER PrimaryHeader;
++
++ // Test that a normal PrimaryHeader passes validation
++ PrimaryHeader.NumberOfPartitionEntries = 5;
++ PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_NOT_EFI_ERROR (Status);
++
++ // Test that the allocation size is correct compared to the existing logic
++ UT_ASSERT_EQUAL (AllocationSize, PrimaryHeader.NumberOfPartitionEntries * PrimaryHeader.SizeOfPartitionEntry);
++
++ // Test that an overflow is detected
++ PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32;
++ PrimaryHeader.SizeOfPartitionEntry = 5;
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ // Test the inverse
++ PrimaryHeader.NumberOfPartitionEntries = 5;
++ PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ // Test the worst case scenario
++ PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32;
++ PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
++ Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
++
++ return UNIT_TEST_PASSED;
++}
++
++/**
++ This function tests the SanitizePrimaryHeaderGptEventSize function.
++ It's intent is to test that the untrusted input from a EFI_GPT_DATA structure
++ will not cause an overflow when calculating the event size.
++
++ @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
++TestSanitizePrimaryHeaderGptEventSize (
++ IN UNIT_TEST_CONTEXT Context
++ )
++{
++ UINT32 EventSize;
++ UINT32 ExistingLogicEventSize;
++ 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;
++ PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY;
++
++ // set the number of partitions
++ NumberOfPartition = 13;
++
++ // that the primary event size is correct
++ Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize);
++ UT_ASSERT_NOT_EFI_ERROR (Status);
++
++ // Calculate the existing logic event size
++ ExistingLogicEventSize = (UINT32)(sizeof (TCG_PCR_EVENT_HDR) + OFFSET_OF (EFI_GPT_DATA, Partitions)
++ + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry);
++
++ // Check that the event size is correct
++ UT_ASSERT_EQUAL (EventSize, ExistingLogicEventSize);
++
++ // Tests that the primary event size may not overflow
++ Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, MAX_UINT32, &EventSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ // Test that the size of partition entries may not overflow
++ PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
++ Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize);
++ UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
++
++ DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
++
++ return UNIT_TEST_PASSED;
++}
++
++// *--------------------------------------------------------------------*
++// * Unit Test Code Main Function
++// *--------------------------------------------------------------------*
++
++/**
++ This function acts as the entry point for the unit tests.
++
++ @param argc - The number of command line arguments
++ @param argv - The command line arguments
++
++ @return int - The status of the test
++**/
++EFI_STATUS
++EFIAPI
++UefiTestMain (
++ VOID
++ )
++{
++ EFI_STATUS Status;
++ UNIT_TEST_FRAMEWORK_HANDLE Framework;
++ UNIT_TEST_SUITE_HANDLE TcgMeasureBootLibValidationTestSuite;
++
++ Framework = NULL;
++
++ DEBUG ((DEBUG_INFO, "%a: TestMain() - Start\n", UNIT_TEST_NAME));
++
++ Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "%a: Failed in InitUnitTestFramework. Status = %r\n", UNIT_TEST_NAME, Status));
++ goto EXIT;
++ }
++
++ Status = CreateUnitTestSuite (&TcgMeasureBootLibValidationTestSuite, Framework, "TcgMeasureBootLibValidationTestSuite", "Common.TcgMeasureBootLibValidation", NULL, NULL);
++ if (EFI_ERROR (Status)) {
++ DEBUG ((DEBUG_ERROR, "%s: Failed in CreateUnitTestSuite for TcgMeasureBootLibValidationTestSuite\n", UNIT_TEST_NAME));
++ Status = EFI_OUT_OF_RESOURCES;
++ goto EXIT;
++ }
++
++ // -----------Suite---------------------------------Description----------------------------Class----------------------------------Test Function------------------------Pre---Clean-Context
++ 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);
++
++ Status = RunAllTestSuites (Framework);
++
++EXIT:
++ if (Framework != NULL) {
++ FreeUnitTestFramework (Framework);
++ }
++
++ DEBUG ((DEBUG_INFO, "%a: TestMain() - End\n", UNIT_TEST_NAME));
++ return Status;
++}
++
++///
++/// Avoid ECC error for function name that starts with lower case letter
++///
++#define DxeTpmMeasureBootLibUnitTestMain main
++
++/**
++ Standard POSIX C entry point for host based unit test execution.
++
++ @param[in] Argc Number of arguments
++ @param[in] Argv Array of pointers to arguments
++
++ @retval 0 Success
++ @retval other Error
++**/
++INT32
++DxeTpmMeasureBootLibUnitTestMain (
++ IN INT32 Argc,
++ IN CHAR8 *Argv[]
++ )
++{
++ return (INT32)UefiTestMain ();
++}
+diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf
+new file mode 100644
+index 0000000000..47b0811b00
+--- /dev/null
++++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf
+@@ -0,0 +1,28 @@
++## @file
++# This file builds the unit tests for DxeTpmMeasureBootLib
++#
++# Copyright (C) Microsoft Corporation.<BR>
++# SPDX-License-Identifier: BSD-2-Clause-Patent
++##
++
++[Defines]
++ INF_VERSION = 0x00010006
++ BASE_NAME = DxeTpmMeasuredBootLibTest
++ FILE_GUID = eb01bc38-309c-4d3e-967e-9f078c90772f
++ MODULE_TYPE = HOST_APPLICATION
++ VERSION_STRING = 1.0
++ ENTRY_POINT = main
++
++[Sources]
++ DxeTpmMeasureBootLibSanitizationTest.c
++ ../DxeTpmMeasureBootLibSanitization.c
++
++[Packages]
++ MdePkg/MdePkg.dec
++
++[LibraryClasses]
++ BaseLib
++ DebugLib
++ UnitTestLib
++ PrintLib
++ SafeIntLib
+diff --git a/SecurityPkg/SecurityPkg.ci.yaml b/SecurityPkg/SecurityPkg.ci.yaml
+index da811fdf93..0e40eaa0fe 100644
+--- a/SecurityPkg/SecurityPkg.ci.yaml
++++ b/SecurityPkg/SecurityPkg.ci.yaml
+@@ -16,6 +16,7 @@
+ ## ]
+ "ExceptionList": [
+ "8001", "DxeTpm2MeasureBootLibUnitTestMain",
++ "8001", "DxeTpmMeasureBootLibUnitTestMain"
+ ],
+ ## Both file path and directory path are accepted.
+ "IgnoreFiles": [
+--
+2.40.0
+
new file mode 100644
@@ -0,0 +1,55 @@
+From 1ddcb9fc6b4164e882687b031e8beacfcf7df29e Mon Sep 17 00:00:00 2001
+From: "Douglas Flick [MSFT]" <doug.edk2@gmail.com>
+Date: Fri, 12 Jan 2024 02:16:03 +0800
+Subject: [PATCH] SecurityPkg: : Adding CVE 2022-36763 to SecurityFixes.yaml
+
+This creates / adds a security file that tracks the security fixes
+found in this package and can be used to find the fixes that were
+applied.
+
+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>
+
+CVE: CVE-2022-36763
+
+Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/1ddcb9fc6b4164e882687b031e8beacfcf7df29e]
+
+Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
+---
+ SecurityPkg/SecurityFixes.yaml | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+ create mode 100644 SecurityPkg/SecurityFixes.yaml
+
+diff --git a/SecurityPkg/SecurityFixes.yaml b/SecurityPkg/SecurityFixes.yaml
+new file mode 100644
+index 0000000000..f9e3e7be74
+--- /dev/null
++++ b/SecurityPkg/SecurityFixes.yaml
+@@ -0,0 +1,22 @@
++## @file
++# Security Fixes for SecurityPkg
++#
++# Copyright (c) Microsoft Corporation
++# SPDX-License-Identifier: BSD-2-Clause-Patent
++##
++CVE_2022_36763:
++ commit_titles:
++ - "SecurityPkg: DxeTpm2Measurement: SECURITY PATCH 4117 - CVE 2022-36763"
++ - "SecurityPkg: DxeTpmMeasurement: SECURITY PATCH 4117 - CVE 2022-36763"
++ - "SecurityPkg: : Adding CVE 2022-36763 to SecurityFixes.yaml"
++ cve: CVE-2022-36763
++ date_reported: 2022-10-25 11:31 UTC
++ description: (CVE-2022-36763) - Heap Buffer Overflow in Tcg2MeasureGptTable()
++ note: This patch is related to and supersedes TCBZ2168
++ files_impacted:
++ - Library\DxeTpm2MeasureBootLib\DxeTpm2MeasureBootLib.c
++ - Library\DxeTpmMeasureBootLib\DxeTpmMeasureBootLib.c
++ links:
++ - https://bugzilla.tianocore.org/show_bug.cgi?id=4117
++ - https://bugzilla.tianocore.org/show_bug.cgi?id=2168
++ - https://bugzilla.tianocore.org/show_bug.cgi?id=1990
+--
+2.40.0
+
@@ -27,6 +27,9 @@ SRC_URI = "gitsm://github.com/tianocore/edk2.git;branch=master;protocol=https \
file://0006-reproducible.patch \
file://0001-BaseTools-fix-gcc12-warning.patch \
file://0001-BaseTools-fix-gcc12-warning-1.patch \
+ file://CVE-2022-36763-0001.patch \
+ file://CVE-2022-36763-0002.patch \
+ file://CVE-2022-36763-0003.patch \
"
PV = "edk2-stable202202"