From a167502bf9cf5f494c8e5ebcf28be822809b013f Mon Sep 17 00:00:00 2001 From: George Karpenkov Date: Mon, 9 Oct 2017 23:20:46 +0000 Subject: [PATCH] [Analyzer] Do not segfault on unexpected call_once implementation Fixes https://bugs.llvm.org/show_bug.cgi?id=34869 Differential Revision: https://reviews.llvm.org/D38702 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@315250 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/BodyFarm.cpp | 38 +++++++++++++++++++++---------------- test/Analysis/call_once.cpp | 9 +++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index a6597690c2..81ad999e52 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -327,6 +327,28 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { const ParmVarDecl *Flag = D->getParamDecl(0); const ParmVarDecl *Callback = D->getParamDecl(1); QualType CallbackType = Callback->getType().getNonReferenceType(); + QualType FlagType = Flag->getType().getNonReferenceType(); + CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl(); + if (!FlagCXXDecl) { + DEBUG(llvm::dbgs() << "Flag field is not a CXX record: " + << "unknown std::call_once implementation." + << "Ignoring the call.\n"); + return nullptr; + } + + // Note: here we are assuming libc++ implementation of call_once, + // which has a struct with a field `__state_`. + // Body farming might not work for other `call_once` implementations. + NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_"); + ValueDecl *FieldDecl; + if (FoundDecl) { + FieldDecl = dyn_cast(FoundDecl); + } else { + DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, " + << "unable to synthesize call_once body, ignoring " + << "the call.\n"); + return nullptr; + } bool isLambdaCall = CallbackType->getAsCXXRecordDecl() && CallbackType->getAsCXXRecordDecl()->isLambda(); @@ -355,27 +377,11 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { CallbackCall = create_call_once_funcptr_call(C, M, Callback, CallArgs); } - QualType FlagType = Flag->getType().getNonReferenceType(); DeclRefExpr *FlagDecl = M.makeDeclRefExpr(Flag, /* RefersToEnclosingVariableOrCapture=*/true, /* GetNonReferenceType=*/true); - CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl(); - - // Note: here we are assuming libc++ implementation of call_once, - // which has a struct with a field `__state_`. - // Body farming might not work for other `call_once` implementations. - NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_"); - ValueDecl *FieldDecl; - if (FoundDecl) { - FieldDecl = dyn_cast(FoundDecl); - } else { - DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, " - << "unable to synthesize call_once body, ignoring " - << "the call.\n"); - return nullptr; - } MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl); assert(Deref->isLValue()); diff --git a/test/Analysis/call_once.cpp b/test/Analysis/call_once.cpp index 030b343429..324d9fceb3 100644 --- a/test/Analysis/call_once.cpp +++ b/test/Analysis/call_once.cpp @@ -231,3 +231,12 @@ void test_non_std_call_once() { int x = call_once(); clang_analyzer_eval(x == 5); // expected-warning{{TRUE}} } + +namespace std { +template +void call_once(d, e); +} +void g(); +void test_no_segfault_on_different_impl() { + std::call_once(g, false); // no-warning +} -- 2.50.1