Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -1648,11 +1648,15 @@ } }; - class SanitizeDtor final : public EHScopeStack::Cleanup { + + static void Poison(CodeGenFunction &CGF, llvm::Value *OffsetPtr, + CharUnits::QuantityType PoisonSize); + + class SanitizeDtorMembers final : public EHScopeStack::Cleanup { const CXXDestructorDecl *Dtor; public: - SanitizeDtor(const CXXDestructorDecl *Dtor) : Dtor(Dtor) {} + SanitizeDtorMembers(const CXXDestructorDecl *Dtor) : Dtor(Dtor) {} // Generate function call for handling object poisoning. // Disables tail call elimination, to prevent the current stack frame @@ -1684,11 +1688,11 @@ // Currently on the last field, and it must be poisoned with the // current block. if (fieldIndex == Layout.getFieldCount() - 1) { - PoisonBlock(CGF, startIndex, Layout.getFieldCount()); + PoisonMembers(CGF, startIndex, Layout.getFieldCount()); } } else if (startIndex >= 0) { // No longer within a block of memory to poison, so poison the block - PoisonBlock(CGF, startIndex, fieldIndex); + PoisonMembers(CGF, startIndex, fieldIndex); // Re-set the start index startIndex = -1; } @@ -1701,7 +1705,7 @@ /// start poisoning (inclusive) /// \param layoutEndOffset index of the ASTRecordLayout field to /// end poisoning (exclusive) - void PoisonBlock(CodeGenFunction &CGF, unsigned layoutStartOffset, + void PoisonMembers(CodeGenFunction &CGF, unsigned layoutStartOffset, unsigned layoutEndOffset) { ASTContext &Context = CGF.getContext(); const ASTRecordLayout &Layout = @@ -1732,20 +1736,46 @@ if (PoisonSize == 0) return; - // Pass in void pointer and size of region as arguments to runtime - // function - llvm::Value *Args[] = {CGF.Builder.CreateBitCast(OffsetPtr, CGF.VoidPtrTy), - llvm::ConstantInt::get(CGF.SizeTy, PoisonSize)}; + Poison(CGF, OffsetPtr, PoisonSize); + } + }; + + class SanitizeDtorVTable final : public EHScopeStack::Cleanup { + const CXXDestructorDecl *Dtor; - llvm::Type *ArgTypes[] = {CGF.VoidPtrTy, CGF.SizeTy}; + public: + SanitizeDtorVTable(const CXXDestructorDecl *Dtor) : Dtor(Dtor) {} + + // Generate function call for handling vtable pointer poisoning. + void Emit(CodeGenFunction &CGF, Flags flags) override { + assert(Dtor->getParent()->isDynamicClass()); + ASTContext &Context = CGF.getContext(); + // Poison vtable and vtable ptr if they exist for this class. + llvm::Value *VTablePtr = CGF.LoadCXXThis(); - llvm::FunctionType *FnType = - llvm::FunctionType::get(CGF.VoidTy, ArgTypes, false); - llvm::Value *Fn = - CGF.CGM.CreateRuntimeFunction(FnType, "__sanitizer_dtor_callback"); - CGF.EmitNounwindRuntimeCall(Fn, Args); + CharUnits::QuantityType PoisonSize = + Context.toCharUnitsFromBits(CGF.PointerWidthInBits).getQuantity(); + // Pass in void pointer and size of region as arguments to runtime + // function + Poison(CGF, VTablePtr, PoisonSize); } - }; + }; + + static void Poison(CodeGenFunction &CGF, llvm::Value *OffsetPtr, + CharUnits::QuantityType PoisonSize) { + // Pass in void pointer and size of region as arguments to runtime + // function + llvm::Value *Args[] = {CGF.Builder.CreateBitCast(OffsetPtr, CGF.VoidPtrTy), + llvm::ConstantInt::get(CGF.SizeTy, PoisonSize)}; + + llvm::Type *ArgTypes[] = {CGF.VoidPtrTy, CGF.SizeTy}; + + llvm::FunctionType *FnType = + llvm::FunctionType::get(CGF.VoidTy, ArgTypes, false); + llvm::Value *Fn = + CGF.CGM.CreateRuntimeFunction(FnType, "__sanitizer_dtor_callback"); + CGF.EmitNounwindRuntimeCall(Fn, Args); + } } /// \brief Emit all code that comes at the end of class's @@ -1800,6 +1830,11 @@ } assert(DtorType == Dtor_Base); + // Poison the vtable pointer such that access after the base + // and member destructors are invoked is invalid. + if (CGM.getCodeGenOpts().SanitizeMemoryUseAfterDtor && + SanOpts.has(SanitizerKind::Memory) && DD->getParent()->isDynamicClass()) + EHStack.pushCleanup(NormalAndEHCleanup, DD); // Destroy non-virtual bases. for (const auto &Base : ClassDecl->bases()) { @@ -1822,7 +1857,7 @@ // invoked, and before the base class destructor runs, is invalid. if (CGM.getCodeGenOpts().SanitizeMemoryUseAfterDtor && SanOpts.has(SanitizerKind::Memory)) - EHStack.pushCleanup(NormalAndEHCleanup, DD); + EHStack.pushCleanup(NormalAndEHCleanup, DD); // Destroy direct fields. for (const auto *Field : ClassDecl->fields()) { Index: test/CodeGenCXX/sanitize-dtor-derived-class.cpp =================================================================== --- test/CodeGenCXX/sanitize-dtor-derived-class.cpp +++ test/CodeGenCXX/sanitize-dtor-derived-class.cpp @@ -52,14 +52,17 @@ // CHECK-NOT: call void @__sanitizer_dtor_callback // CHECK: ret void +// Poison member and vtable pointer. // CHECK-LABEL: define {{.*}}BaseD2Ev // CHECK: call void @__sanitizer_dtor_callback +// CHECK: call void @__sanitizer_dtor_callback // CHECK-NOT: call void @__sanitizer_dtor_callback // CHECK: ret void +// Poison member and vtable pointer. // 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: call void @__sanitizer_dtor_callback // CHECK: ret void Index: test/CodeGenCXX/sanitize-dtor-vtable.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/sanitize-dtor-vtable.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -O0 -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 + +class A { + public: + int x; + A() {} + virtual ~A() {} +}; +A a; + +// CHECK-LABEL: define {{.*}}AD2Ev +// CHECK: call void @__sanitizer_dtor_callback +// CHECK: call void @__sanitizer_dtor_callback{{.*}}i64 8 +// CHECK-NOT: call void @__sanitizer_dtor_callback +// CHECK: ret void