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
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))
// Kernel Hardware-assisted AddressSanitizer (KHWASan)
SANITIZER("kernel-hwaddress", KernelHWAddress)
+// A variant of AddressSanitizer using AArch64 MTE extension.
+SANITIZER("memtag", MemTag)
+
// MemorySanitizer
SANITIZER("memory", Memory)
!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);
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))
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();
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);
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);
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);
}
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 |
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},
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;
Res |= SanitizerKind::HWAddress;
Res |= SanitizerKind::KernelHWAddress;
}
+ if (IsAArch64)
+ Res |= SanitizerKind::MemTag;
return Res;
}
--- /dev/null
+// 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$}}
// 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
--- /dev/null
+// 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
// 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();