From: Evgeniy Stepanov Date: Mon, 15 Jul 2019 20:02:23 +0000 (+0000) Subject: ARM MTE stack sanitizer. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=647bdd3207072e2a8417980976636fd57ce9e068;p=clang ARM MTE stack sanitizer. Add "memtag" sanitizer that detects and mitigates stack memory issues using armv8.5 Memory Tagging Extension. It is similar in principle to HWASan, which is a software implementation of the same idea, but there are enough differencies to warrant a new sanitizer type IMHO. It is also expected to have very different performance properties. The new sanitizer does not have a runtime library (it may grow one later, along with a "debugging" mode). Similar to SafeStack and StackProtector, the instrumentation pass (in a follow up change) will be inserted in all cases, but will only affect functions marked with the new sanitize_memtag attribute. Reviewers: pcc, hctim, vitalybuka, ostannard Subscribers: srhines, mehdi_amini, javed.absar, kristof.beyls, hiraditya, cryptoad, steven_wu, dexonsmith, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D64169 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@366123 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Features.def b/include/clang/Basic/Features.def index 7ab7c4d170..7081c02e83 100644 --- a/include/clang/Basic/Features.def +++ b/include/clang/Basic/Features.def @@ -42,6 +42,7 @@ FEATURE(address_sanitizer, FEATURE(hwaddress_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress)) +FEATURE(memtag_sanitizer, LangOpts.Sanitize.has(SanitizerKind::MemTag)) FEATURE(xray_instrument, LangOpts.XRayInstrument) FEATURE(undefined_behavior_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Undefined)) diff --git a/include/clang/Basic/Sanitizers.def b/include/clang/Basic/Sanitizers.def index 1bb433a2b0..0037cc2146 100644 --- a/include/clang/Basic/Sanitizers.def +++ b/include/clang/Basic/Sanitizers.def @@ -55,6 +55,9 @@ SANITIZER("hwaddress", HWAddress) // Kernel Hardware-assisted AddressSanitizer (KHWASan) SANITIZER("kernel-hwaddress", KernelHWAddress) +// A variant of AddressSanitizer using AArch64 MTE extension. +SANITIZER("memtag", MemTag) + // MemorySanitizer SANITIZER("memory", Memory) diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 1c7c6fb541..7a0605b845 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -369,6 +369,10 @@ llvm::Function *CodeGenModule::CreateGlobalInitOrDestructFunction( !isInSanitizerBlacklist(SanitizerKind::KernelHWAddress, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeHWAddress); + if (getLangOpts().Sanitize.has(SanitizerKind::MemTag) && + !isInSanitizerBlacklist(SanitizerKind::MemTag, Fn, Loc)) + Fn->addFnAttr(llvm::Attribute::SanitizeMemTag); + if (getLangOpts().Sanitize.has(SanitizerKind::Thread) && !isInSanitizerBlacklist(SanitizerKind::Thread, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeThread); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 937648700a..eafe266744 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -696,6 +696,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, Fn->addFnAttr(llvm::Attribute::SanitizeAddress); if (SanOpts.hasOneOf(SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress)) Fn->addFnAttr(llvm::Attribute::SanitizeHWAddress); + if (SanOpts.has(SanitizerKind::MemTag)) + Fn->addFnAttr(llvm::Attribute::SanitizeMemTag); if (SanOpts.has(SanitizerKind::Thread)) Fn->addFnAttr(llvm::Attribute::SanitizeThread); if (SanOpts.hasOneOf(SanitizerKind::Memory | SanitizerKind::KernelMemory)) diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index ba501d645c..7ab960e8bc 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -2239,9 +2239,11 @@ bool CodeGenModule::isInSanitizerBlacklist(llvm::GlobalVariable *GV, SourceLocation Loc, QualType Ty, StringRef Category) const { // For now globals can be blacklisted only in ASan and KASan. - const SanitizerMask EnabledAsanMask = LangOpts.Sanitize.Mask & + const SanitizerMask EnabledAsanMask = + LangOpts.Sanitize.Mask & (SanitizerKind::Address | SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress); + SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress | + SanitizerKind::MemTag); if (!EnabledAsanMask) return false; const auto &SanitizerBL = getContext().getSanitizerBlacklist(); diff --git a/lib/CodeGen/SanitizerMetadata.cpp b/lib/CodeGen/SanitizerMetadata.cpp index 3211a3e74d..ebc9cd5529 100644 --- a/lib/CodeGen/SanitizerMetadata.cpp +++ b/lib/CodeGen/SanitizerMetadata.cpp @@ -20,14 +20,17 @@ using namespace CodeGen; SanitizerMetadata::SanitizerMetadata(CodeGenModule &CGM) : CGM(CGM) {} +static bool isAsanHwasanOrMemTag(const SanitizerSet& SS) { + return SS.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress | + SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress | + SanitizerKind::MemTag); +} + void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, SourceLocation Loc, StringRef Name, QualType Ty, bool IsDynInit, bool IsBlacklisted) { - if (!CGM.getLangOpts().Sanitize.hasOneOf(SanitizerKind::Address | - SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | - SanitizerKind::KernelHWAddress)) + if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) return; IsDynInit &= !CGM.isInSanitizerBlacklist(GV, Loc, Ty, "init"); IsBlacklisted |= CGM.isInSanitizerBlacklist(GV, Loc, Ty); @@ -58,10 +61,7 @@ void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, const VarDecl &D, bool IsDynInit) { - if (!CGM.getLangOpts().Sanitize.hasOneOf(SanitizerKind::Address | - SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | - SanitizerKind::KernelHWAddress)) + if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) return; std::string QualName; llvm::raw_string_ostream OS(QualName); @@ -78,10 +78,7 @@ void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, void SanitizerMetadata::disableSanitizerForGlobal(llvm::GlobalVariable *GV) { // For now, just make sure the global is not modified by the ASan // instrumentation. - if (CGM.getLangOpts().Sanitize.hasOneOf(SanitizerKind::Address | - SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | - SanitizerKind::KernelHWAddress)) + if (isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize)) reportGlobalToASan(GV, SourceLocation(), "", QualType(), false, true); } diff --git a/lib/Driver/SanitizerArgs.cpp b/lib/Driver/SanitizerArgs.cpp index 8d4b9fc85d..9132faa917 100644 --- a/lib/Driver/SanitizerArgs.cpp +++ b/lib/Driver/SanitizerArgs.cpp @@ -40,7 +40,8 @@ static const SanitizerMask NeedsUnwindTables = static const SanitizerMask SupportsCoverage = SanitizerKind::Address | SanitizerKind::HWAddress | SanitizerKind::KernelAddress | SanitizerKind::KernelHWAddress | - SanitizerKind::Memory | SanitizerKind::KernelMemory | SanitizerKind::Leak | + SanitizerKind::MemTag | SanitizerKind::Memory | + SanitizerKind::KernelMemory | SanitizerKind::Leak | SanitizerKind::Undefined | SanitizerKind::Integer | SanitizerKind::ImplicitConversion | SanitizerKind::Nullability | SanitizerKind::DataFlow | SanitizerKind::Fuzzer | @@ -122,6 +123,7 @@ static void addDefaultBlacklists(const Driver &D, SanitizerMask Kinds, SanitizerMask Mask; } Blacklists[] = {{"asan_blacklist.txt", SanitizerKind::Address}, {"hwasan_blacklist.txt", SanitizerKind::HWAddress}, + {"memtag_blacklist.txt", SanitizerKind::MemTag}, {"msan_blacklist.txt", SanitizerKind::Memory}, {"tsan_blacklist.txt", SanitizerKind::Thread}, {"dfsan_abilist.txt", SanitizerKind::DataFlow}, @@ -420,7 +422,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, SanitizerKind::Address | SanitizerKind::HWAddress | SanitizerKind::Leak | SanitizerKind::Thread | SanitizerKind::Memory | SanitizerKind::KernelAddress | - SanitizerKind::Scudo | SanitizerKind::SafeStack)}; + SanitizerKind::Scudo | SanitizerKind::SafeStack), + std::make_pair(SanitizerKind::MemTag, + SanitizerKind::Address | SanitizerKind::KernelAddress | + SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress)}; // Enable toolchain specific default sanitizers if not explicitly disabled. SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove; diff --git a/lib/Driver/ToolChains/Linux.cpp b/lib/Driver/ToolChains/Linux.cpp index b4e19b12c8..d900508ad9 100644 --- a/lib/Driver/ToolChains/Linux.cpp +++ b/lib/Driver/ToolChains/Linux.cpp @@ -1026,6 +1026,8 @@ SanitizerMask Linux::getSupportedSanitizers() const { Res |= SanitizerKind::HWAddress; Res |= SanitizerKind::KernelHWAddress; } + if (IsAArch64) + Res |= SanitizerKind::MemTag; return Res; } diff --git a/test/CodeGen/memtag-attr.cpp b/test/CodeGen/memtag-attr.cpp new file mode 100644 index 0000000000..f0b0785367 --- /dev/null +++ b/test/CodeGen/memtag-attr.cpp @@ -0,0 +1,19 @@ +// Make sure the sanitize_memtag attribute is emitted when using MemTag sanitizer. +// Make sure __attribute__((no_sanitize("memtag")) disables instrumentation. + +// RUN: %clang_cc1 -triple aarch64-unknown-linux -disable-O0-optnone \ +// RUN: -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-NO %s + +// RUN: %clang_cc1 -triple aarch64-unknown-linux -fsanitize=memtag \ +// RUN: -disable-O0-optnone -emit-llvm -o - %s | \ +// RUN: FileCheck -check-prefix=CHECK-MEMTAG %s + +int HasSanitizeMemTag() { return 1; } +// CHECK-NO: {{Function Attrs: noinline nounwind$}} +// CHECK-MEMTAG: Function Attrs: noinline nounwind sanitize_memtag + +__attribute__((no_sanitize("memtag"))) int NoSanitizeQuoteAddress() { + return 0; +} +// CHECK-NO: {{Function Attrs: noinline nounwind$}} +// CHECK-MEMTAG: {{Function Attrs: noinline nounwind$}} diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index a275b57668..01367c7e67 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -181,6 +181,16 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=hwaddress,address -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANHA-SANA // CHECK-SANHA-SANA: '-fsanitize=hwaddress' not allowed with '-fsanitize=address' +// RUN: %clang -target aarch64-linux-android -fsanitize=memtag,address -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-SANA +// CHECK-SANMT-SANA: '-fsanitize=memtag' not allowed with '-fsanitize=address' + +// RUN: %clang -target aarch64-linux-android -fsanitize=memtag,hwaddress -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-SANHA +// CHECK-SANMT-SANHA: '-fsanitize=memtag' not allowed with '-fsanitize=hwaddress' + +// RUN: %clang -target i386-linux-android -fsanitize=memtag -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-BAD-ARCH +// RUN: %clang -target x86_64-linux-android -fsanitize=memtag -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-BAD-ARCH +// CHECK-SANMT-BAD-ARCH: unsupported option '-fsanitize=memtag' for target + // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-address-use-after-scope %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-USE-AFTER-SCOPE // RUN: %clang_cl --target=x86_64-windows -fsanitize=address -fsanitize-address-use-after-scope -### -- %s 2>&1 | FileCheck %s --check-prefix=CHECK-USE-AFTER-SCOPE // CHECK-USE-AFTER-SCOPE: -cc1{{.*}}-fsanitize-address-use-after-scope diff --git a/test/Lexer/has_feature_memtag_sanitizer.cpp b/test/Lexer/has_feature_memtag_sanitizer.cpp new file mode 100644 index 0000000000..64e55d7c23 --- /dev/null +++ b/test/Lexer/has_feature_memtag_sanitizer.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -E -fsanitize=memtag %s -o - | FileCheck --check-prefix=CHECK-MEMTAG %s +// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-MEMTAG %s + +#if __has_feature(memtag_sanitizer) +int MemTagSanitizerEnabled(); +#else +int MemTagSanitizerDisabled(); +#endif + +// CHECK-MEMTAG: MemTagSanitizerEnabled +// CHECK-NO-MEMTAG: MemTagSanitizerDisabled diff --git a/test/SemaCXX/attr-no-sanitize.cpp b/test/SemaCXX/attr-no-sanitize.cpp index 02bc9a9e7f..feff7ef616 100644 --- a/test/SemaCXX/attr-no-sanitize.cpp +++ b/test/SemaCXX/attr-no-sanitize.cpp @@ -30,3 +30,8 @@ int f5() __attribute__((no_sanitize("address", "thread", "hwaddress"))); // DUMP: NoSanitizeAttr {{.*}} unknown // PRINT: int f6() __attribute__((no_sanitize("unknown"))) int f6() __attribute__((no_sanitize("unknown"))); // expected-warning{{unknown sanitizer 'unknown' ignored}} + +// DUMP-LABEL: FunctionDecl {{.*}} f7 +// DUMP: NoSanitizeAttr {{.*}} memtag +// PRINT: int f7() {{\[\[}}clang::no_sanitize("memtag")]] +[[clang::no_sanitize("memtag")]] int f7();