Index: clang/include/clang/StaticAnalyzer/Core/CheckerManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -395,7 +395,8 @@ /// Run checkers for evaluating a call. /// /// Warning: Currently, the CallEvent MUST come from a CallExpr! - void runCheckersForEvalCall(ExplodedNodeSet &Dst, + /// Return true if the call evaluated conservatively. + bool runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &CE, ExprEngine &Eng); Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -672,7 +672,8 @@ const CallEvent &Call); /// Default implementation of call evaluation. - void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, + /// Returns true if the call was evaluated conservatively. + bool defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred, const CallEvent &Call, const EvalCallOptions &CallOpts = {}); @@ -750,7 +751,8 @@ /// Either inline or process the call conservatively (or both), based /// on DynamicDispatchBifurcation data. - void BifurcateCall(const MemRegion *BifurReg, + /// Returns true if the call was evaluated conservatively. + bool BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred); Index: clang/lib/StaticAnalyzer/Core/CheckerManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -647,7 +647,7 @@ /// Run checkers for evaluating a call. /// Only one checker will evaluate the call. -void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, +bool CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng) { @@ -687,9 +687,10 @@ // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); - Eng.defaultEvalCall(B, Pred, Call); + return Eng.defaultEvalCall(B, Pred, Call); } } + return false; } /// Run checkers for the entire Translation Unit. Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -18,6 +18,8 @@ #include "clang/Analysis/ConstructionContext.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" @@ -576,15 +578,14 @@ // Run any pre-call checks using the generic call interface. ExplodedNodeSet dstPreVisit; - getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, - Call, *this); + getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this); // Actually evaluate the function call. We try each of the checkers // to see if the can evaluate the function call, and get a callback at // defaultEvalCall if all of them fail. ExplodedNodeSet dstCallEvaluated; - getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, - Call, *this); + bool WasConservative = getCheckerManager().runCheckersForEvalCall( + dstCallEvaluated, dstPreVisit, Call, *this); // If there were other constructors called for object-type arguments // of this call, clean them up. @@ -593,8 +594,68 @@ finishArgumentConstruction(dstArgumentCleanup, I, Call); // Finally, run any post-call checks. - getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup, + if (!WasConservative) { + getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup, Call, + *this); + return; + } + + // Escaping symbols conjured during invalidationg the regions above. + class CollectReachableSymbolsCallback final : public SymbolVisitor { + ProgramStateRef State; + InvalidatedSymbols &Symbols; + + public: + explicit CollectReachableSymbolsCallback(ProgramStateRef State, + InvalidatedSymbols &Symbols) + : State(State), Symbols(Symbols) {} + + bool VisitSymbol(SymbolRef Sym) override { return true; } + bool VisitMemRegion(const MemRegion *MR) override { + if (MR->hasStackStorage()) + return false; + SVal StoredVal = State->getSVal(MR); + if (SymbolRef Sym = StoredVal.getAsSymbol()) + Symbols.insert(Sym); + return true; + } + }; + + ExplodedNodeSet dstPostCall; + getCheckerManager().runCheckersForPostCall(dstPostCall, dstArgumentCleanup, Call, *this); + + // Run pointerEscape callback with the newly conjured symbols. + for (auto I : dstPostCall) { + NodeBuilder B(I, Dst, *currBldrCtx); + InvalidatedSymbols Symbols; + ProgramStateRef State = I->getState(); + CollectReachableSymbolsCallback Scanner(State, Symbols); + const FunctionDecl *FuncDecl = + dyn_cast_or_null(Call.getDecl()); + if (FuncDecl) { + for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { + if (Arg >= FuncDecl->getNumParams()) + break; + QualType ParamTy = FuncDecl->getParamDecl(Arg)->getType(); + if (ParamTy.isNull() || + (!ParamTy->isPointerType() && !ParamTy->isReferenceType())) + continue; + if (ParamTy->getPointeeType().isConstQualified()) + continue; + State->scanReachableSymbols(Call.getArgSVal(Arg), Scanner); + } + } + + // TODO: the PSK is a lie. + State = getCheckerManager().runCheckersForPointerEscape( + State, Symbols, &Call, PSK_DirectEscapeOnCall, nullptr); + + if (State != I->getState()) + B.generateNode(I->getLocation(), State, I); + else + Dst.insert(I); + } } ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, @@ -644,8 +705,8 @@ ITraits.setTrait(TargetR, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); State = State->invalidateRegions(TargetR, E, Count, LCtx, - /* CausesPointerEscape=*/false, nullptr, - &Call, &ITraits); + /* CausesPointerEscape=*/false, + nullptr, &Call, &ITraits); R = State->getSVal(Target.castAs(), E->getType()); } else { @@ -670,8 +731,7 @@ // Conservatively evaluate call by invalidating regions and binding // a conjured return value. void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, - ProgramStateRef State) { + ExplodedNode *Pred, ProgramStateRef State) { State = Call.invalidateRegions(currBldrCtx->blockCount(), State); State = bindReturnValue(Call, Pred->getLocationContext(), State); @@ -999,7 +1059,7 @@ return MD->isTrivial(); } -void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, +bool ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate, const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. @@ -1009,7 +1069,7 @@ // Special-case trivial assignment operators. if (isTrivialObjectAssignment(*Call)) { performTrivialCopy(Bldr, Pred, *Call); - return; + return false; } // Try to inline the call. @@ -1030,28 +1090,28 @@ // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); - return; + return BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { conservativeEvalCall(*Call, Bldr, Pred, State); - return; + return true; } } // We are not bifurcating and we do have a Decl, so just inline. if (inlineCall(*Call, D, Bldr, Pred, State)) - return; + return false; } } // If we can't inline it, handle the return value and invalidate the regions. conservativeEvalCall(*Call, Bldr, Pred, State); + return true; } -void ExprEngine::BifurcateCall(const MemRegion *BifurReg, +bool ExprEngine::BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred) { assert(BifurReg); @@ -1066,12 +1126,12 @@ // If we are on "inline path", keep inlining if possible. if (*BState == DynamicDispatchModeInlined) if (inlineCall(Call, D, Bldr, Pred, State)) - return; + return false; // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the // return value and invalidate the regions. conservativeEvalCall(Call, Bldr, Pred, State); - return; + return true; } // If we got here, this is the first time we process a message to this @@ -1087,6 +1147,8 @@ conservativeEvalCall(Call, Bldr, Pred, NoIState); NumOfDynamicDispatchPathSplits++; + // TODO: it is a path sensitive property if a call was inlined. + return false; } void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,