From e8363137ea1970195ee47cf60fa46c3247571800 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Mon, 15 Jul 2019 20:02:23 +0000 Subject: [PATCH] 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 llvm-svn: 366123 --- docs/BitCodeFormat.rst | 1 + docs/LangRef.rst | 4 +++ include/llvm/Bitcode/LLVMBitCodes.h | 3 ++- include/llvm/IR/Attributes.td | 4 +++ lib/AsmParser/LLLexer.cpp | 1 + lib/AsmParser/LLParser.cpp | 4 +++ lib/AsmParser/LLToken.h | 1 + lib/Bitcode/Reader/BitcodeReader.cpp | 8 +++++- lib/Bitcode/Writer/BitcodeWriter.cpp | 2 ++ lib/IR/Attributes.cpp | 2 ++ lib/IR/Verifier.cpp | 1 + lib/Transforms/IPO/ForceFunctionAttrs.cpp | 1 + lib/Transforms/Utils/CodeExtractor.cpp | 1 + test/Bitcode/attributes.ll | 11 +++++++-- test/Transforms/Inline/attributes.ll | 30 +++++++++++++++++++++++ utils/emacs/llvm-mode.el | 2 +- 16 files changed, 71 insertions(+), 5 deletions(-) diff --git a/docs/BitCodeFormat.rst b/docs/BitCodeFormat.rst index 5e1c5cacb43..4e653ae55d5 100644 --- a/docs/BitCodeFormat.rst +++ b/docs/BitCodeFormat.rst @@ -1057,6 +1057,7 @@ The integer codes are mapped to well-known attributes as follows. * code 56: ``nocf_check`` * code 57: ``optforfuzzing`` * code 58: ``shadowcallstack`` +* code 64: ``sanitize_memtag`` .. note:: The ``allocsize`` attribute has a special encoding for its arguments. Its two diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 18f760d9b05..87e8a557504 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -1681,6 +1681,10 @@ example: This attribute indicates that HWAddressSanitizer checks (dynamic address safety analysis based on tagged pointers) are enabled for this function. +``sanitize_memtag`` + This attribute indicates that MemTagSanitizer checks + (dynamic address safety analysis based on Armv8 MTE) are enabled for + this function. ``speculative_load_hardening`` This attribute indicates that `Speculative Load Hardening `_ diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index 4582a6a4d83..decd4dd3a96 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -630,7 +630,8 @@ enum AttributeKindCodes { ATTR_KIND_IMMARG = 60, ATTR_KIND_WILLRETURN = 61, ATTR_KIND_NOFREE = 62, - ATTR_KIND_NOSYNC = 63 + ATTR_KIND_NOSYNC = 63, + ATTR_KIND_SANITIZE_MEMTAG = 64, }; enum ComdatSelectionKindCodes { diff --git a/include/llvm/IR/Attributes.td b/include/llvm/IR/Attributes.td index a549f305900..153046d2311 100644 --- a/include/llvm/IR/Attributes.td +++ b/include/llvm/IR/Attributes.td @@ -185,6 +185,9 @@ def SanitizeMemory : EnumAttr<"sanitize_memory">; /// HWAddressSanitizer is on. def SanitizeHWAddress : EnumAttr<"sanitize_hwaddress">; +/// MemTagSanitizer is on. +def SanitizeMemTag : EnumAttr<"sanitize_memtag">; + /// Speculative Load Hardening is enabled. /// /// Note that this uses the default compatibility (always compatible during @@ -233,6 +236,7 @@ def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; +def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 2c2361a6abc..72d2357c293 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -679,6 +679,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(shadowcallstack); KEYWORD(sanitize_address); KEYWORD(sanitize_hwaddress); + KEYWORD(sanitize_memtag); KEYWORD(sanitize_thread); KEYWORD(sanitize_memory); KEYWORD(speculative_load_hardening); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index ce8c1c4fc81..87dff6468f2 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -1311,6 +1311,8 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B, B.addAttribute(Attribute::SanitizeAddress); break; case lltok::kw_sanitize_hwaddress: B.addAttribute(Attribute::SanitizeHWAddress); break; + case lltok::kw_sanitize_memtag: + B.addAttribute(Attribute::SanitizeMemTag); break; case lltok::kw_sanitize_thread: B.addAttribute(Attribute::SanitizeThread); break; case lltok::kw_sanitize_memory: @@ -1668,6 +1670,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) { case lltok::kw_returns_twice: case lltok::kw_sanitize_address: case lltok::kw_sanitize_hwaddress: + case lltok::kw_sanitize_memtag: case lltok::kw_sanitize_memory: case lltok::kw_sanitize_thread: case lltok::kw_speculative_load_hardening: @@ -1766,6 +1769,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_returns_twice: case lltok::kw_sanitize_address: case lltok::kw_sanitize_hwaddress: + case lltok::kw_sanitize_memtag: case lltok::kw_sanitize_memory: case lltok::kw_sanitize_thread: case lltok::kw_speculative_load_hardening: diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index 4afe8a6c084..0e9ba4db474 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -176,6 +176,7 @@ enum Kind { kw_argmemonly, kw_sanitize_address, kw_sanitize_hwaddress, + kw_sanitize_memtag, kw_builtin, kw_byval, kw_inalloca, diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 6cad3b94e5e..29dc7f61639 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1296,6 +1296,9 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) { case Attribute::AllocSize: llvm_unreachable("allocsize not supported in raw format"); break; + case Attribute::SanitizeMemTag: + llvm_unreachable("sanitize_memtag attribute not supported in raw format"); + break; } llvm_unreachable("Unsupported attribute type"); } @@ -1305,7 +1308,8 @@ static void addRawAttributeValue(AttrBuilder &B, uint64_t Val) { for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds; I = Attribute::AttrKind(I + 1)) { - if (I == Attribute::Dereferenceable || + if (I == Attribute::SanitizeMemTag || + I == Attribute::Dereferenceable || I == Attribute::DereferenceableOrNull || I == Attribute::ArgMemOnly || I == Attribute::AllocSize || @@ -1534,6 +1538,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::ZExt; case bitc::ATTR_KIND_IMMARG: return Attribute::ImmArg; + case bitc::ATTR_KIND_SANITIZE_MEMTAG: + return Attribute::SanitizeMemTag; } } diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index a23b44f4751..5c7b970a3a7 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -723,6 +723,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_Z_EXT; case Attribute::ImmArg: return bitc::ATTR_KIND_IMMARG; + case Attribute::SanitizeMemTag: + return bitc::ATTR_KIND_SANITIZE_MEMTAG; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index 1ba703bb14c..bb90bcd7dd7 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -283,6 +283,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return "sanitize_address"; if (hasAttribute(Attribute::SanitizeHWAddress)) return "sanitize_hwaddress"; + if (hasAttribute(Attribute::SanitizeMemTag)) + return "sanitize_memtag"; if (hasAttribute(Attribute::AlwaysInline)) return "alwaysinline"; if (hasAttribute(Attribute::ArgMemOnly)) diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index cee5bf7dc8d..9346c8bda75 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -1516,6 +1516,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) { case Attribute::ReturnsTwice: case Attribute::SanitizeAddress: case Attribute::SanitizeHWAddress: + case Attribute::SanitizeMemTag: case Attribute::SanitizeThread: case Attribute::SanitizeMemory: case Attribute::MinSize: diff --git a/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/lib/Transforms/IPO/ForceFunctionAttrs.cpp index cd1fc379820..b38cb6d0ed3 100644 --- a/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ b/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -57,6 +57,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) { .Case("sanitize_hwaddress", Attribute::SanitizeHWAddress) .Case("sanitize_memory", Attribute::SanitizeMemory) .Case("sanitize_thread", Attribute::SanitizeThread) + .Case("sanitize_memtag", Attribute::SanitizeMemTag) .Case("speculative_load_hardening", Attribute::SpeculativeLoadHardening) .Case("ssp", Attribute::StackProtect) .Case("sspreq", Attribute::StackProtectReq) diff --git a/lib/Transforms/Utils/CodeExtractor.cpp b/lib/Transforms/Utils/CodeExtractor.cpp index da137da8f7b..fa6d3f8ae87 100644 --- a/lib/Transforms/Utils/CodeExtractor.cpp +++ b/lib/Transforms/Utils/CodeExtractor.cpp @@ -850,6 +850,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::SanitizeMemory: case Attribute::SanitizeThread: case Attribute::SanitizeHWAddress: + case Attribute::SanitizeMemTag: case Attribute::SpeculativeLoadHardening: case Attribute::StackProtect: case Attribute::StackProtectReq: diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll index c6e146791d8..03a98a58ef0 100644 --- a/test/Bitcode/attributes.ll +++ b/test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ define void @f34() ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #39 +; CHECK: call void @nobuiltin() #40 ret void; } @@ -368,6 +368,12 @@ define void @f62() nosync ret void } +; CHECK: define void @f63() #39 +define void @f63() sanitize_memtag +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -407,4 +413,5 @@ define void @f62() nosync ; CHECK: attributes #36 = { willreturn } ; CHECK: attributes #37 = { nofree } ; CHECK: attributes #38 = { nosync } -; CHECK: attributes #39 = { nobuiltin } +; CHECK: attributes #39 = { sanitize_memtag } +; CHECK: attributes #40 = { nobuiltin } diff --git a/test/Transforms/Inline/attributes.ll b/test/Transforms/Inline/attributes.ll index 028f3b0f197..81c189a3c7a 100644 --- a/test/Transforms/Inline/attributes.ll +++ b/test/Transforms/Inline/attributes.ll @@ -22,6 +22,10 @@ define i32 @sanitize_memory_callee(i32 %i) sanitize_memory { ret i32 %i } +define i32 @sanitize_memtag_callee(i32 %i) sanitize_memtag { + ret i32 %i +} + define i32 @safestack_callee(i32 %i) safestack { ret i32 %i } @@ -50,6 +54,10 @@ define i32 @alwaysinline_sanitize_memory_callee(i32 %i) alwaysinline sanitize_me ret i32 %i } +define i32 @alwaysinline_sanitize_memtag_callee(i32 %i) alwaysinline sanitize_memtag { + ret i32 %i +} + define i32 @alwaysinline_safestack_callee(i32 %i) alwaysinline safestack { ret i32 %i } @@ -104,6 +112,17 @@ define i32 @test_no_sanitize_thread(i32 %arg) { ; CHECK-NEXT: ret i32 } +define i32 @test_no_sanitize_memtag(i32 %arg) { + %x1 = call i32 @noattr_callee(i32 %arg) + %x2 = call i32 @sanitize_memtag_callee(i32 %x1) + %x3 = call i32 @alwaysinline_callee(i32 %x2) + %x4 = call i32 @alwaysinline_sanitize_memtag_callee(i32 %x3) + ret i32 %x4 +; CHECK-LABEL: @test_no_sanitize_memtag( +; CHECK-NEXT: @sanitize_memtag_callee +; CHECK-NEXT: ret i32 +} + ; Check that: ; * noattr callee is not inlined into sanitize_(address|memory|thread) caller, @@ -154,6 +173,17 @@ define i32 @test_sanitize_thread(i32 %arg) sanitize_thread { ; CHECK-NEXT: ret i32 } +define i32 @test_sanitize_memtag(i32 %arg) sanitize_memtag { + %x1 = call i32 @noattr_callee(i32 %arg) + %x2 = call i32 @sanitize_memtag_callee(i32 %x1) + %x3 = call i32 @alwaysinline_callee(i32 %x2) + %x4 = call i32 @alwaysinline_sanitize_memtag_callee(i32 %x3) + ret i32 %x4 +; CHECK-LABEL: @test_sanitize_memtag( +; CHECK-NEXT: @noattr_callee +; CHECK-NEXT: ret i32 +} + define i32 @test_safestack(i32 %arg) safestack { %x1 = call i32 @noattr_callee(i32 %arg) %x2 = call i32 @safestack_callee(i32 %x1) diff --git a/utils/emacs/llvm-mode.el b/utils/emacs/llvm-mode.el index c4a1b1f02bf..73b02763d01 100644 --- a/utils/emacs/llvm-mode.el +++ b/utils/emacs/llvm-mode.el @@ -26,7 +26,7 @@ "inaccessiblemem_or_argmemonly" "inlinehint" "jumptable" "minsize" "naked" "nobuiltin" "noduplicate" "noimplicitfloat" "noinline" "nonlazybind" "noredzone" "noreturn" "norecurse" "nounwind" "optnone" "optsize" "readnone" "readonly" "returns_twice" - "speculatable" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" + "speculatable" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" "sanitize_thread" "sanitize_memory" "strictfp" "uwtable" "writeonly" "immarg") 'symbols) . font-lock-constant-face) ;; Variables '("%[-a-zA-Z$._][-a-zA-Z$._0-9]*" . font-lock-variable-name-face)