summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTaylor Beebe <taylor.d.beebe@gmail.com>2024-06-14 14:07:33 -0700
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2024-09-13 03:58:46 +0000
commitcac0955658cb591d4629bf90aaa542a66e25be55 (patch)
treea8c16d55551ab754938506748a50beb5b48a06e1
parent5e07b970940ec1bdbcbec01e733b20532c0b547c (diff)
downloadedk2-cac0955658cb591d4629bf90aaa542a66e25be55.tar.gz
edk2-cac0955658cb591d4629bf90aaa542a66e25be55.tar.bz2
edk2-cac0955658cb591d4629bf90aaa542a66e25be55.zip
BaseTools: Update Stack Cookie Logic
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 <osde@linux.microsoft.com>
-rwxr-xr-xBaseTools/Source/Python/AutoGen/GenC.py31
-rwxr-xr-xBaseTools/Source/Python/Common/GlobalData.py3
-rwxr-xr-xBaseTools/Source/Python/build/build.py21
3 files changed, 54 insertions, 1 deletions
diff --git a/BaseTools/Source/Python/AutoGen/GenC.py b/BaseTools/Source/Python/AutoGen/GenC.py
index 5ad10cee28..86991e7675 100755
--- a/BaseTools/Source/Python/AutoGen/GenC.py
+++ b/BaseTools/Source/Python/AutoGen/GenC.py
@@ -21,6 +21,9 @@ from .StrGather import *
from .GenPcdDb import CreatePcdDatabaseCode
from .IdfClassObject import *
+import json
+import secrets
+
## PCD type string
gItemTypeStringDatabase = {
TAB_PCDS_FEATURE_FLAG : TAB_PCDS_FIXED_AT_BUILD,
@@ -2039,6 +2042,34 @@ def CreateFooterCode(Info, AutoGenC, AutoGenH):
def CreateCode(Info, AutoGenC, AutoGenH, StringH, UniGenCFlag, UniGenBinBuffer, StringIdf, IdfGenCFlag, IdfGenBinBuffer):
CreateHeaderCode(Info, AutoGenC, AutoGenH)
+ # The only 32 bit archs we have are IA32 and ARM, everything else is 64 bit
+ Bitwidth = 32 if Info.Arch == 'IA32' or Info.Arch == 'ARM' else 64
+
+ if GlobalData.gStackCookieValues64 == [] and os.path.exists(os.path.join(Info.PlatformInfo.BuildDir, "StackCookieValues64.json")):
+ with open (os.path.join(Info.PlatformInfo.BuildDir, "StackCookieValues64.json"), "r") as file:
+ GlobalData.gStackCookieValues64 = json.load(file)
+ if GlobalData.gStackCookieValues32 == [] and os.path.exists(os.path.join(Info.PlatformInfo.BuildDir, "StackCookieValues32.json")):
+ with open (os.path.join(Info.PlatformInfo.BuildDir, "StackCookieValues32.json"), "r") as file:
+ GlobalData.gStackCookieValues32 = json.load(file)
+
+ try:
+ if Bitwidth == 32:
+ CookieValue = int(GlobalData.gStackCookieValues32[hash(Info.Guid) % len(GlobalData.gStackCookieValues32)])
+ else:
+ CookieValue = int(GlobalData.gStackCookieValues64[hash(Info.Guid) % len(GlobalData.gStackCookieValues64)])
+ except:
+ EdkLogger.warn("build", "Failed to get Stack Cookie Value List! Generating random value.", ExtraData="[%s]" % str(Info))
+ if Bitwidth == 32:
+ CookieValue = secrets.randbelow (0xFFFFFFFF)
+ else:
+ CookieValue = secrets.randbelow (0xFFFFFFFFFFFFFFFF)
+
+ AutoGenH.Append((
+ '#define STACK_COOKIE_VALUE 0x%XULL\n' % CookieValue
+ if Bitwidth == 64 else
+ '#define STACK_COOKIE_VALUE 0x%X\n' % CookieValue
+ ))
+
CreateGuidDefinitionCode(Info, AutoGenC, AutoGenH)
CreateProtocolDefinitionCode(Info, AutoGenC, AutoGenH)
CreatePpiDefinitionCode(Info, AutoGenC, AutoGenH)
diff --git a/BaseTools/Source/Python/Common/GlobalData.py b/BaseTools/Source/Python/Common/GlobalData.py
index 11849e863f..dd5316d283 100755
--- a/BaseTools/Source/Python/Common/GlobalData.py
+++ b/BaseTools/Source/Python/Common/GlobalData.py
@@ -122,4 +122,5 @@ gEnableGenfdsMultiThread = True
gSikpAutoGenCache = set()
# Common lock for the file access in multiple process AutoGens
file_lock = None
-
+gStackCookieValues32 = []
+gStackCookieValues64 = []
diff --git a/BaseTools/Source/Python/build/build.py b/BaseTools/Source/Python/build/build.py
index 51fb1f433e..ce1bb87126 100755
--- a/BaseTools/Source/Python/build/build.py
+++ b/BaseTools/Source/Python/build/build.py
@@ -28,6 +28,8 @@ import threading
from linecache import getlines
from subprocess import Popen,PIPE, STDOUT
from collections import OrderedDict, defaultdict
+import json
+import secrets
from AutoGen.PlatformAutoGen import PlatformAutoGen
from AutoGen.ModuleAutoGen import ModuleAutoGen
@@ -282,6 +284,22 @@ def LaunchCommand(Command, WorkingDir,ModuleAuto = None):
iau.CreateDepsTarget()
return "%dms" % (int(round((time.time() - BeginTime) * 1000)))
+def GenerateStackCookieValues():
+ if GlobalData.gBuildDirectory == "":
+ return
+
+ # Check if the 32 bit values array needs to be created
+ if not os.path.exists(os.path.join(GlobalData.gBuildDirectory, "StackCookieValues32.json")):
+ StackCookieValues32 = [secrets.randbelow(0xFFFFFFFF) for _ in range(0, 100)]
+ with open (os.path.join(GlobalData.gBuildDirectory, "StackCookieValues32.json"), "w") as file:
+ json.dump(StackCookieValues32, file)
+
+ # Check if the 64 bit values array needs to be created
+ if not os.path.exists(os.path.join(GlobalData.gBuildDirectory, "StackCookieValues64.json")):
+ StackCookieValues64 = [secrets.randbelow(0xFFFFFFFFFFFFFFFF) for _ in range(0, 100)]
+ with open (os.path.join(GlobalData.gBuildDirectory, "StackCookieValues64.json"), "w") as file:
+ json.dump(StackCookieValues64, file)
+
## The smallest unit that can be built in multi-thread build mode
#
# This is the base class of build unit. The "Obj" parameter must provide
@@ -1794,6 +1812,7 @@ class Build():
self.UniFlag,
self.Progress
)
+ GenerateStackCookieValues()
self.Fdf = Wa.FdfFile
self.LoadFixAddress = Wa.Platform.LoadFixAddress
self.BuildReport.AddPlatformReport(Wa)
@@ -1897,6 +1916,7 @@ class Build():
self.Progress,
self.ModuleFile
)
+ GenerateStackCookieValues()
self.Fdf = Wa.FdfFile
self.LoadFixAddress = Wa.Platform.LoadFixAddress
Wa.CreateMakeFile(False)
@@ -2147,6 +2167,7 @@ class Build():
self.UniFlag,
self.Progress
)
+ GenerateStackCookieValues()
self.Fdf = Wa.FdfFile
self.LoadFixAddress = Wa.Platform.LoadFixAddress
self.BuildReport.AddPlatformReport(Wa)