diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index d1ca03862..f385fcbbf 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -2754,12 +2754,17 @@ extension Driver { // Support is determined by existence of the sanitizer library. // FIXME: Should we do this? This prevents cross-compiling with sanitizers // enabled. - var sanitizerSupported = try toolchain.runtimeLibraryExists( - for: stableAbi ? .address_stable_abi : sanitizer, - targetInfo: targetInfo, - parsedOptions: &parsedOptions, - isShared: sanitizer != .fuzzer && !stableAbi - ) + var sanitizerSupported = true + + // memtag-stack sanitizer doesn't have a runtime library + if sanitizer.hasRuntimeLibrary { + sanitizerSupported = try toolchain.runtimeLibraryExists( + for: stableAbi ? .address_stable_abi : sanitizer, + targetInfo: targetInfo, + parsedOptions: &parsedOptions, + isShared: sanitizer != .fuzzer && !stableAbi + ) + } if sanitizer == .thread { // TSAN is unavailable on Windows @@ -2813,6 +2818,16 @@ extension Driver { ) } + // Address and memtag-stack sanitizers can not be enabled concurrently. + if set.contains(.memtag_stack) && set.contains(.address) { + diagnosticEngine.emit( + .error_argument_not_allowed_with( + arg: "-sanitize=memtag-stack", + other: "-sanitize=address" + ) + ) + } + // Scudo can only be run with ubsan. if set.contains(.scudo) { let allowedSanitizers: Set = [.scudo, .undefinedBehavior] diff --git a/Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift index f2fad3ad0..3eef72cf3 100644 --- a/Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift +++ b/Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift @@ -65,17 +65,22 @@ extension Toolchain { return result } + /// Returns true if a runtime library exists for this sanitizer. Note: some + /// sanitizers don't have runtime libraries - ideally this function should + /// not be called on them - first check with `sanitizer.hasRuntimeLibrary` func runtimeLibraryExists( for sanitizer: Sanitizer, targetInfo: FrontendTargetInfo, parsedOptions: inout ParsedOptions, isShared: Bool ) throws -> Bool { - let runtimeName = try runtimeLibraryName( + guard let runtimeName = try runtimeLibraryName( for: sanitizer, targetTriple: targetInfo.target.triple, isShared: isShared - ) + ) else { + return false + } let path = try clangLibraryPath( for: targetInfo, parsedOptions: &parsedOptions diff --git a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift index 2277bb2d0..eb97593dd 100644 --- a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift @@ -168,9 +168,12 @@ public final class DarwinToolchain: Toolchain { for sanitizer: Sanitizer, targetTriple: Triple, isShared: Bool - ) throws -> String { + ) throws -> String? { + guard let libraryName = sanitizer.runtimeLibraryName else { + return nil + } return """ - libclang_rt.\(sanitizer.libraryName)_\ + libclang_rt.\(libraryName)_\ \(targetTriple.darwinPlatform!.libraryNameSuffix)\ \(isShared ? "_dynamic.dylib" : ".a") """ diff --git a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift index fc241bd96..b1558fe12 100644 --- a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift @@ -148,9 +148,12 @@ public final class GenericUnixToolchain: Toolchain { for sanitizer: Sanitizer, targetTriple: Triple, isShared: Bool - ) throws -> String { + ) throws -> String? { + guard let runtimeLibraryName = sanitizer.runtimeLibraryName else { + return nil + } let environment = (targetTriple.environment == .android) ? "-android" : "" - return "libclang_rt.\(sanitizer.libraryName)-\(targetTriple.archName)\(environment).a" + return "libclang_rt.\(runtimeLibraryName)-\(targetTriple.archName)\(environment).a" } public func addPlatformSpecificCommonFrontendOptions( diff --git a/Sources/SwiftDriver/Toolchains/Toolchain.swift b/Sources/SwiftDriver/Toolchains/Toolchain.swift index e366bde6e..eb073e2a0 100644 --- a/Sources/SwiftDriver/Toolchains/Toolchain.swift +++ b/Sources/SwiftDriver/Toolchains/Toolchain.swift @@ -143,11 +143,12 @@ public protocol Toolchain { targetInfo: FrontendTargetInfo ) throws -> ResolvedTool + /// Returns the runtime library name for a given sanitizer (or nil if the sanitizer does not have a runtime library) func runtimeLibraryName( for sanitizer: Sanitizer, targetTriple: Triple, isShared: Bool - ) throws -> String + ) throws -> String? func platformSpecificInterpreterEnvironmentVariables( env: ProcessEnvironmentBlock, diff --git a/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift b/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift index db95a4f5e..99abffa80 100644 --- a/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift @@ -146,10 +146,10 @@ public final class WebAssemblyToolchain: Toolchain { for sanitizer: Sanitizer, targetTriple: Triple, isShared: Bool - ) throws -> String { + ) throws -> String? { switch sanitizer { case .address: - return "libclang_rt.\(sanitizer.libraryName)-\(targetTriple.archName).a" + return "libclang_rt.\(sanitizer.runtimeLibraryName!)-\(targetTriple.archName).a" default: throw Error.sanitizersUnsupportedForTarget(targetTriple.triple) } diff --git a/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift b/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift index d9e341c8c..218b2a7b3 100644 --- a/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift @@ -135,14 +135,17 @@ extension WindowsToolchain.ToolchainValidationError { public var globalDebugPathRemapping: String? { nil } public func runtimeLibraryName(for sanitizer: Sanitizer, targetTriple: Triple, - isShared: Bool) throws -> String { + isShared: Bool) throws -> String? { // TODO(compnerd) handle shared linking // FIXME(compnerd) when should `clang_rt.ubsan_standalone_cxx` be used? if sanitizer == .undefinedBehavior { return "clang_rt.ubsan_standalone.lib" } - return "clang_rt.\(sanitizer.libraryName).lib" + if sanitizer == .memtag_stack { + throw ToolchainValidationError.unsupportedSanitizer(sanitizer) + } + return "clang_rt.\(sanitizer.runtimeLibraryName!).lib" } public func validateArgs(_ parsedOptions: inout ParsedOptions, diff --git a/Sources/SwiftDriver/Utilities/Sanitizer.swift b/Sources/SwiftDriver/Utilities/Sanitizer.swift index 33f84c832..1ae90648f 100644 --- a/Sources/SwiftDriver/Utilities/Sanitizer.swift +++ b/Sources/SwiftDriver/Utilities/Sanitizer.swift @@ -33,8 +33,19 @@ public enum Sanitizer: String, Hashable { /// Scudo hardened allocator case scudo - /// The name inside the `compiler_rt` library path (e.g. libclang_rt.{name}.a) - var libraryName: String { + /// Memory-Tagging-based stack sanitizer + case memtag_stack = "memtag-stack" + + /// Does this sanitizer have a runtime library + var hasRuntimeLibrary: Bool { + if self == .memtag_stack { + return false + } + return true + } + + /// The name inside the `compiler_rt` runtime library path (e.g. libclang_rt.{name}.a) + var runtimeLibraryName: String? { switch self { case .address: return "asan" case .address_stable_abi: return "asan_abi" @@ -42,6 +53,7 @@ public enum Sanitizer: String, Hashable { case .undefinedBehavior: return "ubsan" case .fuzzer: return "fuzzer" case .scudo: return "scudo" + case .memtag_stack: return nil } } } diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 57559f00d..35683994d 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -3101,6 +3101,16 @@ final class SwiftDriverTests: XCTestCase { XCTAssertJobInvocationMatches(jobs[2], .flag("-fsanitize=undefined")) } + do { + // memory tagging stack sanitizer + var driver = try Driver(args: commonArgs + ["-sanitize=memtag-stack"]) + let jobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(jobs.count, 3) + XCTAssertJobInvocationMatches(jobs[0], .flag("-sanitize=memtag-stack")) + // No runtime for memtag-stack - thus no linker arg required + } + // FIXME: This test will fail when run on macOS, because the driver uses // the existence of the runtime support libraries to determine if // a sanitizer is supported. Until we allow cross-compiling with