| # StackCheckLib Overview | |
| ## Table of Contents | |
| - [StackCheckLib](#stackchecklib) | |
| - [Table of Contents](#table-of-contents) | |
| - [Introduction and Library Instances](#introduction-and-library-instances) | |
| - [StackCheckLib](#stackchecklib) | |
| - [DynamicStackCookieEntryPointLib](#dynamicstackcookieentrypointlib) | |
| - [StackCheckLibNull](#stackchecklibnull) | |
| - [How Failures are Handled](#how-failures-are-handled) | |
| - [Debugging Stack Cookie Check Failures](#debugging-stack-cookie-check-failures) | |
| - [Usage](#usage) | |
| ## Introduction and Library Instances | |
| `StackCheckLib` contains the required functionality for initializing the stack cookie | |
| value (based on a randomly generated value during build time), checking the value, | |
| and triggering an interrupt when a mismatch occurs. The stack cookie is a random value | |
| placed on the stack between the stack variables and the return address so that | |
| continuously writing past the stack variables will cause the stack cookie to be | |
| overwritten. Before the function returns, the stack cookie value will be checked and | |
| if there is a mismatch then `StackCheckLib` handles the failure. | |
| Because UEFI doesn't use the C runtime libraries provided by MSVC, the stack | |
| check code is written in assembly within this library. GCC and Clang compilers | |
| have built-in support for stack cookie checking, so this library only handles failures. | |
| The stack cookie value is initialized at compile time via updates to the AutoGen process. | |
| Each module will define `STACK_COOKIE_VALUE` which is used for the module stack cookie | |
| value. | |
| The entry point libraries under `MdePkg/DynamicStackCookieEntryPointLib/` update the stack | |
| cookie value at runtime for improved security, but there are cases where the stack cookie | |
| global cannot be written to such as in execute-in-place (XIP) modules and during the | |
| temporary RAM phase of the boot process. It is always preferable to use | |
| one of the dynamic stack cookie entry points when possible. | |
| ### StackCheckLib | |
| `StackCheckLib` provides the stack cookie checking functionality per architecture and | |
| toolchain. The currently supported pairs are IA32{GCC,MSVC}, X64{GCC, MSVC}, | |
| and AARCH64{GCC, MSVC}. `StackCheckLib` is agnostic as to whether the stack cookie was | |
| updated during build time or run time; it simply checks the cookie in the MSVC case and | |
| in both GCC and MSVC responds to stack cookie checking failures. | |
| To add support for other architectures/toolchains, additional assembly files | |
| should be added to `StackCheckLib.inf` and scoped to that architecture/toolchain. | |
| Note: Stack cookie failures generate exceptions and SEC and PEI_CORE may not have | |
| exception handlers registered. In order to safely use stack cookie checking in | |
| these phases, a platform should implement exception handlers because unhandled | |
| exceptions may lead to a hung system state. If a platform does not implement | |
| exception handlers in SEC and PEI_CORE, it is recommended to use `StackCheckLibNull` | |
| for these phases, except for development purposes. | |
| ### DynamicStackCookieEntryPointLib | |
| Each EntryPoint lib under `MdePkg/DynamicStackCookieEntryPointLib/` is an instance of | |
| that module type entry point lib which updates the stack cookie value for the module at | |
| runtime. This is the preferred method for stack cookie initialization as it provides | |
| improved security. The stack cookie value is initialized at runtime in `_ModuleEntryPoint` | |
| by calling `rdrand` on x86 and `RNDR` on AARCH64. If the random number generator is not | |
| supported on that platform or otherwise returns an error, then the value will still have | |
| the build-time randomized value to fall back on. | |
| Typically, dynamic cookies cannot be used for SEC, PEI_CORE, and PEIM modules, due to | |
| the lack of the ability to write to globals for many architectures. If a given platform | |
| can write to globals during these phases, it is recommended to use the provided dynamic | |
| stack cookie entry point lib for those types. Note that SEC does not have a universal | |
| entry point, so there is no dynamic stack cookie entry point lib there. | |
| The dynamic stack cookie entry point lib is used in place of the standard entry point lib, | |
| e.g. for UefiDriverEntryPoint to have dynamic stack cookies, a platform would remove | |
| MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf from its DSC and instead | |
| include MdePkg/Library/DynamicStackCookieEntryPointLib/UefiDriverEntryPoint.inf. | |
| See the Usage section for other ways of including these libraries. | |
| Note: Standalone MM Core support for dynamic cookies for AARCH64 is currently not | |
| supported, due to the unique entry point mechanism there. This support will be | |
| added at a future date. | |
| ### StackCheckLibNull | |
| `StackCheckLibNull` is an instance of `StackCheckLib` which does not perform any stack | |
| cookie checks. This is useful for modules which will fail if stack cookie checks are | |
| inserted. Of course, this is not recommended for production code outside of | |
| SEC and PEI_CORE. | |
| ## How Failures are Handled | |
| When a stack cookie check fails, the `StackCheckLib` library will first call into a hook | |
| function `StackCheckFailureHook()` which only has a NULL implementation in edk2. | |
| The NULL implementation will simply print the failure address and return, but a platform | |
| can implement their own instance of this library which can perform additional actions | |
| before the system triggers an interrupt. | |
| After `StackCheckFailureHook()` returns, the library will trigger an interrupt with | |
| PcdStackCookieExceptionVector. | |
| - On IA32 and X64 platforms, PcdStackCookieExceptionVector is used as an index into the | |
| Interrupt Descriptor Table. | |
| - On AARCH64 platforms, a supervisor call (`SVC`) is called with the value | |
| of PcdStackCookieExceptionVector. This value can similarly be retrieved by the | |
| handler to determine if the interrupt was triggered by the stack cookie check. Reference: | |
| [Arm A64 Instruction Set Architecture Version 2024-3](https://developer.arm.com/documentation/ddi0602/2024-03/Base-Instructions/SVC--Supervisor-Call-?lang=en) | |
| ## Debugging Stack Cookie Check Failures | |
| Tracking down the origin of stack cookie failures can be difficult. Programmers may attempt | |
| printf debugging to determine which function has an overflow only to find that the failure | |
| disappears on the next boot. This curiosity is usually due to the black-box heuristic used | |
| by compilers to determine where to put stack cookie checks or compiler optimization features | |
| removing the failing check. The address where the failed stack cookie check occurred will | |
| be printed using DebugLib. If .map files are available, the address combined with the image | |
| offset can be used to determine the function which failed. | |
| GNU-based compilers have the `-fstack-protector-all` flag to force stack cookie checks on | |
| all functions which could create a more consistent environment for debugging assuming an | |
| earlier failure doesn't mask the targeted one and the flash space can accommodate the | |
| increased size. | |
| The Visual Studio (MSVC) toolchain has the ability to generate `.cod` files during compilation | |
| which interleave C and the generated assembly code. These files will contain the stack cookie | |
| checks and are useful for determining where the checks are placed. To generate these files, | |
| append `/FAcs` to the build options for each target module. The easiest way to do this is to | |
| update the tools_def file so the `<TARGET>_<TOOLCHAIN>_<ARCH>_CC_FLAGS` includes `/FAcs`. | |
| ## Usage | |
| edk2 updated 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` instance linked to satisfy | |
| these references. So every module doesn't need to add `StackCheckLib` to the LibraryClasses | |
| section of the INF file, `StackCheckLib` is added as a dependency for each entry point lib. | |
| This means that custom entry point libs need to have StackCheckLib added as a dependency. | |
| The only exception to this is MSVC built 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`. A `StackCheckLibHostApplication.inf` is linked | |
| by `UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc` that provides the stack | |
| cookie functions for GCC HOST_APPLICATIONS but not for MSVC HOST_APPLICATIONS. | |
| ### Default Stack Check Library Configuration | |
| `MdePkg/MdeLibs.dsc.inc` links `StackCheckLibNull` and `StackCheckFailureLibNull` for all | |
| types. | |
| As stated above, all HOST_APPLICATIONS will link against a HOST_APPLICATION specific | |
| implementation provided in `UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc`. | |
| ### Custom Stack Check Library Configuration | |
| In order to use a different instance of StackCheckLib than `MdeLibs.dsc.inc` provides, a DSC | |
| should add one of the following: | |
| #### Static Stack Check Cookie Configuration | |
| ```inf | |
| [Defines] | |
| DEFINE CUSTOM_STACK_CHECK_LIB = STATIC | |
| ``` | |
| This will cause `MdeLibs.dsc.inc` to not link `StackCheckLibNull` and instead link | |
| `StackCheckLib` to perform stack cookie checking on the static stack cookies, but not update | |
| any of the stack cookies at runtime. | |
| Because edk2 does not implement exception handling for `SEC` and `PEI_CORE`, `MdeLibs.dsc.inc` | |
| uses `StackCheckLibNull` for these phases always. If a platform wishes to use `StackCheckLib` | |
| for these phases, it should override this in its DSC, e.g.: | |
| ```inf | |
| [LibraryClasses.common.SEC, LibraryClasses.common.PEI_CORE] | |
| StackCheckLib|MdePkg/Library/StackCheckLib/StackCheckLib.inf | |
| ``` | |
| It is recommended that a platform only do this for debugging or if they have implemented | |
| exception handlers for these phases. | |
| #### Dynamic Stack Cookie Configuration | |
| ```inf | |
| [Defines] | |
| DEFINE CUSTOM_STACK_CHECK_LIB = DYNAMIC | |
| ``` | |
| This will cause `MdeLibs.dsc.inc` to not link `StackCheckLibNull` and instead link | |
| `StackCheckLib` to perform stack cookie checking. It will also link the dynamic | |
| stack cookie updating versions of `DxeCoreEntryPoint`, `StandaloneMmDriverEntryPoint`, | |
| `UefiApplicationEntryPoint`, and `UefiDriverEntryPoint`. | |
| Because edk2 does not implement exception handling for `SEC` and `PEI_CORE`, | |
| `MdeLibs.dsc.inc` uses `StackCheckLibNull` for these phases always. If a | |
| platform wishes to use `StackCheckLib` for these phases, it can enable static | |
| stack cookie checking, as documented in the previous section. Due to the fact | |
| that writable global variables are not supported in the `SEC` or `PEI` phases | |
| of execution, dynamic stack cookie checking is not supported here. | |
| It is recommended that a platform only do this for debugging or if they have implemented | |
| exception handlers for these phases. | |
| Note: `StandaloneMmCoreEntryPoint` is recommended to use the dynamic stack cookie if | |
| possible, but as it is not supported on AARCH64 today, it is not included in MdeLibs.dsc.inc. | |
| Platforms should include this separately, e.g.: | |
| ```inf | |
| [LibraryClasses.X64] | |
| StandaloneMmCoreEntryPoint|MdePkg/Library/DynamicStackCookieEntryPointLib/StandaloneMmCoreEntryPoint.inf | |
| ``` | |
| Platforms then must remove any references to these entry point libs in their DSC, so that | |
| the `MdeLibs.dsc.inc` versions are chosen. Alternatively, for better DSC readability, | |
| a platform can directly reference the dynamic stack cookie entry points. | |
| ### Disable Stack Check Library | |
| If a platform would like to disable stack cookies (say for debugging purposes), | |
| they can add the following to their DSC: | |
| ```inf | |
| [BuildOptions] | |
| MSVC:*_*_*_CC_FLAGS = /GS- | |
| GCC:*_*_*_CC_FLAGS = -fno-stack-protector | |
| ``` | |
| The same build options can be put in a module's INF to only disable stack cookies | |
| for that module. | |
| Alternatively, a module can have the stack cookies inserted but checking disabled | |
| by including the following in a DSC: | |
| ```inf | |
| SomePkg/SomeDirectory/SomeModule.inf { | |
| <LibraryClasses> | |
| StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf | |
| } | |
| ``` | |
| It is not recommended to disable stack cookie checking in production scenarios. |