hctim created this revision. hctim added a reviewer: eugenis. Herald added subscribers: dexonsmith, dang, arichardson, emaste. Herald added a reviewer: MaskRay. hctim requested review of this revision. Herald added projects: clang, LLVM. Herald added subscribers: llvm-commits, cfe-commits.
Currently, enablement of heap MTE on Android is specified by an ELF note, which signals to the linker to enable heap MTE. This change allows -fsanitize=memtag-heap to synthesize these notes, rather than adding them through the build system. We need to extend this feature to also signal the linker to do special work for MTE globals (in future) and MTE stack (currently implemented in the toolchain, but not implemented in the loader). Current Android uses a non-backwards-compatible ELF note, called ".note.android.memtag". Stack MTE is an ABI break anyway, so we don't mind that we won't be able to run executables with stack MTE on Android 11/12 devices. The current expectation is to support the verbiage used by Android, in that "SYNC" means MTE Synchronous mode, and "ASYNC" effectively means "fast", using the Kernel auto-upgrade feature that allows hardware-specific and core-specific configuration as to whether "ASYNC" would end up being Asynchronous, Asymmetric, or Synchronous on that particular core, whichever has a reasonable performance delta. Of course, this is platform and loader-specific. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D118948 Files: clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/Basic/Features.def clang/include/clang/Basic/Sanitizers.def clang/include/clang/Driver/Options.td clang/include/clang/Driver/SanitizerArgs.h clang/lib/CodeGen/CGDeclCXX.cpp clang/lib/CodeGen/CodeGenFunction.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/Driver/SanitizerArgs.cpp clang/lib/Driver/ToolChains/CommonArgs.cpp clang/test/CodeGen/memtag-attr.cpp clang/test/Driver/fsanitize.c clang/test/Driver/memtag-ld.c clang/test/Driver/memtag-stack.c clang/test/Driver/memtag.c clang/test/Lexer/has_feature_memtag_heap.cpp clang/test/Lexer/has_feature_memtag_sanitizer.cpp clang/test/Lexer/has_feature_memtag_stack.cpp lld/ELF/Config.h lld/ELF/Driver.cpp lld/ELF/Options.td lld/ELF/SyntheticSections.cpp lld/ELF/SyntheticSections.h lld/ELF/Writer.cpp lld/test/ELF/memtag-android-abi.s llvm/include/llvm/BinaryFormat/ELF.h
Index: llvm/include/llvm/BinaryFormat/ELF.h =================================================================== --- llvm/include/llvm/BinaryFormat/ELF.h +++ llvm/include/llvm/BinaryFormat/ELF.h @@ -1531,6 +1531,22 @@ NT_GNU_PROPERTY_TYPE_0 = 5, }; +// Android note types. +enum { + NT_TYPE_IDENT = 1, + NT_TYPE_KUSER = 3, + NT_TYPE_MEMTAG = 4, +}; + +enum { + NT_MEMTAG_LEVEL_DEFAULT = 0, + NT_MEMTAG_LEVEL_ASYNC = 1, + NT_MEMTAG_LEVEL_SYNC = 2, + NT_MEMTAG_LEVEL_MASK = 3, + NT_MEMTAG_HEAP = 4, + NT_MEMTAG_STACK = 8, +}; + // Property types used in GNU_PROPERTY_TYPE_0 notes. enum : unsigned { GNU_PROPERTY_STACK_SIZE = 1, Index: lld/test/ELF/memtag-android-abi.s =================================================================== --- /dev/null +++ lld/test/ELF/memtag-android-abi.s @@ -0,0 +1,63 @@ +// Old versions of Android (Android 11 & 12) have very strict parsing logic on +// the layout of the ELF note. This test serves as a "hey, you're going to break +// the ABI" check. Basically below is the copied version of the Android parsing +// logic. We create a no-op aarch64 binary with an elf note, and then consume it +// using the parsing logic on the host system. Because we don't pull in any +// libraries or headers, this should be runnable on any system that uses linux +// (technically, any system that can parse ELF, but I'm not rewriting it in +// python to run on Windows...). Note that MTE stack is an ABI break, so we +// expect it to crash under this parsing logic. + +// RUN: llvm-mc --filetype=obj -triple=aarch64-linux-none-gnu %s -o %t.o +// RUN: ld.lld --memtag-mode=async %t.o -o %t +// RUN: llvm-readelf -S %t | FileCheck %s +// RUN: llvm-objdump -Dz -j .note.android.memtag %t | \ +// RUN: FileCheck %s --check-prefixes=NOTE,ASYNC-NOTE + +// RUN: ld.lld --memtag-mode=sync %t.o -o %t +// RUN: llvm-readelf -S %t | FileCheck %s +// RUN: llvm-objdump -Dz -j .note.android.memtag %t | \ +// RUN: FileCheck %s --check-prefixes=NOTE,SYNC-NOTE + +// RUN: ld.lld --memtag-mode=async --memtag-stack %t.o -o %t +// RUN: llvm-readelf -S %t | FileCheck %s +// RUN: llvm-objdump -Dz -j .note.android.memtag %t | \ +// RUN: FileCheck %s --check-prefixes=NOTE,STACK-ASYNC + +// RUN: ld.lld --memtag-mode=sync --memtag-stack %t.o -o %t +// RUN: llvm-readelf -S %t | FileCheck %s +// RUN: llvm-objdump -Dz -j .note.android.memtag %t | \ +// RUN: FileCheck %s --check-prefixes=NOTE,STACK-SYNC + +// CHECK: .note.android.memtag + +// NOTE: <.note.android.memtag>: +// NOTE-NEXT: : 08 00 00 00 +// NOTE-NEXT: : 04 00 00 00 +// NOTE-NEXT: : 04 00 00 00 +// NOTE-NEXT: : 41 6e 64 72 +// NOTE-NEXT: : 6f 69 64 00 + +// The next few lines are an ABI enforcement for Android 11 and 12. If you're +// compiling with just heap MTE (i.e just --memtag-mode), then these lines MUST +// be unchanging. +// ASYNC-NOTE-NEXT: : 05 00 00 00 +// SYNC-NOTE-NEXT: : 06 00 00 00 + +// Stack MTE is, as of Android 12, unimplemented. However, we pre-emptively emit +// a bit that signifies to the dynamic loader to map the primary and thread +// stacks as PROT_MTE, in preparation for the bionic support. +// STACK-ASYNC-NEXT: : 0d 00 00 00 +// STACK-SYNC-NEXT: : 0e 00 00 00 + +// RUN: not ld.lld --memtag-stack 2>&1 | \ +// RUN: FileCheck %s --check-prefix=MISSING-MODE +// MISSING-MODE: --memtag-stack requires a --memtag-mode value + +// RUN: not ld.lld --memtag-mode=asymm 2>&1 | \ +// RUN: FileCheck %s --check-prefix=BAD-MODE +// BAD-MODE: --memtag-mode value of "asymm" invalid + +.globl _start +_start: + ret Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -362,6 +362,13 @@ part.dynSymTab = std::make_unique<SymbolTableSection<ELFT>>(*part.dynStrTab); part.dynamic = std::make_unique<DynamicSection<ELFT>>(); + + if (config->emachine == EM_AARCH64 && + config->memtagMode != llvm::ELF::NT_MEMTAG_LEVEL_DEFAULT) { + part.memtagAndroidNote = std::make_unique<MemtagAndroidNote>(); + add(*part.memtagAndroidNote); + } + if (config->androidPackDynRelocs) part.relaDyn = std::make_unique<AndroidPackedRelocationSection<ELFT>>(relaDynName); Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -1188,6 +1188,15 @@ void writeTo(uint8_t *buf) override; }; +class MemtagAndroidNote : public SyntheticSection { +public: + MemtagAndroidNote() + : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, + /* alignment */ 4, ".note.android.memtag") {} + void writeTo(uint8_t *buf) override; + size_t getSize() const override; +}; + InputSection *createInterpSection(); MergeInputSection *createCommentSection(); template <class ELFT> void splitSections(); @@ -1223,6 +1232,7 @@ std::unique_ptr<VersionDefinitionSection> verDef; std::unique_ptr<SyntheticSection> verNeed; std::unique_ptr<VersionTableSection> verSym; + std::unique_ptr<MemtagAndroidNote> memtagAndroidNote; unsigned getNumber() const { return this - &partitions[0] + 1; } }; Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -29,6 +29,7 @@ #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Compression.h" @@ -3838,6 +3839,39 @@ symTabShndx.reset(); } +static constexpr char kMemtagAndroidNoteName[] = "Android"; +void MemtagAndroidNote::writeTo(uint8_t *buf) { + /*** nhdr ***/ + llvm::ELF::Elf64_Nhdr header; + header.n_namesz = sizeof(kMemtagAndroidNoteName); + header.n_descsz = sizeof(uint32_t); + header.n_type = llvm::ELF::NT_TYPE_MEMTAG; + memcpy(buf, &header, sizeof(llvm::ELF::Elf64_Nhdr)); + buf += sizeof(llvm::ELF::Elf64_Nhdr); + + /*** name[] ***/ + assert(sizeof(kMemtagAndroidNoteName) == 8); + strncpy(reinterpret_cast<char *>(buf), kMemtagAndroidNoteName, + sizeof(kMemtagAndroidNoteName)); + buf += sizeof(kMemtagAndroidNoteName); + + /*** value ***/ + uint32_t value = 0; + value |= llvm::ELF::NT_MEMTAG_HEAP; + value |= config->memtagMode; + // Note, MTE stack is an ABI break. Attempting to run an MTE stack-enabled + // binary on Android 11 or 12 will result in a checkfail in the loader. + if (config->memtagStack) + value |= llvm::ELF::NT_MEMTAG_STACK; + *reinterpret_cast<uint32_t *>(buf) = value; +} + +size_t MemtagAndroidNote::getSize() const { + return sizeof(llvm::ELF::Elf64_Nhdr) + + /* namesz */ sizeof(kMemtagAndroidNoteName) + + /* descsz */ sizeof(uint32_t); +} + InStruct elf::in; std::vector<Partition> elf::partitions; Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -719,3 +719,21 @@ "Perform additional validation of the written dynamic relocations", "Do not perform additional validation of the written dynamic relocations">, Flags<[HelpHidden]>; + +// Hidden options, used by clang's -fsanitize=memtag-* options to emit an ELF +// note to designate what kinds of memory (stack/globals/heap) should be +// protected using ARM's MTE on armv8.5+. +// +// A binary's request for MTE globals is done through its own specific global +// descriptor note. Specifying -memtag-mode implies that heap MTE will be +// enabled. A binary's desire for stack MTE can't be obtained implicitly, so we +// have a specific bit in the note to signal to the loader to remap the stack as +// PROT_MTE. +defm memtag_stack: B<"memtag-stack", + "Instruct the dynamic loader to prepare for MTE stack instrumentation", "">; + +defm memtag_android_old_note: B<"memtag-android-old-note", + "Synthesize the old Android-specific ELF note, as well as the new one", "">; + +defm memtag_mode: Eq<"memtag-mode", + "Instruct the dynamic loader to start under MTE mode {async, sync}">; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -1066,6 +1066,21 @@ args.hasFlag(OPT_lto_unique_basic_block_section_names, OPT_no_lto_unique_basic_block_section_names, false); config->mapFile = args.getLastArgValue(OPT_Map); + config->memtagStack = + args.hasFlag(OPT_memtag_stack, OPT_no_memtag_stack, false); + StringRef memtagMode = args.getLastArgValue(OPT_memtag_mode); + if (memtagMode == "async") { + config->memtagMode = llvm::ELF::NT_MEMTAG_LEVEL_ASYNC; + } else if (memtagMode == "sync") { + config->memtagMode = llvm::ELF::NT_MEMTAG_LEVEL_SYNC; + } else if (memtagMode == "none" || memtagMode.empty()) { + if (config->memtagStack) + error("--memtag-stack requires a --memtag-mode value."); + config->memtagMode = 0; + } else { + error("--memtag-mode value of \"" + memtagMode + + "\" invalid. Should be one of {async, sync, none}."); + } config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0); config->mergeArmExidx = args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true); Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -341,6 +341,16 @@ // 4 for ELF32, 8 for ELF64. int wordsize; + + // Mode of MTE to write to the ELF note. Should be one of NT_MEMTAG_ASYNC (for + // async), NT_MEMTAG_SYNC (for sync), or NT_MEMTAG_DEFAULT (for none). If + // async or sync is enabled, write the ELF note specifying the default MTE + // mode, which implicitly enables heap MTE. + int memtagMode; + // In addition to enabling heap MTE, also signal to the dynamic loader that + // this binary expects stack MTE. Generally, this means to map the primary and + // thread stacks as PROT_MTE. Note: This is not supported on Android 11 & 12. + bool memtagStack; }; // The only instance of Configuration struct. Index: clang/test/Lexer/has_feature_memtag_stack.cpp =================================================================== --- /dev/null +++ clang/test/Lexer/has_feature_memtag_stack.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -E -fsanitize=memtag-stack %s -o - | FileCheck --check-prefix=CHECK-MEMTAG %s +// RUN: %clang -E -fsanitize=memtag --target=aarch64-unknown-linux -march=armv8a+memtag %s -o - \ +// RUN: | FileCheck --check-prefix=CHECK-MEMTAG %s +// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-MEMTAG %s + +#if __has_feature(memtag_stack) +int MemTagSanitizerEnabled(); +#else +int MemTagSanitizerDisabled(); +#endif + +// CHECK-MEMTAG: MemTagSanitizerEnabled +// CHECK-NO-MEMTAG: MemTagSanitizerDisabled Index: clang/test/Lexer/has_feature_memtag_sanitizer.cpp =================================================================== --- clang/test/Lexer/has_feature_memtag_sanitizer.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// 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 Index: clang/test/Lexer/has_feature_memtag_heap.cpp =================================================================== --- /dev/null +++ clang/test/Lexer/has_feature_memtag_heap.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -E -fsanitize=memtag-heap %s -o - | FileCheck --check-prefix=CHECK-MEMTAG %s +// RUN: %clang -E -fsanitize=memtag --target=aarch64-unknown-linux -march=armv8a+memtag %s -o - \ +// RUN: | FileCheck --check-prefix=CHECK-MEMTAG %s +// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-MEMTAG %s + +#if __has_feature(memtag_heap) +int MemTagSanitizerEnabled(); +#else +int MemTagSanitizerDisabled(); +#endif + +// CHECK-MEMTAG: MemTagSanitizerEnabled +// CHECK-NO-MEMTAG: MemTagSanitizerDisabled Index: clang/test/Driver/memtag.c =================================================================== --- /dev/null +++ clang/test/Driver/memtag.c @@ -1,20 +0,0 @@ -// RUN: %clang -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SAFETY -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O2 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O3 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY - -// REQUIRES: aarch64-registered-target - -int z; -__attribute__((noinline)) void use(int *p) { *p = z; } -int foo() { - int x; - use(&x); - return x; -} - -// CHECK-NO-SAFETY-NOT: allocas uses - -// CHECK-SAFETY-LABEL: @foo -// CHECK-SAFETY-LABEL: allocas uses: -// CHECK-SAFETY-NEXT: [4]: [0,4) Index: clang/test/Driver/memtag-ld.c =================================================================== --- /dev/null +++ clang/test/Driver/memtag-ld.c @@ -0,0 +1,44 @@ +// REQUIRES: aarch64-registered-target + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-ASYNC,CHECK-STACK + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag-stack %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-ASYNC,CHECK-STACK + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag-heap %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-ASYNC,CHECK-NO-STACK + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag -fsanitize-memtag-mode=sync %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-SYNC,CHECK-STACK + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag-stack -fsanitize-memtag-mode=sync %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-SYNC,CHECK-STACK + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag-heap -fsanitize-memtag-mode=sync %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-SYNC,CHECK-NO-STACK + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag-heap -fsanitize-memtag-mode=asymm %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-INVALID-MODE + +// RUN: %clang -### --target=aarch64-linux-android -march=armv8+memtag \ +// RUN: -fsanitize=memtag-stack -fsanitize-memtag-mode=asymm \ +// RUN: -fno-sanitize=memtag %s 2>&1 \ +// RUN: | FileCheck %s --check-prefixes=CHECK-NONE + +// CHECK-ASYNC: ld{{.*}} "-memtag-mode=async" +// CHECK-SYNC: ld{{.*}} "-memtag-mode=sync" +// CHECK-STACK: "-memtag-stack" +// CHECK-NO-STACK-NOT: "-memtag-stack" +// CHECK-INVALID-MODE: memtag-mode +// CHECK-INVALID-MODE-SAME: expected one of: {async, sync} +// CHECK-NONE-NOT: ld{{.*}} "-memtag + +void f() {} Index: clang/test/Driver/fsanitize.c =================================================================== --- clang/test/Driver/fsanitize.c +++ clang/test/Driver/fsanitize.c @@ -196,13 +196,13 @@ // RUN: %clang -target aarch64-linux -fsanitize=memtag -march=armv8-a+memtag %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-MT // CHECK-SANMT-MT: "-target-feature" "+mte" -// CHECK-SANMT-MT-SAME: "-fsanitize=memtag" +// CHECK-SANMT-MT-SAME: "-fsanitize=memtag-stack,memtag-heap" // RUN: %clang -target aarch64-linux -fsanitize=memtag %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-NOMT-0 -// CHECK-SANMT-NOMT-0: '-fsanitize=memtag' requires hardware support (+memtag) +// CHECK-SANMT-NOMT-0: '-fsanitize=memtag-stack' requires hardware support (+memtag) // RUN: %clang -target aarch64-linux -fsanitize=memtag -I +mte %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANMT-NOMT-1 -// CHECK-SANMT-NOMT-1: '-fsanitize=memtag' requires hardware support (+memtag) +// CHECK-SANMT-NOMT-1: '-fsanitize=memtag-stack' requires hardware support (+memtag) // 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 @@ -822,7 +822,7 @@ // CHECK-INTSAN-MINIMAL: "-fsanitize-minimal-runtime" // RUN: %clang -target aarch64-linux-android -march=armv8-a+memtag -fsanitize=memtag -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MEMTAG-MINIMAL -// CHECK-MEMTAG-MINIMAL: "-fsanitize=memtag" +// CHECK-MEMTAG-MINIMAL: "-fsanitize=memtag-stack,memtag-heap" // CHECK-MEMTAG-MINIMAL: "-fsanitize-minimal-runtime" // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=function -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MINIMAL Index: clang/test/CodeGen/memtag-attr.cpp =================================================================== --- clang/test/CodeGen/memtag-attr.cpp +++ clang/test/CodeGen/memtag-attr.cpp @@ -4,16 +4,27 @@ // 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: %clang_cc1 -triple aarch64-unknown-linux -fsanitize=memtag-stack \ // RUN: -disable-O0-optnone -emit-llvm -o - %s | \ // RUN: FileCheck -check-prefix=CHECK-MEMTAG %s +// RUN: %clang --target=aarch64-unknown-linux -march=armv8a+memtag \ +// RUN: -fsanitize=memtag -disable-O0-optnone -S -emit-llvm -o - %s | \ +// RUN: FileCheck -check-prefix=CHECK-MEMTAG %s + int HasSanitizeMemTag() { return 1; } -// CHECK-NO: {{Function Attrs: mustprogress noinline nounwind$}} -// CHECK-MEMTAG: Function Attrs: mustprogress noinline nounwind sanitize_memtag +// CHECK-NO: Function Attrs +// CHECK-NO-NOT: sanitize_memtag +// CHECK-NO-NEXT: define {{.*}}HasSanitizeMemTag +// CHECK-MEMTAG: Function Attrs: {{.*}} sanitize_memtag +// CHECK-MEMTAG-NEXT: define {{.*}}HasSanitizeMemTag __attribute__((no_sanitize("memtag"))) int NoSanitizeQuoteAddress() { return 0; } -// CHECK-NO: {{Function Attrs: mustprogress noinline nounwind$}} -// CHECK-MEMTAG: {{Function Attrs: mustprogress noinline nounwind$}} +// CHECK-NO: Function Attrs +// CHECK-NO-NOT: sanitize_memtag +// CHECK-NO-NEXT: define {{.*}}NoSanitizeQuoteAddress +// CHECK-MEMTAG: Function Attrs +// CHECK-MEMTAG-NOT: sanitize_memtag +// CHECK-MEMTAG-NEXT: define {{.*}}NoSanitizeQuoteAddress Index: clang/lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.cpp +++ clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -972,6 +972,13 @@ if (SanArgs.hasCrossDsoCfi() && !AddExportDynamic) CmdArgs.push_back("--export-dynamic-symbol=__cfi_check"); + if (SanArgs.hasMemTag() && TC.getTriple().isAndroid()) { + CmdArgs.push_back( + Args.MakeArgString("-memtag-mode=" + SanArgs.getMemtagMode())); + if (SanArgs.hasMemtagStack()) + CmdArgs.push_back("-memtag-stack"); + } + return !StaticRuntimes.empty() || !NonWholeStaticRuntimes.empty(); } Index: clang/lib/Driver/SanitizerArgs.cpp =================================================================== --- clang/lib/Driver/SanitizerArgs.cpp +++ clang/lib/Driver/SanitizerArgs.cpp @@ -44,8 +44,8 @@ static const SanitizerMask SupportsCoverage = SanitizerKind::Address | SanitizerKind::HWAddress | SanitizerKind::KernelAddress | SanitizerKind::KernelHWAddress | - SanitizerKind::MemTag | SanitizerKind::Memory | - SanitizerKind::KernelMemory | SanitizerKind::Leak | + SanitizerKind::MemtagStack | SanitizerKind::MemtagHeap | + SanitizerKind::Memory | SanitizerKind::KernelMemory | SanitizerKind::Leak | SanitizerKind::Undefined | SanitizerKind::Integer | SanitizerKind::Bounds | SanitizerKind::ImplicitConversion | SanitizerKind::Nullability | SanitizerKind::DataFlow | SanitizerKind::Fuzzer | @@ -73,7 +73,7 @@ SanitizerKind::CFIUnrelatedCast; static const SanitizerMask CompatibleWithMinimalRuntime = TrappingSupported | SanitizerKind::Scudo | SanitizerKind::ShadowCallStack | - SanitizerKind::MemTag; + SanitizerKind::MemtagStack | SanitizerKind::MemtagHeap; enum CoverageFeature { CoverageFunc = 1 << 0, @@ -652,6 +652,17 @@ MsanParamRetval = false; } + if (AllAddedKinds & SanitizerKind::MemTag) { + StringRef S = + Args.getLastArgValue(options::OPT_fsanitize_memtag_mode_EQ, "async"); + if (S.equals("async") || S.equals("sync")) { + MemtagMode = S.str(); + } else { + D.Diag(clang::diag::err_drv_invalid_value_with_suggestion) + << "-fsanitize-memtag-mode=" << S << "{async, sync}"; + } + } + if (AllAddedKinds & SanitizerKind::Thread) { TsanMemoryAccess = Args.hasFlag( options::OPT_fsanitize_thread_memory_access, @@ -1230,7 +1241,8 @@ << "-fvisibility="; } - if (Sanitizers.has(SanitizerKind::MemTag) && !hasTargetFeatureMTE(CmdArgs)) + if (Sanitizers.has(SanitizerKind::MemtagStack) && + !hasTargetFeatureMTE(CmdArgs)) TC.getDriver().Diag(diag::err_stack_tagging_requires_hardware_feature); } Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -726,7 +726,7 @@ LangOptions::SignReturnAddressScopeKind::None) getModule().addModuleFlag(llvm::Module::Override, "sign-return-address-buildattr", 1); - if (LangOpts.Sanitize.has(SanitizerKind::MemTag)) + if (LangOpts.Sanitize.has(SanitizerKind::MemtagStack)) getModule().addModuleFlag(llvm::Module::Override, "tag-stack-memory-buildattr", 1); Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -772,7 +772,7 @@ Fn->addFnAttr(llvm::Attribute::SanitizeAddress); if (SanOpts.hasOneOf(SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress)) Fn->addFnAttr(llvm::Attribute::SanitizeHWAddress); - if (SanOpts.has(SanitizerKind::MemTag)) + if (SanOpts.has(SanitizerKind::MemtagStack)) Fn->addFnAttr(llvm::Attribute::SanitizeMemTag); if (SanOpts.has(SanitizerKind::Thread)) Fn->addFnAttr(llvm::Attribute::SanitizeThread); Index: clang/lib/CodeGen/CGDeclCXX.cpp =================================================================== --- clang/lib/CodeGen/CGDeclCXX.cpp +++ clang/lib/CodeGen/CGDeclCXX.cpp @@ -458,8 +458,8 @@ !isInNoSanitizeList(SanitizerKind::KernelHWAddress, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeHWAddress); - if (getLangOpts().Sanitize.has(SanitizerKind::MemTag) && - !isInNoSanitizeList(SanitizerKind::MemTag, Fn, Loc)) + if (getLangOpts().Sanitize.has(SanitizerKind::MemtagStack) && + !isInNoSanitizeList(SanitizerKind::MemtagStack, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SanitizeMemTag); if (getLangOpts().Sanitize.has(SanitizerKind::Thread) && Index: clang/include/clang/Driver/SanitizerArgs.h =================================================================== --- clang/include/clang/Driver/SanitizerArgs.h +++ clang/include/clang/Driver/SanitizerArgs.h @@ -64,6 +64,8 @@ llvm::AsanDetectStackUseAfterReturnMode AsanUseAfterReturn = llvm::AsanDetectStackUseAfterReturnMode::Invalid; + std::string MemtagMode = ""; + public: /// Parses the sanitizer arguments from an argument list. SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, @@ -97,6 +99,15 @@ bool needsStatsRt() const { return Stats; } bool needsScudoRt() const { return Sanitizers.has(SanitizerKind::Scudo); } + bool hasMemTag() const { return hasMemtagHeap() || hasMemtagStack(); } + bool hasMemtagHeap() const { + return Sanitizers.has(SanitizerKind::MemtagHeap); + } + bool hasMemtagStack() const { + return Sanitizers.has(SanitizerKind::MemtagStack); + } + const std::string &getMemtagMode() const { return MemtagMode; } + bool requiresPIE() const; bool needsUnwindTables() const; bool needsLTO() const; Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1614,6 +1614,9 @@ def fno_sanitize_address_outline_instrumentation : Flag<["-"], "fno-sanitize-address-outline-instrumentation">, Group<f_clang_Group>, HelpText<"Use default code inlining logic for the address sanitizer">; +def fsanitize_memtag_mode_EQ : Joined<["-"], "fsanitize-memtag-mode=">, + Group<f_clang_Group>, + HelpText<"Set default MTE mode to 'async' (default) or 'sync'.">; def fsanitize_hwaddress_experimental_aliasing : Flag<["-"], "fsanitize-hwaddress-experimental-aliasing">, Group<f_clang_Group>, Index: clang/include/clang/Basic/Sanitizers.def =================================================================== --- clang/include/clang/Basic/Sanitizers.def +++ clang/include/clang/Basic/Sanitizers.def @@ -56,7 +56,9 @@ SANITIZER("kernel-hwaddress", KernelHWAddress) // A variant of AddressSanitizer using AArch64 MTE extension. -SANITIZER("memtag", MemTag) +SANITIZER("memtag-stack", MemtagStack) +SANITIZER("memtag-heap", MemtagHeap) +SANITIZER_GROUP("memtag", MemTag, MemtagStack | MemtagHeap) // MemorySanitizer SANITIZER("memory", Memory) Index: clang/include/clang/Basic/Features.def =================================================================== --- clang/include/clang/Basic/Features.def +++ clang/include/clang/Basic/Features.def @@ -45,7 +45,10 @@ FEATURE(hwaddress_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress)) -FEATURE(memtag_sanitizer, LangOpts.Sanitize.has(SanitizerKind::MemTag)) +FEATURE(memtag_stack, + LangOpts.Sanitize.has(SanitizerKind::MemtagStack)) +FEATURE(memtag_heap, + LangOpts.Sanitize.has(SanitizerKind::MemtagHeap)) FEATURE(xray_instrument, LangOpts.XRayInstrument) FEATURE(undefined_behavior_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Undefined)) Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -472,7 +472,8 @@ "ROPI is not compatible with c++">; def err_stack_tagging_requires_hardware_feature : Error< - "'-fsanitize=memtag' requires hardware support (+memtag)">; + "'-fsanitize=memtag-stack' requires hardware support (+memtag). For Armv8, " + "try compiling with -march=armv8a+memtag.">; def err_cmse_pi_are_incompatible : Error< "cmse is not compatible with %select{RWPI|ROPI}0">;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits