Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -1376,9 +1376,30 @@ const ASTRecordLayout &Layout = CGF.getContext().getASTRecordLayout(Dtor->getParent()); + // Nothing to poison + if(Layout.getFieldCount() == 0) + return; + + // Construct pointer to region to begin poisoning, and calculate poison + // size, so that only members declared in this class are poisoned. + llvm::Value *OffsetPtr; + CharUnits::QuantityType PoisonSize; + ASTContext &Context = CGF.getContext(); + + llvm::ConstantInt *OffsetSizePtr = llvm::ConstantInt::get( + CGF.SizeTy, Context.toCharUnitsFromBits(Layout.getFieldOffset(0)). + getQuantity()); + + OffsetPtr = CGF.Builder.CreateGEP(CGF.Builder.CreateBitCast( + CGF.LoadCXXThis(), CGF.Int8PtrTy), OffsetSizePtr); + + PoisonSize = Layout.getSize().getQuantity() - + Context.toCharUnitsFromBits(Layout.getFieldOffset(0)).getQuantity(); + llvm::Value *Args[] = { - CGF.Builder.CreateBitCast(CGF.LoadCXXThis(), CGF.VoidPtrTy), - llvm::ConstantInt::get(CGF.SizeTy, Layout.getSize().getQuantity())}; + CGF.Builder.CreateBitCast(OffsetPtr, CGF.VoidPtrTy), + llvm::ConstantInt::get(CGF.SizeTy, PoisonSize)}; + llvm::Type *ArgTypes[] = {CGF.VoidPtrTy, CGF.SizeTy}; llvm::FunctionType *FnType = @@ -1386,6 +1407,8 @@ llvm::Value *Fn = CGF.CGM.CreateRuntimeFunction(FnType, "__sanitizer_dtor_callback"); + // Disables tail call elimination, to prevent the current stack frame from + // disappearing from the stack trace. CGF.CurFn->addFnAttr("disable-tail-calls", "true"); CGF.EmitNounwindRuntimeCall(Fn, Args); } @@ -1468,6 +1491,13 @@ // the caller's body. if (getLangOpts().AppleKext) CurFn->addFnAttr(llvm::Attribute::AlwaysInline); + + // Insert memory-poisoning instrumentation, before final clean ups, + // to ensure this class's members are protected from invalid access. + if (CGM.getCodeGenOpts().SanitizeMemoryUseAfterDtor + && SanOpts.has(SanitizerKind::Memory)) + EmitDtorSanitizerCallback(*this, Dtor); + break; } @@ -1477,11 +1507,6 @@ // Exit the try if applicable. if (isTryBody) ExitCXXTryStmt(*cast(Body), true); - - // Insert memory-poisoning instrumentation. - if (CGM.getCodeGenOpts().SanitizeMemoryUseAfterDtor - && SanOpts.has(SanitizerKind::Memory)) - EmitDtorSanitizerCallback(*this, Dtor); } void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args) { Index: test/CodeGenCXX/sanitize-dtor-callback.cpp =================================================================== --- test/CodeGenCXX/sanitize-dtor-callback.cpp +++ test/CodeGenCXX/sanitize-dtor-callback.cpp @@ -1,24 +1,26 @@ // Test -fsanitize-memory-use-after-dtor // RUN: %clang_cc1 -fsanitize=memory -fsanitize-memory-use-after-dtor -std=c++11 -triple=x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s +// Sanitizing dtor is emitted in dtor for every class + struct Simple { + int x; ~Simple() {} }; Simple s; // Simple internal member is poisoned by compiler-generated dtor // CHECK-LABEL: define {{.*}}SimpleD1Ev -// CHECK: call void @__sanitizer_dtor_callback -// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}SimpleD2Ev // CHECK: ret void struct Inlined { + int y; inline ~Inlined() {} }; Inlined i; // Simple internal member is poisoned by compiler-generated dtor // CHECK-LABEL: define {{.*}}InlinedD1Ev -// CHECK: call void @__sanitizer_dtor_callback -// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}InlinedD2Ev // CHECK: ret void struct Defaulted_Trivial { @@ -32,7 +34,6 @@ // no destructor defined. // CHECK-LABEL: define {{.*}}create_def_trivial // CHECK-NOT: call {{.*}}Defaulted_Trivial -// CHECK-NOT: call void @__sanitizer_dtor_callback // CHECK: ret void struct Defaulted_Non_Trivial { @@ -44,8 +45,7 @@ // By including a Simple member in the struct, the compiler is // forced to generate a non-trivial destructor. // CHECK-LABEL: define {{.*}}Defaulted_Non_TrivialD1Ev -// CHECK: call void @__sanitizer_dtor_callback -// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}Defaulted_Non_TrivialD2 // CHECK: ret void Index: test/CodeGenCXX/sanitize-dtor-derived-class.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/sanitize-dtor-derived-class.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -fsanitize=memory -fsanitize-memory-use-after-dtor -disable-llvm-optzns -std=c++11 -triple=x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -O1 -fsanitize=memory -fsanitize-memory-use-after-dtor -disable-llvm-optzns -std=c++11 -triple=x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s + +// Only the last dtor of a class invokes the sanitizing callback +// Sanitizing callback emited prior to base class dtor invocations + +class Base { + public: + int x; + Base() { + x = 5; + } + virtual ~Base() { + x += 1; + } +}; + +class Derived : public Base { + public: + int y; + Derived() { + y = 10; + } + ~Derived() { + y += 1; + } +}; + +Derived d; + +// CHECK-LABEL: define {{.*}}DerivedD1Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}DerivedD2Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void + +// CHECK-LABEL: define {{.*}}DerivedD0Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}DerivedD1Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void + +// CHECK-LABEL: define {{.*}}BaseD1Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}BaseD2Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void + +// CHECK-LABEL: define {{.*}}BaseD0Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}BaseD1Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void + +// CHECK-LABEL: define {{.*}}BaseD2Ev +// CHECK: call void @__sanitizer_dtor_callback +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void + +// CHECK-LABEL: define {{.*}}DerivedD2Ev +// CHECK: call void @__sanitizer_dtor_callback +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}BaseD2Ev +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void Index: test/CodeGenCXX/sanitize-dtor-fn-attribute.cpp =================================================================== --- test/CodeGenCXX/sanitize-dtor-fn-attribute.cpp +++ test/CodeGenCXX/sanitize-dtor-fn-attribute.cpp @@ -1,6 +1,8 @@ // Test -fsanitize-memory-use-after-dtor // RUN: %clang_cc1 -fsanitize=memory -fsanitize-memory-use-after-dtor -std=c++11 -triple=x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s -// RUN: %clang_cc1 -DATTRIBUTE -fsanitize=memory -fsanitize-memory-use-after-dtor -std=c++11 -triple=x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-ATTR + +// The no_sanitize_memory attribute, when applied to a destructor, +// represses emission of sanitizing callback template class Vector { public: @@ -10,11 +12,8 @@ struct No_San { Vector v; No_San() { } -#ifdef ATTRIBUTE - __attribute__((no_sanitize_memory)) ~No_San() = default; -#else + //__attribute__((no_sanitize_memory)) ~No_San() = default; -#endif }; int main() { @@ -27,25 +26,19 @@ // instrumentation of the destructor // CHECK: define {{.*}}No_SanD1Ev{{.*}} [[ATTRIBUTE:#[0-9]+]] // CHECK: call void {{.*}}No_SanD2Ev -// CHECK: call void @__sanitizer_dtor_callback // CHECK: ret void -// CHECK-ATTR: define {{.*}}No_SanD1Ev{{.*}} [[ATTRIBUTE:#[0-9]+]] -// CHECK-ATTR: call void {{.*}}No_SanD2Ev -// CHECK-ATTR-NOT: call void @__sanitizer_dtor_callback -// CHECK-ATTR: ret void - - // CHECK: define {{.*}}No_SanD2Ev{{.*}} [[ATTRIBUTE:#[0-9]+]] -// CHECK: call void {{.*}}Vector -// CHECK: call void @__sanitizer_dtor_callback +// CHECK: call void {{.*}}VectorIiED1Ev // CHECK: ret void -// CHECK-ATTR: define {{.*}}No_SanD2Ev{{.*}} [[ATTRIBUTE:#[0-9]+]] -// CHECK-ATTR: call void {{.*}}Vector -// CHECK-ATTR-NOT: call void @__sanitizer_dtor_callback -// CHECK-ATTR: ret void +// CHECK: define {{.*}}VectorIiED1Ev +// CHECK: call void {{.*}}VectorIiED2Ev +// CHECK: ret void + +// CHECK: define {{.*}}VectorIiED2Ev +// CHECK: call void {{.*}}sanitizer_dtor_callback +// CHECK: ret void // When attribute is repressed, the destructor does not emit any tail calls -// CHECK: attributes [[ATTRIBUTE]] = {{.*}} sanitize_memory -// CHECK-ATTR-NOT: attributes [[ATTRIBUTE]] = {{.*}} sanitize_memory +// CHECK-NOT: attributes [[ATTRIBUTE]] = {{.*}} sanitize_memory