From 4c5d1d1c3c78d4329a550ba6f5d049834d456dcc Mon Sep 17 00:00:00 2001 From: Oliver Smith-Denny Date: Tue, 10 Oct 2023 16:18:32 -0700 Subject: [PATCH 01/15] Only call HdwPortWrite if DebugLevel Met (#311) ## Description The DebugLevel is checked twice in the hot path on serial path writes and the HdwPortWrite call is made even if the upper layer knows that the message being logged does not meet the DebugLevel criteria. Closes #309. In order to maintain backwards compatibility, if the LoggerInfo block is found to have a version less than the hardware logging level version, the PCD is checked to decide whether to call HdwPortWrite or not. In SEC, because we may not have the LoggerInfo structure, we check the PCD to see if the message should be logged at this DebugLevel. For each item, place an "x" in between `[` and `]` if true. Example: `[x]`. _(you can also check items in the GitHub UI)_ - [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, ... - [ ] 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 Build tested. ## Integration Instructions If a bespoke AdvLoggerHdwPortLib is used, AdvLoggerHdwPortWrite should not check DebugLevel, but simply write the message to the hardware port. Co-authored-by: kuqin12 <42554914+kuqin12@users.noreply.github.com> --- .../AdvancedLoggerHdwPortLib.c | 9 +-------- .../AdvancedLoggerHdwPortLib.inf | 7 +------ .../AdvancedLoggerLib/AdvancedLoggerCommon.c | 17 ++++++++++++++--- .../AdvancedLoggerLib/Dxe/AdvancedLoggerLib.inf | 1 + .../MmCore/AdvancedLoggerLib.inf | 5 ++++- .../AdvancedLoggerLib/Pei/AdvancedLoggerLib.inf | 1 + .../Pei64/AdvancedLoggerLib.inf | 1 + .../Runtime/AdvancedLoggerLib.inf | 1 + .../AdvancedLoggerLib/Sec/AdvancedLoggerLib.inf | 1 + .../SmmCore/AdvancedLoggerLib.inf | 1 + 10 files changed, 26 insertions(+), 18 deletions(-) diff --git a/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.c b/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.c index 7a480e0593..1b18fe1548 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.c +++ b/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.c @@ -58,12 +58,5 @@ AdvancedLoggerHdwPortWrite ( IN UINTN NumberOfBytes ) { - UINTN NumberReturned; - - NumberReturned = NumberOfBytes; - if (DebugLevel & PcdGet32 (PcdAdvancedLoggerHdwPortDebugPrintErrorLevel)) { - NumberReturned = SerialPortWrite (Buffer, NumberOfBytes); - } - - return NumberReturned; + return SerialPortWrite (Buffer, NumberOfBytes); } diff --git a/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.inf index 25a1b6e26d..1c275a0dac 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerHdwPortLib/AdvancedLoggerHdwPortLib.inf @@ -1,5 +1,5 @@ ## @file -# Advanced Logger Access library. +# Advanced Logger Hardware Port Library. # # Copyright (c) Microsoft Corporation. # @@ -30,10 +30,5 @@ [LibraryClasses] SerialPortLib -[Protocols] - -[Pcd] - gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## CONSUMES - [Depex] TRUE diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/AdvancedLoggerCommon.c b/AdvLoggerPkg/Library/AdvancedLoggerLib/AdvancedLoggerCommon.c index 2638db2b33..40a829a9fa 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/AdvancedLoggerCommon.c +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/AdvancedLoggerCommon.c @@ -142,13 +142,24 @@ AdvancedLoggerWrite ( // If LoggerInfo == NULL, assume there is a HdwPort and it has not been disabled. This // does occur in SEC if ((LoggerInfo == NULL) || (!LoggerInfo->HdwPortDisabled)) { + if (DebugLevel & PcdGet32 (PcdAdvancedLoggerHdwPortDebugPrintErrorLevel)) { + AdvancedLoggerHdwPortWrite (DebugLevel, (UINT8 *)Buffer, NumberOfBytes); + } + } + #else if ((LoggerInfo != NULL) && (!LoggerInfo->HdwPortDisabled)) { + // if we are at a high enough version to support HW_LVL logging, only call the HdwPortWrite if this DebugLevel + // is asked to be logged + // if we are at an older version, check the PCD to see if we should log this message if (LoggerInfo->Version >= ADVANCED_LOGGER_HW_LVL_VER) { - DebugLevel = (DebugLevel & LoggerInfo->HwPrintLevel); + if (DebugLevel & LoggerInfo->HwPrintLevel) { + AdvancedLoggerHdwPortWrite (DebugLevel, (UINT8 *)Buffer, NumberOfBytes); + } + } else if (DebugLevel & PcdGet32 (PcdAdvancedLoggerHdwPortDebugPrintErrorLevel)) { + AdvancedLoggerHdwPortWrite (DebugLevel, (UINT8 *)Buffer, NumberOfBytes); } + } #endif - AdvancedLoggerHdwPortWrite (DebugLevel, (UINT8 *)Buffer, NumberOfBytes); - } } diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/Dxe/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/Dxe/AdvancedLoggerLib.inf index 88e663c53c..c8a1133e38 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/Dxe/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/Dxe/AdvancedLoggerLib.inf @@ -37,3 +37,4 @@ gEfiDebugPortProtocolGuid ## CONSUMES [Pcd] + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/MmCore/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/MmCore/AdvancedLoggerLib.inf index 5273e39b2e..eac348b636 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/MmCore/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/MmCore/AdvancedLoggerLib.inf @@ -40,5 +40,8 @@ [Guids] gAdvancedLoggerHobGuid +[Pcd] + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES + [Depex] - TRUE \ No newline at end of file + TRUE diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei/AdvancedLoggerLib.inf index 4d14cc22b9..347dc0b7b4 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei/AdvancedLoggerLib.inf @@ -36,3 +36,4 @@ gAdvancedLoggerPpiGuid ## CONSUMES [Pcd] + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei64/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei64/AdvancedLoggerLib.inf index 9f2cacaef5..5293106094 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei64/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/Pei64/AdvancedLoggerLib.inf @@ -39,3 +39,4 @@ [Pcd] gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerBase ## CONSUMES + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/Runtime/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/Runtime/AdvancedLoggerLib.inf index 9e34e04672..eb9f355af1 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/Runtime/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/Runtime/AdvancedLoggerLib.inf @@ -43,3 +43,4 @@ gAdvancedLoggerProtocolGuid ## CONSUMES [Pcd] + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/Sec/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/Sec/AdvancedLoggerLib.inf index a5f332126a..976f03d134 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/Sec/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/Sec/AdvancedLoggerLib.inf @@ -45,6 +45,7 @@ gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerBase ## CONSUMES gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerPreMemPages ## CONSUMES gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerPages ## CONSUMES + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## CONSUMES [BuildOptions] *_*_*_CC_FLAGS = -D ADVANCED_LOGGER_SEC=1 diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/SmmCore/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/SmmCore/AdvancedLoggerLib.inf index 2b03dc9870..480a989d6e 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/SmmCore/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/SmmCore/AdvancedLoggerLib.inf @@ -45,3 +45,4 @@ gAdvancedLoggerProtocolGuid ## CONSUMES [Pcd] + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES From 115ef0b24bdaf267d3ac08171fdd85a575704084 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 02:31:55 +0000 Subject: [PATCH 02/15] pip: bump edk2-pytool-library from 0.18.2 to 0.19.0 (#330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [edk2-pytool-library](https://github.com/tianocore/edk2-pytool-library) from 0.18.2 to 0.19.0.
Release notes

Sourced from edk2-pytool-library's releases.

v0.19.0

What's Changed

Breaking Changes

edk2-pytool-library has officially dropped support for python 3.9 with this release. To continue to receive improvements to edk2-pytool-library passed v0.18, you will need to upgrade to a supported version of python (3.10, 3.11, or 3.12).

Full Changelog: https://github.com/tianocore/edk2-pytool-library/compare/v0.18.2...v0.19.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=edk2-pytool-library&package-manager=pip&previous-version=0.18.2&new-version=0.19.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pip-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pip-requirements.txt b/pip-requirements.txt index 13a3bbd902..c064e93a27 100644 --- a/pip-requirements.txt +++ b/pip-requirements.txt @@ -12,7 +12,7 @@ # https://www.python.org/dev/peps/pep-0440/#version-specifiers ## -edk2-pytool-library==0.18.2 +edk2-pytool-library==0.19.0 edk2-pytool-extensions==0.24.1 edk2-basetools==0.1.29 antlr4-python3-runtime==4.13.1 From bd605b16a2008a25dfc86126685a2c6928388200 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 04:28:39 +0000 Subject: [PATCH 03/15] pip: bump edk2-pytool-extensions from 0.24.1 to 0.25.0 (#329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [edk2-pytool-extensions](https://github.com/tianocore/edk2-pytool-extensions) from 0.24.1 to 0.25.0.
Release notes

Sourced from edk2-pytool-extensions's releases.

v0.25.0

What's Changed

Breaking Changes

edk2-pytool-extensions has officially dropped support for python 3.9 with this release. To continue to receive improvements to edk2-pytool-extensions, passed v0.24, you will need to upgrade to a supported version of python (3.10, 3.11, or 3.12).

Dependency Updates

Full Changelog: https://github.com/tianocore/edk2-pytool-extensions/compare/v0.24.1...v0.25.0

Commits
  • 3c2e15c Update Supported Python versions to 3.10, 3.11, and 3.12 (#669)
  • d061515 build(deps-dev): bump ruff from 0.0.290 to 0.0.291 (#666)
  • 834d7e8 build(deps-dev): bump coverage from 7.3.0 to 7.3.1 (#667)
  • d09361e build(deps-dev): bump mkdocstrings-python from 1.7.0 to 1.7.1 (#668)
  • f88f276 edk2toolext/edk2_logging.py: Log rust errors (#665)
  • 21f9d65 build(deps-dev): bump mkdocs-material from 9.2.5 to 9.4.2 (#664)
  • 355d8d4 build(deps-dev): bump setuptools from 68.1.2 to 68.2.2 (#661)
  • 0ac595d build(deps-dev): bump black from 23.7.0 to 23.9.1 (#659)
  • b720c08 build(deps-dev): bump mkdocstrings-python from 1.6.0 to 1.7.0 (#662)
  • 2968d2f build(deps-dev): bump mkdocstrings[python] from 0.22.0 to 0.23.0 (#663)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=edk2-pytool-extensions&package-manager=pip&previous-version=0.24.1&new-version=0.25.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pip-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pip-requirements.txt b/pip-requirements.txt index c064e93a27..6eec245971 100644 --- a/pip-requirements.txt +++ b/pip-requirements.txt @@ -13,7 +13,7 @@ ## edk2-pytool-library==0.19.0 -edk2-pytool-extensions==0.24.1 +edk2-pytool-extensions==0.25.0 edk2-basetools==0.1.29 antlr4-python3-runtime==4.13.1 regex==2023.10.3 From ec920bacb71f973240723115c3ecb3f214c827c3 Mon Sep 17 00:00:00 2001 From: "Project Mu UEFI Bot [bot]" <45776386+uefibot@users.noreply.github.com> Date: Wed, 11 Oct 2023 02:16:08 -0400 Subject: [PATCH 04/15] Repo File Sync: synced file(s) with microsoft/mu_devops (#328) --- .azurepipelines/MuDevOpsWrapper.yml | 2 +- .github/workflows/auto-approve.yml | 2 +- .github/workflows/auto-merge.yml | 2 +- .github/workflows/codeql.yml | 4 ++-- .github/workflows/issue-assignment.yml | 2 +- .github/workflows/label-issues.yml | 2 +- .github/workflows/label-sync.yml | 2 +- .github/workflows/release-draft.yml | 2 +- .github/workflows/stale.yml | 2 +- .github/workflows/triage-issues.yml | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.azurepipelines/MuDevOpsWrapper.yml b/.azurepipelines/MuDevOpsWrapper.yml index e16e59919a..1735ec82bc 100644 --- a/.azurepipelines/MuDevOpsWrapper.yml +++ b/.azurepipelines/MuDevOpsWrapper.yml @@ -17,7 +17,7 @@ resources: type: github endpoint: microsoft name: microsoft/mu_devops - ref: refs/tags/v6.5.1 + ref: refs/tags/v7.0.0 parameters: - name: do_ci_build diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 77928eaeb5..eeb9bc007c 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -25,5 +25,5 @@ jobs: approval_check: if: | github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'uefibot' - uses: microsoft/mu_devops/.github/workflows/AutoApprover.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/AutoApprover.yml@v7.0.0 secrets: inherit diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index b3c0bba446..306bef54c0 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -26,5 +26,5 @@ jobs: merge_check: if: | github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'uefibot' - uses: microsoft/mu_devops/.github/workflows/AutoMerger.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/AutoMerger.yml@v7.0.0 secrets: inherit diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 466bda220b..cb059e8d27 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,7 +48,7 @@ jobs: - name: Install Python uses: actions/setup-python@v4 with: - python-version: '>=3.11' + python-version: '3.12' - name: Generate Package Matrix id: generate_matrix @@ -94,7 +94,7 @@ jobs: - name: Install Python uses: actions/setup-python@v4 with: - python-version: '>=3.11' + python-version: '3.12' cache: 'pip' cache-dependency-path: 'pip-requirements.txt' diff --git a/.github/workflows/issue-assignment.yml b/.github/workflows/issue-assignment.yml index f6e32cd987..a6799388cd 100644 --- a/.github/workflows/issue-assignment.yml +++ b/.github/workflows/issue-assignment.yml @@ -18,4 +18,4 @@ on: jobs: apply: - uses: microsoft/mu_devops/.github/workflows/IssueAssignment.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/IssueAssignment.yml@v7.0.0 diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yml index 6490ecdc11..f1e54d4bbf 100644 --- a/.github/workflows/label-issues.yml +++ b/.github/workflows/label-issues.yml @@ -31,4 +31,4 @@ on: jobs: apply: - uses: microsoft/mu_devops/.github/workflows/Labeler.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/Labeler.yml@v7.0.0 diff --git a/.github/workflows/label-sync.yml b/.github/workflows/label-sync.yml index 79de1ec6fc..a6610549f0 100644 --- a/.github/workflows/label-sync.yml +++ b/.github/workflows/label-sync.yml @@ -24,4 +24,4 @@ on: jobs: sync: - uses: microsoft/mu_devops/.github/workflows/LabelSyncer.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/LabelSyncer.yml@v7.0.0 diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml index afbff8a8c8..6e8beff455 100644 --- a/.github/workflows/release-draft.yml +++ b/.github/workflows/release-draft.yml @@ -27,5 +27,5 @@ on: jobs: draft: - uses: microsoft/mu_devops/.github/workflows/ReleaseDrafter.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/ReleaseDrafter.yml@v7.0.0 secrets: inherit diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6c1c733037..95b9998d83 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -24,4 +24,4 @@ on: jobs: check: - uses: microsoft/mu_devops/.github/workflows/Stale.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/Stale.yml@v7.0.0 diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index e226281334..eb16fbdcec 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -19,4 +19,4 @@ on: jobs: triage: - uses: microsoft/mu_devops/.github/workflows/IssueTriager.yml@v6.5.1 + uses: microsoft/mu_devops/.github/workflows/IssueTriager.yml@v7.0.0 From 142315478f245d0cb686286d79502912397530c1 Mon Sep 17 00:00:00 2001 From: kuqin12 <42554914+kuqin12@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:16:46 -0700 Subject: [PATCH 05/15] AdvLoggerPkg: BaseAdvancedLoggerLib: Fixing a missed PCD for AARCH64 usage (#331) # Preface Please ensure you have read the [contribution docs](https://github.com/microsoft/mu/blob/master/CONTRIBUTING.md) prior to submitting the pull request. In particular, [pull request guidelines](https://github.com/microsoft/mu/blob/master/CONTRIBUTING.md#pull-request-best-practices). ## Description There was a missed PCD not caught in the previous PR (https://github.com/microsoft/mu_plus/pull/311) when it comes to the usage on AARCH64 platform. This change added the PCD entry in the library inf file. For each item, place an "x" in between `[` and `]` if true. Example: `[x]`. _(you can also check items in the GitHub UI)_ - [ ] 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, ... - [ ] 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 This was tested on FVP based AARCH64 platform. ## Integration Instructions N/A --- .../Library/AdvancedLoggerLib/BaseArm/AdvancedLoggerLib.inf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AdvLoggerPkg/Library/AdvancedLoggerLib/BaseArm/AdvancedLoggerLib.inf b/AdvLoggerPkg/Library/AdvancedLoggerLib/BaseArm/AdvancedLoggerLib.inf index 0d0290fd1c..10db7ffc89 100644 --- a/AdvLoggerPkg/Library/AdvancedLoggerLib/BaseArm/AdvancedLoggerLib.inf +++ b/AdvLoggerPkg/Library/AdvancedLoggerLib/BaseArm/AdvancedLoggerLib.inf @@ -37,7 +37,8 @@ SynchronizationLib [Pcd] - gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerBase + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerBase ## CONSUMES + gAdvLoggerPkgTokenSpaceGuid.PcdAdvancedLoggerHdwPortDebugPrintErrorLevel ## SOMETIMES_CONSUMES [Depex] - TRUE \ No newline at end of file + TRUE From f58171d7ee43338a939db0fab782295f15e67353 Mon Sep 17 00:00:00 2001 From: kuqin12 <42554914+kuqin12@users.noreply.github.com> Date: Wed, 11 Oct 2023 19:28:02 -0700 Subject: [PATCH 06/15] Document current data flow of debug logging filtering (#332) ## Description This change adds a short description of how logging level works in advanced logger as well as a flowchart for visualization. - [ ] 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, ... - [ ] 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, ... - [x] 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 N/A ## Integration Instructions N/A --------- Co-authored-by: Michael Kubacki --- AdvLoggerPkg/Docs/ReadMe.md | 6 ++++++ AdvLoggerPkg/Docs/debug_log_level.png | Bin 0 -> 66582 bytes 2 files changed, 6 insertions(+) create mode 100644 AdvLoggerPkg/Docs/debug_log_level.png diff --git a/AdvLoggerPkg/Docs/ReadMe.md b/AdvLoggerPkg/Docs/ReadMe.md index 831fe471f6..18f4216899 100644 --- a/AdvLoggerPkg/Docs/ReadMe.md +++ b/AdvLoggerPkg/Docs/ReadMe.md @@ -247,6 +247,12 @@ and the follow change is needed in the .fdf: The v3 data header supports a new field of hardware debugging level to support setting the serial print configurable during boot time. +All debug prints will be filtered by multiple build time flags, such as `PcdDebugPrintErrorLevel`, `MDEPKG_NDEBUG`, etc. +Prints that pass such filters will be logged to memory, and then fed to the hardware port library to be checked +against the hardware print level. A full data flow chart is shown below: + +![Debug Logging Level Filters](debug_log_level.png) + The default value will be initialized to the value of `PcdAdvancedLoggerHdwPortDebugPrintErrorLevel` in the PEI core, DXE core, or MM core, whichever comes first during the boot process. diff --git a/AdvLoggerPkg/Docs/debug_log_level.png b/AdvLoggerPkg/Docs/debug_log_level.png new file mode 100644 index 0000000000000000000000000000000000000000..2df74e1409fe14df91de70828bf0bc5b3a0659bf GIT binary patch literal 66582 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 literal 0 HcmV?d00001 From 0b235e6e4c1de3ab753948d30eab233daa060100 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Mon, 2 Oct 2023 20:46:16 -0700 Subject: [PATCH 07/15] Paging Audit: Add 8 Tests to Shell App Description This change adds 8 tests to the paging audit shell app. Which check the following: 1. Unallocated memory is EFI_MEMORY_RP 2. Memory Attribute Protocol is present 3. Calls to allocate pages and pools return buffers with restrictive access attributes 4. NULL page is EFI_MEMORY_RP 5. MMIO Regions are Non Executable 6. Image code sections are EFI_MEMORY_RO and and data sections are EFI_MEMORY_XP 7. BSP stack is EFI_MEMORY_XP and has EFI_MEMORY_RP guard page 8. Memory outside of the EFI Memory Map is inaccessible - [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, ... - [ ] 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, ... - [x] 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 Integration Instructions N/A --- .../UEFI/Dxe/App/DxePagingAuditTestApp.c | 1289 +++++++++++++++-- .../UEFI/DxePagingAuditTestApp.inf | 3 + UefiTestingPkg/UefiTestingPkg.ci.yaml | 3 +- 3 files changed, 1195 insertions(+), 100 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c index cf1b869906..f37a70a4f5 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c @@ -14,45 +14,124 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include #include #include +#include +#include #define UNIT_TEST_APP_NAME "Paging Audit Test" -#define UNIT_TEST_APP_VERSION "1" -#define MAX_CHARS_TO_READ 3 +#define UNIT_TEST_APP_VERSION "2" +#define MAX_CHARS_TO_READ 4 // TRUE if A interval subsumes B interval #define CHECK_SUBSUMPTION(AStart, AEnd, BStart, BEnd) \ ((AStart <= BStart) && (AEnd >= BEnd)) -CHAR8 *mMemoryInfoDatabaseBuffer = NULL; -UINTN mMemoryInfoDatabaseSize = 0; -UINTN mMemoryInfoDatabaseAllocSize = 0; -MEMORY_PROTECTION_SPECIAL_REGION *mSpecialRegions = NULL; -IMAGE_RANGE_DESCRIPTOR *mNonProtectedImageList = NULL; -UINTN mSpecialRegionCount = 0; -EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mMemorySpaceMap = NULL; -UINTN mMemorySpaceMapCount = 0; -PAGE_MAP mMap = { 0 }; +// TRUE if A and B have overlapping intervals +#define CHECK_OVERLAP(AStart, AEnd, BStart, BEnd) \ + ((AEnd > AStart) && (BEnd > BStart) && \ + ((AStart <= BStart && AEnd > BStart) || \ + (BStart <= AStart && BEnd > AStart))) + +// Aligns the input address down to the nearest page boundary +#define ALIGN_ADDRESS(Address) ((Address / EFI_PAGE_SIZE) * EFI_PAGE_SIZE) + +// Globals required to create the memory info database +CHAR8 *mMemoryInfoDatabaseBuffer = NULL; +UINTN mMemoryInfoDatabaseSize = 0; +UINTN mMemoryInfoDatabaseAllocSize = 0; + +// Globals for memory protection special regions +MEMORY_PROTECTION_SPECIAL_REGION *mSpecialRegions = NULL; +UINTN mSpecialRegionCount = 0; + +// Global for the non-protected image list +IMAGE_RANGE_DESCRIPTOR *mNonProtectedImageList = NULL; + +// Globals for the memory space map +EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mMemorySpaceMap = NULL; +UINTN mMemorySpaceMapCount = 0; + +// Globals for the EFI memory map +UINTN mEfiMemoryMapSize = 0; +EFI_MEMORY_DESCRIPTOR *mEfiMemoryMap = NULL; +UINTN mEfiMemoryMapDescriptorSize = 0; + +// Global for the flat page table +PAGE_MAP mMap = { 0 }; + +// ------------------------------------------------- +// GLOBALS SUPPORT FUNCTIONS +// ------------------------------------------------- /** - Frees the entries in the mMap global. + Return if the PE image section is aligned. This function must + only be called using a loaded image's code type or EfiReservedMemoryType. + Calling with a different type will ASSERT. + + @param[in] SectionAlignment PE/COFF section alignment + @param[in] MemoryType PE/COFF image memory type + + @retval TRUE The PE image section is aligned. + @retval FALSE The PE image section is not aligned. +**/ +BOOLEAN +IsLoadedImageSectionAligned ( + IN UINT32 SectionAlignment, + IN EFI_MEMORY_TYPE MemoryType + ) +{ + UINT32 PageAlignment; + + switch (MemoryType) { + case EfiRuntimeServicesCode: + case EfiACPIMemoryNVS: + PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + break; + case EfiRuntimeServicesData: + case EfiACPIReclaimMemory: + ASSERT (FALSE); + PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + break; + case EfiBootServicesCode: + case EfiLoaderCode: + case EfiReservedMemoryType: + PageAlignment = EFI_PAGE_SIZE; + break; + default: + ASSERT (FALSE); + PageAlignment = EFI_PAGE_SIZE; + break; + } + + if ((SectionAlignment & (PageAlignment - 1)) != 0) { + return FALSE; + } else { + return TRUE; + } +} - @param[in] Context Unit test context. +/** + Frees the entries in the mMap global. **/ STATIC VOID -EFIAPI FreePageTableMap ( - IN UNIT_TEST_CONTEXT Context + VOID ) { if (mMap.Entries != NULL) { FreePages (mMap.Entries, mMap.EntryPagesAllocated); + mMap.Entries = NULL; } + + mMap.ArchSignature = 0; + mMap.EntryCount = 0; + mMap.EntryPagesAllocated = 0; } /** @@ -65,19 +144,23 @@ FreePageTableMap ( @retval EFI_INVALID_PARAMETER An error occurred while parsing the page table. **/ STATIC -UNIT_TEST_STATUS -EFIAPI +EFI_STATUS PopulatePageTableMap ( - IN UNIT_TEST_CONTEXT Context + VOID ) { EFI_STATUS Status; + if (mMap.Entries != NULL) { + return EFI_SUCCESS; + } + Status = CreateFlatPageTable (&mMap); - while (Status == RETURN_BUFFER_TOO_SMALL) { + while (Status == EFI_BUFFER_TOO_SMALL) { if ((mMap.Entries != NULL) && (mMap.EntryPagesAllocated > 0)) { FreePages (mMap.Entries, mMap.EntryPagesAllocated); + mMap.Entries = NULL; } mMap.EntryPagesAllocated = EFI_SIZE_TO_PAGES (mMap.EntryCount * sizeof (PAGE_MAP_ENTRY)); @@ -92,17 +175,49 @@ PopulatePageTableMap ( } if ((Status != EFI_SUCCESS) && (mMap.Entries != NULL)) { - FreePageTableMap (Context); + FreePageTableMap (); } - return Status == EFI_SUCCESS ? UNIT_TEST_PASSED : UNIT_TEST_ERROR_PREREQUISITE_NOT_MET; + return Status; } /** - Populates the non protected image list global + Frees the mNonProtectedImageList global **/ +STATIC VOID -GetNonProtectedImageList ( +FreeNonProtectedImageList ( + VOID + ) +{ + LIST_ENTRY *ImageRecordLink; + IMAGE_RANGE_DESCRIPTOR *CurrentImageRangeDescriptor; + + if (mNonProtectedImageList != NULL) { + ImageRecordLink = &mNonProtectedImageList->Link; + while (!IsListEmpty (ImageRecordLink)) { + CurrentImageRangeDescriptor = CR ( + ImageRecordLink->ForwardLink, + IMAGE_RANGE_DESCRIPTOR, + Link, + IMAGE_RANGE_DESCRIPTOR_SIGNATURE + ); + + RemoveEntryList (&CurrentImageRangeDescriptor->Link); + FreePool (CurrentImageRangeDescriptor); + } + + FreePool (mNonProtectedImageList); + mNonProtectedImageList = NULL; + } +} + +/** + Populates the mNonProtectedImageList global +**/ +STATIC +EFI_STATUS +PopulateNonProtectedImageList ( VOID ) { @@ -112,7 +227,7 @@ GetNonProtectedImageList ( MemoryProtectionProtocol = NULL; if (mNonProtectedImageList != NULL) { - return; + return EFI_SUCCESS; } Status = gBS->LocateProtocol ( @@ -132,13 +247,36 @@ GetNonProtectedImageList ( DEBUG ((DEBUG_ERROR, "%a:%d - Unable to fetch non-protected image list\n", __FUNCTION__, __LINE__)); mNonProtectedImageList = NULL; } + + return Status; } /** - Populates the special region array global + Frees the mSpecialRegions global **/ +STATIC VOID -GetSpecialRegions ( +FreeSpecialRegions ( + VOID + ) +{ + if (mSpecialRegions != NULL) { + FreePool (mSpecialRegions); + mSpecialRegions = NULL; + } + + mSpecialRegionCount = 0; +} + +/** + Populates the special region array global + + @retval EFI_SUCCESS The special region array is populated successfully. + @retval other An error occurred while populating the special region array. +**/ +STATIC +EFI_STATUS +PopulateSpecialRegions ( VOID ) { @@ -148,7 +286,7 @@ GetSpecialRegions ( SpecialRegionProtocol = NULL; if (mSpecialRegions != NULL) { - return; + return EFI_SUCCESS; } Status = gBS->LocateProtocol ( @@ -166,10 +304,254 @@ GetSpecialRegions ( if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a:%d - Unable to fetch special region list\n", __FUNCTION__, __LINE__)); - mNonProtectedImageList = NULL; + mSpecialRegions = NULL; + } + + return Status; +} + +/** + Frees the mMemorySpaceMap global +**/ +STATIC +VOID +FreeMemorySpaceMap ( + VOID + ) +{ + if (mMemorySpaceMap != NULL) { + FreePool (mMemorySpaceMap); + mMemorySpaceMap = NULL; + } + + mMemorySpaceMapCount = 0; +} + +/** + Populates the memory space map global + + @retval EFI_SUCCESS The memory space map is populated successfully. + @retval other An error occurred while populating the memory space map. +**/ +STATIC +EFI_STATUS +PopulateMemorySpaceMap ( + VOID + ) +{ + EFI_STATUS Status; + + if (mMemorySpaceMap != NULL) { + return EFI_SUCCESS; + } + + Status = gDS->GetMemorySpaceMap (&mMemorySpaceMapCount, &mMemorySpaceMap); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a:%d - Unable to fetch memory space map\n", __FUNCTION__, __LINE__)); + mMemorySpaceMap = NULL; + } + + return Status; +} + +/** + Frees the memory space map global +**/ +STATIC +VOID +FreeEfiMemoryMap ( + VOID + ) +{ + if (mEfiMemoryMap != NULL) { + FreePool (mEfiMemoryMap); + mEfiMemoryMap = NULL; + } + + mEfiMemoryMapSize = 0; + mEfiMemoryMapDescriptorSize = 0; +} + +/** + Populates the memory space map global + + @retval EFI_SUCCESS The memory space map is populated successfully. + @retval other An error occurred while populating the memory space map. +**/ +STATIC +EFI_STATUS +PopulateEfiMemoryMap ( + VOID + ) +{ + UINTN EfiMapKey; + UINT32 EfiDescriptorVersion; + EFI_STATUS Status; + + // Get the EFI memory map. + mEfiMemoryMapSize = 0; + mEfiMemoryMap = NULL; + mEfiMemoryMapDescriptorSize = 0; + + Status = gBS->GetMemoryMap ( + &mEfiMemoryMapSize, + mEfiMemoryMap, + &EfiMapKey, + &mEfiMemoryMapDescriptorSize, + &EfiDescriptorVersion + ); + + // Loop to allocate space for the memory map and then copy it in. + do { + mEfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocateZeroPool (mEfiMemoryMapSize); + if (mEfiMemoryMap == NULL) { + ASSERT (mEfiMemoryMap != NULL); + DEBUG ((DEBUG_ERROR, "%a - Unable to allocate memory for the EFI memory map.\n", __FUNCTION__)); + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->GetMemoryMap ( + &mEfiMemoryMapSize, + mEfiMemoryMap, + &EfiMapKey, + &mEfiMemoryMapDescriptorSize, + &EfiDescriptorVersion + ); + if (EFI_ERROR (Status)) { + FreePool (mEfiMemoryMap); + } + } while (Status == EFI_BUFFER_TOO_SMALL); + + return Status; +} + +/** + Checks the input flat page/translation table for the input region and converts the associated + table entries to EFI access attributes. + + @param[in] Map Pointer to the PAGE_MAP struct to be parsed + @param[in] Address Start address of the region + @param[in] Length Length of the region + @param[out] Attributes EFI Attributes of the region + + @retval EFI_SUCCESS The output Attributes is valid + @retval EFI_INVALID_PARAMETER The flat translation table has not been built or + Attributes was NULL or Length was 0 + @retval EFI_NOT_FOUND The input region could not be found. +**/ +EFI_STATUS +EFIAPI +GetRegionCommonAccessAttributes ( + IN PAGE_MAP *Map, + IN UINT64 Address, + IN UINT64 Length, + OUT UINT64 *Attributes + ) +{ + UINTN Index; + UINT64 EntryStartAddress; + UINT64 EntryEndAddress; + UINT64 InputEndAddress; + BOOLEAN FoundRange; + + if ((Map->Entries == NULL) || (Map->EntryCount == 0) || (Attributes == NULL) || (Length == 0)) { + return EFI_INVALID_PARAMETER; + } + + FoundRange = FALSE; + Index = 0; + InputEndAddress = 0; + + if (EFI_ERROR (SafeUint64Add (Address, Length - 1, &InputEndAddress))) { + return EFI_INVALID_PARAMETER; + } + + do { + EntryStartAddress = Map->Entries[Index].LinearAddress; + if (EFI_ERROR ( + SafeUint64Add ( + Map->Entries[Index].LinearAddress, + Map->Entries[Index].Length - 1, + &EntryEndAddress + ) + )) + { + return EFI_ABORTED; + } + + if (CHECK_OVERLAP (Address, InputEndAddress, EntryStartAddress, EntryEndAddress)) { + if (!FoundRange) { + *Attributes = EFI_MEMORY_ACCESS_MASK; + FoundRange = TRUE; + } + + if (IsPageExecutable (Map->Entries[Index].PageEntry)) { + *Attributes &= ~EFI_MEMORY_XP; + } + + if (IsPageWritable (Map->Entries[Index].PageEntry)) { + *Attributes &= ~EFI_MEMORY_RO; + } + + if (IsPageReadable (Map->Entries[Index].PageEntry)) { + *Attributes &= ~EFI_MEMORY_RP; + } + + Address = EntryEndAddress + 1; + } + + if (EntryEndAddress >= InputEndAddress) { + break; + } + } while (++Index < Map->EntryCount); + + return FoundRange ? EFI_SUCCESS : EFI_NOT_FOUND; +} + +// ---------------------- +// CLEANUP FUNCTION +// ---------------------- + +/** + Cleanup function for the unit test. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED Always passes +**/ +STATIC +VOID +EFIAPI +GeneralTestCleanup ( + IN UNIT_TEST_CONTEXT Context + ) +{ + if (mMap.Entries != NULL) { + FreePageTableMap (); + } + + if (mSpecialRegions != NULL) { + FreeSpecialRegions (); + } + + if (mNonProtectedImageList != NULL) { + FreeNonProtectedImageList (); + } + + if (mMemorySpaceMap != NULL) { + FreeMemorySpaceMap (); + } + + if (mEfiMemoryMap != NULL) { + FreeEfiMemoryMap (); } } +// --------------------------------- +// UNIT TEST SUPPORT FUNCTIONS +// --------------------------------- + /** Checks if a region is allowed to be read/write/execute based on the special region array and non protected image list @@ -247,52 +629,6 @@ CanRegionBeRWX ( } /** - 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 FS handle to the opened volume. @@ -422,22 +758,774 @@ OpenAppSFS ( return Status; } +// ------------------------- +// UNIT TEST FUNCTIONS +// ------------------------- + /** - DxePagingAuditTestAppEntryPoint + Checks if the page/translation table has any read/write/execute + regions. - @param[in] ImageHandle The firmware allocated handle for the EFI image. - @param[in] SystemTable A pointer to the EFI System Table. + @param[in] Context Unit test context - @retval EFI_SUCCESS The entry point executed successfully. - @retval other Some error occurred when executing this entry point. + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed **/ -EFI_STATUS +UNIT_TEST_STATUS EFIAPI -DxePagingAuditTestAppEntryPoint ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) +NoReadWriteExecute ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINTN Index; + BOOLEAN TestFailure; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + PopulateSpecialRegions (); + PopulateNonProtectedImageList (); + UT_ASSERT_NOT_EFI_ERROR (PopulateMemorySpaceMap ()); + UT_ASSERT_NOT_NULL (mMemorySpaceMap); + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + Index = 0; + TestFailure = 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 + ); + TestFailure = TRUE; + } + } + } + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Checks that EfiConventionalMemory is EFI_MEMORY_RP or + is not mapped. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +UNIT_TEST_STATUS +EFIAPI +UnallocatedMemoryIsRP ( + IN UNIT_TEST_CONTEXT Context + ) +{ + BOOLEAN TestFailure; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; + UINTN Attributes; + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + UT_ASSERT_NOT_EFI_ERROR (PopulateEfiMemoryMap ()); + UT_ASSERT_NOT_NULL (mEfiMemoryMap); + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + TestFailure = FALSE; + + EfiMemoryMapEntry = mEfiMemoryMap; + EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)mEfiMemoryMap + mEfiMemoryMapSize); + + while (EfiMemoryMapEntry < EfiMemoryMapEnd) { + if (EfiMemoryMapEntry->Type == EfiConventionalMemory) { + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + EfiMemoryMapEntry->PhysicalStart, + EfiMemoryMapEntry->NumberOfPages * EFI_PAGE_SIZE, + &Attributes + ); + if (Status != EFI_NOT_FOUND) { + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for range 0x%llx - 0x%llx\n", + EfiMemoryMapEntry->PhysicalStart, + EfiMemoryMapEntry->PhysicalStart + (EfiMemoryMapEntry->NumberOfPages * EFI_PAGE_SIZE) + ); + TestFailure = TRUE; + } else if ((Attributes & EFI_MEMORY_RP) == 0) { + UT_LOG_ERROR ( + "Memory Range 0x%llx-0x%llx is not EFI_MEMORY_RP\n", + EfiMemoryMapEntry->PhysicalStart, + EfiMemoryMapEntry->PhysicalStart + (EfiMemoryMapEntry->NumberOfPages * EFI_PAGE_SIZE) + ); + TestFailure = TRUE; + } + } + } + + EfiMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (EfiMemoryMapEntry, mEfiMemoryMapDescriptorSize); + } + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Checks if the EFI Memory Attribute Protocol is Present. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +UNIT_TEST_STATUS +EFIAPI +IsMemoryAttributeProtocolPresent ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + EFI_MEMORY_ATTRIBUTE_PROTOCOL *MemoryAttribute; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + Status = gBS->LocateProtocol ( + &gEfiMemoryAttributeProtocolGuid, + NULL, + (VOID **)&MemoryAttribute + ); + + UT_ASSERT_NOT_EFI_ERROR (Status); + + return UNIT_TEST_PASSED; +} + +/** + Allocates Pages and Pools of each memory type and + checks that the returned buffers have restrictive + access attributes. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +UNIT_TEST_STATUS +EFIAPI +AllocatedPagesAndPoolsAreProtected ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT64 Attributes; + UINTN Index; + EFI_STATUS Status; + BOOLEAN TestFailure; + UINTN *PageAllocations[EfiMaxMemoryType]; + UINTN *PoolAllocations[EfiMaxMemoryType]; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + TestFailure = FALSE; + ZeroMem (PageAllocations, sizeof (PageAllocations)); + ZeroMem (PoolAllocations, sizeof (PoolAllocations)); + + for (Index = 0; Index < EfiMaxMemoryType; Index++) { + if ((Index != EfiConventionalMemory) && (Index != EfiPersistentMemory)) { + PageAllocations[Index] = AllocatePages (1); + if (PageAllocations[Index] == NULL) { + UT_LOG_ERROR ("Failed to allocate one page for memory type %d\n", Index); + TestFailure = TRUE; + } + + PoolAllocations[Index] = AllocatePool (8); + if (PoolAllocations[Index] == NULL) { + UT_LOG_ERROR ("Failed to allocate an 8 byte pool for memory type %d\n", Index); + TestFailure = TRUE; + } + } + } + + Status = PopulatePageTableMap (); + + if (!EFI_ERROR (Status) && (mMap.Entries != NULL)) { + for (Index = 0; Index < EfiMaxMemoryType; Index++) { + if ((Index != EfiConventionalMemory) && (Index != EfiPersistentMemory)) { + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + (UINT64)PageAllocations[Index], + EFI_PAGE_SIZE, + &Attributes + ); + if (Status != EFI_NOT_FOUND) { + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for range 0x%llx - 0x%llx\n", + PageAllocations[Index], + PageAllocations[Index] + EFI_PAGE_SIZE + ); + TestFailure = TRUE; + } + + if (Attributes == 0) { + UT_LOG_ERROR ( + "Page range 0x%llx - 0x%llx has no restrictive access attributes\n", + PageAllocations[Index], + PageAllocations[Index] + EFI_PAGE_SIZE + ); + TestFailure = TRUE; + } + } + + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + ALIGN_ADDRESS ((UINTN)PoolAllocations[Index]), + EFI_PAGE_SIZE, + &Attributes + ); + if ((Status != EFI_NOT_FOUND)) { + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for range 0x%llx - 0x%llx\n", + ALIGN_ADDRESS ((UINTN)PoolAllocations[Index]), + ALIGN_ADDRESS ((UINTN)PoolAllocations[Index]) + EFI_PAGE_SIZE + ); + TestFailure = TRUE; + } else if (Attributes == 0) { + UT_LOG_ERROR ( + "Pool range 0x%llx - 0x%llx has no restrictive access attributes\n", + PoolAllocations[Index], + PoolAllocations[Index] + sizeof (UINT64) + ); + TestFailure = TRUE; + } + } + } + } + } else { + UT_LOG_ERROR ("Failed to populate page table map\n"); + TestFailure = TRUE; + } + + for (Index = 0; Index < EfiMaxMemoryType; Index++) { + if (PageAllocations[Index] != NULL) { + FreePages (PageAllocations[Index], 1); + } + + if (PoolAllocations[Index] != NULL) { + FreePool (PoolAllocations[Index]); + } + } + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Checks that the NULL page is not mapped or is + EFI_MEMORY_RP. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +STATIC +UNIT_TEST_STATUS +EFIAPI +NullCheck ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT64 Attributes; + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + Attributes = 0; + Status = GetRegionCommonAccessAttributes (&mMap, 0, EFI_PAGE_SIZE, &Attributes); + + if ((Status != EFI_NOT_FOUND)) { + if (EFI_ERROR (Status)) { + UT_ASSERT_NOT_EFI_ERROR (Status); + } else { + UT_ASSERT_NOT_EQUAL (Attributes & EFI_MEMORY_RP, 0); + } + } + + return UNIT_TEST_PASSED; +} + +/** + Checks that MMIO regions in the EFI memory map are + EFI_MEMORY_XP. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +STATIC +UNIT_TEST_STATUS +EFIAPI +MmioIsXp ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd; + UINT64 Attributes; + BOOLEAN TestFailure; + EFI_STATUS Status; + UINTN Index; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + UT_ASSERT_NOT_EFI_ERROR (PopulateEfiMemoryMap ()); + UT_ASSERT_NOT_NULL (mEfiMemoryMap); + UT_ASSERT_NOT_EFI_ERROR (PopulateMemorySpaceMap ()); + UT_ASSERT_NOT_NULL (mMemorySpaceMap); + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + TestFailure = FALSE; + + EfiMemoryMapEntry = mEfiMemoryMap; + EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)mEfiMemoryMap + mEfiMemoryMapSize); + + while (EfiMemoryMapEntry < EfiMemoryMapEnd) { + if (EfiMemoryMapEntry->Type == EfiMemoryMappedIO) { + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + EfiMemoryMapEntry->PhysicalStart, + EfiMemoryMapEntry->NumberOfPages * EFI_PAGE_SIZE, + &Attributes + ); + + if (Status != EFI_NOT_FOUND) { + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for range 0x%llx - 0x%llx\n", + EfiMemoryMapEntry->PhysicalStart, + EfiMemoryMapEntry->PhysicalStart + (EfiMemoryMapEntry->NumberOfPages * EFI_PAGE_SIZE) + ); + TestFailure = TRUE; + } else if ((Attributes & EFI_MEMORY_XP) == 0) { + UT_LOG_ERROR ( + "Memory Range 0x%llx-0x%llx is not EFI_MEMORY_XP\n", + EfiMemoryMapEntry->PhysicalStart, + EfiMemoryMapEntry->PhysicalStart + (EfiMemoryMapEntry->NumberOfPages * EFI_PAGE_SIZE) + ); + TestFailure = TRUE; + } + } + } + + EfiMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (EfiMemoryMapEntry, mEfiMemoryMapDescriptorSize); + } + + for (Index = 0; Index < mMemorySpaceMapCount; Index++) { + if (mMemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) { + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + mMemorySpaceMap[Index].BaseAddress, + mMemorySpaceMap[Index].Length, + &Attributes + ); + + if (Status != EFI_NOT_FOUND) { + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for range 0x%llx - 0x%llx\n", + mMemorySpaceMap[Index].BaseAddress, + mMemorySpaceMap[Index].BaseAddress + mMemorySpaceMap[Index].Length + ); + TestFailure = TRUE; + } else if ((Attributes & EFI_MEMORY_XP) == 0) { + UT_LOG_ERROR ( + "Memory Range 0x%llx-0x%llx is not EFI_MEMORY_XP\n", + mMemorySpaceMap[Index].BaseAddress, + mMemorySpaceMap[Index].BaseAddress + mMemorySpaceMap[Index].Length + ); + TestFailure = TRUE; + } + } + } + } + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Checks that loaded image sections containing code + are EFI_MEMORY_RO and sections containing data + are EFI_MEMORY_XP. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +STATIC +UNIT_TEST_STATUS +EFIAPI +ImageCodeSectionsRoDataSectionsXp ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINTN NoHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + UINTN Index2; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT32 PeCoffHeaderOffset; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + UINT32 SectionAlignment; + EFI_IMAGE_SECTION_HEADER *Section; + UINT64 Attributes; + BOOLEAN TestFailure; + UINT64 SectionStart; + UINT64 SectionEnd; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + TestFailure = FALSE; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadedImageProtocolGuid, + NULL, + &NoHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status) && (NoHandles == 0)) { + UT_LOG_ERROR ("Unable to query EFI Loaded Image Protocol\n"); + UT_ASSERT_NOT_EFI_ERROR (Status); + UT_ASSERT_NOT_EQUAL (NoHandles, 0); + } + + for (Index = 0; Index < NoHandles; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (EFI_ERROR (Status)) { + continue; + } + + // Check PE/COFF image + DosHdr = (EFI_IMAGE_DOS_HEADER *)(UINTN)LoadedImage->ImageBase; + PeCoffHeaderOffset = 0; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + PeCoffHeaderOffset = DosHdr->e_lfanew; + } + + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)LoadedImage->ImageBase + PeCoffHeaderOffset); + + // Get SectionAlignment + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment; + } else { + SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment; + } + + if (!IsLoadedImageSectionAligned (SectionAlignment, LoadedImage->ImageCodeType)) { + UT_LOG_ERROR ( + "Image 0x%llx - 0x%llx is not aligned\n", + (UINTN)LoadedImage->ImageBase, + (UINTN)LoadedImage->ImageBase + LoadedImage->ImageSize + ); + TestFailure = TRUE; + continue; + } + + Section = (EFI_IMAGE_SECTION_HEADER *)( + (UINT8 *)(UINTN)LoadedImage->ImageBase + + PeCoffHeaderOffset + + sizeof (UINT32) + + sizeof (EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + + for (Index2 = 0; Index2 < Hdr.Pe32->FileHeader.NumberOfSections; Index2++) { + Attributes = 0; + SectionStart = (UINT64)LoadedImage->ImageBase + Section[Index2].VirtualAddress; + SectionEnd = SectionStart + ALIGN_VALUE (Section[Index2].SizeOfRawData, SectionAlignment); + + // Check if the section contains code and data + if (((Section[Index2].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) && + ((Section[Index2].Characteristics & + (EFI_IMAGE_SCN_CNT_INITIALIZED_DATA || EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA)) != 0)) + { + UT_LOG_ERROR ( + "Image Section 0x%llx-0x%llx contains code and data\n", + SectionStart, + SectionEnd + ); + TestFailure = TRUE; + } + + Status = GetRegionCommonAccessAttributes ( + &mMap, + SectionStart, + SectionEnd - SectionStart, + &Attributes + ); + + if (EFI_ERROR (Status)) { + TestFailure = TRUE; + UT_LOG_ERROR ( + "Failed to get attributes for memory range 0x%llx-0x%llx\n", + SectionStart, + SectionEnd + ); + } else if ((Section[Index2].Characteristics & + (EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_EXECUTE)) == EFI_IMAGE_SCN_MEM_EXECUTE) + { + if ((Attributes & EFI_MEMORY_RO) == 0) { + UT_LOG_ERROR ( + "Image Section 0x%llx-0x%llx is not EFI_MEMORY_RO\n", + SectionStart, + SectionEnd + ); + TestFailure = TRUE; + } + } else { + if ((Attributes & EFI_MEMORY_XP) == 0) { + UT_LOG_ERROR ( + "Image Section 0x%llx-0x%llx is not EFI_MEMORY_XP\n", + SectionStart, + SectionEnd + ); + TestFailure = TRUE; + } + } + } + } + + FreePool (HandleBuffer); + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Checks that the stack is EFI_MEMORY_XP and has + an EFI_MEMORY_RP page to catch overflow. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +STATIC +UNIT_TEST_STATUS +EFIAPI +BspStackIsXpAndHasGuardPage ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; + BOOLEAN TestFailure; + EFI_PHYSICAL_ADDRESS StackBase; + UINT64 StackLength; + UINT64 Attributes; + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + Hob.Raw = GetHobList (); + TestFailure = FALSE; + + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { + MemoryHob = Hob.MemoryAllocation; + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) { + StackBase = (EFI_PHYSICAL_ADDRESS)((MemoryHob->AllocDescriptor.MemoryBaseAddress / EFI_PAGE_SIZE) * EFI_PAGE_SIZE); + StackLength = (EFI_PHYSICAL_ADDRESS)(EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (MemoryHob->AllocDescriptor.MemoryLength))); + + UT_LOG_INFO ("BSP stack located at 0x%llx - 0x%llx", StackBase, StackBase + StackLength); + + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + StackBase, + EFI_PAGE_SIZE, + &Attributes + ); + if ((Status != EFI_NOT_FOUND)) { + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for memory range 0x%llx-0x%llx\n", + StackBase, + StackBase + EFI_PAGE_SIZE + ); + TestFailure = TRUE; + } else if (((Attributes & EFI_MEMORY_RP) == 0)) { + UT_LOG_ERROR ( + "Stack 0x%llx-0x%llx does not have an EFI_MEMORY_RP page to catch overflow\n", + StackBase, + StackBase + EFI_PAGE_SIZE + ); + TestFailure = TRUE; + } + } + + Attributes = 0; + Status = GetRegionCommonAccessAttributes ( + &mMap, + StackBase + EFI_PAGE_SIZE, + StackLength - EFI_PAGE_SIZE, + &Attributes + ); + + if (EFI_ERROR (Status)) { + UT_LOG_ERROR ( + "Failed to get attributes for memory range 0x%llx-0x%llx\n", + StackBase + EFI_PAGE_SIZE, + StackBase + StackLength + ); + TestFailure = TRUE; + } else if ((Attributes & EFI_MEMORY_XP) == 0) { + UT_LOG_ERROR ( + "Stack 0x%llx-0x%llx is executable\n", + StackBase + EFI_PAGE_SIZE, + StackBase + StackLength + ); + TestFailure = TRUE; + } + + break; + } + + Hob.Raw = GET_NEXT_HOB (Hob); + } + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Checks that memory ranges not in the EFI + memory map will cause a CPU fault if accessed. + + @param[in] Context Unit test context + + @retval UNIT_TEST_PASSED The unit test passed + @retval other The unit test failed +**/ +STATIC +UNIT_TEST_STATUS +EFIAPI +MemoryOutsideEfiMemoryMapIsInaccessible ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT64 StartOfAddressSpace; + UINT64 EndOfAddressSpace; + UINT64 StartOfEfiMemoryMap; + UINT64 EndOfEfiMemoryMap; + EFI_MEMORY_DESCRIPTOR *FinalEfiMemoryMapDescriptor; + UINTN Index; + BOOLEAN TestFailure; + UINT64 StartOfMapEntry; + UINT64 EndOfMapEntry; + + DEBUG ((DEBUG_INFO, "%a Enter...\n", __FUNCTION__)); + + UT_ASSERT_NOT_EFI_ERROR (PopulateEfiMemoryMap ()); + UT_ASSERT_NOT_NULL (mEfiMemoryMap); + UT_ASSERT_NOT_EFI_ERROR (PopulateMemorySpaceMap ()); + UT_ASSERT_NOT_NULL (mMemorySpaceMap); + UT_ASSERT_NOT_EFI_ERROR (PopulatePageTableMap ()); + UT_ASSERT_NOT_NULL (mMap.Entries); + + StartOfAddressSpace = mMemorySpaceMap[0].BaseAddress; + EndOfAddressSpace = mMemorySpaceMap[mMemorySpaceMapCount - 1].BaseAddress + mMemorySpaceMap[mMemorySpaceMapCount - 1].Length; + TestFailure = FALSE; + + StartOfEfiMemoryMap = mEfiMemoryMap->PhysicalStart; + FinalEfiMemoryMapDescriptor = (EFI_MEMORY_DESCRIPTOR *)(((UINT8 *)mEfiMemoryMap + mEfiMemoryMapSize) - mEfiMemoryMapDescriptorSize); + EndOfEfiMemoryMap = FinalEfiMemoryMapDescriptor->PhysicalStart + (FinalEfiMemoryMapDescriptor->NumberOfPages * EFI_PAGE_SIZE); + + for (Index = 0; Index < mMap.EntryCount; Index++) { + StartOfMapEntry = mMap.Entries[Index].LinearAddress; + EndOfMapEntry = mMap.Entries[Index].LinearAddress + mMap.Entries[Index].Length; + if (CHECK_OVERLAP (StartOfMapEntry, EndOfMapEntry, StartOfAddressSpace, StartOfEfiMemoryMap)) { + if (IsPageReadable (mMap.Entries[Index].PageEntry)) { + UT_LOG_ERROR ( + "Memory Range 0x%llx-0x%llx is accessible\n", + StartOfMapEntry, + EndOfMapEntry + ); + TestFailure = TRUE; + } + } else if (CHECK_OVERLAP (StartOfMapEntry, EndOfMapEntry, EndOfEfiMemoryMap, EndOfAddressSpace)) { + if (IsPageReadable (mMap.Entries[Index].PageEntry)) { + UT_LOG_ERROR ( + "Memory Range 0x%llx-0x%llx is accessible\n", + StartOfMapEntry, + EndOfMapEntry + ); + TestFailure = TRUE; + } + } + } + + UT_ASSERT_FALSE (TestFailure); + + return UNIT_TEST_PASSED; +} + +/** + Entry Point of the shell app. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point executed successfully. + @retval other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +DxePagingAuditTestAppEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) { EFI_STATUS Status; UNIT_TEST_FRAMEWORK_HANDLE Fw = NULL; @@ -447,7 +1535,6 @@ DxePagingAuditTestAppEntryPoint ( EFI_FILE *Fs_Handle; DEBUG ((DEBUG_ERROR, "%a()\n", __FUNCTION__)); - DEBUG ((DEBUG_ERROR, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); Status = gBS->HandleProtocol ( @@ -463,9 +1550,9 @@ DxePagingAuditTestAppEntryPoint ( if (ShellParams->Argc > 1) { RunTests = FALSE; - if (StrnCmp (ShellParams->Argv[1], L"-r", 4) == 0) { + if (StrnCmp (ShellParams->Argv[1], L"-r", MAX_CHARS_TO_READ) == 0) { RunTests = TRUE; - } else if (StrnCmp (ShellParams->Argv[1], L"-d", 4) == 0) { + } else if (StrnCmp (ShellParams->Argv[1], L"-d", MAX_CHARS_TO_READ) == 0) { Status = OpenAppSFS (&Fs_Handle); if (!EFI_ERROR ((Status))) { @@ -474,12 +1561,12 @@ DxePagingAuditTestAppEntryPoint ( DumpPagingInfo (NULL); } } else { - if (StrnCmp (ShellParams->Argv[1], L"-h", 4) != 0) { + if (StrnCmp (ShellParams->Argv[1], L"-h", MAX_CHARS_TO_READ) != 0) { DEBUG ((DEBUG_INFO, "Invalid argument!\n")); } DEBUG ((DEBUG_INFO, "-h : Print available flags\n")); - DEBUG ((DEBUG_INFO, "-d : Dump the page table files to the EFI partition\n")); + DEBUG ((DEBUG_INFO, "-d : Dump the page table files\n")); DEBUG ((DEBUG_INFO, "-r : Run the application tests\n")); DEBUG ((DEBUG_INFO, "NOTE: Combined flags (i.e. -rd) is not supported\n")); } @@ -505,26 +1592,30 @@ DxePagingAuditTestAppEntryPoint ( goto EXIT; } - GetSpecialRegions (); - GetNonProtectedImageList (); - Status = gDS->GetMemorySpaceMap (&mMemorySpaceMapCount, &mMemorySpaceMap); - if (EFI_ERROR (Status)) { 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, PopulatePageTableMap, FreePageTableMap, NULL); + AddTestCase (Misc, "No pages are readable, writable, and executable", "Security.Misc.NoReadWriteExecute", NoReadWriteExecute, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "Unallocated memory is EFI_MEMORY_RP", "Security.Misc.UnallocatedMemoryIsRP", UnallocatedMemoryIsRP, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "Memory Attribute Protocol is present", "Security.Misc.IsMemoryAttributeProtocolPresent", IsMemoryAttributeProtocolPresent, NULL, NULL, NULL); + AddTestCase (Misc, "Calls to allocate pages and pools return buffers with restrictive access attributes", "Security.Misc.AllocatedPagesAndPoolsAreProtected", AllocatedPagesAndPoolsAreProtected, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "NULL page is EFI_MEMORY_RP", "Security.Misc.NullCheck", NullCheck, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "MMIO Regions are EFI_MEMORY_XP", "Security.Misc.MmioIsXp", MmioIsXp, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "Image code sections are EFI_MEMORY_RO and and data sections are EFI_MEMORY_XP", "Security.Misc.ImageCodeSectionsRoDataSectionsXp", ImageCodeSectionsRoDataSectionsXp, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "BSP stack is EFI_MEMORY_XP and has EFI_MEMORY_RP guard page", "Security.Misc.BspStackIsXpAndHasGuardPage", BspStackIsXpAndHasGuardPage, NULL, GeneralTestCleanup, NULL); + AddTestCase (Misc, "Memory outside of the EFI Memory Map is inaccessible", "Security.Misc.MemoryOutsideEfiMemoryMapIsInaccessible", MemoryOutsideEfiMemoryMapIsInaccessible, NULL, GeneralTestCleanup, NULL); // // Execute the tests. // Status = RunAllTestSuites (Fw); -EXIT: + } - if (Fw) { - FreeUnitTestFramework (Fw); - } +EXIT: + if (Fw != NULL) { + FreeUnitTestFramework (Fw); } return EFI_SUCCESS; -} // DxePagingAuditTestAppEntryPoint() +} diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf index 0ed078d09e..998e1e6c12 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditTestApp.inf @@ -51,6 +51,7 @@ DxeMemoryProtectionHobLib FileHandleLib FlatPageTableLib + SafeIntLib [LibraryClasses.AARCH64] ArmLib @@ -71,6 +72,8 @@ gMemoryProtectionSpecialRegionProtocolGuid gEfiShellParametersProtocolGuid gCpuMpDebugProtocolGuid + gEfiMemoryAttributeProtocolGuid + gEfiLoadedImageProtocolGuid [Protocols.X64] gEfiSmmBase2ProtocolGuid diff --git a/UefiTestingPkg/UefiTestingPkg.ci.yaml b/UefiTestingPkg/UefiTestingPkg.ci.yaml index bb41acf8ef..d590dbb3b1 100644 --- a/UefiTestingPkg/UefiTestingPkg.ci.yaml +++ b/UefiTestingPkg/UefiTestingPkg.ci.yaml @@ -92,7 +92,8 @@ "MPIDRs", "PAGESs", "SMRRs", - "unsplit" + "unsplit", + "lfanew", # PE/COFF ], "AdditionalIncludePaths": [] # Additional paths to spell check relative to package root (wildcards supported) } From 972c92703beffd011176a7155f918938acd795a6 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 20:54:14 -0700 Subject: [PATCH 08/15] Paging Audit: Minor Formatting Fixes in Shell App Description Fixes minor formatting issues in DxePagingAuditTestApp.c. - [ ] 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, ... - [ ] 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 N/A Integration Instructions N/A --- .../PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c index f37a70a4f5..bbfce0cf06 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/Dxe/App/DxePagingAuditTestApp.c @@ -137,8 +137,6 @@ FreePageTableMap ( /** 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. @@ -168,7 +166,7 @@ PopulatePageTableMap ( 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; + return EFI_OUT_OF_RESOURCES; } Status = CreateFlatPageTable (&mMap); @@ -214,6 +212,9 @@ FreeNonProtectedImageList ( /** Populates the mNonProtectedImageList global + + @retval EFI_SUCCESS The non-protected image list is populated successfully. + @retval other An error occurred while populating the non-protected image list. **/ STATIC EFI_STATUS From 8b73b5bd787acb652439af34085459c3c4720095 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 19:32:21 -0700 Subject: [PATCH 09/15] Paging Audit: Collect Memory Attribute Protocol Data Description Collects the Memory Attribute Protocol presence data in PlatformInfo.dat and adds its value to the output HTML report. - [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, ... - [ ] 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, ... - [x] 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 Integration Instructions N/A --- .../UEFI/AArch64/PagingAuditProcessor.c | 19 +++++++++++------ .../PagingAudit/UEFI/DxePagingAuditDriver.inf | 1 + .../UEFI/SmmPagingAuditTestApp.inf | 1 + .../UEFI/X64/PagingAuditProcessor.c | 21 ++++++++++++------- .../PagingAudit/Windows/BinaryParsing.py | 2 ++ .../Windows/DxePaging_template_AArch64.html | 3 ++- .../Windows/DxePaging_template_X64.html | 3 ++- .../AuditTests/PagingAudit/Windows/Globals.py | 1 + .../Windows/PagingReportGenerator.py | 9 ++++++-- 9 files changed, 43 insertions(+), 17 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c index 72b61211fa..693d458e8d 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include +#include #include "../PagingAuditCommon.h" extern MEMORY_PROTECTION_DEBUG_PROTOCOL *mMemoryProtectionProtocol; @@ -251,10 +252,11 @@ DumpPlatforminfo ( VOID ) { - CHAR8 TempString[MAX_STRING_SIZE]; - UINTN ExecutionLevel; - CHAR8 *ElString; - UINTN StringIndex; + CHAR8 TempString[MAX_STRING_SIZE]; + UINTN ExecutionLevel; + CHAR8 *ElString; + UINTN StringIndex; + EFI_MEMORY_ATTRIBUTE_PROTOCOL *MemoryAttributeProtocol; ExecutionLevel = ArmReadCurrentEL (); @@ -268,13 +270,18 @@ DumpPlatforminfo ( ElString = "Unknown"; } + if (EFI_ERROR (gBS->LocateProtocol (&gEfiMemoryAttributeProtocolGuid, NULL, (VOID **)&MemoryAttributeProtocol))) { + MemoryAttributeProtocol = NULL; + } + // Dump the execution level of UEFI StringIndex = AsciiSPrint ( &TempString[0], MAX_STRING_SIZE, - "Architecture,AARCH64\nBitwidth,%d\nPhase,DXE\nExecutionLevel,%a\n", + "Architecture,AARCH64\nBitwidth,%d\nPhase,DXE\nExecutionLevel,%a\nMemoryAttributeProtocolPresent,%a\n", CalculateMaximumSupportAddressBits (), - ElString + ElString, + (MemoryAttributeProtocol == NULL) ? "FALSE" : "TRUE" ); WriteBufferToFile (L"PlatformInfo", TempString, StringIndex); diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditDriver.inf b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditDriver.inf index af6eaecbfe..e5024f0230 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditDriver.inf +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/DxePagingAuditDriver.inf @@ -66,6 +66,7 @@ gMemoryProtectionDebugProtocolGuid gEfiSimpleFileSystemProtocolGuid gCpuMpDebugProtocolGuid + gEfiMemoryAttributeProtocolGuid [Protocols.X64] gEfiSmmBase2ProtocolGuid diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/SmmPagingAuditTestApp.inf b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/SmmPagingAuditTestApp.inf index 194826c7c9..262431b2ba 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/SmmPagingAuditTestApp.inf +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/SmmPagingAuditTestApp.inf @@ -50,6 +50,7 @@ gEfiSmmCommunicationProtocolGuid gMemoryProtectionDebugProtocolGuid gCpuMpDebugProtocolGuid + gEfiMemoryAttributeProtocolGuid [Protocols.X64] gEfiSmmBase2ProtocolGuid diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/X64/PagingAuditProcessor.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/X64/PagingAuditProcessor.c index be29d246ef..556bdfab35 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/X64/PagingAuditProcessor.c +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/X64/PagingAuditProcessor.c @@ -13,6 +13,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #define AMD_64_SMM_ADDR 0xC0010112 #define AMD_64_SMM_MASK 0xC0010113 @@ -747,11 +748,12 @@ DumpPlatforminfo ( VOID ) { - CHAR8 TempString[MAX_STRING_SIZE]; - UINTN StringIndex; - EFI_SMM_BASE2_PROTOCOL *mSmmBase2; - BOOLEAN InSmm; - CHAR8 *PhaseString; + CHAR8 TempString[MAX_STRING_SIZE]; + UINTN StringIndex; + EFI_SMM_BASE2_PROTOCOL *mSmmBase2; + BOOLEAN InSmm; + CHAR8 *PhaseString; + EFI_MEMORY_ATTRIBUTE_PROTOCOL *MemoryAttributeProtocol; InSmm = FALSE; mSmmBase2 = NULL; @@ -769,12 +771,17 @@ DumpPlatforminfo ( PhaseString = "DXE"; } + if (EFI_ERROR (gBS->LocateProtocol (&gEfiMemoryAttributeProtocolGuid, NULL, (VOID **)&MemoryAttributeProtocol))) { + MemoryAttributeProtocol = NULL; + } + StringIndex = AsciiSPrint ( &TempString[0], MAX_STRING_SIZE, - "Architecture,X64\nPhase,%a\nBitwidth,%d\n", + "Architecture,X64\nPhase,%a\nBitwidth,%d\nMemoryAttributeProtocolPresent,%a\n", PhaseString, - CalculateMaximumSupportAddressBits () + CalculateMaximumSupportAddressBits (), + (MemoryAttributeProtocol == NULL) ? "FALSE" : "TRUE" ); WriteBufferToFile (L"PlatformInfo", TempString, StringIndex); diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/BinaryParsing.py b/UefiTestingPkg/AuditTests/PagingAudit/Windows/BinaryParsing.py index 5da39edde1..5ce1860df7 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/BinaryParsing.py +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/BinaryParsing.py @@ -54,6 +54,8 @@ def ParsePlatforminfofile(fileName): Globals.Bitwidth = int(row[1]) elif row[0] == "Phase": Globals.Phase = row[1] + elif row[0] == "MemoryAttributeProtocolPresent": + Globals.MemoryAttributeProtocolPresent = row[1] else: logging.error("Unknown key found in PlatformInfo.dat: %s" % row[0]) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html index ac58e0d12b..461114df63 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html @@ -181,7 +181,8 @@

External Licenses

diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html index f74b9e077e..8e1a381871 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html @@ -187,7 +187,8 @@

External Licenses

diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/Globals.py b/UefiTestingPkg/AuditTests/PagingAudit/Windows/Globals.py index 57f74cb126..d20e9eb7a2 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/Globals.py +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/Globals.py @@ -8,3 +8,4 @@ ExecutionLevel = "" Phase = "" Bitwidth = 0 +MemoryAttributeProtocolPresent = "" \ No newline at end of file diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/PagingReportGenerator.py b/UefiTestingPkg/AuditTests/PagingAudit/Windows/PagingReportGenerator.py index d3d2c08851..8dbce66976 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/PagingReportGenerator.py +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/PagingReportGenerator.py @@ -230,8 +230,13 @@ def OutputHtmlReport(self, ToolVersion, OutputFilePath): template = open(os.path.join(sp, "SmmPaging_template.html"), "r") for line in template.readlines(): - if "%TO_BE_FILLED_IN_BY_PYTHON_SCRIPT%" in line: - line = line.replace("%TO_BE_FILLED_IN_BY_PYTHON_SCRIPT%", js) + if "% TO_BE_FILLED_IN_BY_PYTHON_SCRIPT %" in line: + line = line.replace("% TO_BE_FILLED_IN_BY_PYTHON_SCRIPT %", js) + if "% IS_MEMORY_ATTRIBUTE_PROTOCOL_PRESENT %" in line: + if Globals.MemoryAttributeProtocolPresent != "": + line = line.replace("% IS_MEMORY_ATTRIBUTE_PROTOCOL_PRESENT %", "\"" + Globals.MemoryAttributeProtocolPresent + "\"") + else: + line = line.replace("% IS_MEMORY_ATTRIBUTE_PROTOCOL_PRESENT %", "\"FALSE\"") f.write(line) template.close() f.close() From 4ab7c89787765bb45dc2efec7775a7126b26b2d9 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 19:33:21 -0700 Subject: [PATCH 10/15] Paging Audit: Skip Collecting Invalid Pages on AArch64 Description The valid bit is no longer displayed on the AArch64 paging audit output, so unmapped pages shouldn't be collected in the .dat files. - [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, ... - [ ] 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 Integration Instructions N/A --- .../PagingAudit/UEFI/AArch64/PagingAuditProcessor.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c index 693d458e8d..e16d3ceceb 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c +++ b/UefiTestingPkg/AuditTests/PagingAudit/UEFI/AArch64/PagingAuditProcessor.c @@ -76,7 +76,6 @@ GetFlatPageTableData ( UINTN NumPage1GNotPresent = 0; UINT64 RootEntryCount = 0; UINT64 Address; - BOOLEAN Valid; // Count parameters should be provided. if ((Pte1GCount == NULL) || (Pte2MCount == NULL) || (Pte4KCount == NULL) || (PdeCount == NULL) || (GuardCount == NULL)) { @@ -117,13 +116,12 @@ GetFlatPageTableData ( for (Index1 = 0x0; Index1 < TT_ENTRY_COUNT; Index1++ ) { Index2 = 0; Index3 = 0; - Valid = TRUE; if ((Pte1G[Index1] & 0x1) == 0) { NumPage1GNotPresent++; - Valid = FALSE; + continue; } - if (!IS_BLOCK (Pte1G[Index1], 1) && Valid) { + if (!IS_BLOCK (Pte1G[Index1], 1)) { Pte2M = (UINT64 *)(Pte1G[Index1] & TT_ADDRESS_MASK); MyPdeCount++; @@ -133,13 +131,12 @@ GetFlatPageTableData ( for (Index2 = 0x0; Index2 < TT_ENTRY_COUNT; Index2++ ) { Index3 = 0; - Valid = TRUE; if ((Pte2M[Index2] & 0x1) == 0) { NumPage2MNotPresent++; - Valid = FALSE; + continue; } - if (!IS_BLOCK (Pte2M[Index2], 2) && Valid) { + if (!IS_BLOCK (Pte2M[Index2], 2)) { Pte4K = (UINT64 *)(Pte2M[Index2] & TT_ADDRESS_MASK); MyPdeCount++; From c54d64347d5e4427cf57579796b84511e2e5fb74 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 19:34:23 -0700 Subject: [PATCH 11/15] Bugfix: Ensure EntryCount is Updated in AArch64 CreateFlatPageTable() Description When the input PAGE_MAP in CreateFlatPageTable() doesn't have enough memory to hold the flat page table, the function will return EFI_BUFFER_TOO_SMALL. This fixes this return case to also update the EntryCount in the PAGE_MAP so that the caller can know how many entries are needed. - [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, ... - [ ] 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 Integration Instructions N/A --- .../Library/FlatPageTableLib/AArch64/FlatPageTableAArch64.c | 1 + 1 file changed, 1 insertion(+) diff --git a/UefiTestingPkg/Library/FlatPageTableLib/AArch64/FlatPageTableAArch64.c b/UefiTestingPkg/Library/FlatPageTableLib/AArch64/FlatPageTableAArch64.c index bf1ddf73e4..cc86a31ef7 100644 --- a/UefiTestingPkg/Library/FlatPageTableLib/AArch64/FlatPageTableAArch64.c +++ b/UefiTestingPkg/Library/FlatPageTableLib/AArch64/FlatPageTableAArch64.c @@ -314,6 +314,7 @@ CreateFlatPageTable ( ); if (LocalEntryCount > Map->EntryCount) { + Map->EntryCount = LocalEntryCount; return EFI_BUFFER_TOO_SMALL; } From 26e2e457413df9cae6b1cb3c1f39db4c05fc1c9c Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 19:37:13 -0700 Subject: [PATCH 12/15] Paging Audit: Reformat HTML Templates Description Auto-format the HTML templates according to the default HTML formatter in vscode. - [ ] 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, ... - [ ] 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 N/A Integration Instructions N/A --- .../Windows/DxePaging_template_AArch64.html | 361 +++++++++-------- .../Windows/DxePaging_template_X64.html | 371 ++++++++++-------- 2 files changed, 413 insertions(+), 319 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html index 461114df63..20dd7c381f 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html @@ -1,14 +1,19 @@ + DXE Paging Analysis - - - - +

DXE Paging Analysis

@@ -48,22 +54,30 @@

Test Results

Page Filter
Page Size - +

Access Flag Attribute - +

- Execute Attribute - + Execute Attribute +

- Read/Write Attribute - + Read/Write Attribute +

@@ -74,27 +88,37 @@

Test Results

Memory Range Filter
UEFI Memory Type - +

GCD Memory Type - +

Special Memory Regions - +

Memory Contents - +

- Section Type - + Section Type +

@@ -104,6 +128,7 @@

Test Results

Actions
Filters: +

@@ -152,11 +177,17 @@

License


- Copyright (C) Microsoft Corporation. All rights reserved.
+ Copyright (C) Microsoft Corporation. All rights + reserved.
- All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

+ All rights reserved. Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following conditions are + met:
+ 1. Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -186,12 +217,24 @@

External Licenses

- - - - - - + + + + + + - + + \ No newline at end of file diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html index 8e1a381871..69f318b39b 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html @@ -1,14 +1,19 @@ + DXE Paging Analysis - - - - +

DXE Paging Analysis

@@ -48,27 +54,37 @@

Test Results

Page Filter
Page Size - +

Present Attribute - +

- Execute Attribute - + Execute Attribute +

- Read/Write Attribute - + Read/Write Attribute +

- Privilege Level - + Privilege Level +

@@ -79,27 +95,37 @@

Test Results

Memory Range Filter
UEFI Memory Type - +

GCD Memory Type - +

Special Memory Regions - +

Memory Contents - +

- Section Type - + Section Type +

@@ -158,11 +184,17 @@

License


- Copyright (C) Microsoft Corporation. All rights reserved.
+ Copyright (C) Microsoft Corporation. All rights + reserved.
- All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

+ All rights reserved. Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following conditions are + met:
+ 1. Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -192,12 +224,24 @@

External Licenses

- - - - - - + + + + + + - + + \ No newline at end of file From 43bd789b7c10a4bb2d87692a543cbd6a7dd0b6b8 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 20:14:13 -0700 Subject: [PATCH 13/15] Paging Audit: Make Header in Memory Data Tab Horizontally Scrollable Description Make the header in the Memory Data tab horizontally scrollable so that when scrolling through the data, the header is always aligned. - [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, ... - [ ] 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 Integration Instructions N/A --- .../PagingAudit/Windows/DxePaging_template_AArch64.html | 1 + .../AuditTests/PagingAudit/Windows/DxePaging_template_X64.html | 1 + 2 files changed, 2 insertions(+) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html index 20dd7c381f..a4d875462b 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html @@ -310,6 +310,7 @@

External Licenses

"paginate": false, "autoWidth": false, "scrollY": ($(window).height() - DATA_TABLE_OFFSET) + "px", + "scrollX": true, "sorting": [[0, "asc"]], "columnDefs": [ { diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html index 69f318b39b..2c258216e0 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html @@ -317,6 +317,7 @@

External Licenses

"paginate": false, "autoWidth": false, "scrollY": ($(window).height() - DATA_TABLE_OFFSET) + "px", + "scrollX": true, "sorting": [[0, "asc"]], "columnDefs": [ { From b4f1594135f4e5f4e3dd1b142773fab91d4f22b8 Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 20:36:30 -0700 Subject: [PATCH 14/15] Paging Audit: Refactor Filter Logic and add Logical OR Filtering Description 1. Refactor the filtering logic to be more general instead of requiring an update function for each filterable column. 2. Add a toggle to the filter logic to switch between logical AND and logical OR filtering when comparing column values in each row. This is a nice to have feature when parsing the data. - [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, ... - [ ] 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 Integration Instructions N/A --- .../Windows/DxePaging_template_AArch64.html | 309 +++++++++--------- .../Windows/DxePaging_template_X64.html | 300 ++++++++--------- 2 files changed, 286 insertions(+), 323 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html index a4d875462b..03762dd8d6 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html @@ -127,8 +127,8 @@

Test Results

Actions
- Filters: - + +

@@ -303,6 +303,18 @@

External Licenses

$('button#ClearPageFilter').click(); }); + var currentLogic = "AND"; + + $(document).on("click", "#toggleLogic", function () { + if (currentLogic === "AND") { + currentLogic = "OR"; + $(this).text("Filter Logic: OR"); + } else { + currentLogic = "AND"; + $(this).text("Filter Logic: AND"); + } + applyFilter(currentLogic); + }); //table for modules var mTable = $('table#MemoryRanges').dataTable({ @@ -367,171 +379,149 @@

External Licenses

} ], //end of column def initComplete: function () { - //Setup the filters - this.api().column(2).every(function () { - var column = this; - var select = $("#PageSizeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 2 - Page Size + var tableApi = this.api(); - this.api().column(6).every(function () { + tableApi.columns().every(function () { var column = this; - var select = $("#ExecuteFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 6 - execute Attribute - - this.api().column(4).every(function () { - var column = this; - var select = $("#AccessFlagFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 4 - Present Attribute - - this.api().column(5).every(function () { - var column = this; - var select = $("#RWFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 5 - RW attribute - - this.api().column(9).every(function () { - var column = this; - var select = $("#SectionFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column.search(SearchRegex, true, false).draw(); - }); // on change + var select; + + switch (column.index()) { + case 2: + select = $("#PageSizeFilter"); + break; + case 4: + select = $("#AccessFlagFilter"); + break; + case 5: + select = $("#RWFilter"); + break; + case 6: + select = $("#ExecuteFilter"); + break; + case 7: + select = $("#MemoryTypeFilter"); + break; + case 8: + select = $("#MemorySpaceTypeFilter"); + break; + case 9: + select = $("#SectionFilter"); + break; + case 10: + select = $("#SpecialMemoryRegionsFilter"); + break; + case 11: + select = $("#MemoryContentsFilter"); + break; + default: + console.log("Unhandled column index in init:", column.index()); + return; + } column.data().unique().sort().each(function (d, j) { - select.append('') + if (d !== null) { + select.append(''); + } }); - }); //column 10 - Section Type for code/data sections + }); - this.api().column(7).every(function () { - var column = this; - var select = $("#MemoryTypeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 8 - Memory Type + $(".selectpicker").on("change", function () { + applyFilter(currentLogic); + }); + } + }) + + var columnKeyMapping = { + "Page Size": "PageSize", + "Execute": "Execute", + "Access Flag": "AccessFlag", + "Read/Write": "RW", + "UEFI Memory Type": "MemoryType", + "GCD Memory Type": "MemorySpaceType", + "Special Memory Usage": "SpecialMemoryRegions", + "UEFI Memory Contents": "MemoryContents", + "Section Type": "SectionType" + }; + + function applyFilter(logic) { + $.fn.dataTable.ext.search = []; + + var selectedValues = { + PageSize: $("#PageSizeFilter").val(), + Execute: $("#ExecuteFilter").val(), + AccessFlag: $("#AccessFlagFilter").val(), + RW: $("#RWFilter").val(), + MemoryType: $("#MemoryTypeFilter").val(), + MemorySpaceType: $("#MemorySpaceTypeFilter").val(), + SpecialMemoryRegions: $("#SpecialMemoryRegionsFilter").val(), + MemoryContents: $("#MemoryContentsFilter").val(), + SectionType: $("#SectionFilter").val() + } - this.api().column(8).every(function () { - var column = this; - var select = $("#MemorySpaceTypeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 9 - GCD Memory Type + // Bail if no filters are selected + var noFiltersSelected = Object.values(selectedValues).every(arr => arr.length === 0); + if (noFiltersSelected) { + mTable.api().search('').columns().search('').draw(); + return; + } - this.api().column(10).every(function () { - var column = this; - var select = $("#SpecialMemoryRegionsFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 11 - special Memory Type + var searchRegexes = { + PageSize: CreateRegexForSearchMultiselect(selectedValues.PageSize), + Execute: CreateRegexForSearchMultiselect(selectedValues.Execute), + AccessFlag: CreateRegexForSearchMultiselect(selectedValues.AccessFlag), + RW: CreateRegexForSearchMultiselect(selectedValues.RW), + MemoryType: CreateRegexForSearchMultiselect(selectedValues.MemoryType), + MemorySpaceType: CreateRegexForSearchMultiselect(selectedValues.MemorySpaceType), + SpecialMemoryRegions: CreateRegexForSearchMultiselect(selectedValues.SpecialMemoryRegions), + MemoryContents: CreateRegexForSearchMultiselect(selectedValues.MemoryContents), + SectionType: CreateRegexForSearchMultiselect(selectedValues.SectionType) + }; + + if (logic === "AND") { + mTable.api().columns().every(function () { + var columnTitle = this.header().textContent.trim(); + var searchKey = columnKeyMapping[columnTitle]; + if (searchRegexes[searchKey]) { + this.search(searchRegexes[searchKey], true, false); + } else { + this.search(''); + } + }); + } else if (logic === "OR") { + mTable.api().columns().search(''); + $.fn.dataTable.ext.search.push(function (settings, searchData, index, rowData, counter) { + for (var key in columnKeyMapping) { + var columnTitle = key; + var searchKey = columnKeyMapping[columnTitle]; + var regexStr = searchRegexes[searchKey]; + var regex = new RegExp(regexStr); + if (regexStr && regex.test(searchData[settings.aoColumns.findIndex(col => col.sTitle === columnTitle)])) { + return true; + } + } + return false; + }); + } + mTable.api().draw(); + } - this.api().column(11).every(function () { - var column = this; - var select = $("#MemoryContentsFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - if (d === null) { - d = "Nothing Found"; //this must align with the default content - } - select.append('') - }); - }); //column 12 - Memory Contents - } //end init complete func - }) //end of modules table - - /** Memory Range JSON looks like: - {"Page Size": "4k", - "Present": "Yes", - "Read/Write": "Enabled", - "Execute": "UX/PX", - "Start": "0x0000058000", - "End": "0x0000058FFF", - "Number of Entries": 1, - "Memory Type": "EfiReservedMemoryType", - "GCD Memory Type": "EfiGcdMemoryTypeReserved" - "Attributes": "EFI_MEMORY_RO, EFI_MEMORY_RUNTIME" - "System Memory": "None" + /** Memory Range JSON looks like: + {"Page Size": "4k", + "Access Flag": "No", + "Read/Write": "Enabled", + "Execute": "Enabled", + "Start": "0x0000000000", + "End": "0x0000000FFF", + "Number of Entries": 1, + "Memory Type": "None", + "GCD Memory Type": "EfiGcdMemoryTypeNonExistent", + "Section Type": "Not Tracked", + "System Memory": "NULL Page", "Memory Contents": null, - "Partial Page": False + "Partial Page": false } - **/ + **/ var SavedFilters = []; SavedFilters.push({ "Name": "RW+X", @@ -647,19 +637,12 @@

External Licenses

function CreateRegexForSearchMultiselect(mylist) { var re = ''; - // console.log(mylist); mylist.forEach(function (v, i, a) { if (i > 0) { re += "|"; - } else { - re += "(" } - re += '^' + $.fn.dataTable.util.escapeRegex(v) + '?'; + re += '^' + $.fn.dataTable.util.escapeRegex(v) + '$'; }); - if (re !== '') { - re += ")" - } - // console.log(re); return re; } diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html index 2c258216e0..e51764f51b 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html @@ -134,7 +134,8 @@

Test Results

Actions
- Filters: + +

@@ -310,6 +311,18 @@

External Licenses

$('button#ClearPageFilter').click(); }); + var currentLogic = "AND"; + + $(document).on("click", "#toggleLogic", function () { + if (currentLogic === "AND") { + currentLogic = "OR"; + $(this).text("Filter Logic: OR"); + } else { + currentLogic = "AND"; + $(this).text("Filter Logic: AND"); + } + applyFilter(currentLogic); + }); //table for modules var mTable = $('table#MemoryRanges').dataTable({ @@ -378,166 +391,140 @@

External Licenses

} ], //end of column def initComplete: function () { - //Setup the filters - this.api().column(2).every(function () { - var column = this; - var select = $("#PageSizeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 2 - Page Size - - this.api().column(6).every(function () { - var column = this; - var select = $("#ExecuteFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 6 - execute Attribute - - this.api().column(4).every(function () { - var column = this; - var select = $("#PresentFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 4 - Present Attribute - - this.api().column(5).every(function () { - var column = this; - var select = $("#RWFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 5 - RW attribute - - this.api().column(7).every(function () { - var column = this; - var select = $("#PrivilegeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column.search(SearchRegex, true, false).draw(); - }); // on change + var tableApi = this.api(); - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 7 - Privilege (User / Supervisor) - - this.api().column(10).every(function () { + tableApi.columns().every(function () { var column = this; - var select = $("#SectionFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column.search(SearchRegex, true, false).draw(); - }); // on change - - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 10 - Section Type for code/data sections - - this.api().column(8).every(function () { - var column = this; - var select = $("#MemoryTypeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 8 - Memory Type + var select; + + switch (column.index()) { + case 2: + select = $("#PageSizeFilter"); + break; + case 4: + select = $("#PresentFilter"); + break; + case 6: + select = $("#ExecuteFilter"); + break; + case 5: + select = $("#RWFilter"); + break; + case 7: + select = $("#PrivilegeFilter"); + break; + case 8: + select = $("#MemoryTypeFilter"); + break; + case 9: + select = $("#MemorySpaceTypeFilter"); + break; + case 10: + select = $("#SectionFilter"); + break; + case 11: + select = $("#SpecialMemoryRegionsFilter"); + break; + case 12: + select = $("#MemoryContentsFilter"); + break; + default: + console.log("Unhandled column index in init:", column.index()); + return; + } - this.api().column(9).every(function () { - var column = this; - var select = $("#MemorySpaceTypeFilter").on('change', function () { - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options column.data().unique().sort().each(function (d, j) { - select.append('') + if (d !== null) { + select.append(''); + } }); - }); //column 9 - GCD Memory Type + }); - this.api().column(11).every(function () { - var column = this; - var select = $("#SpecialMemoryRegionsFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - select.append('') - }); - }); //column 11 - special Memory Type + $(".selectpicker").on("change", function () { + applyFilter(currentLogic); + }); + } + }) + + var columnKeyMapping = { + "Page Size": "PageSize", + "Present": "Present", + "Read/Write": "RW", + "Execute": "Execute", + "Privilege": "Privilege", + "UEFI Memory Type": "MemoryType", + "GCD Memory Type": "MemorySpaceType", + "Section Type": "SectionType", + "Special Memory Usage": "SpecialMemoryRegions", + "UEFI Memory Contents": "MemoryContents" + }; + + function applyFilter(logic) { + $.fn.dataTable.ext.search = []; + + var selectedValues = { + PageSize: $("#PageSizeFilter").val(), + Execute: $("#ExecuteFilter").val(), + Present: $("#PresentFilter").val(), + RW: $("#RWFilter").val(), + Privilege: $("#PrivilegeFilter").val(), + MemoryType: $("#MemoryTypeFilter").val(), + MemorySpaceType: $("#MemorySpaceTypeFilter").val(), + SpecialMemoryRegions: $("#SpecialMemoryRegionsFilter").val(), + MemoryContents: $("#MemoryContentsFilter").val(), + SectionType: $("#SectionFilter").val() + } + // Check if filters are selected + var noFiltersSelected = Object.values(selectedValues).every(arr => arr.length === 0); + if (noFiltersSelected) { + mTable.api().search('').columns().search('').draw(); + return; + } - this.api().column(12).every(function () { - var column = this; - var select = $("#MemoryContentsFilter").on('change', function () { - //var SelectedValues = $(this).val() || []; - var SearchRegex = CreateRegexForSearchMultiselect($(this).val()); - column - //.search( val ? '^'+val+'$' : '', true, false ) - .search(SearchRegex, true, false) - .draw(); - }); // on change - - //populate with options - column.data().unique().sort().each(function (d, j) { - if (d === null) { - d = "Nothing Found"; //this must align with the default content + var searchRegexes = { + PageSize: CreateRegexForSearchMultiselect(selectedValues.PageSize), + Execute: CreateRegexForSearchMultiselect(selectedValues.Execute), + Present: CreateRegexForSearchMultiselect(selectedValues.Present), + RW: CreateRegexForSearchMultiselect(selectedValues.RW), + Privilege: CreateRegexForSearchMultiselect(selectedValues.Privilege), + MemoryType: CreateRegexForSearchMultiselect(selectedValues.MemoryType), + MemorySpaceType: CreateRegexForSearchMultiselect(selectedValues.MemorySpaceType), + SpecialMemoryRegions: CreateRegexForSearchMultiselect(selectedValues.SpecialMemoryRegions), + MemoryContents: CreateRegexForSearchMultiselect(selectedValues.MemoryContents), + SectionType: CreateRegexForSearchMultiselect(selectedValues.SectionType) + }; + + if (logic === "AND") { + mTable.api().columns().every(function () { + var columnTitle = this.header().textContent.trim(); + var searchKey = columnKeyMapping[columnTitle]; + if (searchRegexes[searchKey]) { + this.search(searchRegexes[searchKey], true, false); + } else { + this.search(''); + } + }); + } else if (logic === "OR") { + mTable.api().columns().search(''); + + // Push our custom search function + $.fn.dataTable.ext.search.push(function (settings, searchData, index, rowData, counter) { + for (var key in columnKeyMapping) { + var columnTitle = key; + var searchKey = columnKeyMapping[columnTitle]; + var regexStr = searchRegexes[searchKey]; + var regex = new RegExp(regexStr); + if (regexStr && regex.test(searchData[settings.aoColumns.findIndex(col => col.sTitle === columnTitle)])) { + return true; } - select.append('') - }); - }); //column 12 - Memory Contents - } //end init complete func - }) //end of modules table + } + return false; + }); + + } + mTable.api().draw(); + } /** Memory Range JSON looks like: {"Page Size": "4k", @@ -666,19 +653,12 @@

External Licenses

function CreateRegexForSearchMultiselect(mylist) { var re = ''; - // console.log(mylist); mylist.forEach(function (v, i, a) { if (i > 0) { re += "|"; - } else { - re += "(" } - re += '^' + $.fn.dataTable.util.escapeRegex(v) + '?'; + re += '^' + $.fn.dataTable.util.escapeRegex(v) + '$'; }); - if (re !== '') { - re += ")" - } - // console.log(re); return re; } From 4ca8d648e28d7ee866b591cb690276f50d4a97ad Mon Sep 17 00:00:00 2001 From: Taylor Beebe Date: Sat, 7 Oct 2023 20:36:53 -0700 Subject: [PATCH 15/15] Paging Audit: Add 5 Tests to HTML Templates Description Adds the following tests to the HTML templates: 1. Test that the NULL page is EFI_MEMORY_RP 2. Check that MMIO memory is non-executable. 3. Check that EfiConventionalMemory is non-executable. 4. Check that memory not in the EFI memory map is not accessible. 5. Check that the memory attribute protocol is present on the platform. - [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, ... - [ ] 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, ... - [x] 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 Integration Instructions N/A --- .../Windows/DxePaging_template_AArch64.html | 110 ++++++++++++++--- .../Windows/DxePaging_template_X64.html | 114 +++++++++++++++--- 2 files changed, 187 insertions(+), 37 deletions(-) diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html index 03762dd8d6..013d82a274 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_AArch64.html @@ -523,18 +523,34 @@

External Licenses

} **/ var SavedFilters = []; + SavedFilters.push({ + "Name": "NULL Page Check", + "Description": "NULL page should be EFI_MEMORY_RP", + "Filter": function (mrObject) { + var isTargetType = mrObject["System Memory"] === "NULL Page"; + var hasInvalidAttributes = mrObject["Access Flag"] !== "No"; + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("SpecialMemoryRegionsFilter", ["NULL Page"]); + return true; + } //end of configuring filter inputs + }); + SavedFilters.push({ "Name": "RW+X", "Description": "No memory range should have page attributes that allow read, write, and execute", "Filter": function (mrObject) { - if ((mrObject["Execute"] !== "Disabled") && (mrObject["Read/Write"] === "Enabled") && (mrObject["Access Flag"] === "Yes") && (mrObject["GCD Memory Type"] !== "EfiGcdMemoryTypeNonExistent")) { - return true; - } - return false; - }, //end of Filter function + isTargetType = (mrObject["GCD Memory Type"] !== "EfiGcdMemoryTypeNonExistent"); + hasInvalidAttributes = (mrObject["Execute"] === "Enabled") && + (mrObject["Read/Write"] === "Enabled") && + (mrObject["Access Flag"] === "Yes"); + return isTargetType && hasInvalidAttributes; + }, "ConfigureFilter": function () { $("button#ClearAllFilter").click(); //clear the filters - SetMultiselectTo("ExecuteFilter", ["UX", "PX", "UX/PX", "Enabled"]) + SetMultiselectTo("ExecuteFilter", ["Enabled"]) SetMultiselectTo("AccessFlagFilter", ["Yes"]) SetMultiselectTo("RWFilter", ["Enabled"]) SetMultiselectTo("MemorySpaceTypeFilter", @@ -553,14 +569,13 @@

External Licenses

"Name": "Data Sections are No-Execute", "Description": "Image data sections should be no-execute", "Filter": function (mrObject) { - if ((mrObject["Execute"] !== "Disabled") && (mrObject["Section Type"] === "DATA")) { - return true; - } - return false; + isTargetType = (mrObject["Section Type"] === "DATA"); + hasInvalidAttributes = (mrObject["Execute"] === "Enabled"); + return isTargetType && hasInvalidAttributes; }, //end of Filter function "ConfigureFilter": function () { $("button#ClearAllFilter").click(); //clear the filters - SetMultiselectTo("ExecuteFilter", ["UX", "PX", "UX/PX", "Enabled"]) + SetMultiselectTo("ExecuteFilter", ["Disabled"]) SetMultiselectTo("SectionFilter", ["DATA"]) return true; } //end of configuring filter inputs @@ -570,10 +585,9 @@

External Licenses

"Name": "Code Sections are Read-Only", "Description": "Image code sections should be read-only", "Filter": function (mrObject) { - if ((mrObject["Read/Write"] === "Enabled") && (mrObject["Section Type"] === "CODE")) { - return true; - } - return false; + isTargetType = (mrObject["Section Type"] === "CODE"); + hasInvalidAttributes = (mrObject["Read/Write"] === "Enabled"); + return isTargetType && hasInvalidAttributes; }, //end of Filter function "ConfigureFilter": function () { $("button#ClearAllFilter").click(); //clear the filters @@ -583,6 +597,58 @@

External Licenses

} //end of configuring filter inputs }); + SavedFilters.push({ + "Name": "MMIO Execute Check", + "Description": "MMIO ranges should be non executable", + "Filter": function (mrObject) { + var isTargetType = (mrObject["GCD Memory Type"] === "EfiGcdMemoryTypeMemoryMappedIo") || + (mrObject["Memory Type"] === "EfiMemoryMappedIO"); + var hasInvalidAttributes = (mrObject["Execute"] !== "Disabled") && + (mrObject["Access Flag"] !== "No"); + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("MemorySpaceTypeFilter", ["EfiGcdMemoryTypeMemoryMappedIo"]); + SetMultiselectTo("MemoryTypeFilter", ["EfiMemoryMappedIO"]); + SetMultiselectTo("ExecuteFilter", ["Enabled"]); + SetMultiselectTo("AccessFlagFilter", ["Yes"]); + return true; + } //end of configuring filter inputs + }); + + SavedFilters.push({ + "Name": "Free Memory Check", + "Description": "Free EFI memory should not be readable", + "Filter": function (mrObject) { + var isTargetType = mrObject["Memory Type"] === "EfiConventionalMemory"; + var hasInvalidAttributes = mrObject["Access Flag"] !== "No"; + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("MemoryTypeFilter", ["EfiConventionalMemory"]); + SetMultiselectTo("AccessFlagFilter", ["Yes"]); + return true; + } //end of configuring filter inputs + }); + + SavedFilters.push({ + "Name": "Check Memory Not in EFI Memory Map is Inaccessible", + "Description": "Memory not in the EFI memory map should cause a fault if accessed", + "Filter": function (mrObject) { + var isTargetType = mrObject["Memory Type"] === "None"; + var hasInvalidAttributes = mrObject["Access Flag"] !== "No"; + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("MemoryTypeFilter", ["None"]); + SetMultiselectTo("AccessFlagFilter", ["Yes"]); + return true; + } //end of configuring filter inputs + }); + //Fill in the test results tab SavedFilters.forEach(function (TestObject) { var FailedCount = EmbeddedJd.MemoryRanges.filter(TestObject.Filter); @@ -608,6 +674,16 @@

External Licenses

} }); + var testName = "Memory Attribute Protocol is Installed"; + var testDescription = "Checks if the platform produces the memory attribute protocol"; + if (IsMemoryAttributeProtocolPresent === "TRUE") { + var b = $("

" + testName + "

Description:" + testDescription + "
Status: Success

"); + b.appendTo("div#TestStatusListWrapper"); + } else { + var b = $("

" + testName + "

Description:" + testDescription + "
Status: Failed

"); + b.appendTo("div#TestStatusListWrapper"); + } + $('div#tabs-3 select.selectpicker').selectpicker("refresh").change(); //Show warning if there are parsing errors @@ -672,7 +748,6 @@

External Licenses

@ret boolean status of setting all requested values **/ function SetMultiselectTo(selectName, listOfValuesSelected) { - //var allOptions = $("select#" + selectName +" > option").map(function() { return $(this).val(); }).get(); //create array $.each($("select#" + selectName + " option"), function (i, v) { var index = listOfValuesSelected.indexOf($(v).text()); if (index > -1) { @@ -685,9 +760,6 @@

External Licenses

}); $("select#" + selectName).change(); $("select#" + selectName).selectpicker('refresh'); - listOfValuesSelected.forEach(function (v, i, a) { - AddAlert("Can't set " + selectName + " value to " + v, "warning"); - }); return (listOfValuesSelected.length === 0); } diff --git a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html index e51764f51b..8403a9c241 100644 --- a/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html +++ b/UefiTestingPkg/AuditTests/PagingAudit/Windows/DxePaging_template_X64.html @@ -545,13 +545,30 @@

External Licenses

**/ var SavedFilters = []; SavedFilters.push({ - "Name": "RW+X", "Description": "No memory range should have page attributes that allow read, write, and execute", + "Name": "NULL Page Check", + "Description": "NULL page should be EFI_MEMORY_RP", "Filter": function (mrObject) { - if ((mrObject["Execute"] === "Enabled") && (mrObject["Read/Write"] === "Enabled") && (mrObject["Present"] === "Yes") && (mrObject["GCD Memory Type"] !== "EfiGcdMemoryTypeNonExistent")) { - return true; - } - return false; + var isTargetType = mrObject["System Memory"] === "NULL Page"; + var hasInvalidAttributes = mrObject["Present"] !== "No"; + return isTargetType && hasInvalidAttributes; }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("SpecialMemoryRegionsFilter", ["NULL Page"]); + return true; + } //end of configuring filter inputs + }); + + SavedFilters.push({ + "Name": "RW+X", + "Description": "No memory range should have page attributes that allow read, write, and execute", + "Filter": function (mrObject) { + isTargetType = (mrObject["GCD Memory Type"] !== "EfiGcdMemoryTypeNonExistent"); + hasInvalidAttributes = (mrObject["Execute"] === "Enabled") && + (mrObject["Read/Write"] === "Enabled") && + (mrObject["Present"] === "Yes"); + return isTargetType && hasInvalidAttributes; + }, "ConfigureFilter": function () { $("button#ClearAllFilter").click(); //clear the filters SetMultiselectTo("ExecuteFilter", ["Enabled"]) @@ -570,11 +587,12 @@

External Licenses

}); SavedFilters.push({ - "Name": "Data Sections are No-Execute", "Description": "Image data sections should be no-execute", "Filter": function (mrObject) { - if ((mrObject["Execute"] === "Enabled") && (mrObject["Section Type"] === "DATA")) { - return true; - } - return false; + "Name": "Data Sections are No-Execute", + "Description": "Image data sections should be no-execute", + "Filter": function (mrObject) { + isTargetType = (mrObject["Section Type"] === "DATA"); + hasInvalidAttributes = (mrObject["Execute"] === "Enabled"); + return isTargetType && hasInvalidAttributes; }, //end of Filter function "ConfigureFilter": function () { $("button#ClearAllFilter").click(); //clear the filters @@ -585,11 +603,12 @@

External Licenses

}); SavedFilters.push({ - "Name": "Code Sections are Read-Only", "Description": "Image code sections should be read-only", "Filter": function (mrObject) { - if ((mrObject["Read/Write"] === "Enabled") && (mrObject["Section Type"] === "CODE")) { - return true; - } - return false; + "Name": "Code Sections are Read-Only", + "Description": "Image code sections should be read-only", + "Filter": function (mrObject) { + isTargetType = (mrObject["Section Type"] === "CODE"); + hasInvalidAttributes = (mrObject["Read/Write"] === "Enabled"); + return isTargetType && hasInvalidAttributes; }, //end of Filter function "ConfigureFilter": function () { $("button#ClearAllFilter").click(); //clear the filters @@ -599,6 +618,58 @@

External Licenses

} //end of configuring filter inputs }); + SavedFilters.push({ + "Name": "MMIO Execute Check", + "Description": "MMIO ranges should be non executable", + "Filter": function (mrObject) { + var isTargetType = (mrObject["GCD Memory Type"] === "EfiGcdMemoryTypeMemoryMappedIo") || + (mrObject["Memory Type"] === "EfiMemoryMappedIO"); + var hasInvalidAttributes = (mrObject["Execute"] !== "Disabled") && + (mrObject["Present"] !== "No"); + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("MemorySpaceTypeFilter", ["EfiGcdMemoryTypeMemoryMappedIo"]); + SetMultiselectTo("MemoryTypeFilter", ["EfiMemoryMappedIO"]); + SetMultiselectTo("ExecuteFilter", ["Enabled"]); + SetMultiselectTo("PresentFilter", ["Yes"]); + return true; + } //end of configuring filter inputs + }); + + SavedFilters.push({ + "Name": "Free Memory Check", + "Description": "Free EFI memory should not be readable", + "Filter": function (mrObject) { + var isTargetType = mrObject["Memory Type"] === "EfiConventionalMemory"; + var hasInvalidAttributes = mrObject["Present"] !== "No"; + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("MemoryTypeFilter", ["EfiConventionalMemory"]); + SetMultiselectTo("PresentFilter", ["Yes"]); + return true; + } //end of configuring filter inputs + }); + + SavedFilters.push({ + "Name": "Check Memory Not in EFI Memory Map is Inaccessible", + "Description": "Memory not in the EFI memory map should cause a fault if accessed", + "Filter": function (mrObject) { + var isTargetType = mrObject["Memory Type"] === "None"; + var hasInvalidAttributes = mrObject["Present"] !== "Yes"; + return isTargetType && hasInvalidAttributes; + }, //end of Filter function + "ConfigureFilter": function () { + $("button#ClearAllFilter").click(); //clear the filters + SetMultiselectTo("MemoryTypeFilter", ["None"]); + SetMultiselectTo("PresentFilter", ["Yes"]); + return true; + } //end of configuring filter inputs + }); + //Fill in the test results tab SavedFilters.forEach(function (TestObject) { var FailedCount = EmbeddedJd.MemoryRanges.filter(TestObject.Filter); @@ -624,6 +695,16 @@

External Licenses

} }); + var testName = "Memory Attribute Protocol is Installed"; + var testDescription = "Checks if the platform produces the memory attribute protocol"; + if (IsMemoryAttributeProtocolPresent === "TRUE") { + var b = $("

" + testName + "

Description:" + testDescription + "
Status: Success

"); + b.appendTo("div#TestStatusListWrapper"); + } else { + var b = $("

" + testName + "

Description:" + testDescription + "
Status: Failed

"); + b.appendTo("div#TestStatusListWrapper"); + } + $('div#tabs-3 select.selectpicker').selectpicker("refresh").change(); //Show warning if there are parsing errors @@ -701,9 +782,6 @@

External Licenses

}); $("select#" + selectName).change(); $("select#" + selectName).selectpicker('refresh'); - listOfValuesSelected.forEach(function (v, i, a) { - AddAlert("Can't set " + selectName + " value to " + v, "warning"); - }); return (listOfValuesSelected.length === 0); }