From ad6e856c2697564f2619f125721dbeaa75a81686 Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Thu, 13 Nov 2025 12:12:25 +0000 Subject: [PATCH] [Sanitizers] Add support for -sanitize=memtag-stack This sanitizer adds MTE (memory tagging extension) checks to stack variable accesses. Enablement requires setting an attribute on function definitions, and the instrumentation is added by LLVM. --- include/swift/Basic/Sanitizers.def | 11 +++++----- lib/IRGen/IRGenSIL.cpp | 2 ++ lib/Option/SanitizerOptions.cpp | 17 ++++++++++++++++ test/Driver/sanitizers.swift | 32 ++++++++++++++++++++++++++++++ test/IRGen/memtag_stack.swift | 11 ++++++++++ 5 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 test/IRGen/memtag_stack.swift diff --git a/include/swift/Basic/Sanitizers.def b/include/swift/Basic/Sanitizers.def index d79fecb595145..152473c200576 100644 --- a/include/swift/Basic/Sanitizers.def +++ b/include/swift/Basic/Sanitizers.def @@ -20,10 +20,11 @@ // SANITIZER(enum_bit, kind, name, file) -SANITIZER(0, Address, address, asan) -SANITIZER(1, Thread, thread, tsan) -SANITIZER(2, Undefined, undefined, ubsan) -SANITIZER(3, Fuzzer, fuzzer, fuzzer) -SANITIZER(4, Scudo, scudo, scudo_standalone) +SANITIZER(0, Address, address, asan) +SANITIZER(1, Thread, thread, tsan) +SANITIZER(2, Undefined, undefined, ubsan) +SANITIZER(3, Fuzzer, fuzzer, fuzzer) +SANITIZER(4, Scudo, scudo, scudo_standalone) +SANITIZER(5, MemTagStack, memtag-stack, memtag-stack) #undef SANITIZER diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index c1dabec314a35..32840f2c98f0d 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1899,6 +1899,8 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f, // being in the external file or via annotations. if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Address) CurFn->addFnAttr(llvm::Attribute::SanitizeAddress); + if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::MemTagStack) + CurFn->addFnAttr(llvm::Attribute::SanitizeMemTag); if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) { auto declContext = f->getDeclContext(); if (isa_and_nonnull(declContext)) { diff --git a/lib/Option/SanitizerOptions.cpp b/lib/Option/SanitizerOptions.cpp index 1c10d69d4a539..897ad865d561a 100644 --- a/lib/Option/SanitizerOptions.cpp +++ b/lib/Option/SanitizerOptions.cpp @@ -152,6 +152,11 @@ OptionSet swift::parseSanitizerArgValues( bool isShared = (kind != SanitizerKind::Fuzzer); bool sanitizerSupported = sanitizerRuntimeLibExists(fileName, isShared); + if (kind == SanitizerKind::MemTagStack) + // MemTagStack requires no runtime, so ignore library check above + sanitizerSupported = + Triple.isOSDarwin() && Triple.getArch() == llvm::Triple::aarch64; + // TSan is explicitly not supported for 32 bits. if (kind == SanitizerKind::Thread && !Triple.isArch64Bit()) sanitizerSupported = false; @@ -176,6 +181,18 @@ OptionSet swift::parseSanitizerArgValues( Triple.getTriple()); } + // MemTagStack and ASan can not be enabled concurrently. + if ((sanitizerSet & SanitizerKind::MemTagStack) + && (sanitizerSet & SanitizerKind::Address)) { + SmallString<128> b1; + SmallString<128> b2; + Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with, + (A->getOption().getPrefixedName() + + toStringRef(SanitizerKind::Address)).toStringRef(b1), + (A->getOption().getPrefixedName() + + toStringRef(SanitizerKind::MemTagStack)).toStringRef(b2)); + } + // Address and thread sanitizers can not be enabled concurrently. if ((sanitizerSet & SanitizerKind::Thread) && (sanitizerSet & SanitizerKind::Address)) { diff --git a/test/Driver/sanitizers.swift b/test/Driver/sanitizers.swift index a5b1c8d3e128f..08a2ac57b7f34 100644 --- a/test/Driver/sanitizers.swift +++ b/test/Driver/sanitizers.swift @@ -39,6 +39,21 @@ // RUN: %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=undefined -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=UBSAN_LINUX %s // RUN: %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=undefined -target x86_64-unknown-windows-msvc %s 2>&1 | %FileCheck -check-prefix=UBSAN_WINDOWS %s +/* + * MemTagStack Sanitizer Tests + */ +// RUN: %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target arm64-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN -check-prefix=MEMTAGSAN_OSX_ARM64 %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target x86_64-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_OSX_X86 %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target x86-apple-macosx10.9 %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_OSX_32 %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target x86_64-apple-ios7.1-simulator %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_IOSSIM %s +// RUN: %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target arm64-apple-ios7.1 %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_IOS %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target x86_64-apple-tvos9.0-simulator %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_tvOS_SIM %s +// RUN: %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target arm64-apple-tvos9.0 %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_tvOS %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target i386-apple-watchos2.0-simulator %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_watchOS_SIM %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target armv7k-apple-watchos2.0 %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_watchOS %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target x86_64-unknown-windows-msvc %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_WINDOWS %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=memtag-stack -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck -check-prefix=MEMTAGSAN_LINUX %s + /* * Multiple Sanitizers At Once */ @@ -50,6 +65,7 @@ // RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -target x86_64-apple-macosx10.9 -sanitize=address,unknown %s 2>&1 | %FileCheck -check-prefix=BADARG %s // RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -target x86_64-apple-macosx10.9 -sanitize=address -sanitize=unknown %s 2>&1 | %FileCheck -check-prefix=BADARG %s // RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -target x86_64-apple-macosx10.9 -sanitize=address,thread %s 2>&1 | %FileCheck -check-prefix=INCOMPATIBLESANITIZERS %s +// RUN: not %swiftc_driver -sdk '""' -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -target arm64-apple-macosx10.9 -sanitize=memtag-stack,address %s 2>&1 | %FileCheck -check-prefix=INCOMPATIBLESANITIZERS_2 %s /* * Make sure we don't accidentally add the sanitizer library path when building libraries or modules @@ -106,7 +122,23 @@ // UBSAN: -rpath @executable_path +// MEMTAGSAN: swift +// MEMTAGSAN: -sanitize=memtag-stack + +// MEMTAGSAN_OSX_ARM64-NOT: unsupported option '-sanitize=memtag-stack' for target 'arm64-apple-macosx10.9' +// MEMTAGSAN_OSX_X86: unsupported option '-sanitize=memtag-stack' for target 'x86_64-apple-macosx10.9' +// MEMTAGSAN_OSX_32: unsupported option '-sanitize=memtag-stack' for target 'x86-apple-macosx10.9' +// MEMTAGSAN_IOSSIM: unsupported option '-sanitize=memtag-stack' for target 'x86_64-apple-ios7.1-simulator' +// MEMTAGSAN_IOS-NOT: unsupported option '-sanitize=memtag-stack' for target 'arm64-apple-ios7.1' +// MEMTAGSAN_tvOS_SIM: unsupported option '-sanitize=memtag-stack' for target 'x86_64-apple-tvos9.0-simulator' +// MEMTAGSAN_tvOS-NOT: unsupported option '-sanitize=memtag-stack' for target 'arm64-apple-tvos9.0' +// MEMTAGSAN_watchOS_SIM: unsupported option '-sanitize=memtag-stack' for target 'i386-apple-watchos2.0-simulator' +// MEMTAGSAN_watchOS: unsupported option '-sanitize=memtag-stack' for target 'armv7k-apple-watchos2.0' +// MEMTAGSAN_LINUX: unsupported option '-sanitize=memtag-stack' for target 'x86_64-unknown-linux-gnu' +// MEMTAGSAN_WINDOWS: unsupported option '-sanitize=memtag-stack' for target 'x86_64-unknown-windows-msvc' + // MULTIPLE_SAN_LINUX: -fsanitize=address,undefined,fuzzer // BADARG: unsupported argument 'unknown' to option '-sanitize=' // INCOMPATIBLESANITIZERS: argument '-sanitize=address' is not allowed with '-sanitize=thread' +// INCOMPATIBLESANITIZERS_2: argument '-sanitize=address' is not allowed with '-sanitize=memtag-stack' diff --git a/test/IRGen/memtag_stack.swift b/test/IRGen/memtag_stack.swift new file mode 100644 index 0000000000000..5a93ada525969 --- /dev/null +++ b/test/IRGen/memtag_stack.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -emit-ir -sanitize=memtag-stack -parse-as-library -target arm64-apple-macosx10.9 %s | %FileCheck %s + +// Check that functions have the sanitize_memtag attribute when -sanitize=memtag-stack is enabled + +// CHECK-LABEL: define {{.*}} @"${{.*}}testFunction{{.*}}" +// CHECK-SAME: [[ATTRS:#[0-9]+]] +// CHECK: attributes [[ATTRS]] = {{.*}} sanitize_memtag + +func testFunction() -> Int { + return 42 +}