mirror of https://github.com/acidanthera/audk.git
388 lines
15 KiB
C
388 lines
15 KiB
C
/** @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 = TpmSanitizeEfiPartitionTableHeader (&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 = TpmSanitizeEfiPartitionTableHeader (&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 = TpmSanitizeEfiPartitionTableHeader (&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 = TpmSanitizeEfiPartitionTableHeader (&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 = TpmSanitizePrimaryHeaderAllocationSize (&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 = TpmSanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
|
|
UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
|
|
|
|
// Test the inverse
|
|
PrimaryHeader.NumberOfPartitionEntries = 5;
|
|
PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
|
|
Status = TpmSanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize);
|
|
UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
|
|
|
|
// Test the worst case scenario
|
|
PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32;
|
|
PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32;
|
|
Status = TpmSanitizePrimaryHeaderAllocationSize (&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;
|
|
|
|
// 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 = TpmSanitizePrimaryHeaderGptEventSize (&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 = TpmSanitizePrimaryHeaderGptEventSize (&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 = TpmSanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize);
|
|
UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE);
|
|
|
|
DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__));
|
|
|
|
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 = TpmSanitizePeImageEventSize (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 = TpmSanitizePeImageEventSize (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
|
|
// *--------------------------------------------------------------------*
|
|
|
|
/**
|
|
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);
|
|
AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests PE Image and FileSize checks for overflow", "Common.TcgMeasureBootLibValidation", TestSanitizePeImageEventSize, 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 ();
|
|
}
|