Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -604,6 +604,11 @@ const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) override; + /// A simple wrapper when you only need to notify checkers of pointer-escape + /// of a single value. + ProgramStateRef escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const; + public: // FIXME: 'tag' should be removed, and a LocationContext should be used // instead. Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1231,23 +1231,27 @@ } } -namespace { - -class CollectReachableSymbolsCallback final : public SymbolVisitor { - InvalidatedSymbols Symbols; - -public: - explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} - - const InvalidatedSymbols &getSymbols() const { return Symbols; } - - bool VisitSymbol(SymbolRef Sym) override { - Symbols.insert(Sym); - return true; - } -}; - -} // namespace +ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const { + class CollectReachableSymbolsCallback final : public SymbolVisitor { + InvalidatedSymbols Symbols; + + public: + explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} + + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) override { + Symbols.insert(Sym); + return true; + } + }; + + const CollectReachableSymbolsCallback &Scanner = + State->scanReachableSymbols(V); + return getCheckerManager().runCheckersForPointerEscape( + State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr); +} void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { @@ -1529,17 +1533,8 @@ ->getType()->isRecordType())) for (auto Child : Ex->children()) { assert(Child); - SVal Val = State->getSVal(Child, LCtx); - - CollectReachableSymbolsCallback Scanner = - State->scanReachableSymbols( - Val); - const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); - - State = getCheckerManager().runCheckersForPointerEscape( - State, EscapedSymbols, - /*CallEvent*/ nullptr, PSK_EscapeOther, nullptr); + State = escapeValue(State, Val, PSK_EscapeOther); } Bldr2.generateNode(S, N, State); @@ -2759,15 +2754,7 @@ // Otherwise, find all symbols referenced by 'val' that we are tracking // and stop tracking them. - CollectReachableSymbolsCallback Scanner = - State->scanReachableSymbols(Val); - const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); - State = getCheckerManager().runCheckersForPointerEscape(State, - EscapedSymbols, - /*CallEvent*/ nullptr, - PSK_EscapeOnBind, - nullptr); - + State = escapeValue(State, Val, PSK_EscapeOnBind); return State; } Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -258,12 +258,15 @@ QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr, ExplodedNode* Pred) { // Delegate to SValBuilder to process. - SVal V = state->getSVal(Ex, LCtx); - V = svalBuilder.evalCast(V, T, ExTy); + SVal OrigV = state->getSVal(Ex, LCtx); + SVal V = svalBuilder.evalCast(OrigV, T, ExTy); // Negate the result if we're treating the boolean as a signed i1 if (CastE->getCastKind() == CK_BooleanToSignedIntegral) V = evalMinus(V); state = state->BindExpr(CastE, LCtx, V); + if (V.isUnknown() && !OrigV.isUnknown()) { + state = escapeValue(state, OrigV, PSK_EscapeOther); + } Bldr.generateNode(CastE, Pred, state); return state; Index: test/Analysis/malloc.mm =================================================================== --- test/Analysis/malloc.mm +++ test/Analysis/malloc.mm @@ -320,3 +320,13 @@ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; if (string) free(characters); // expected-warning{{Attempt to free non-owned memory}} } + +void *test_reinterpret_cast_to_block() { + // Used to leak because the pointer was disappearing + // during the reinterpret_cast. + using BlockPtrTy = void (^)(); + struct Block {}; + Block* block = static_cast(malloc(sizeof(Block))); + BlockPtrTy blockPtr = reinterpret_cast(block); // no-warning + return blockPtr; +} Index: test/Analysis/pr22954.c =================================================================== --- test/Analysis/pr22954.c +++ test/Analysis/pr22954.c @@ -624,9 +624,10 @@ clang_analyzer_eval(m29[i].s3[1] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(m29[i].s3[2] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(m29[i].s3[3] == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m29[j].s3[k] == 1); // expected-warning{{TRUE}}\ - expected-warning{{Potential leak of memory pointed to by field 's4'}} + clang_analyzer_eval(m29[j].s3[k] == 1); // expected-warning{{TRUE}} clang_analyzer_eval(l29->s1[m] == 2); // expected-warning{{UNKNOWN}} + // FIXME: Should warn that m29[i].s4 leaks. But not on the previous line, + // because l29 and m29 alias. return 0; }