From 2703c3159b19ed25ae1b8d835857fc68d25d822b Mon Sep 17 00:00:00 2001 From: Tarcisio Fischer Date: Tue, 18 Nov 2025 13:23:57 +0000 Subject: [PATCH 1/2] [MTE] Add an attribute to opt-in memory tagging of global variables while using fsanitize=memtag-globals (#166380) --- clang/include/clang/Basic/Attr.td | 6 ++++++ clang/include/clang/Basic/AttrDocs.td | 10 ++++++++++ clang/lib/CodeGen/CodeGenModule.cpp | 7 +++++++ clang/lib/CodeGen/CodeGenModule.h | 2 ++ clang/lib/Sema/SemaDeclAttr.cpp | 7 +++++++ clang/test/CodeGen/memtag-globals.cpp | 6 ++++++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 3 ++- 7 files changed, 40 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8dfe4bc08c48e..10a3c95f56f43 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3908,6 +3908,12 @@ def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr let Documentation = [X86ForceAlignArgPointerDocs]; } +def SectionMemtag : InheritableAttr { + let Spellings = [ClangGCC<"section_memtag">]; + let Subjects = SubjectList<[GlobalVar]>; + let Documentation = [SectionMemtagDocs]; +} + def NoSanitize : InheritableAttr { let Spellings = [ClangGCC<"no_sanitize">]; let Args = [VariadicStringArgument<"Sanitizers">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 4813191d2d602..678789c2d0b9c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3655,6 +3655,16 @@ full list of supported sanitizer flags. }]; } +def SectionMemtagDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +Use the ``section_memtag`` attribute on a global variable declaration that also +has the attribute section and the memory tag sanitizer is active to force the +global variable to be MTE tagged. Global variables under sections are not +tagged by default, so you need to explicitly opt-in using this attribute. + }]; +} + def DisableSanitizerInstrumentationDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 3eeb1718e455a..7228de8637abb 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3823,6 +3823,10 @@ bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind, return false; } +bool CodeGenModule::userForcedSectionMemtag(llvm::GlobalVariable *GV) const { + return GV->getMetadata("section_memtag") != nullptr; +} + bool CodeGenModule::imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, StringRef Category) const { const auto &XRayFilter = getContext().getXRayFilter(); @@ -6141,6 +6145,9 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, if (NeedsGlobalCtor || NeedsGlobalDtor) EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); + if (D->hasAttr()) { + GV->setMetadata("section_memtag", llvm::MDNode::get(GV->getContext(), {})); + } SanitizerMD->reportGlobal(GV, *D, NeedsGlobalCtor); // Emit global variable debug information. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 2acfc83338a0c..f6ac61b6ce0cf 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1520,6 +1520,8 @@ class CodeGenModule : public CodeGenTypeCache { SourceLocation Loc, QualType Ty, StringRef Category = StringRef()) const; + bool userForcedSectionMemtag(llvm::GlobalVariable *GV) const; + /// Imbue XRay attributes to a function, applying the always/never attribute /// lists in the process. Returns true if we did imbue attributes this way, /// false otherwise. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a9e7b44ac9d73..9c5e289fa44b6 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6455,6 +6455,10 @@ static bool isSanitizerAttributeAllowedOnGlobals(StringRef Sanitizer) { Sanitizer == "memtag"; } +static void handleSectionMemtagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + D->addAttr(SectionMemtagAttr::CreateImplicit(S.Context, AL)); +} + static void handleNoSanitizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!AL.checkAtLeastNumArgs(S, 1)) return; @@ -7650,6 +7654,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_PtGuardedVar: handlePtGuardedVarAttr(S, D, AL); break; + case ParsedAttr::AT_SectionMemtag: + handleSectionMemtagAttr(S, D, AL); + break; case ParsedAttr::AT_NoSanitize: handleNoSanitizeAttr(S, D, AL); break; diff --git a/clang/test/CodeGen/memtag-globals.cpp b/clang/test/CodeGen/memtag-globals.cpp index 407eea1c37cfa..74475b5e9a483 100644 --- a/clang/test/CodeGen/memtag-globals.cpp +++ b/clang/test/CodeGen/memtag-globals.cpp @@ -11,6 +11,7 @@ int global; int __attribute__((__section__("my_section"))) section_global; +int __attribute__((__section__("my_section"))) __attribute__((section_memtag)) section_global_tagged; int __attribute__((no_sanitize("memtag"))) attributed_global; int __attribute__((disable_sanitizer_instrumentation)) disable_instrumentation_global; int ignorelisted_global; @@ -28,6 +29,11 @@ void func() { // This DOES NOT mean we are instrumenting the section global, // but we are ignoring it in AsmPrinter rather than in clang. // CHECK: @{{.*}}section_global{{.*}} ={{.*}} sanitize_memtag + +// In order to opt-in memory tagging in globals in sections, +// __attribute__((section_memtag)) must be used. +// CHECK: @{{.*}}section_global_tagged{{.*}} ={{.*}} sanitize_memtag,{{.*}} !section_memtag + // CHECK: @{{.*}}attributed_global{{.*}} = // CHECK-NOT: sanitize_memtag // CHECK: @{{.*}}disable_instrumentation_global{{.*}} = diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 3aa245b7f3f1e..e5d863afcc838 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2633,7 +2633,8 @@ static bool shouldTagGlobal(const llvm::GlobalVariable &G) { // To mitigate both these cases, and because specifying a section is rare // outside of these two cases, disable MTE protection for globals in any // section. - if (G.hasSection()) + bool ForceSectionMemtag = G.getMetadata("section_memtag") != nullptr; + if (G.hasSection() && !ForceSectionMemtag) return false; return globalSize(G) > 0; From c940efa93ab46aed6175282386408eefe0635727 Mon Sep 17 00:00:00 2001 From: Tarcisio Fischer Date: Mon, 1 Dec 2025 11:40:10 +0000 Subject: [PATCH 2/2] Rename section to 'force_memtag' and fix tests --- clang/include/clang/Basic/Attr.td | 6 +++--- clang/include/clang/Basic/AttrDocs.td | 4 ++-- clang/lib/CodeGen/CodeGenModule.cpp | 6 +++--- clang/lib/Sema/SemaDeclAttr.cpp | 8 ++++---- clang/test/CodeGen/memtag-globals-asm.cpp | 2 ++ clang/test/CodeGen/memtag-globals.cpp | 6 +++--- .../Misc/pragma-attribute-supported-attributes-list.test | 1 + llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 2 +- 8 files changed, 19 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 10a3c95f56f43..e798382df7ecb 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3908,10 +3908,10 @@ def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr let Documentation = [X86ForceAlignArgPointerDocs]; } -def SectionMemtag : InheritableAttr { - let Spellings = [ClangGCC<"section_memtag">]; +def ForceMemtag : InheritableAttr { + let Spellings = [ClangGCC<"force_memtag">]; let Subjects = SubjectList<[GlobalVar]>; - let Documentation = [SectionMemtagDocs]; + let Documentation = [ForceMemtagDocs]; } def NoSanitize : InheritableAttr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 678789c2d0b9c..cac61839b979c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3655,10 +3655,10 @@ full list of supported sanitizer flags. }]; } -def SectionMemtagDocs : Documentation { +def ForceMemtagDocs : Documentation { let Category = DocCatVariable; let Content = [{ -Use the ``section_memtag`` attribute on a global variable declaration that also +Use the ``force_memtag`` attribute on a global variable declaration that also has the attribute section and the memory tag sanitizer is active to force the global variable to be MTE tagged. Global variables under sections are not tagged by default, so you need to explicitly opt-in using this attribute. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 7228de8637abb..68082b62ad6fb 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3824,7 +3824,7 @@ bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind, } bool CodeGenModule::userForcedSectionMemtag(llvm::GlobalVariable *GV) const { - return GV->getMetadata("section_memtag") != nullptr; + return GV->getMetadata("force_memtag") != nullptr; } bool CodeGenModule::imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, @@ -6145,8 +6145,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, if (NeedsGlobalCtor || NeedsGlobalDtor) EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); - if (D->hasAttr()) { - GV->setMetadata("section_memtag", llvm::MDNode::get(GV->getContext(), {})); + if (D->hasAttr()) { + GV->setMetadata("force_memtag", llvm::MDNode::get(GV->getContext(), {})); } SanitizerMD->reportGlobal(GV, *D, NeedsGlobalCtor); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9c5e289fa44b6..531e8004aeac9 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6455,8 +6455,8 @@ static bool isSanitizerAttributeAllowedOnGlobals(StringRef Sanitizer) { Sanitizer == "memtag"; } -static void handleSectionMemtagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - D->addAttr(SectionMemtagAttr::CreateImplicit(S.Context, AL)); +static void handleForceMemtagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + D->addAttr(ForceMemtagAttr::CreateImplicit(S.Context, AL)); } static void handleNoSanitizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -7654,8 +7654,8 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_PtGuardedVar: handlePtGuardedVarAttr(S, D, AL); break; - case ParsedAttr::AT_SectionMemtag: - handleSectionMemtagAttr(S, D, AL); + case ParsedAttr::AT_ForceMemtag: + handleForceMemtagAttr(S, D, AL); break; case ParsedAttr::AT_NoSanitize: handleNoSanitizeAttr(S, D, AL); diff --git a/clang/test/CodeGen/memtag-globals-asm.cpp b/clang/test/CodeGen/memtag-globals-asm.cpp index fb3958dd8bcb6..eb62523ce7cc9 100644 --- a/clang/test/CodeGen/memtag-globals-asm.cpp +++ b/clang/test/CodeGen/memtag-globals-asm.cpp @@ -292,6 +292,7 @@ CONSTRUCTOR(".preinit_array") func_t preinit_array = func_constructor; CONSTRUCTOR("array_of_globals") int global1; CONSTRUCTOR("array_of_globals") int global2; CONSTRUCTOR("array_of_globals") int global_string; +CONSTRUCTOR("opt_in_memtag") __attribute__((force_memtag)) int tagged_global; // CHECK-NOT: .memtag func_constructor // CHECK-NOT: .memtag func_init @@ -304,3 +305,4 @@ CONSTRUCTOR("array_of_globals") int global_string; // CHECK-NOT: .memtag global1 // CHECK-NOT: .memtag global2 // CHECK-NOT: .memtag global_string +// CHECK: .memtag tagged_global diff --git a/clang/test/CodeGen/memtag-globals.cpp b/clang/test/CodeGen/memtag-globals.cpp index 74475b5e9a483..4fa45a65b58de 100644 --- a/clang/test/CodeGen/memtag-globals.cpp +++ b/clang/test/CodeGen/memtag-globals.cpp @@ -11,7 +11,7 @@ int global; int __attribute__((__section__("my_section"))) section_global; -int __attribute__((__section__("my_section"))) __attribute__((section_memtag)) section_global_tagged; +int __attribute__((__section__("my_section"))) __attribute__((force_memtag)) section_global_tagged; int __attribute__((no_sanitize("memtag"))) attributed_global; int __attribute__((disable_sanitizer_instrumentation)) disable_instrumentation_global; int ignorelisted_global; @@ -31,8 +31,8 @@ void func() { // CHECK: @{{.*}}section_global{{.*}} ={{.*}} sanitize_memtag // In order to opt-in memory tagging in globals in sections, -// __attribute__((section_memtag)) must be used. -// CHECK: @{{.*}}section_global_tagged{{.*}} ={{.*}} sanitize_memtag,{{.*}} !section_memtag +// __attribute__((force_memtag)) must be used. +// CHECK: @{{.*}}section_global_tagged{{.*}} ={{.*}} sanitize_memtag,{{.*}} !force_memtag // CHECK: @{{.*}}attributed_global{{.*}} = // CHECK-NOT: sanitize_memtag diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index ab4153a64f028..71dda3d6d082a 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -86,6 +86,7 @@ // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum) // CHECK-NEXT: Flatten (SubjectMatchRule_function) +// CHECK-NEXT: ForceMemtag (SubjectMatchRule_variable_is_global) // CHECK-NEXT: FunctionReturnThunks (SubjectMatchRule_function) // CHECK-NEXT: GNUInline (SubjectMatchRule_function) // CHECK-NEXT: HIPManaged (SubjectMatchRule_variable) diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index e5d863afcc838..ae23cdb5eadbb 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2633,7 +2633,7 @@ static bool shouldTagGlobal(const llvm::GlobalVariable &G) { // To mitigate both these cases, and because specifying a section is rare // outside of these two cases, disable MTE protection for globals in any // section. - bool ForceSectionMemtag = G.getMetadata("section_memtag") != nullptr; + bool ForceSectionMemtag = G.getMetadata("force_memtag") != nullptr; if (G.hasSection() && !ForceSectionMemtag) return false;