Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1268,6 +1268,12 @@ "false", Released, Hide>, + CmdLineOption, CmdLineOption { @@ -165,6 +166,15 @@ llvm::errs() << "RegionChanges\n"; return State; } + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + if (isCallbackEnabled(State, "PointerEscape")) + llvm::errs() << "PointerEscape\n"; + return State; + } }; } // end anonymous namespace Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -592,9 +592,71 @@ for (auto I : dstCallEvaluated) finishArgumentConstruction(dstArgumentCleanup, I, Call); - // Finally, run any post-call checks. - getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup, + ExplodedNodeSet dstPostCall; + getCheckerManager().runCheckersForPostCall(dstPostCall, dstArgumentCleanup, Call, *this); + + // Escaping symbols conjured during invalidationg the regions above. + // Note that, for inlined calls the nodes were put back into the worklist, + // so we can assume that every node belongs to a conservative call at this + // point. + 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; + QualType T; + if (const TypedRegion *TR = dyn_cast(MR)) + T = TR->getLocationType(); + else if (const SymbolicRegion *SR = dyn_cast(MR)) + T = SR->getSymbol()->getType(); + if (T->isVoidPointerType()) + return false; + SVal StoredVal = State->getSVal(MR); + if (SymbolRef Sym = StoredVal.getAsSymbol()) + Symbols.insert(Sym); + return true; + } + }; + + // 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); + } + } + + State = getCheckerManager().runCheckersForPointerEscape( + State, Symbols, &Call, PSK_EscapeOnConservativeCall, nullptr); + + if (State == I->getState()) + Dst.insert(I); + else + B.generateNode(I->getLocation(), State, I); + } } ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, @@ -670,8 +732,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); Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ clang/test/Analysis/analyzer-config.c @@ -37,6 +37,7 @@ // CHECK-NEXT: debug.AnalysisOrder:EndFunction = false // CHECK-NEXT: debug.AnalysisOrder:LiveSymbols = false // CHECK-NEXT: debug.AnalysisOrder:NewAllocator = false +// CHECK-NEXT: debug.AnalysisOrder:PointerEscape = false // CHECK-NEXT: debug.AnalysisOrder:PostCall = false // CHECK-NEXT: debug.AnalysisOrder:PostStmtArraySubscriptExpr = false // CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXNewExpr = false @@ -97,4 +98,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 94 +// CHECK-NEXT: num-entries = 95 Index: clang/test/Analysis/ponter-escape-on-conservative-calls.c =================================================================== --- /dev/null +++ clang/test/Analysis/ponter-escape-on-conservative-calls.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.AnalysisOrder -analyzer-config debug.AnalysisOrder:PointerEscape=true -analyzer-config debug.AnalysisOrder:PostCall=true %s 2>&1 | FileCheck %s + + +void f(int *); + +int main() { + int a; + f(&a); + return 0; +} + +// CHECK: PostCall +// CHECK-NEXT: PointerEscape