From: Artem Dergachev Date: Fri, 18 Jan 2019 23:05:07 +0000 (+0000) Subject: [analyzer] pr37688: Fix a crash upon evaluating a deleted destructor of a union. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e6604f101fb09ae6fa05da0d4df230493e48579f;p=clang [analyzer] pr37688: Fix a crash upon evaluating a deleted destructor of a union. Add a defensive check against an invalid destructor in the CFG. Unions with fields with destructors have their own destructor implicitly deleted. Due to a bug in the CFG we're still trying to evaluate them at the end of the object's lifetime and crash because we are unable to find the destructor's declaration. rdar://problem/47362608 Differential Revision: https://reviews.llvm.org/D56899 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351610 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 6445b9df5a..dd4345784b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -604,6 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, ExplodedNode *Pred, ExplodedNodeSet &Dst, const EvalCallOptions &CallOpts) { + assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -611,6 +612,19 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); + // FIXME: There should always be a Decl, otherwise the destructor call + // shouldn't have been added to the CFG in the first place. + if (!DtorDecl) { + // Skip the invalid destructor. We cannot simply return because + // it would interrupt the analysis instead. + static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); + // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway. + PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef Call = CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); @@ -629,7 +643,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); - ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); } diff --git a/test/Analysis/cfg.cpp b/test/Analysis/cfg.cpp index f43a809c77..ea028e06f3 100644 --- a/test/Analysis/cfg.cpp +++ b/test/Analysis/cfg.cpp @@ -468,6 +468,37 @@ void test_lifetime_extended_temporaries() { } +// FIXME: The destructor for 'a' shouldn't be there because it's deleted +// in the union. +// CHECK-LABEL: void foo() +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// WARNINGS-NEXT: 1: (CXXConstructExpr, struct pr37688_deleted_union_destructor::A) +// ANALYZER-NEXT: 1: (CXXConstructExpr, [B1.2], struct pr37688_deleted_union_destructor::A) +// CHECK-NEXT: 2: pr37688_deleted_union_destructor::A a; +// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + +namespace pr37688_deleted_union_destructor { +struct S { ~S(); }; +struct A { + ~A() noexcept {} + union { + struct { + S s; + } ss; + }; +}; +void foo() { + A a; +} +} // end namespace pr37688_deleted_union_destructor + + // CHECK-LABEL: template<> int *PR18472() // CHECK: [B2 (ENTRY)] // CHECK-NEXT: Succs (1): B1 diff --git a/test/Analysis/unions.cpp b/test/Analysis/unions.cpp index 618d4c314a..2aa4824791 100644 --- a/test/Analysis/unions.cpp +++ b/test/Analysis/unions.cpp @@ -1,6 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection %s -analyzer-config eagerly-assume=false -verify extern void clang_analyzer_eval(bool); +extern void clang_analyzer_warnIfReached(); extern "C" char *strdup(const char *s); namespace PR14054_reduced { @@ -121,3 +122,22 @@ void test() { y = 1 / y; // no-warning } } // end namespace assume_union_contents + +namespace pr37688_deleted_union_destructor { +struct S { ~S(); }; +struct A { + ~A() noexcept {} + union { + struct { + S s; + } ss; + }; +}; +void foo() { + A a; +} // no-crash +void bar() { + foo(); + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} +} // end namespace pr37688_deleted_union_destructor