Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IA32, X64, ARM, and AARCH64 Stack Cookie Support for MSVC and GCC #5957

Merged
merged 34 commits into from
Sep 13, 2024

Conversation

os-d
Copy link
Contributor

@os-d os-d commented Jul 23, 2024

Description

This PR adds broader stack cookie support to the firmware across toolchains
and architectures.

This also pulls in a Unit Test Infrastructure commit that splits some common dependencies out from the Target and Host dsc.inc files so that Host does not depend on Target any longer. This is required for stack cookies because Target builds have stack cookies enabled and Host builds do not, without this commit we will have linkage failures.

Add StackCheckLibNull, StackCheckLibStaticInit

These libs provide the functions to link against that clang, gcc, and MSVC generate when stack cookies are enabled. For more information on what each lib does, see the README included in this PR. Each DSC then gets this linked against it.

Per the conversation on this PR, the dynamic instance is deferred to a later PR to continue working on the implementation.

Enable Stack Cookies

Finally, stack cookies are enabled for IA32, X64, ARM, and AARCH64 for gcc and MSVC.

See the README included in this patchset for full details.

  • Breaking change?
    • Breaking change - Does this PR cause a break in build or boot behavior?
    • Examples: Does it add a new library class or move a module to a different repo.
  • Impacts security?
    • Security - Does this PR have a direct security impact?
    • Examples: Crypto algorithm change or buffer overflow fix.
  • Includes tests?
    • Tests - Does this PR include any explicit test code?
    • Examples: Unit tests or integration tests.

How This Was Tested

Used to enable stack cookies on Project Mu devices and examine failures.

Integration Instructions

Follow the ReadMe in this PR. A summary is:

This updates the tools_def to add /GS to VS2022 and VS2019 IA32/X64 builds and
-fstack-protector to GCC builds. This will cause stack cookie references to be inserted
throughout the code. Every module should have a StackCheckLib instances linked to satisfy
these references. So every module doesn't need to add StackCheckLib to the LibraryClasses
section of the INF file, StackCheckLib instances should be linked as NULL in the platform
DSC fies. The only exception to this is host-based unit tests as they will be compiled with
the runtime libraries which already contain the stack cookie definitions and will collide
with StackCheckLib.

SEC and PEI_CORE modules should always use StackCheckLibNull and pre-memory modules
should use StackCheckLibStaticInit. All other modules should use StackCheckLibDynamicInit once it is enabled.
Below is an example of how to link the StackCheckLib instances in the platform DSC file
but it may need customization based on the platform's requirements:

[LibraryClasses.common.SEC, LibraryClasses.common.PEI_CORE]
  NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf
[LibraryClasses.common.PEIM]
  NULL|MdePkg/Library/StackCheckLib/StackCheckLibStaticInit.inf
[LibraryClasses.common.MM_CORE_STANDALONE, LibraryClasses.common.MM_STANDALONE, LibraryClasses.common.DXE_CORE, LibraryClasses.common.SMM_CORE, LibraryClasses.common.DXE_SMM_DRIVER, LibraryClasses.common.DXE_DRIVER, LibraryClasses.common.DXE_RUNTIME_DRIVER, LibraryClasses.common.DXE_SAL_DRIVER, LibraryClasses.common.UEFI_DRIVER, LibraryClasses.common.UEFI_APPLICATION]
  NULL|MdePkg/Library/StackCheckLib/StackCheckLibDynamicInit.inf

@github-actions github-actions bot added impact:breaking-change This change breaks existing APIs impacting build or boot. impact:security This change has a direct security impact such as changing a crypto algorithm. labels Jul 23, 2024
@lgao4
Copy link
Contributor

lgao4 commented Jul 24, 2024

MdePkg\MdeLibs.dsc.inc can be updated to add the default NULL library instance. If so, single package DSC will not be updated.

I understand this change. I want to confirm what purpose for it? And, why this change only for VS2019/VS2022 IA32 and X64 arch?

@os-d
Copy link
Contributor Author

os-d commented Jul 24, 2024

@lgao4, thanks for taking a look, I actually have a bit more cleanup to do here, I put up this PR in draft form to start testing the CI, but happy to have discussion while I get it into final form.

My concern about MdeLibs.dsc.inc is that an actual platform may not know about the updated stack check lib and just always get the null version. Furthermore, the concern would be that if a platform included MdeLibs.dsc.inc after they had specified the correct stack check lib, the version they chose would be overrode by the null version silently. If we think these issues are acceptable for platform owners to navigate, then I can drop most of the commits and simply add it to MdeLibs.dsc.inc.

The purpose for this change is to add general stack cookie support to edk2. Today it is only supported in a limited form for ARM/AARCH64 GCC. Stack cookies help catch stack corruption, either unintentional or intentional, so protect against both programming errors and some classes of attack. See more details at https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 and https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector.

Agreed that the VS2019/2022 change was not complete, I will push an update that includes ARM/AARCH64.

@os-d os-d force-pushed the stack_cookies branch 9 times, most recently from 881bfd2 to c937581 Compare July 25, 2024 22:40
@os-d os-d marked this pull request as ready for review July 26, 2024 03:38
@os-d
Copy link
Contributor Author

os-d commented Jul 26, 2024

@lgao4 @mdkinney @bcran @liyi77 @jyao1 @Wenxing-hou @ardbiesheuvel @leiflindholm I have moved the PR to active now that the pipelines are passing (CodeQL failures are from the overall edk2 CodeQL infra issue). Can you please review when you have a chance?

@lgao4
Copy link
Contributor

lgao4 commented Jul 26, 2024

@lgao4, thanks for taking a look, I actually have a bit more cleanup to do here, I put up this PR in draft form to start testing the CI, but happy to have discussion while I get it into final form.

My concern about MdeLibs.dsc.inc is that an actual platform may not know about the updated stack check lib and just always get the null version. Furthermore, the concern would be that if a platform included MdeLibs.dsc.inc after they had specified the correct stack check lib, the version they chose would be overrode by the null version silently. If we think these issues are acceptable for platform owners to navigate, then I can drop most of the commits and simply add it to MdeLibs.dsc.inc.

The purpose for this change is to add general stack cookie support to edk2. Today it is only supported in a limited form for ARM/AARCH64 GCC. Stack cookies help catch stack corruption, either unintentional or intentional, so protect against both programming errors and some classes of attack. See more details at https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 and https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector.

Agreed that the VS2019/2022 change was not complete, I will push an update that includes ARM/AARCH64.

MdeLibs.dsc.inc is designed as the default library instance. Platform always includes it the begin of their DSC file. So, it will not override the platform setting.

@os-d
Copy link
Contributor Author

os-d commented Jul 26, 2024

@lgao4, thanks for taking a look, I actually have a bit more cleanup to do here, I put up this PR in draft form to start testing the CI, but happy to have discussion while I get it into final form.
My concern about MdeLibs.dsc.inc is that an actual platform may not know about the updated stack check lib and just always get the null version. Furthermore, the concern would be that if a platform included MdeLibs.dsc.inc after they had specified the correct stack check lib, the version they chose would be overrode by the null version silently. If we think these issues are acceptable for platform owners to navigate, then I can drop most of the commits and simply add it to MdeLibs.dsc.inc.
The purpose for this change is to add general stack cookie support to edk2. Today it is only supported in a limited form for ARM/AARCH64 GCC. Stack cookies help catch stack corruption, either unintentional or intentional, so protect against both programming errors and some classes of attack. See more details at https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 and https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector.
Agreed that the VS2019/2022 change was not complete, I will push an update that includes ARM/AARCH64.

MdeLibs.dsc.inc is designed as the default library instance. Platform always includes it the begin of their DSC file. So, it will not override the platform setting.

@lgao4, while doing the CI for OvmfPkg, I realized we cannot do MdeLibs.dsc.inc. The reason is that StackCheckLib has a dependency on __ModuleEntryPoint, which does not exist for some SEC ASM files that exist in OvmfPkg (and on real platforms), so when the null lib is attempted to be linked to a module like https://github.com/tianocore/edk2/blob/6589843cc619b3a5e2d2c0e5b12451b11a3f2288/OvmfPkg/ResetVector/ResetVector.inf, it will fail to link because __ModuleEntryPoint does not exist. So, on platforms like that, SEC cannot be generically linked against and instead each SEC module must have the LibraryClasses overridden with the StackCheckLib and not for the ASM only modules. See the OvmfPkg commit in this PR. This possibility exists in other phases, too, of course, though most likely in SEC.

So, we cannot generically apply this to every DSC, even if the majority would be able to take it.

@os-d
Copy link
Contributor Author

os-d commented Jul 27, 2024

@TaylorBeebe if you are interested in reviewing.

@tianocore-assign-reviewers
Copy link

WARNING: Cannot add some reviewers: A user specified as a reviewer for this PR is not a collaborator of the repository. Please add them as a collaborator to the repository so they can be requested in the future.

Non-collaborators requested:

Attn Admins:


Admin Instructions:

  • Add the non-collaborators as collaborators to the appropriate team(s) listed in teams
  • If they are no longer needed as reviewers, remove them from Maintainers.txt

@os-d
Copy link
Contributor Author

os-d commented Sep 12, 2024

Rebased and fixed merge conflicts (in every package, since my CompilerIntrinsicsLib PR also touched every DSC and went in before this one)

Oliver Smith-Denny and others added 13 commits September 12, 2024 12:58
Remove the old stack check lib now that MdeLibs.inc includes
the new one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Remove the old stack check lib now that MdeLibs.inc includes
the new one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Remove the old stack check lib now that MdeLibs.inc includes
the new one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Remove the old stack check lib now that MdeLibs.inc includes
the new one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Remove the old stack check lib now that MdeLibs.inc includes
the new one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Add null implementation of StackCheckLib

Signed-off-by: Oliver Smith-Denny <[email protected]>
Remove the old stack check lib now that MdeLibs.inc includes
the new one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
SecCore and SecCoreNative require StackCheckLib and so the NULL
instance is linked against them here.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Add null implementation of StackCheckLib

Signed-off-by: Oliver Smith-Denny <[email protected]>
Add StackCheckLib for Target and Host based unit tests. Host
based unit tests are treated specially, because MSVC built
host based unit tests use the MSVC C runtime lib to provide
the stack cookie definitions, but GCC built host based unit
tests use our implementation, as we do not link against a
C runtime lib that provides the definitions.

Signed-off-by: Oliver Smith-Denny <[email protected]>
This patch updates the GenC logic to generate a random stack cookie value
for the stack check libraries. These random values improve security
for modules which cannot update the global intrinsics.

If the stack cookie value is randomized in the AutoGen.h file each
build, the build system will determine the module/library must be
rebuilt causing effectively a clean build every time. This also makes
binary reproducibility impossible.

This patch updates the early build scripts to create 32 and 64-bit JSON
files in the build output directory which each contain 100 randomized
stack cookie values for each bitwidth. If the JSON files are already
present, then they are not recreated which allows them to be stored and
moved to other builds for binary reproducibility. Because they are in
the build directory, a clean build will cause the values to be
regenerated.

The logic which creates AutoGen.h will read these JSON files and use a
hash of the module GUID (the hash seed is fixed in Basetools) to index
into the array of stack cookie values for the module bitwidth. This
model is necessary because there isn't thread-consistent data so we
cannot use a locking mechanism to ensure only one thread is writing to
the stack cookie files at a time. With this model, the build threads
only need to read from the files.

Signed-off-by: Oliver Smith-Denny <[email protected]>
This patch directs MSVC and GCC to build stack cookie support into
binaries.

Signed-off-by: Oliver Smith-Denny <[email protected]>
Now that the new stack check lib implementation is being used
everywhere, remove the old one.

Signed-off-by: Oliver Smith-Denny <[email protected]>
@os-d
Copy link
Contributor Author

os-d commented Sep 12, 2024

Missed one merge conflict in PrmPkg.dsc, fixed and pushed.

@tianocore-assign-reviewers
Copy link

WARNING: Cannot add some reviewers: A user specified as a reviewer for this PR is not a collaborator of the repository. Please add them as a collaborator to the repository so they can be requested in the future.

Non-collaborators requested:

Attn Admins:


Admin Instructions:

  • Add the non-collaborators as collaborators to the appropriate team(s) listed in teams
  • If they are no longer needed as reviewers, remove them from Maintainers.txt

@tianocore-assign-reviewers
Copy link

WARNING: Cannot add some reviewers: A user specified as a reviewer for this PR is not a collaborator of the repository. Please add them as a collaborator to the repository so they can be requested in the future.

Non-collaborators requested:

Attn Admins:


Admin Instructions:

  • Add the non-collaborators as collaborators to the appropriate team(s) listed in teams
  • If they are no longer needed as reviewers, remove them from Maintainers.txt

@lgao4
Copy link
Contributor

lgao4 commented Sep 13, 2024

@lgao4, thanks for taking a look, I actually have a bit more cleanup to do here, I put up this PR in draft form to start testing the CI, but happy to have discussion while I get it into final form.
My concern about MdeLibs.dsc.inc is that an actual platform may not know about the updated stack check lib and just always get the null version. Furthermore, the concern would be that if a platform included MdeLibs.dsc.inc after they had specified the correct stack check lib, the version they chose would be overrode by the null version silently. If we think these issues are acceptable for platform owners to navigate, then I can drop most of the commits and simply add it to MdeLibs.dsc.inc.
The purpose for this change is to add general stack cookie support to edk2. Today it is only supported in a limited form for ARM/AARCH64 GCC. Stack cookies help catch stack corruption, either unintentional or intentional, so protect against both programming errors and some classes of attack. See more details at https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 and https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector.
Agreed that the VS2019/2022 change was not complete, I will push an update that includes ARM/AARCH64.

MdeLibs.dsc.inc is designed as the default library instance. Platform always includes it the begin of their DSC file. So, it will not override the platform setting.

@lgao4, while doing the CI for OvmfPkg, I realized we cannot do MdeLibs.dsc.inc. The reason is that StackCheckLib has a dependency on __ModuleEntryPoint, which does not exist for some SEC ASM files that exist in OvmfPkg (and on real platforms), so when the null lib is attempted to be linked to a module like https://github.com/tianocore/edk2/blob/6589843cc619b3a5e2d2c0e5b12451b11a3f2288/OvmfPkg/ResetVector/ResetVector.inf, it will fail to link because __ModuleEntryPoint does not exist. So, on platforms like that, SEC cannot be generically linked against and instead each SEC module must have the LibraryClasses overridden with the StackCheckLib and not for the ASM only modules. See the OvmfPkg commit in this PR. This possibility exists in other phases, too, of course, though most likely in SEC.

So, we cannot generically apply this to every DSC, even if the majority would be able to take it.

I agree we need special handle for SEC module.

@lgao4
Copy link
Contributor

lgao4 commented Sep 13, 2024

@lgao4, thanks for taking a look, I actually have a bit more cleanup to do here, I put up this PR in draft form to start testing the CI, but happy to have discussion while I get it into final form.
My concern about MdeLibs.dsc.inc is that an actual platform may not know about the updated stack check lib and just always get the null version. Furthermore, the concern would be that if a platform included MdeLibs.dsc.inc after they had specified the correct stack check lib, the version they chose would be overrode by the null version silently. If we think these issues are acceptable for platform owners to navigate, then I can drop most of the commits and simply add it to MdeLibs.dsc.inc.
The purpose for this change is to add general stack cookie support to edk2. Today it is only supported in a limited form for ARM/AARCH64 GCC. Stack cookies help catch stack corruption, either unintentional or intentional, so protect against both programming errors and some classes of attack. See more details at https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 and https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector.
Agreed that the VS2019/2022 change was not complete, I will push an update that includes ARM/AARCH64.

MdeLibs.dsc.inc is designed as the default library instance. Platform always includes it the begin of their DSC file. So, it will not override the platform setting.

@lgao4, while doing the CI for OvmfPkg, I realized we cannot do MdeLibs.dsc.inc. The reason is that StackCheckLib has a dependency on __ModuleEntryPoint, which does not exist for some SEC ASM files that exist in OvmfPkg (and on real platforms), so when the null lib is attempted to be linked to a module like https://github.com/tianocore/edk2/blob/6589843cc619b3a5e2d2c0e5b12451b11a3f2288/OvmfPkg/ResetVector/ResetVector.inf, it will fail to link because __ModuleEntryPoint does not exist. So, on platforms like that, SEC cannot be generically linked against and instead each SEC module must have the LibraryClasses overridden with the StackCheckLib and not for the ASM only modules. See the OvmfPkg commit in this PR. This possibility exists in other phases, too, of course, though most likely in SEC.
So, we cannot generically apply this to every DSC, even if the majority would be able to take it.

I agree we need special handle for SEC module.

But some packages have no SEC module, their pacakge DSC files are not requried to be updated, such as MdeModulePkg, FatPkg, ShellPkg, PcatChipsetPkg...

@mergify mergify bot merged commit a9b3830 into tianocore:master Sep 13, 2024
126 checks passed
@os-d os-d deleted the stack_cookies branch September 13, 2024 14:40
os-d pushed a commit to os-d/edk2-platforms that referenced this pull request Sep 13, 2024
edk2 PR tianocore/edk2#5957 removed
BaseStackCheckLib and added StackCheckLibNull (amongst others).
This PR updates all dscs/dsc.incs to remove the old BaseStackCheckLib
and if appropriate, add StackCheckLibNull to LibraryClasses.common.SEC
or to specific SEC libs. For all other component types, MdeLibs.dsc.inc
links them to StackCheckLibNull.

This does all dscs at once as it is formulaic and fixes build breaks.

Continuous-integration-options: PatchCheck.ignore-multi-package

Signed-off-by: Oliver Smith-Denny <[email protected]>
os-d added a commit to os-d/edk2 that referenced this pull request Sep 13, 2024
Commit ac43bba added StackCheckLibNull
to MdeLibs.dsc.inc per review requests on the PR:
tianocore#5957 (comment)
tianocore#5957 (comment).

The PR was adapted to move specifying StackCheckLibNull in every DSC
to MdeLibs.dsc.inc. However, while this works, it does not allow for
a platform to use one of the other StackCheckLibs (such as
StackCheckLibStaticInit) because we get a linker error by having the
compiler defined stack cookie variables defined more than once (once
from StackCheckLibNull in MdeLibs.dsc.inc and the other in the actual
StackCheckLib implementation). Every platform must include MdeLibs.dsc.inc
and there is no way to override a NULL library class. So, we must go
back to the original solution and include StackCheckLib in each
DSC with whatever the preferred version is.

In order to avoid build breaks, this PR updates all DSCs and relevant
dsc.incs to add StackCheckLibNull for the CI build. It also removes
it from MdeLibs.dsc.inc. As per the original PR, StackCheckLib cannot
be generically linked against all SEC modules, on some IA32/X64
SEC modules, they do not include _ModuleEntryPoint, which is required
when linking against a module.

This has been tested that StackCheckLibStaticInit can be used in a
package's CI instead of the null version now.

Continuous-integration-options: PatchCheck.ignore-multi-package

Signed-off-by: Oliver Smith-Denny <[email protected]>
os-d added a commit to os-d/edk2 that referenced this pull request Sep 19, 2024
Commit tianocore@ac43bba
added StackCheckLibNull to MdeLibs.dsc.inc per review requests on the PR:
tianocore#5957 (comment)
tianocore#5957 (comment)

The PR was adapted to move specifying StackCheckLibNull in every DSC
to MdeLibs.dsc.inc. However, while this works, it does not allow for
a platform to use one of the other StackCheckLibs (such as
StackCheckLibStaticInit) because we get a linker error by having the
compiler defined stack cookie variables defined more than once (once
from StackCheckLibNull in MdeLibs.dsc.inc and the other in the actual
StackCheckLib implementation). Every platform must include MdeLibs.dsc.inc
and there is no way to override a NULL library class.

In order to avoid build breaks, this PR updates all DSCs and relevant
dsc.incs to remove StackCheckLibNull for the CI build. It also changes
it to the StackCheckLib library class in MdeLibs.dsc.inc. StackCheckLib
is added as a LibraryClass dependency to each of the Entry Point libs.

This has been tested that StackCheckLibStaticInit can be used in a
package's CI instead of the null version now.

Continuous-integration-options: PatchCheck.ignore-multi-package

Signed-off-by: Oliver Smith-Denny <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
impact:breaking-change This change breaks existing APIs impacting build or boot. impact:security This change has a direct security impact such as changing a crypto algorithm. push Auto push patch series in PR if all checks pass
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants