目錄

介紹 StackCheckLib(stack cookie UEFI 實作) 與解決建置失敗問題

這兩天 Tianocore 更新了最新版的 edk2-stable-202411,然後地獄的 build error 又莫名地出現了。不出所料,又是 secirity 的更新造成的 ….

又來了,一更新就壞

照慣例我會把我自己寫的一些小 tool 用最新的 code base 重新 build 一次看看會不會有什麼問題,而最近感覺 UEFI 沒什麼能玩了,近期不管改什麼都可以扯到 security,在我更新了 edk2-stable-202411 之後,出現了如下的 errors。

1
2
3
4
BaseLib.lib(SafeString.obj) : error LNK2001: unresolved external symbol __security_check_cookie
BaseLib.lib(SafeString.obj) : error LNK2001: unresolved external symbol __GSHandlerCheck
BaseLib.lib(SafeString.obj) : error LNK2001: unresolved external symbol __security_cookie
UefiDevicePathLib.lib(DevicePathToText.obj) : error LNK2001: unresolved external symbol __report_rangecheckfailure

經過了一連串的 git log 搜尋,看起來問題是出在 StackCheckLib 這個新增加的 stack cookie 實作。經過了一點點學習,簡單說它就是在 stack 中插入一個亂數值,然後在 return 前檢查該值是否改變來以避免有人竄改 stack。

這造成了 compiler 會在編譯的時候會嘗試取得上述 error 內提及的 symbol,若找不到當然就會 build error。

Tianocore 也寫了一份完整的 Readme.md 來描述該新增的機制,文件連結如下:

https://github.com/tianocore/edk2/tree/master/MdePkg/Library/StackCheckLib

基本上該文件已經寫得很清楚了,不過我這邊再記錄一下我大概改了什麼東西。

StackCheckLib 實作分類

依照文件建議,目前有三種 StackCheckLib 實作,分別建議在不同的 phase 中使用。

  • MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf

    SEC, PEI_CORE 中使用

  • MdePkg/Library/StackCheckLib/StackCheckLibStaticInit.inf

    PEIM 中使用

  • MdePkg/Library/StackCheckLib/StackCheckLibDynamicInit.inf

    注意,這個 DynamicInit 實際上還沒有在這一版 edk2-stable-202411 中實現,所以我自己是先改成用 StackCheckLibNull(因為 Shell 也沒換用 StackCheckLibStaticInit 所以我就假設官方建議先不要開)

如果你有自己寫的 package,請跟我這樣改

你應該有已經有一個現成的 dsc 檔

  1. 加上 MdeLibs.dsc.inc

MdePkg/MdeLibs.dsc.inc 已經有預先拉進一些 library, 你可以 review 一下是否跟現在的 dsc 檔內容有重複,若有重複可以刪除。

1
!include MdePkg/MdeLibs.dsc.inc

這邊要注意的是下面這段,如果你沒有 define CUSTOM_STACK_CHECK_LIB 代表直接使用 StackCheckLibNull,這樣可以修復 build error 問題,但不會打開 stack cookie 保護。

也就是說只要加上 MdeLibs.dsc.inc 其實你就 build 的過了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
  StackCheckFailureHookLib|MdePkg/Library/StackCheckFailureHookLibNull/StackCheckFailureHookLibNull.inf

!ifndef CUSTOM_STACK_CHECK_LIB
  # If CUSTOM_STACK_CHECK_LIB is set, MdeLibs.dsc.inc will not link StackCheckLibNull and it is expected that the
  # DSC being built is providing it's own implementation of StackCheckLib.
  NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf

!endif

[LibraryClasses.ARM, LibraryClasses.AARCH64]
  #
  # It is not possible to prevent the ARM/AARCH64 compilers from inserting generic intrinsic functions.
  # This library provides the intrinsic functions generated by these compilers.
  #
  # Linking this here as a null library will cause all ARM/AARCH64 files to link against it and have
  # definitions for the intrinsic functions.
  #
  NULL|MdePkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
  1. 加上 define DEFINE CUSTOM_STACK_CHECK_LIB 來使用指定的 StackCheckLib

若想要開啟保護,接著在 dsc 宣告 CUSTOM_STACK_CHECK_LIB,然後補上文件建議的實作。

1
DEFINE CUSTOM_STACK_CHECK_LIB   = TRUE

根據 Tianocore 提供的 StackCheckLib Readmd.md 來加上你所需的 StackCheckLib

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[LibraryClasses.common.SEC, LibraryClasses.common.PEI_CORE]
  NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf

[LibraryClasses.common.PEIM]
  NULL|MdePkg/Library/StackCheckLib/StackCheckLibStaticInit.inf

# Refer to edk2\MdePkg\Library\StackCheckLib\Readme.md, StackCheckLibDynamicInit is not finished yet.
[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
  NULL|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf

這樣就大功告成了。

參考