Author: Philipp Dominik Schubert Date: 2026-09-30T17:47:58Z New Revision: bc5b9040d28773b03d4d9ec3ca50271bdf776d77
URL: https://github.com/llvm/llvm-project/commit/bc5b9040d28773b03d4d9ec3ca50271bdf776d77 DIFF: https://github.com/llvm/llvm-project/commit/bc5b9040d28773b03d4d9ec3ca50271bdf776d77.diff LOG: [clang][CodeGen] Fix stack-use-after-return in deferred annotations (#226942) CodeGenModule::DeferredAnnotations was keyed by StringRef, but not every mangled name passed to GetOrCreateLLVMFunction outlives the call. CodeGenVTables::maybeEmitThunk mangles the thunk name into a stack-local SmallString and hands it to GetAddrOfThunk, so for an annotated virtual function the map retained a reference into a frame that was gone by the time EmitGlobalAnnotations looked the key up. ASan reports this as a stack-use-after-return in EmitGlobalAnnotations, with the freed frame being maybeEmitThunk's 'Name'. The user-visible effect is that annotations silently disappear from the this-adjusting thunks once the dead frame has been reused. Make the key own its storage, using StringMap<unsigned> as the MapVector map type so lookups still hash a StringRef without allocating. The clang/test/CodeGenCXX/attr-annotate-member-functions.cpp test has been created with help of an AI. Added: clang/test/CodeGenCXX/attr-annotate-member-functions.cpp Modified: clang/docs/ReleaseNotes.md clang/lib/CodeGen/CodeGenModule.cpp clang/lib/CodeGen/CodeGenModule.h Removed: clang/test/CodeGenCXX/attr-annotate-constructor.cpp clang/test/CodeGenCXX/attr-annotate-destructor.cpp ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index b82a8b4e00b44..ca0846e917ac4 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -818,6 +818,7 @@ features cannot lower the translation-unit ABI level; - Fixed an assertion failure when a method or function definition follows an Objective-C `@implementation` that was ended by a nested `@interface`, `@protocol` or `@implementation` before its `@end`. (#GH209503) +- Fixed use-after-free with annotate attribute on a C++ class method with a this-adjusting thunk. ### OpenACC Specific Changes diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index a058d1fe7a2a3..e802dec4634b3 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4866,7 +4866,7 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) { if (FD->hasAttr<AnnotateAttr>()) { StringRef MangledName = getMangledName(GD); if (GetGlobalValue(MangledName)) - DeferredAnnotations[MangledName] = FD; + DeferredAnnotations[MangledName.str()] = FD; } // Forward declarations are emitted lazily on first use. @@ -5787,7 +5787,7 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction( // Store the declaration associated with this function so it is potentially // updated by further declarations or definitions and emitted at the end. if (D && D->hasAttr<AnnotateAttr>()) - DeferredAnnotations[MangledName] = cast<ValueDecl>(D); + DeferredAnnotations[MangledName.str()] = cast<ValueDecl>(D); // If we already created a function with the same mangled name (but diff erent // type) before, take its name and add it to the list of functions to be diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 9e3f073c20f4a..28e9bcf9e866b 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -506,7 +506,10 @@ class CodeGenModule : public CodeGenTypeCache { // Store deferred function annotations so they can be emitted at the end with // most up to date ValueDecl that will have all the inherited annotations. - llvm::MapVector<StringRef, const ValueDecl *> DeferredAnnotations; + // The key owns its storage: not every mangled name handed to + // GetOrCreateLLVMFunction outlives the call. + llvm::MapVector<std::string, const ValueDecl *, llvm::StringMap<unsigned>> + DeferredAnnotations; /// Map used to get unique annotation strings. llvm::StringMap<llvm::Constant*> AnnotationStrings; diff --git a/clang/test/CodeGenCXX/attr-annotate-constructor.cpp b/clang/test/CodeGenCXX/attr-annotate-constructor.cpp deleted file mode 100644 index 7a115137f1a67..0000000000000 --- a/clang/test/CodeGenCXX/attr-annotate-constructor.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: %clang %s -S -emit-llvm -target x86_64-unknown-linux -o - - -// Test annotation attributes on constructors do not crash. - -class Foo { -public: - [[clang::annotate("test")]] Foo() {} -}; - -Foo foo; diff --git a/clang/test/CodeGenCXX/attr-annotate-destructor.cpp b/clang/test/CodeGenCXX/attr-annotate-destructor.cpp deleted file mode 100644 index 4e5a2190a4585..0000000000000 --- a/clang/test/CodeGenCXX/attr-annotate-destructor.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-unknown-linux-gnu -o - | FileCheck %s - -// Test annotation attributes on destructors do not crash. - -struct k { - ~k() __attribute__((annotate(""))) {} -}; -void m() { k(); } - -// CHECK: @llvm.global.annotations = appending global [2 x { ptr, ptr, ptr, i32, ptr }] [{ diff --git a/clang/test/CodeGenCXX/attr-annotate-member-functions.cpp b/clang/test/CodeGenCXX/attr-annotate-member-functions.cpp new file mode 100644 index 0000000000000..9d9490e8a74ce --- /dev/null +++ b/clang/test/CodeGenCXX/attr-annotate-member-functions.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-unknown-linux-gnu -o - | FileCheck %s + +// Test annotation attributes on C++ constructors, destructors and virtual +// member functions. + +// Annotations on a constructor do not crash. + +class Foo { +public: + [[clang::annotate("test")]] Foo() {} +}; + +Foo foo; + +// Annotations on a destructor do not crash. + +struct k { + ~k() __attribute__((annotate(""))) {} +}; + +void m() { k(); } + +// Annotations on a virtual function are deferred to the end of the TU, keyed +// by mangled name. For a this-adjusting thunk that name is mangled into a +// stack buffer in CodeGenVTables::maybeEmitThunk, so the deferred-annotation +// map must own a copy of the key instead of referencing the caller's storage. +// Each annotation is recorded for the function itself and for the thunk that +// adjusts `this` to the B subobject. + +struct A { + virtual void f(); + virtual ~A(); +}; + +struct B { + virtual void g(); + virtual ~B(); +}; + +struct C : A, B { + void f() override; + __attribute__((annotate("annotated_method"))) void g() override; + __attribute__((annotate("annotated_dtor"))) ~C() override; +}; + +void C::f() {} +void C::g() {} +C::~C() {} + +// CHECK: @[[TEST:[.a-z0-9_]+]] = private unnamed_addr constant [5 x i8] c"test\00", section "llvm.metadata" +// CHECK: @[[EMPTY:[.a-z0-9_]+]] = private unnamed_addr constant [1 x i8] zeroinitializer, section "llvm.metadata" +// CHECK: @[[METHOD:[.a-z0-9_]+]] = private unnamed_addr constant [17 x i8] c"annotated_method\00", section "llvm.metadata" +// CHECK: @[[DTOR:[.a-z0-9_]+]] = private unnamed_addr constant [15 x i8] c"annotated_dtor\00", section "llvm.metadata" +// CHECK: @llvm.global.annotations = appending global [11 x { ptr, ptr, ptr, i32, ptr }] [ +// CHECK-SAME: { ptr @_ZN3FooC1Ev, ptr @[[TEST]], +// CHECK-SAME: { ptr @_ZN1kD1Ev, ptr @[[EMPTY]], +// CHECK-SAME: { ptr @_ZN1C1gEv, ptr @[[METHOD]], +// CHECK-SAME: { ptr @_ZThn8_N1C1gEv, ptr @[[METHOD]], +// CHECK-SAME: { ptr @_ZN1CD2Ev, ptr @[[DTOR]], +// CHECK-SAME: { ptr @_ZN1CD1Ev, ptr @[[DTOR]], +// CHECK-SAME: { ptr @_ZThn8_N1CD1Ev, ptr @[[DTOR]], +// CHECK-SAME: { ptr @_ZN1CD0Ev, ptr @[[DTOR]], +// CHECK-SAME: { ptr @_ZThn8_N1CD0Ev, ptr @[[DTOR]], +// CHECK-SAME: { ptr @_ZN3FooC2Ev, ptr @[[TEST]], +// CHECK-SAME: { ptr @_ZN1kD2Ev, ptr @[[EMPTY]], _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
