From 7374c1764a4ba1264bec20b4f274b5922a981626 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Thu, 28 Sep 2023 11:07:04 -0700 Subject: [PATCH] Update DxePagingAudit to use FlatPageTableLib Description Now that FlatPageTableLib is available, update DxePagingAudit to use it instead of needing to implement its own parsing logic. - [x] Impacts functionality? - **Functionality** - Does the change ultimately impact how firmware functions? - Examples: Add a new library, publish a new PPI, update an algorithm, ... - [ ] Impacts security? - **Security** - Does the change have a direct security impact on an application, flow, or firmware? - Examples: Crypto algorithm change, buffer overflow fix, parameter validation improvement, ... - [x] Breaking change? - **Breaking change** - Will anyone consuming this change experience a break in build or boot behavior? - Examples: Add a new library class, move a module to a different repo, call a function in a new library class in a pre-existing module, ... - [ ] Includes tests? - **Tests** - Does the change include any explicit test code? - Examples: Unit tests, integration tests, robot tests, ... - [ ] Includes documentation? - **Documentation** - Does the change contain explicit documentation additions outside direct code modifications (and comments)? - Examples: Update readme file, add feature readme file, link to documentation on an a separate Web page, ... How This Was Tested Tested on Q35 and SBSA by adding a temporary test to check the output of the Memory Attribute Protocol against the output of FlatPageTableLib. Integration Instructions An instance of FlatPageTableLib will need to be added to platforms which build the paging audit. --- .../Dxe/App/AArch64/DxePagingAuditTests.c | 145 ------------------ .../UEFI/Dxe/App/DxePagingAuditTestApp.c | 116 +++++++++++++- .../UEFI/Dxe/App/DxePagingAuditTestApp.h | 45 ------ .../UEFI/Dxe/App/X64/DxePagingAuditTests.c | 79 ---------- .../UEFI/DxePagingAuditTestApp.inf | 4 +- 5 files changed, 115 insertions(+), 274 deletions(-) delete mode 100644 UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/AArch64/DxePagingAuditTests.c delete mode 100644 UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.h delete mode 100644 UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/X64/DxePagingAuditTests.c diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/AArch64/DxePagingAuditTests.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/AArch64/DxePagingAuditTests.c deleted file mode 100644 index 8455c0e23e..0000000000 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/AArch64/DxePagingAuditTests.c +++ /dev/null @@ -1,145 +0,0 @@ -/** @file -- DxePagingAuditTests.c - ARM64 implementations for DXE paging audit tests - - Copyright (c) Microsoft Corporation. - SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include -#include -#include "../DxePagingAuditTestApp.h" - -#define TT_ADDRESS_MASK (0xFFFFFFFFFULL << 12) -#define IS_TABLE(page, level) ((level == 3) ? FALSE : (((page) & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) -#define IS_BLOCK(page, level) ((level == 3) ? (((page) & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3) : ((page & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY)) -#define ROOT_TABLE_LEN(T0SZ) (TT_ENTRY_COUNT >> ((T0SZ) - 16) % 9) -#define IS_VALID 0x1 -#define IS_READ_WRITE(page) (((page & TT_AP_RW_RW) != 0) || ((page & TT_AP_MASK) == 0)) -#define IS_EXECUTABLE(page) ((ArmReadCurrentEL () == AARCH64_EL2) ? ((page & TT_XN_MASK) == 0) : ((page & TT_UXN_MASK) == 0 || (page & TT_PXN_MASK) == 0)) -#define IS_ACCESSIBLE(page) ((page & TT_AF) != 0) - -/** - Check the page table for Read/Write/Execute regions. - - @param[in] Context Unit test context - - @retval UNIT_TEST_PASSED The unit test passed - @retval other The unit test failed - -**/ -UNIT_TEST_STATUS -EFIAPI -NoReadWriteExecute ( - IN UNIT_TEST_CONTEXT Context - ) -{ - UINT64 *Pml0; - UINT64 *Pte1G; - UINT64 *Pte2M; - UINT64 *Pte4K; - BOOLEAN FoundRWXAddress; - UINT64 Index3; - UINT64 Index2; - UINT64 Index1; - UINT64 Index0; - UINT64 RootEntryCount; - UINT64 Address; - - FoundRWXAddress = FALSE; - - Pml0 = (UINT64 *)ArmGetTTBR0BaseAddress (); - RootEntryCount = ROOT_TABLE_LEN (ArmGetTCR () & TCR_T0SZ_MASK); - - for (Index0 = 0x0; Index0 < RootEntryCount; Index0++) { - Index1 = 0; - Index2 = 0; - Index3 = 0; - - if (!IS_TABLE (Pml0[Index0], 0)) { - continue; - } - - // Level 0 must always be table entries - Pte1G = (UINT64 *)(Pml0[Index0] & TT_ADDRESS_MASK); - - for (Index1 = 0x0; Index1 < TT_ENTRY_COUNT; Index1++ ) { - Index2 = 0; - Index3 = 0; - - // If the entry is not valid, skip it - if ((Pte1G[Index1] & IS_VALID) == 0) { - continue; - } - - if (!IS_BLOCK (Pte1G[Index1], 1)) { - // This is a table - Pte2M = (UINT64 *)(Pte1G[Index1] & TT_ADDRESS_MASK); - - for (Index2 = 0x0; Index2 < TT_ENTRY_COUNT; Index2++ ) { - Index3 = 0; - - // If the entry is not valid, skip it - if ((Pte2M[Index2] & IS_VALID) == 0) { - continue; - } - - if (!IS_BLOCK (Pte2M[Index2], 2)) { - // This is a table - Pte4K = (UINT64 *)(Pte2M[Index2] & TT_ADDRESS_MASK); - - for (Index3 = 0x0; Index3 < TT_ENTRY_COUNT; Index3++ ) { - // If the entry is not valid, skip it - if ((Pte4K[Index3] & IS_VALID) == 0) { - continue; - } - - // This is a block - if (IS_READ_WRITE (Pte4K[Index3]) && // Read/Write - IS_EXECUTABLE (Pte4K[Index3]) && // Execute - IS_ACCESSIBLE (Pte4K[Index3])) // Access Flag (0 for guard pages) - { - Address = IndexToAddress (Index0, Index1, Index2, Index3); - - if (!CanRegionBeRWX (Address, SIZE_4KB)) { - UT_LOG_ERROR ("Memory Range 0x%llx-0x%llx is Read/Write/Execute\n", Address, Address + SIZE_4KB); - FoundRWXAddress = TRUE; - } - } - } - } else { - // This is an block - if (IS_READ_WRITE (Pte2M[Index2]) && // Read/Write - IS_EXECUTABLE (Pte2M[Index2]) && // Execute - IS_ACCESSIBLE (Pte2M[Index2])) // Access Flag (0 for guard pages) - { - Address = IndexToAddress (Index0, Index1, Index2, Index3); - - if (!CanRegionBeRWX (Address, SIZE_2MB)) { - UT_LOG_ERROR ("Memory Range 0x%llx-0x%llx is Read/Write/Execute\n", Address, Address + SIZE_2MB); - FoundRWXAddress = TRUE; - } - } - } - } - } else { - // This is an block - if (IS_READ_WRITE (Pte1G[Index1]) && // Read/Write - IS_EXECUTABLE (Pte1G[Index1]) && // Execute - IS_ACCESSIBLE (Pte1G[Index1])) // Access Flag (0 for guard pages) - { - Address = IndexToAddress (Index0, Index1, Index2, Index3); - - if (!CanRegionBeRWX (Address, SIZE_1GB)) { - UT_LOG_ERROR ("Memory Range 0x%llx-0x%llx is Read/Write/Execute\n", Address, Address + SIZE_1GB); - FoundRWXAddress = TRUE; - } - } - } - } - } - - UT_ASSERT_FALSE (FoundRWXAddress); - - return UNIT_TEST_PASSED; -} diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c index a21d009c2e..cf1b869906 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c @@ -7,13 +7,18 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include "DxePagingAuditTestApp.h" +#include "../../PagingAuditCommon.h" #include #include #include +#include +#include + #include #include +#include +#include #define UNIT_TEST_APP_NAME "Paging Audit Test" #define UNIT_TEST_APP_VERSION "1" @@ -31,6 +36,67 @@ IMAGE_RANGE_DESCRIPTOR *mNonProtectedImageList = NULL; UINTN mSpecialRegionCount = 0; EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mMemorySpaceMap = NULL; UINTN mMemorySpaceMapCount = 0; +PAGE_MAP mMap = { 0 }; + +/** + Frees the entries in the mMap global. + + @param[in] Context Unit test context. +**/ +STATIC +VOID +EFIAPI +FreePageTableMap ( + IN UNIT_TEST_CONTEXT Context + ) +{ + if (mMap.Entries != NULL) { + FreePages (mMap.Entries, mMap.EntryPagesAllocated); + } +} + +/** + Populate the global flat page table map. + + @param[in] Context Unit test context. + + @retval EFI_SUCCESS The page table is parsed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the page table map. + @retval EFI_INVALID_PARAMETER An error occurred while parsing the page table. +**/ +STATIC +UNIT_TEST_STATUS +EFIAPI +PopulatePageTableMap ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + + Status = CreateFlatPageTable (&mMap); + + while (Status == RETURN_BUFFER_TOO_SMALL) { + if ((mMap.Entries != NULL) && (mMap.EntryPagesAllocated > 0)) { + FreePages (mMap.Entries, mMap.EntryPagesAllocated); + } + + mMap.EntryPagesAllocated = EFI_SIZE_TO_PAGES (mMap.EntryCount * sizeof (PAGE_MAP_ENTRY)); + mMap.Entries = AllocatePages (mMap.EntryPagesAllocated); + + if (mMap.Entries == NULL) { + UT_LOG_ERROR ("Failed to allocate %d pages for page table map!\n", mMap.EntryPagesAllocated); + return UNIT_TEST_ERROR_PREREQUISITE_NOT_MET; + } + + Status = CreateFlatPageTable (&mMap); + } + + if ((Status != EFI_SUCCESS) && (mMap.Entries != NULL)) { + FreePageTableMap (Context); + } + + return Status == EFI_SUCCESS ? UNIT_TEST_PASSED : UNIT_TEST_ERROR_PREREQUISITE_NOT_MET; +} /** Populates the non protected image list global @@ -114,6 +180,7 @@ GetSpecialRegions ( @retval TRUE The region is allowed to be read/write/execute @retval FALSE The region is not allowed to be read/write/execute **/ +STATIC BOOLEAN CanRegionBeRWX ( IN UINT64 Address, @@ -179,6 +246,51 @@ CanRegionBeRWX ( return FALSE; } +/** + Check the page table for Read/Write/Execute regions. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed + +**/ +UNIT_TEST_STATUS +EFIAPI +NoReadWriteExecute ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINTN Index; + BOOLEAN FoundRWXAddress; + + UT_ASSERT_NOT_NULL (mMap.Entries); + UT_ASSERT_NOT_EQUAL (mMap.EntryCount, 0); + + Index = 0; + FoundRWXAddress = FALSE; + + for ( ; Index < mMap.EntryCount; Index++) { + if (IsPageExecutable (mMap.Entries[Index].PageEntry) && + IsPageReadable (mMap.Entries[Index].PageEntry) && + IsPageWritable (mMap.Entries[Index].PageEntry)) + { + if (!CanRegionBeRWX (mMap.Entries[Index].LinearAddress, mMap.Entries[Index].Length)) { + UT_LOG_ERROR ( + "Memory Range 0x%llx-0x%llx is Read/Write/Execute\n", + mMap.Entries[Index].LinearAddress, + mMap.Entries[Index].LinearAddress + mMap.Entries[Index].Length + ); + FoundRWXAddress = TRUE; + } + } + } + + UT_ASSERT_FALSE (FoundRWXAddress); + + return UNIT_TEST_PASSED; +} + /** Locates and opens the SFS volume containing the application and, if successful, returns an @@ -401,7 +513,7 @@ DxePagingAuditTestAppEntryPoint ( DEBUG ((DEBUG_ERROR, "%a - Unable to fetch the GCD memory map. Test results may be inaccurate. Status: %r\n", __FUNCTION__, Status)); } - AddTestCase (Misc, "No pages can be read,write,execute", "Security.Misc.NoReadWriteExecute", NoReadWriteExecute, NULL, NULL, NULL); + AddTestCase (Misc, "No pages can be read,write,execute", "Security.Misc.NoReadWriteExecute", NoReadWriteExecute, PopulatePageTableMap, FreePageTableMap, NULL); // // Execute the tests. diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.h b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.h deleted file mode 100644 index 57e94839c6..0000000000 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.h +++ /dev/null @@ -1,45 +0,0 @@ -/** @file -- DxePagingAuditTestApp.h -This Shell App tests the page table or writes page table and -memory map information to SFS - -Copyright (c) Microsoft Corporation. -SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include "../../PagingAuditCommon.h" - -#include -#include -#include - -/** - Check the page table for Read/Write/Execute regions. - - @param[in] Context Unit test context - - @retval UNIT_TEST_PASSED The unit test passed - @retval other The unit test failed - -**/ -UNIT_TEST_STATUS -EFIAPI -NoReadWriteExecute ( - IN UNIT_TEST_CONTEXT Context - ); - -/** - Checks if a region is allowed to be read/write/execute based on the special region array - and non protected image list - - @param[in] Address Start address of the region - @param[in] Length Length of the region - - @retval TRUE The region is allowed to be read/write/execute - @retval FALSE The region is not allowed to be read/write/execute -**/ -BOOLEAN -CanRegionBeRWX ( - IN UINT64 Address, - IN UINT64 Length - ); diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/X64/DxePagingAuditTests.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/X64/DxePagingAuditTests.c deleted file mode 100644 index fbce0784c5..0000000000 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/X64/DxePagingAuditTests.c +++ /dev/null @@ -1,79 +0,0 @@ -/** @file -- DxePagingAuditTests.c - X64 implementations for DXE paging audit tests - - Copyright (c) Microsoft Corporation. - SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include -#include - -#include "../DxePagingAuditTestApp.h" - -/** - Check the page table for Read/Write/Execute regions. - - @param[in] Context Unit test context - - @retval UNIT_TEST_PASSED The unit test passed - @retval other The unit test failed - -**/ -UNIT_TEST_STATUS -EFIAPI -NoReadWriteExecute ( - IN UNIT_TEST_CONTEXT Context - ) -{ - IA32_MAP_ENTRY *Map; - UINTN MapCount; - UINTN Index; - BOOLEAN FoundRWXAddress; - IA32_CR4 Cr4; - PAGING_MODE PagingMode; - UINTN PagesAllocated = 0; - EFI_STATUS Status; - - Map = NULL; - MapCount = 0; - Index = 0; - FoundRWXAddress = FALSE; - - // Poll CR4 to deterimine the page table depth - Cr4.UintN = AsmReadCr4 (); - - if (Cr4.Bits.LA57 != 0) { - PagingMode = Paging5Level; - } else { - PagingMode = Paging4Level; - } - - // CR3 is the page table pointer - Status = PageTableParse (AsmReadCr3 (), PagingMode, NULL, &MapCount); - - while (Status == RETURN_BUFFER_TOO_SMALL) { - if ((Map != NULL) && (PagesAllocated > 0)) { - FreePages (Map, PagesAllocated); - } - - PagesAllocated = EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY)); - Map = AllocatePages (PagesAllocated); - - UT_ASSERT_NOT_NULL (Map); - Status = PageTableParse (AsmReadCr3 (), PagingMode, Map, &MapCount); - } - - for ( ; Index < MapCount; Index++) { - if ((Map[Index].Attribute.Bits.ReadWrite != 0) && (Map[Index].Attribute.Bits.Nx == 0)) { - if (!CanRegionBeRWX (Map[Index].LinearAddress, Map[Index].Length)) { - UT_LOG_ERROR ("Memory Range 0x%llx-0x%llx is Read/Write/Execute\n", Map[Index].LinearAddress, Map[Index].LinearAddress + Map[Index].Length); - FoundRWXAddress = TRUE; - } - } - } - - UT_ASSERT_FALSE (FoundRWXAddress); - - return UNIT_TEST_PASSED; -} diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf index f5c30cc8a6..0ed078d09e 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf @@ -18,17 +18,14 @@ [Sources] Dxe/App/DxePagingAuditTestApp.c - Dxe/App/DxePagingAuditTestApp.h PagingAuditCommon.c PagingAuditCommon.h [Sources.X64] X64/PagingAuditProcessor.c - Dxe/App/X64/DxePagingAuditTests.c [Sources.AARCH64] AArch64/PagingAuditProcessor.c - Dxe/App/AArch64/DxePagingAuditTests.c [Packages.AARCH64] ArmPkg/ArmPkg.dec @@ -53,6 +50,7 @@ UnitTestLib DxeMemoryProtectionHobLib FileHandleLib + FlatPageTableLib [LibraryClasses.AARCH64] ArmLib