From d7d9486724f2e7ffe9aec661e20e0e40a559e336 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 24 Jul 2025 17:11:19 -0700 Subject: [PATCH] Add compiler build settings for Enhanced Security Add a new "Security" build settings group and an "Enhanced Security" build setting that enables a number of security settings at once, including: * Enabling pointer authentication * Enabling the typed allocator * Enabling hardened libc++ * Enabling stack zero initialization * Enabling security relevant compiler warnings The goal is that enabling the Enhanced Security build setting should enable all of these other settings by default and that projects will be able to explicitly disable individual settings if needed. rdar://151195113 --- Sources/SWBCore/Specs/CoreBuildSystem.xcspec | 27 + .../Specs/en.lproj/CoreBuildSystem.strings | 29 + .../SWBUniversalPlatform/Specs/Clang.xcspec | 164 ++++- Sources/SWBUniversalPlatform/Specs/Ld.xcspec | 1 - .../Specs/en.lproj/Apple Clang.strings | 17 + .../BuildOperationTests.swift | 84 ++- .../TaskConstructionTests.swift | 608 +++++++++++++++++- 7 files changed, 915 insertions(+), 15 deletions(-) diff --git a/Sources/SWBCore/Specs/CoreBuildSystem.xcspec b/Sources/SWBCore/Specs/CoreBuildSystem.xcspec index 0514414a..b1ad1195 100644 --- a/Sources/SWBCore/Specs/CoreBuildSystem.xcspec +++ b/Sources/SWBCore/Specs/CoreBuildSystem.xcspec @@ -3535,6 +3535,33 @@ For more information on mergeable libraries, see [Configuring your project to us Category = BuildOptions; Description = "Enables building with code coverage instrumentation. This is only used when the build has code coverage enabled, which is typically done via the Xcode scheme or test plan settings."; }, + { Name = ENABLE_ENHANCED_SECURITY; + Type = Boolean; + DefaultValue = NO; + Category = SecurityPolicy; + }, + { Name = ENABLE_POINTER_AUTHENTICATION; + Type = Boolean; + DefaultValue = "$(ENABLE_ENHANCED_SECURITY)"; + Category = SecurityPolicy; + }, + { Name = ENABLE_SECURITY_COMPILER_WARNINGS; + Type = Boolean; + DefaultValue = "$(ENABLE_ENHANCED_SECURITY)"; + Category = SecurityPolicy; + }, + { + Name = "ENABLE_C_BOUNDS_SAFETY"; + Type = Boolean; + DefaultValue = NO; + Category = Security; + }, + { + Name = "ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS"; + Type = Boolean; + DefaultValue = NO; + Category = Security; + }, { Name = ENABLE_TESTABILITY; Type = Boolean; DefaultValue = NO; diff --git a/Sources/SWBCore/Specs/en.lproj/CoreBuildSystem.strings b/Sources/SWBCore/Specs/en.lproj/CoreBuildSystem.strings index 4aeafd33..d2482add 100644 --- a/Sources/SWBCore/Specs/en.lproj/CoreBuildSystem.strings +++ b/Sources/SWBCore/Specs/en.lproj/CoreBuildSystem.strings @@ -669,6 +669,35 @@ Typically this path is not set per target, but is provided as an option on the c "[MODULE_STOP]-name" = "Module Stop Routine"; "[MODULE_STOP]-description" = "This defines the name of the kernel module stop routine. This is only used when building kernel extensions."; +// Security Settings + +"[SecurityPolicy]-category" = "Security"; + +"[ENABLE_ENHANCED_SECURITY]-name" = "Enable Enhanced Security"; +"[ENABLE_ENHANCED_SECURITY]-description" = "Enables a set of security build settings, including pointer authentication, typed allocator support, hardened C++ standard library, and security-related compiler warnings. These settings can be disabled individually."; +"[ENABLE_ENHANCED_SECURITY]-value-[YES]" = "Yes"; +"[ENABLE_ENHANCED_SECURITY]-value-[NO]" = "No"; + +"[ENABLE_POINTER_AUTHENTICATION]-name" = "Enable Pointer Authentication"; +"[ENABLE_POINTER_AUTHENTICATION]-description" = "Builds the target with pointer authentication enabled. Adds an additional architectural slice (arm64e) with pointer authentication instructions."; +"[ENABLE_POINTER_AUTHENTICATION]-value-[YES]" = "Yes"; +"[ENABLE_POINTER_AUTHENTICATION]-value-[NO]" = "No"; + +"[ENABLE_SECURITY_COMPILER_WARNINGS]-name" = "Enable Security-Relevant Compiler Warnings"; +"[ENABLE_SECURITY_COMPILER_WARNINGS]-description" = "Enables a set of security-relevant compiler warnings that check for common bounds-safety and lifetime-safety issues."; +"[ENABLE_SECURITY_COMPILER_WARNINGS]-value-[YES]" = "Yes"; +"[ENABLE_SECURITY_COMPILER_WARNINGS]-value-[NO]" = "No"; + +"[ENABLE_C_BOUNDS_SAFETY]-name" = "Enable Language Extension for Bounds Safety in C"; +"[ENABLE_C_BOUNDS_SAFETY]-description" = "Enables the -fbounds-safety language extension, which guarantees bounds safety for C"; +"[ENABLE_C_BOUNDS_SAFETY]-value-[NO]" = "No"; +"[ENABLE_C_BOUNDS_SAFETY]-value-[YES]" = "Yes"; + +"[ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS]-name" = "Enforce Bounds-Safe Buffer Usage in C++"; +"[ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS]-description" = "Enables a strict programming model that guarantees bounds safety in C++ by rejecting raw pointer arithmetic (enabling the -Wunsafe-buffer-usage warning as an error) and requiring the use of hardened C++ Standard Library APIs for buffer manipulation."; +"[ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS]-value-[NO]" = "No"; +"[ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS]-value-[YES]" = "Yes"; + // Compiler Version "[GCC_VERSION]-name" = "Compiler for C/C++/Objective-C"; diff --git a/Sources/SWBUniversalPlatform/Specs/Clang.xcspec b/Sources/SWBUniversalPlatform/Specs/Clang.xcspec index f7ce248e..60e91aeb 100644 --- a/Sources/SWBUniversalPlatform/Specs/Clang.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/Clang.xcspec @@ -536,7 +536,7 @@ FileTypes = ( "sourcecode.c.c", ); - DefaultValue = NO; + DefaultValue = "$(ENABLE_C_BOUNDS_SAFETY)"; CommandLineArgs = { YES = ( "-fbounds-safety" ); NO = (); @@ -640,14 +640,74 @@ Category = LanguageCXX; }, { - Name = __LIBRARY_HARDENING_DEFAULT_VALUE_0; + Name = __ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES_YES; + Type = String; + DefaultValue = "YES"; + }, + { + Name = __ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES_NO; + Type = String; + DefaultValue = "YES"; + }, + { + Name = __ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_NO_YES; + Type = String; + DefaultValue = "YES"; + }, + { + Name = __ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_NO_NO; + Type = String; + DefaultValue = "NO"; + }, + { + Name = __ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS; + Type = String; + DefaultValue = "$(__ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_$(ENABLE_ENHANCED_SECURITY)_$(ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS))"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_0_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "debug"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_0_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_NO; Type = String; DefaultValue = "debug"; }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_1_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "fast"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_2_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "fast"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_3_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "fast"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_s_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "fast"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_fast_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "fast"; + }, + { + Name = __LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_z_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_YES; + Type = String; + DefaultValue = "fast"; + }, { Name = __LIBRARY_HARDENING_DEFAULT_VALUE; Type = String; - DefaultValue = "$(__LIBRARY_HARDENING_DEFAULT_VALUE_$(GCC_OPTIMIZATION_LEVEL))"; + DefaultValue = "$(__LIBRARY_HARDENING_DEFAULT_VALUE_OPT_LEVEL_$(GCC_OPTIMIZATION_LEVEL)_ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS_$(__ENHANCED_SECURITY_OR_BOUNDS_SAFE_BUFFERS))"; }, { Name = "CLANG_CXX_STANDARD_LIBRARY_HARDENING"; @@ -736,11 +796,26 @@ ); Category = LanguageCXX; }, + { + Name = "_UNSAFE_BUFFER_USAGE_DEFAULT_ENABLE_SAFE_BUFFERS_NO"; + Type = String; + DefaultValue = "DEFAULT"; + }, + { + Name = "_UNSAFE_BUFFER_USAGE_DEFAULT_ENABLE_SAFE_BUFFERS_YES"; + Type = String; + DefaultValue = "YES_ERROR"; + }, + { + Name = "_UNSAFE_BUFFER_USAGE_DEFAULT"; + Type = String; + DefaultValue = "$(_UNSAFE_BUFFER_USAGE_DEFAULT_ENABLE_SAFE_BUFFERS_$(ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS))"; + }, { Name = "CLANG_WARN_UNSAFE_BUFFER_USAGE"; Type = Enumeration; Values = ( DEFAULT, YES, YES_ERROR, NO ); - DefaultValue = DEFAULT; + DefaultValue = "$(_UNSAFE_BUFFER_USAGE_DEFAULT)"; CommandLineArgs = { DEFAULT = (); NO = ( "-Wno-unsafe-buffer-usage" ); @@ -801,7 +876,7 @@ { Name = "GCC_OPTIMIZATION_LEVEL"; Type = Enumeration; - // NOTE: Updating these values requires updating LLVM_OPTIMIZATION_LEVEL_VAL_X. + // NOTE: Updating these values requires updating LLVM_OPTIMIZATION_LEVEL_VAL_X and CLANG_CXX_STANDARD_LIBRARY_HARDENING. Values = ( 0, 1, @@ -945,6 +1020,22 @@ }; Category = WarningsPolicy; }, + { Name = CLANG_ENABLE_SECURITY_COMPILER_WARNINGS; + Type = Boolean; + DefaultValue = "$(ENABLE_SECURITY_COMPILER_WARNINGS)"; + CommandLineArgs = { + YES = ("-Wbuiltin-memcpy-chk-size", + "-Wformat-nonliteral", + "-Warray-bounds", + "-Warray-bounds-pointer-arithmetic", + "-Wsuspicious-memaccess", + "-Wsizeof-array-div", + "-Wsizeof-pointer-div", + "-Wreturn-stack-address"); + NO = (); + }; + // Hidden. + }, { Name = "GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS"; Type = Boolean; @@ -1381,7 +1472,7 @@ { Name = "CLANG_WARN_EMPTY_BODY"; Type = Boolean; - DefaultValue = NO; + DefaultValue = "$(CLANG_ENABLE_SECURITY_COMPILER_WARNINGS)"; CommandLineArgs = { YES = ( "-Wempty-body" ); NO = ( "-Wno-empty-body" ); @@ -1447,7 +1538,7 @@ { Name = "GCC_WARN_SHADOW"; Type = Boolean; - DefaultValue = NO; + DefaultValue = "$(CLANG_ENABLE_SECURITY_COMPILER_WARNINGS)"; CommandLineArgs = { YES = ( "-Wshadow", @@ -1800,6 +1891,27 @@ }; Category = Warnings; }, + { + Name = "CLANG_ENABLE_STACK_ZERO_INIT"; + Type = Boolean; + DefaultValue = "$(ENABLE_ENHANCED_SECURITY)"; + Category = Security; + }, + { + Name = "_CLANG_TRIVIAL_AUTO_VAR_INIT_DEFAULT_ENABLE_ZERO_INIT_YES"; + Type = Boolean; + DefaultValue = "zero"; + }, + { + Name = "_CLANG_TRIVIAL_AUTO_VAR_INIT_DEFAULT_ENABLE_ZERO_INIT_NO"; + Type = Boolean; + DefaultValue = "default"; + }, + { + Name = "_CLANG_TRIVIAL_AUTO_VAR_INIT_DEFAULT"; + Type = String; + DefaultValue = "$(_CLANG_TRIVIAL_AUTO_VAR_INIT_DEFAULT_ENABLE_ZERO_INIT_$(CLANG_ENABLE_STACK_ZERO_INIT))"; + }, { Name = "CLANG_TRIVIAL_AUTO_VAR_INIT"; Type = Enumeration; @@ -1809,7 +1921,7 @@ zero, pattern, ); - DefaultValue = default; + DefaultValue = "$(_CLANG_TRIVIAL_AUTO_VAR_INIT_DEFAULT)"; CommandLineArgs = { default = ( ); uninitialized = ( "-ftrivial-auto-var-init=uninitialized" ); @@ -2900,7 +3012,17 @@ Condition = "$(CLANG_UNDEFINED_BEHAVIOR_SANITIZER_TRAP_ON_SECURITY_ISSUES) && $(GCC_OPTIMIZATION_LEVEL) != 0"; }, { - Name = "CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT"; + Name = "_CLANG_ENABLE_TYPED_ALLOCATOR_SUPPORT_DEFAULT_ENHANCED_SECURITY_YES"; + Type = Enumeration; + Values = ( + compiler-default, + YES, + NO, + ); + DefaultValue = YES; + }, + { + Name = "_CLANG_ENABLE_TYPED_ALLOCATOR_SUPPORT_DEFAULT_ENHANCED_SECURITY_NO"; Type = Enumeration; Values = ( compiler-default, @@ -2908,11 +3030,32 @@ NO, ); DefaultValue = compiler-default; + }, + { + Name = "_CLANG_ENABLE_TYPED_ALLOCATOR_SUPPORT_DEFAULT"; + Type = Enumeration; + Values = ( + compiler-default, + YES, + NO, + ); + DefaultValue = "$(_CLANG_ENABLE_TYPED_ALLOCATOR_SUPPORT_DEFAULT_ENHANCED_SECURITY_$(ENABLE_ENHANCED_SECURITY))"; + }, + { + Name = "CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT"; + Type = Enumeration; + Values = ( + compiler-default, + YES, + NO, + ); + DefaultValue = "$(_CLANG_ENABLE_TYPED_ALLOCATOR_SUPPORT_DEFAULT)"; CommandLineArgs = { compiler-default = (); YES = ("-ftyped-memory-operations"); NO = ("-fno-typed-memory-operations"); }; + Category = Language; }, { Name = "CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT"; @@ -2922,7 +3065,7 @@ YES, NO, ); - DefaultValue = compiler-default; + DefaultValue = "$(_CLANG_ENABLE_TYPED_ALLOCATOR_SUPPORT_DEFAULT)"; CommandLineArgs = { compiler-default = (); YES = ("-ftyped-cxx-new-delete", "-ftyped-cxx-delete"); @@ -2933,6 +3076,7 @@ YES = ("-ftyped-cxx-new-delete", "-ftyped-cxx-delete"); NO = (); }; + Category = LanguageCXX; }, // Index-while-building options, not visible in build settings. { diff --git a/Sources/SWBUniversalPlatform/Specs/Ld.xcspec b/Sources/SWBUniversalPlatform/Specs/Ld.xcspec index b39abe90..6a9d0d46 100644 --- a/Sources/SWBUniversalPlatform/Specs/Ld.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/Ld.xcspec @@ -736,7 +736,6 @@ ); }; }, - { Name = "__CREATE_INFOPLIST_SECTION_IN_BINARY"; Type = Boolean; diff --git a/Sources/SWBUniversalPlatform/Specs/en.lproj/Apple Clang.strings b/Sources/SWBUniversalPlatform/Specs/en.lproj/Apple Clang.strings index 8a159c12..bd8b3100 100644 --- a/Sources/SWBUniversalPlatform/Specs/en.lproj/Apple Clang.strings +++ b/Sources/SWBUniversalPlatform/Specs/en.lproj/Apple Clang.strings @@ -695,6 +695,23 @@ The restrictions on `offsetof` may be relaxed in a future version of the C++ sta "[CLANG_ENABLE_MODULES]-value-[YES]" = "Yes"; "[CLANG_ENABLE_MODULES]-value-[NO]" = "No"; +"[CLANG_ENABLE_STACK_ZERO_INIT]-name" = "Enable Stack Zero Initialization"; +"[CLANG_ENABLE_STACK_ZERO_INIT]-description" = "Automatically initializes stack variables to zero as a security protection."; +"[CLANG_ENABLE_STACK_ZERO_INIT]-value-[YES]" = "Yes"; +"[CLANG_ENABLE_STACK_ZERO_INIT]-value-[NO]" = "No"; + +"[CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT]-name" = "Enable Typed Allocator in C"; +"[CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT]-description" = "Enables compiler rewriting of allocation calls in C to provide type information to the allocator. Mitigates use-after-free security vulnerabilities."; +"[CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT]-value-[YES]" = "Yes"; +"[CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT]-value-[NO]" = "No"; +"[CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT]-value-[compiler-default]" = "Compiler Default"; + +"[CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT]-name" = "Enable Typed Allocator in C++"; +"[CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT]-description" = "Enables compiler rewriting of allocation calls in C++ to provide type information to the allocator. Mitigates use-after-free security vulnerabilities."; +"[CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT]-value-[YES]" = "Yes"; +"[CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT]-value-[NO]" = "No"; +"[CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT]-value-[compiler-default]" = "Compiler Default"; + "[CLANG_MODULES_AUTOLINK]-name" = "Link Frameworks Automatically"; "[CLANG_MODULES_AUTOLINK]-description" = "Automatically link SDK frameworks that are referenced using `#import` or `#include`. This feature requires also enabling support for modules. This build setting only applies to C-family languages."; "[CLANG_MODULES_AUTOLINK]-value-[YES]" = "Yes (when modules are enabled)"; diff --git a/Tests/SWBBuildSystemTests/BuildOperationTests.swift b/Tests/SWBBuildSystemTests/BuildOperationTests.swift index 68dd3c20..869029c6 100644 --- a/Tests/SWBBuildSystemTests/BuildOperationTests.swift +++ b/Tests/SWBBuildSystemTests/BuildOperationTests.swift @@ -6680,10 +6680,10 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script } @Test(.requireSDKs(.iOS)) - func pointerAuthenticationBuildSetting() async throws { + func pointerAuthenticationBuildSetting_iOS() async throws { func test(buildSettings: [String: String], expectedArchs: [String], line: UInt = #line) async throws { - try await withTemporaryDirectory { tmpDirPath async throws -> Void in + try await withTemporaryDirectory { (tmpDirPath: Path) async throws -> Void in let testWorkspace = try await TestWorkspace( "Test", sourceRoot: tmpDirPath.join("Test"), @@ -6719,7 +6719,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script try await tester.fs.writeFileContents(swiftFile) { stream in } } - try await tester.checkBuild(runDestination: .anyiOSDevice) { results in + try await tester.checkBuild(runDestination: .anyiOSDevice) { results -> Void in results.checkNoErrors() for arch in expectedArchs { @@ -6727,6 +6727,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script results.checkTask(.matchRuleType("CompileC"), .matchRuleItemPattern(.suffix("File.m")), .matchRuleItem(arch)) { _ in } results.checkTask(.matchRuleType("SwiftCompile"), .matchRuleItem(arch)) { _ in } results.checkTask(.matchRuleType("SwiftEmitModule"), .matchRuleItem(arch)) { _ in } + } } } @@ -6734,6 +6735,83 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script try await test(buildSettings: ["ENABLE_POINTER_AUTHENTICATION": "YES"], expectedArchs: ["arm64", "arm64e"]) try await test(buildSettings: ["ENABLE_POINTER_AUTHENTICATION": "NO"], expectedArchs: ["arm64"]) + + // ENABLE_ENHANCED_SECURITY enables pointer authentication unless ENABLE_POINTER_AUTHENTICATION is explicitly disabled. + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "YES"], expectedArchs: ["arm64", "arm64e"]) + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "NO"], expectedArchs: ["arm64"]) + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "YES", "ENABLE_POINTER_AUTHENTICATION": "NO"], expectedArchs: ["arm64"]) + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "NO", "ENABLE_POINTER_AUTHENTICATION": "YES"], expectedArchs: ["arm64e"]) + } + + @Test(.requireSDKs(.macOS)) + func pointerAuthenticationBuildSetting_macOS() async throws { + func test(buildSettings: [String: String], expectedArchs: [String], line: UInt = #line) async throws { + + try await withTemporaryDirectory { (tmpDirPath: Path) async throws -> Void in + let testWorkspace = try await TestWorkspace( + "Test", + sourceRoot: tmpDirPath.join("Test"), + projects: [ + TestProject( + "aProject", + groupTree: TestGroup("Sources", children: [ + TestFile("File.c"), + TestFile("File.swift"), + TestFile("File.m"), + ]), + buildConfigurations: [TestBuildConfiguration( + "Debug", + buildSettings: [ + "SDKROOT": "macosx", + "DONT_GENERATE_INFOPLIST_FILE": "YES", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SWIFT_VERSION": swiftVersion, + ])], + targets: [ + TestStandardTarget( + "aFramework", type: .framework, + buildConfigurations: [TestBuildConfiguration("Debug", buildSettings: buildSettings)], + buildPhases: [ + TestSourcesBuildPhase(["File.c", "File.swift", "File.m"]), + ]), + TestStandardTarget( + "anApp", type: .application, + buildConfigurations: [TestBuildConfiguration("Debug", buildSettings: buildSettings)], + buildPhases: [ + TestSourcesBuildPhase(["File.c", "File.swift", "File.m"]), + ]) + ])]) + + let tester = try await BuildOperationTester(getCore(), testWorkspace, simulated: false) + + // create the files + for file in ["File.swift", "File.c", "File.m"] { + let swiftFile = testWorkspace.sourceRoot.join("aProject/\(file)") + try await tester.fs.writeFileContents(swiftFile) { stream in } + } + + try await tester.checkBuild(runDestination: .anyMac) { results -> Void in + results.checkNoErrors() + + + for arch in expectedArchs { + results.checkTask(.matchRuleType("CompileC"), .matchRuleItemPattern(.suffix("File.c")), .matchRuleItem(arch)) { _ in } + results.checkTask(.matchRuleType("CompileC"), .matchRuleItemPattern(.suffix("File.m")), .matchRuleItem(arch)) { _ in } + results.checkTask(.matchRuleType("SwiftCompile"), .matchRuleItem(arch)) { _ in } + results.checkTask(.matchRuleType("SwiftEmitModule"), .matchRuleItem(arch)) { _ in } + } + } + } + } + + try await test(buildSettings: ["ENABLE_POINTER_AUTHENTICATION": "YES"], expectedArchs: ["x86_64", "arm64", "arm64e"]) + try await test(buildSettings: ["ENABLE_POINTER_AUTHENTICATION": "NO"], expectedArchs: ["x86_64", "arm64"]) + + // ENABLE_ENHANCED_SECURITY enables pointer authentication unless ENABLE_POINTER_AUTHENTICATION is explicitly disabled. + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "YES"], expectedArchs: ["x86_64", "arm64", "arm64e"]) + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "NO"], expectedArchs: ["x86_64", "arm64"]) + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "YES", "ENABLE_POINTER_AUTHENTICATION": "NO"], expectedArchs: ["x86_64", "arm64"]) + try await test(buildSettings: ["ENABLE_ENHANCED_SECURITY": "NO", "ENABLE_POINTER_AUTHENTICATION": "YES"], expectedArchs: ["x86_64", "arm64e"]) } @Test(.requireSDKs(.macOS)) diff --git a/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift b/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift index d2c11ff1..ea146fcd 100644 --- a/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift +++ b/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift @@ -8428,6 +8428,82 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { } } + @Test(.requireSDKs(.macOS)) + func zeroStackInit() async throws { + try await withTemporaryDirectory { tmpDir in + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: [ + TestFile("SourceFile.c"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "GENERATE_INFOPLIST_FILE": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase([ + "SourceFile.c" + ]) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + let enableStackZeroInitSetting = "CLANG_ENABLE_STACK_ZERO_INIT"; + let enableEnhancedSecuritySetting = "ENABLE_ENHANCED_SECURITY" + + func test(task: any PlannedTask, overrides: [String: String]) -> Void { + if let val = overrides[enableStackZeroInitSetting] { + if val == "YES" { + task.checkCommandLineContains(["-ftrivial-auto-var-init=zero"]) + } else if val == "NO" { + task.checkCommandLineNoMatch([.contains("-ftrivial-auto-var-init")]) + } + } else if let val = overrides[enableEnhancedSecuritySetting] { + if val == "YES" { + task.checkCommandLineContains(["-ftrivial-auto-var-init=zero"]) + } else if val == "NO" { + task.checkCommandLineNoMatch([.contains("-ftrivial-auto-var-init")]) + } + } else { + task.checkCommandLineNoMatch([.contains("-ftrivial-auto-var-init")]) + } + } + + let overrides = [ + [:], + [enableStackZeroInitSetting: "YES"], + [enableStackZeroInitSetting: "NO"], + + [enableStackZeroInitSetting: "YES", enableEnhancedSecuritySetting: "YES"], + [enableStackZeroInitSetting: "YES", enableEnhancedSecuritySetting: "NO"], + [enableStackZeroInitSetting: "NO", enableEnhancedSecuritySetting: "YES"], + ] + + for override in overrides { + await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: override), runDestination: .macOS, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), body: {task in test(task: task, overrides: override)}) + } + } + } + } + } + @Test(.requireSDKs(.macOS)) func typedMemoryOperations() async throws { try await withTemporaryDirectory { tmpDir in @@ -8465,8 +8541,10 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { let tester = try TaskConstructionTester(core, testProject) let typedMemoryOperationsC = "CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT"; let typedMemoryOperationsCXX = "CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT"; + let enableEnhancedSecuritySetting = "ENABLE_ENHANCED_SECURITY" func test(task: any PlannedTask, overrides: [String: String]) -> Void { + if let val = overrides[typedMemoryOperationsC] { if val == "YES" { task.checkCommandLineContains(["-ftyped-memory-operations"]) @@ -8487,6 +8565,16 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { task.checkCommandLineDoesNotContain("-fno-typed-cxx-new-delete") task.checkCommandLineDoesNotContain("-fno-typed-cxx-delete") } + } else if let val = overrides[enableEnhancedSecuritySetting] { + if val == "YES" { + task.checkCommandLineContains(["-ftyped-memory-operations"]) + task.checkCommandLineContains(["-ftyped-cxx-new-delete", "-ftyped-cxx-delete"]) + } else if val == "NO" { + task.checkCommandLineDoesNotContain("-ftyped-cxx-new-delete") + task.checkCommandLineDoesNotContain("-ftyped-cxx-delete") + task.checkCommandLineDoesNotContain("-fno-typed-cxx-new-delete") + task.checkCommandLineDoesNotContain("-fno-typed-cxx-delete") + } } } @@ -8497,6 +8585,22 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { [typedMemoryOperationsCXX: "YES"], [typedMemoryOperationsCXX: "NO"], [typedMemoryOperationsCXX: "compiler-default"], + + [enableEnhancedSecuritySetting: "YES"], + [enableEnhancedSecuritySetting: "NO"], + [enableEnhancedSecuritySetting: "NO", typedMemoryOperationsC: "NO"], + [enableEnhancedSecuritySetting: "NO", typedMemoryOperationsC: "YES"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsC: "NO"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsC: "YES"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsC: "compiler-default"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsC: "compiler-default"], + + [enableEnhancedSecuritySetting: "NO", typedMemoryOperationsCXX: "NO"], + [enableEnhancedSecuritySetting: "NO", typedMemoryOperationsCXX: "YES"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsCXX: "NO"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsCXX: "YES"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsCXX: "compiler-default"], + [enableEnhancedSecuritySetting: "YES", typedMemoryOperationsCXX: "compiler-default"], ] for override in overrides { @@ -8514,6 +8618,268 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { } } + @Test(.requireSDKs(.macOS)) + func warnShadow() async throws { + try await withTemporaryDirectory { tmpDir in + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: [ + TestFile("SourceFile.c"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "GENERATE_INFOPLIST_FILE": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase([ + "SourceFile.c" + ]) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + let warnShadowSetting = "GCC_WARN_SHADOW"; + let enableEnhancedSecuritySetting = "ENABLE_ENHANCED_SECURITY" + + func test(task: any PlannedTask, overrides: [String: String]) -> Void { + if let val = overrides[warnShadowSetting] { + if val == "YES" { + task.checkCommandLineContains(["-Wshadow"]) + } else if (val == "NO") { + task.checkCommandLineContains(["-Wno-shadow"]) + } + } else if let val = overrides[enableEnhancedSecuritySetting] { + if val == "YES" { + task.checkCommandLineContains(["-Wshadow"]) + } else if val == "NO" { + task.checkCommandLineDoesNotContain("-Wshadow") + task.checkCommandLineContains(["-Wno-shadow"]) + } + } + } + + let overrides = [ + [warnShadowSetting: "YES"], + [warnShadowSetting: "NO"], + [enableEnhancedSecuritySetting: "YES"], + [enableEnhancedSecuritySetting: "NO"], + + [warnShadowSetting: "NO", enableEnhancedSecuritySetting: "YES"], + [warnShadowSetting: "YES", enableEnhancedSecuritySetting: "NO"], + ] + + for override in overrides { + await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: override), runDestination: .macOS, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), body: {task in test(task: task, overrides: override)}) + } + } + } + } + } + + @Test(.requireSDKs(.macOS)) + func warnEmptyBody() async throws { + try await withTemporaryDirectory { tmpDir in + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: [ + TestFile("SourceFile.c"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "GENERATE_INFOPLIST_FILE": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase([ + "SourceFile.c" + ]) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + let warnEmptyBodySetting = "CLANG_WARN_EMPTY_BODY"; + let enableEnhancedSecuritySetting = "ENABLE_ENHANCED_SECURITY" + + func test(task: any PlannedTask, overrides: [String: String]) -> Void { + if let val = overrides[warnEmptyBodySetting] { + if val == "YES" { + task.checkCommandLineContains(["-Wempty-body"]) + } else if (val == "NO") { + task.checkCommandLineContains(["-Wno-empty-body"]) + } + } else if let val = overrides[enableEnhancedSecuritySetting] { + if val == "YES" { + task.checkCommandLineContains(["-Wempty-body"]) + } else if val == "NO" { + task.checkCommandLineDoesNotContain("-Wempty-body") + task.checkCommandLineContains(["-Wno-empty-body"]) + } + } + } + + let overrides = [ + [warnEmptyBodySetting: "YES"], + [warnEmptyBodySetting: "NO"], + [enableEnhancedSecuritySetting: "YES"], + [enableEnhancedSecuritySetting: "NO"], + + [warnEmptyBodySetting: "NO", enableEnhancedSecuritySetting: "YES"], + [warnEmptyBodySetting: "YES", enableEnhancedSecuritySetting: "NO"], + ] + + for override in overrides { + await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: override), runDestination: .macOS, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), body: {task in test(task: task, overrides: override)}) + } + } + } + } + } + + @Test(.requireSDKs(.macOS)) + func securityCompilerWarnings() async throws { + try await withTemporaryDirectory { tmpDir in + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: [ + TestFile("SourceFile.c"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "GENERATE_INFOPLIST_FILE": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase([ + "SourceFile.c" + ]) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + let enableSecurityCompilerWarningsSetting = "ENABLE_SECURITY_COMPILER_WARNINGS"; + let enableEnhancedSecuritySetting = "ENABLE_ENHANCED_SECURITY" + + func generateWarningFlags(enabled: Bool) -> [String] { + let prefix = enabled ? "-W" : "-Wno-" + return ["\(prefix)builtin-memcpy-chk-size", + "\(prefix)format-nonliteral", + "\(prefix)array-bounds", + "\(prefix)array-bounds-pointer-arithmetic", + "\(prefix)suspicious-memaccess", + "\(prefix)sizeof-array-div", + "\(prefix)sizeof-pointer-div", + "\(prefix)return-stack-address",] + } + + func test(task: any PlannedTask, overrides: [String: String]) -> Void { + + if let val = overrides[enableSecurityCompilerWarningsSetting] { + if val == "YES" { + for enabledWarning in generateWarningFlags(enabled: true) { + task.checkCommandLineContains([enabledWarning]) + } + for disabledWarning in generateWarningFlags(enabled: false) { + task.checkCommandLineDoesNotContain(disabledWarning) + } + } else if val == "NO" { + // Pass nothing on NO + for enabledWarning in generateWarningFlags(enabled: true) { + task.checkCommandLineDoesNotContain(enabledWarning) + } + for disabledWarning in generateWarningFlags(enabled: false) { + task.checkCommandLineDoesNotContain(disabledWarning) + } + } + } else if let val = overrides[enableEnhancedSecuritySetting] { + if val == "YES" { + for enabledWarning in generateWarningFlags(enabled: true) { + task.checkCommandLineContains([enabledWarning]) + } + for disabledWarning in generateWarningFlags(enabled: false) { + task.checkCommandLineDoesNotContain(disabledWarning) + } + } else if val == "NO" { + // Pass nothing on NO + for enabledWarning in generateWarningFlags(enabled: true) { + task.checkCommandLineDoesNotContain(enabledWarning) + } + for disabledWarning in generateWarningFlags(enabled: false) { + task.checkCommandLineDoesNotContain(disabledWarning) + } + } + } + } + + let overrides = [ + [enableSecurityCompilerWarningsSetting: "YES"], + [enableSecurityCompilerWarningsSetting: "NO"], + + [enableEnhancedSecuritySetting: "YES"], + [enableEnhancedSecuritySetting: "NO"], + [enableEnhancedSecuritySetting: "NO", enableSecurityCompilerWarningsSetting: "NO"], + [enableEnhancedSecuritySetting: "NO", enableSecurityCompilerWarningsSetting: "YES"], + [enableEnhancedSecuritySetting: "YES", enableSecurityCompilerWarningsSetting: "NO"], + [enableEnhancedSecuritySetting: "YES", enableSecurityCompilerWarningsSetting: "YES"], + ] + + for override in overrides { + await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: override), runDestination: .macOS, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), body: {task in test(task: task, overrides: override)}) + } + } + } + } + } + + @Test(.requireSDKs(.macOS)) func unsafeBufferUsage() async throws { try await withTemporaryDirectory { tmpDir in @@ -8549,7 +8915,10 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { let core = try await getCore() let tester = try TaskConstructionTester(core, testProject) - let unsafeBufferUsageWarn = "CLANG_WARN_UNSAFE_BUFFER_USAGE"; + let unsafeBufferUsageWarn = "CLANG_WARN_UNSAFE_BUFFER_USAGE" + + // This setting enables both the CLANG_WARN_UNSAFE_BUFFER_USAGE and CLANG_CXX_STANDARD_LIBRARY_HARDENING + let enableBoundsSafeBuffers = "ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS" func test(task: any PlannedTask, overrides: [String: String]) -> Void { if let val = overrides[unsafeBufferUsageWarn] { @@ -8563,6 +8932,15 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { task.checkCommandLineDoesNotContain("-Wunsafe-buffer-usage") task.checkCommandLineDoesNotContain("-Wno-unsafe-buffer-usage") } + } else if let val = overrides[enableBoundsSafeBuffers] { + // When CLANG_WARN_UNSAFE_BUFFER_USAGE is not explicitly set, enable -Wunsafe-buffer-usage as on error when ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS is YES. + if (val == "YES") { + task.checkCommandLineContains(["-Werror=unsafe-buffer-usage"]) + } else if (val == "NO") { + task.checkCommandLineDoesNotContain("-Wunsafe-buffer-usage") + task.checkCommandLineDoesNotContain("-Wno-unsafe-buffer-usage") + task.checkCommandLineDoesNotContain("-Werror=unsafe-buffer-usage") + } } } @@ -8571,6 +8949,233 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { [unsafeBufferUsageWarn: "NO"], [unsafeBufferUsageWarn: "YES_ERROR"], [unsafeBufferUsageWarn: "DEFAULT"], + + [enableBoundsSafeBuffers: "YES"], + [enableBoundsSafeBuffers: "NO"], + + [enableBoundsSafeBuffers: "YES", unsafeBufferUsageWarn: "NO"], + [enableBoundsSafeBuffers: "NO", unsafeBufferUsageWarn: "YES"], + [enableBoundsSafeBuffers: "NO", unsafeBufferUsageWarn: "YES_ERROR"], + [enableBoundsSafeBuffers: "NO", unsafeBufferUsageWarn: "YES_DEFAULT"], + + ] + + for override in overrides { + await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: override), runDestination: .macOS, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), body: {task in test(task: task, overrides: override)}) + } + } + } + } + } + + @Test(.requireSDKs(.macOS)) + func boundsSafetyCLanguageExtension() async throws { + try await withTemporaryDirectory { tmpDir in + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: [ + TestFile("SourceFile.c"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "GENERATE_INFOPLIST_FILE": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase([ + "SourceFile.c" + ]) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + let enableBoundsSafetySetting = "CLANG_ENABLE_BOUNDS_SAFETY"; + + func test(task: any PlannedTask, overrides: [String: String]) -> Void { + if let val = overrides[enableBoundsSafetySetting] { + if val == "YES" { + task.checkCommandLineContains(["-fbounds-safety"]) + } else if (val == "NO") { + task.checkCommandLineDoesNotContain("-fbounds-safety") + } + } + } + + let overrides = [ + [enableBoundsSafetySetting: "YES"], + [enableBoundsSafetySetting: "NO"], + ] + + for override in overrides { + await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: override), runDestination: .macOS, fs: fs) { results -> Void in + results.checkTarget("AppTarget") { target -> Void in + results.checkTask(.matchTarget(target), .matchRuleType("CompileC"), body: {task in test(task: task, overrides: override)}) + } + } + } + } + } + + + @Test(.requireSDKs(.macOS)) + func libCPPLibraryHardening() async throws { + try await withTemporaryDirectory { tmpDir in + let testProject = TestProject( + "aProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", path: "Sources", + children: [ + TestFile("SourceFile.cpp"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug") + ], + targets: [ + TestStandardTarget( + "AppTarget", + type: .application, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "GENERATE_INFOPLIST_FILE": "YES" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase([ + "SourceFile.cpp" + ]) + ] + )] + ) + + let fs = PseudoFS() + + let core = try await getCore() + let tester = try TaskConstructionTester(core, testProject) + let cppHardeningSetting = "CLANG_CXX_STANDARD_LIBRARY_HARDENING" + let gccOptimizationLevelSetting = "GCC_OPTIMIZATION_LEVEL" + + // When either ENABLE_ENHANCED_SECURITY or ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS are YES and CLANG_CXX_STANDARD_LIBRARY_HARDENING + // is not explicitly set, then fast hardening mode should be enabled for all but debug builds. + let enableEnhancedSecuritySetting = "ENABLE_ENHANCED_SECURITY" + let enableBoundsSafeBuffersSetting = "ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS" + + func test(task: any PlannedTask, overrides: [String: String]) -> Void { + enum HardeningMode { + case NONE + case FAST + case EXTENSIVE + case DEBUG + } + + func checkCommandLineHardening(mode: HardeningMode, sourceLocation: SourceLocation = #_sourceLocation) { + task.checkCommandLineContains(["-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_\(mode)"]) + } + + func checkCommandLineNoHardening(sourceLocation: SourceLocation = #_sourceLocation) { + task.checkCommandLineNoMatch([.contains("-D_LIBCPP_HARDENING_MODE")]) + } + + + if let cppHardeningSettingVal = overrides[cppHardeningSetting] { + // When CLANG_CXX_STANDARD_LIBRARY_HARDENING is set, it wins and sets hardening mode policy + switch cppHardeningSettingVal { + case "": + // When the value is not set, no hardening should be passed so that the compiler can determine the appropriate default + // if any, based on the target triple. + checkCommandLineNoHardening() + case "none": + checkCommandLineHardening(mode: .NONE) + case "fast": + checkCommandLineHardening(mode: .FAST) + case "extensive": + checkCommandLineHardening(mode: .EXTENSIVE) + case "debug": + checkCommandLineHardening(mode: .DEBUG) + default: + assertionFailure("Unexpected CLANG_CXX_STANDARD_LIBRARY_HARDENING override") + } + } else { + // When CLANG_CXX_STANDARD_LIBRARY_HARDENING is not set, the command-line depends on GCC_OPTIMIZATION_LEVEL and ENABLE_ENHANCED_SECURITY + + // If the optimization level is set and it is -O0, then the hardening is DEBUG. + // Otherwise, the hardening level depends on ENABLE_ENHANCED_SECURITY. If enhanced security is enabled, then the default for non -O0 is FAST; otherwise + // the default is to not set the hardening level. + + let isOptLevelZero = (overrides[gccOptimizationLevelSetting] == .some("0")) + let isEnhancedSecurityEnabled = (overrides[enableEnhancedSecuritySetting] == .some("YES")) + let isEnableBoundsSafeBuffersEnabled = (overrides[enableBoundsSafeBuffersSetting] == .some("YES")) + + if isOptLevelZero { + checkCommandLineHardening(mode: .DEBUG) + } else if isEnhancedSecurityEnabled || isEnableBoundsSafeBuffersEnabled { + checkCommandLineHardening(mode: .FAST) + } else { + checkCommandLineNoHardening() + } + } + } + + let overrides = [ + [gccOptimizationLevelSetting: "0"], + [gccOptimizationLevelSetting: "1"], + [gccOptimizationLevelSetting: "2"], + [gccOptimizationLevelSetting: "3"], + [gccOptimizationLevelSetting: "fast"], + [gccOptimizationLevelSetting: "s"], + [gccOptimizationLevelSetting: "z"], + + [cppHardeningSetting: ""], + [cppHardeningSetting: "none"], + [cppHardeningSetting: "fast"], + [cppHardeningSetting: "extensive"], + [cppHardeningSetting: "debug"], + + [gccOptimizationLevelSetting: "0", enableEnhancedSecuritySetting: "YES", cppHardeningSetting: "none"], + + [gccOptimizationLevelSetting: "0", enableEnhancedSecuritySetting: "YES"], + [gccOptimizationLevelSetting: "1", enableEnhancedSecuritySetting: "YES"], + [gccOptimizationLevelSetting: "2", enableEnhancedSecuritySetting: "YES"], + [gccOptimizationLevelSetting: "3", enableEnhancedSecuritySetting: "YES"], + [gccOptimizationLevelSetting: "fast", enableEnhancedSecuritySetting: "YES"], + [gccOptimizationLevelSetting: "s", enableEnhancedSecuritySetting: "YES"], + [gccOptimizationLevelSetting: "z", enableEnhancedSecuritySetting: "YES"], + + [gccOptimizationLevelSetting: "0", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "1", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "2", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "3", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "fast", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "s", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "z", enableBoundsSafeBuffersSetting: "YES"], + + [gccOptimizationLevelSetting: "0", enableEnhancedSecuritySetting: "YES", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "0", enableEnhancedSecuritySetting: "YES", enableBoundsSafeBuffersSetting: "NO"], + [gccOptimizationLevelSetting: "0", enableEnhancedSecuritySetting: "NO", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "0", enableEnhancedSecuritySetting: "NO", enableBoundsSafeBuffersSetting: "NO"], + [gccOptimizationLevelSetting: "s", enableEnhancedSecuritySetting: "YES", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "s", enableEnhancedSecuritySetting: "YES", enableBoundsSafeBuffersSetting: "NO"], + [gccOptimizationLevelSetting: "s", enableEnhancedSecuritySetting: "NO", enableBoundsSafeBuffersSetting: "YES"], + [gccOptimizationLevelSetting: "s", enableEnhancedSecuritySetting: "NO", enableBoundsSafeBuffersSetting: "NO"], + + [enableEnhancedSecuritySetting: "YES"], + [enableBoundsSafeBuffersSetting: "YES"], ] for override in overrides { @@ -8583,6 +9188,7 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { } } + @Test(.requireSDKs(.macOS)) func warningSuppression() async throws { try await withTemporaryDirectory { tmpDir in