10b57cec5SDimitry Andric //===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines a set of BugReporter "visitors" which can be used to
100b57cec5SDimitry Andric // enhance the diagnostics reported for a bug.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
150b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
160b57cec5SDimitry Andric #include "clang/AST/Decl.h"
170b57cec5SDimitry Andric #include "clang/AST/DeclBase.h"
180b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h"
190b57cec5SDimitry Andric #include "clang/AST/Expr.h"
200b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h"
210b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h"
220b57cec5SDimitry Andric #include "clang/AST/Stmt.h"
230b57cec5SDimitry Andric #include "clang/AST/Type.h"
240b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
250b57cec5SDimitry Andric #include "clang/Analysis/Analyses/Dominators.h"
260b57cec5SDimitry Andric #include "clang/Analysis/AnalysisDeclContext.h"
270b57cec5SDimitry Andric #include "clang/Analysis/CFG.h"
280b57cec5SDimitry Andric #include "clang/Analysis/CFGStmtMap.h"
29a7dea167SDimitry Andric #include "clang/Analysis/PathDiagnostic.h"
300b57cec5SDimitry Andric #include "clang/Analysis/ProgramPoint.h"
310b57cec5SDimitry Andric #include "clang/Basic/IdentifierTable.h"
320b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
330b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h"
340b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
350b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
360b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
370b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
380b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
390b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
400b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
410b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
420b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
430b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
440b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
450b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
460b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
470b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
480b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
490b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
500b57cec5SDimitry Andric #include "llvm/ADT/SmallPtrSet.h"
510b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
520b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
530b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
540b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
550b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
560b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
570b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
580b57cec5SDimitry Andric #include <cassert>
590b57cec5SDimitry Andric #include <deque>
600b57cec5SDimitry Andric #include <memory>
61bdd1243dSDimitry Andric #include <optional>
620b57cec5SDimitry Andric #include <string>
630b57cec5SDimitry Andric #include <utility>
640b57cec5SDimitry Andric
650b57cec5SDimitry Andric using namespace clang;
660b57cec5SDimitry Andric using namespace ento;
67fe6060f1SDimitry Andric using namespace bugreporter;
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
700b57cec5SDimitry Andric // Utility functions.
710b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
720b57cec5SDimitry Andric
peelOffPointerArithmetic(const BinaryOperator * B)730b57cec5SDimitry Andric static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) {
740b57cec5SDimitry Andric if (B->isAdditiveOp() && B->getType()->isPointerType()) {
750b57cec5SDimitry Andric if (B->getLHS()->getType()->isPointerType()) {
760b57cec5SDimitry Andric return B->getLHS();
770b57cec5SDimitry Andric } else if (B->getRHS()->getType()->isPointerType()) {
780b57cec5SDimitry Andric return B->getRHS();
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric }
810b57cec5SDimitry Andric return nullptr;
820b57cec5SDimitry Andric }
830b57cec5SDimitry Andric
8481ad6265SDimitry Andric /// \return A subexpression of @c Ex which represents the
8581ad6265SDimitry Andric /// expression-of-interest.
8681ad6265SDimitry Andric static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N);
8781ad6265SDimitry Andric
880b57cec5SDimitry Andric /// Given that expression S represents a pointer that would be dereferenced,
890b57cec5SDimitry Andric /// try to find a sub-expression from which the pointer came from.
900b57cec5SDimitry Andric /// This is used for tracking down origins of a null or undefined value:
910b57cec5SDimitry Andric /// "this is null because that is null because that is null" etc.
920b57cec5SDimitry Andric /// We wipe away field and element offsets because they merely add offsets.
930b57cec5SDimitry Andric /// We also wipe away all casts except lvalue-to-rvalue casts, because the
940b57cec5SDimitry Andric /// latter represent an actual pointer dereference; however, we remove
950b57cec5SDimitry Andric /// the final lvalue-to-rvalue cast before returning from this function
960b57cec5SDimitry Andric /// because it demonstrates more clearly from where the pointer rvalue was
970b57cec5SDimitry Andric /// loaded. Examples:
980b57cec5SDimitry Andric /// x->y.z ==> x (lvalue)
990b57cec5SDimitry Andric /// foo()->y.z ==> foo() (rvalue)
getDerefExpr(const Stmt * S)1000b57cec5SDimitry Andric const Expr *bugreporter::getDerefExpr(const Stmt *S) {
1010b57cec5SDimitry Andric const auto *E = dyn_cast<Expr>(S);
1020b57cec5SDimitry Andric if (!E)
1030b57cec5SDimitry Andric return nullptr;
1040b57cec5SDimitry Andric
1050b57cec5SDimitry Andric while (true) {
1060b57cec5SDimitry Andric if (const auto *CE = dyn_cast<CastExpr>(E)) {
1070b57cec5SDimitry Andric if (CE->getCastKind() == CK_LValueToRValue) {
1080b57cec5SDimitry Andric // This cast represents the load we're looking for.
1090b57cec5SDimitry Andric break;
1100b57cec5SDimitry Andric }
1110b57cec5SDimitry Andric E = CE->getSubExpr();
1120b57cec5SDimitry Andric } else if (const auto *B = dyn_cast<BinaryOperator>(E)) {
1130b57cec5SDimitry Andric // Pointer arithmetic: '*(x + 2)' -> 'x') etc.
1140b57cec5SDimitry Andric if (const Expr *Inner = peelOffPointerArithmetic(B)) {
1150b57cec5SDimitry Andric E = Inner;
1160b57cec5SDimitry Andric } else {
1170b57cec5SDimitry Andric // Probably more arithmetic can be pattern-matched here,
1180b57cec5SDimitry Andric // but for now give up.
1190b57cec5SDimitry Andric break;
1200b57cec5SDimitry Andric }
1210b57cec5SDimitry Andric } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
1220b57cec5SDimitry Andric if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf ||
1230b57cec5SDimitry Andric (U->isIncrementDecrementOp() && U->getType()->isPointerType())) {
1240b57cec5SDimitry Andric // Operators '*' and '&' don't actually mean anything.
1250b57cec5SDimitry Andric // We look at casts instead.
1260b57cec5SDimitry Andric E = U->getSubExpr();
1270b57cec5SDimitry Andric } else {
1280b57cec5SDimitry Andric // Probably more arithmetic can be pattern-matched here,
1290b57cec5SDimitry Andric // but for now give up.
1300b57cec5SDimitry Andric break;
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric }
1330b57cec5SDimitry Andric // Pattern match for a few useful cases: a[0], p->f, *p etc.
1340b57cec5SDimitry Andric else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
1355f757f3fSDimitry Andric // This handles the case when the dereferencing of a member reference
1365f757f3fSDimitry Andric // happens. This is needed, because the AST for dereferencing a
1375f757f3fSDimitry Andric // member reference looks like the following:
1385f757f3fSDimitry Andric // |-MemberExpr
1395f757f3fSDimitry Andric // `-DeclRefExpr
1405f757f3fSDimitry Andric // Without this special case the notes would refer to the whole object
1415f757f3fSDimitry Andric // (struct, class or union variable) instead of just the relevant member.
1425f757f3fSDimitry Andric
1435f757f3fSDimitry Andric if (ME->getMemberDecl()->getType()->isReferenceType())
1445f757f3fSDimitry Andric break;
1450b57cec5SDimitry Andric E = ME->getBase();
1460b57cec5SDimitry Andric } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
1470b57cec5SDimitry Andric E = IvarRef->getBase();
1480b57cec5SDimitry Andric } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(E)) {
1490b57cec5SDimitry Andric E = AE->getBase();
1500b57cec5SDimitry Andric } else if (const auto *PE = dyn_cast<ParenExpr>(E)) {
1510b57cec5SDimitry Andric E = PE->getSubExpr();
1520b57cec5SDimitry Andric } else if (const auto *FE = dyn_cast<FullExpr>(E)) {
1530b57cec5SDimitry Andric E = FE->getSubExpr();
1540b57cec5SDimitry Andric } else {
1550b57cec5SDimitry Andric // Other arbitrary stuff.
1560b57cec5SDimitry Andric break;
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric }
1590b57cec5SDimitry Andric
1600b57cec5SDimitry Andric // Special case: remove the final lvalue-to-rvalue cast, but do not recurse
1610b57cec5SDimitry Andric // deeper into the sub-expression. This way we return the lvalue from which
1620b57cec5SDimitry Andric // our pointer rvalue was loaded.
1630b57cec5SDimitry Andric if (const auto *CE = dyn_cast<ImplicitCastExpr>(E))
1640b57cec5SDimitry Andric if (CE->getCastKind() == CK_LValueToRValue)
1650b57cec5SDimitry Andric E = CE->getSubExpr();
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric return E;
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric
getVarDeclForExpression(const Expr * E)1705f757f3fSDimitry Andric static const VarDecl *getVarDeclForExpression(const Expr *E) {
1715f757f3fSDimitry Andric if (const auto *DR = dyn_cast<DeclRefExpr>(E))
1725f757f3fSDimitry Andric return dyn_cast<VarDecl>(DR->getDecl());
1735f757f3fSDimitry Andric return nullptr;
1745f757f3fSDimitry Andric }
1755f757f3fSDimitry Andric
176fe6060f1SDimitry Andric static const MemRegion *
getLocationRegionIfReference(const Expr * E,const ExplodedNode * N,bool LookingForReference=true)177fe6060f1SDimitry Andric getLocationRegionIfReference(const Expr *E, const ExplodedNode *N,
178fe6060f1SDimitry Andric bool LookingForReference = true) {
1795f757f3fSDimitry Andric if (const auto *ME = dyn_cast<MemberExpr>(E)) {
1805f757f3fSDimitry Andric // This handles null references from FieldRegions, for example:
181fe6060f1SDimitry Andric // struct Wrapper { int &ref; };
182fe6060f1SDimitry Andric // Wrapper w = { *(int *)0 };
183fe6060f1SDimitry Andric // w.ref = 1;
1845f757f3fSDimitry Andric const Expr *Base = ME->getBase();
1855f757f3fSDimitry Andric const VarDecl *VD = getVarDeclForExpression(Base);
1865f757f3fSDimitry Andric if (!VD)
187fe6060f1SDimitry Andric return nullptr;
1885f757f3fSDimitry Andric
1895f757f3fSDimitry Andric const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
1905f757f3fSDimitry Andric if (!FD)
1915f757f3fSDimitry Andric return nullptr;
1925f757f3fSDimitry Andric
1935f757f3fSDimitry Andric if (FD->getType()->isReferenceType()) {
1945f757f3fSDimitry Andric SVal StructSVal = N->getState()->getLValue(VD, N->getLocationContext());
1955f757f3fSDimitry Andric return N->getState()->getLValue(FD, StructSVal).getAsRegion();
1965f757f3fSDimitry Andric }
1975f757f3fSDimitry Andric return nullptr;
1985f757f3fSDimitry Andric }
1995f757f3fSDimitry Andric
2005f757f3fSDimitry Andric const VarDecl *VD = getVarDeclForExpression(E);
2015f757f3fSDimitry Andric if (!VD)
2025f757f3fSDimitry Andric return nullptr;
2035f757f3fSDimitry Andric if (LookingForReference && !VD->getType()->isReferenceType())
2045f757f3fSDimitry Andric return nullptr;
2055f757f3fSDimitry Andric return N->getState()->getLValue(VD, N->getLocationContext()).getAsRegion();
206fe6060f1SDimitry Andric }
207fe6060f1SDimitry Andric
2080b57cec5SDimitry Andric /// Comparing internal representations of symbolic values (via
2090b57cec5SDimitry Andric /// SVal::operator==()) is a valid way to check if the value was updated,
2100b57cec5SDimitry Andric /// unless it's a LazyCompoundVal that may have a different internal
2110b57cec5SDimitry Andric /// representation every time it is loaded from the state. In this function we
2120b57cec5SDimitry Andric /// do an approximate comparison for lazy compound values, checking that they
2130b57cec5SDimitry Andric /// are the immediate snapshots of the tracked region's bindings within the
2140b57cec5SDimitry Andric /// node's respective states but not really checking that these snapshots
2150b57cec5SDimitry Andric /// actually contain the same set of bindings.
hasVisibleUpdate(const ExplodedNode * LeftNode,SVal LeftVal,const ExplodedNode * RightNode,SVal RightVal)2160b57cec5SDimitry Andric static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal,
2170b57cec5SDimitry Andric const ExplodedNode *RightNode, SVal RightVal) {
2180b57cec5SDimitry Andric if (LeftVal == RightVal)
2190b57cec5SDimitry Andric return true;
2200b57cec5SDimitry Andric
2210b57cec5SDimitry Andric const auto LLCV = LeftVal.getAs<nonloc::LazyCompoundVal>();
2220b57cec5SDimitry Andric if (!LLCV)
2230b57cec5SDimitry Andric return false;
2240b57cec5SDimitry Andric
2250b57cec5SDimitry Andric const auto RLCV = RightVal.getAs<nonloc::LazyCompoundVal>();
2260b57cec5SDimitry Andric if (!RLCV)
2270b57cec5SDimitry Andric return false;
2280b57cec5SDimitry Andric
2290b57cec5SDimitry Andric return LLCV->getRegion() == RLCV->getRegion() &&
2300b57cec5SDimitry Andric LLCV->getStore() == LeftNode->getState()->getStore() &&
2310b57cec5SDimitry Andric RLCV->getStore() == RightNode->getState()->getStore();
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric
getSValForVar(const Expr * CondVarExpr,const ExplodedNode * N)234bdd1243dSDimitry Andric static std::optional<SVal> getSValForVar(const Expr *CondVarExpr,
235a7dea167SDimitry Andric const ExplodedNode *N) {
2360b57cec5SDimitry Andric ProgramStateRef State = N->getState();
2370b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
2380b57cec5SDimitry Andric
239a7dea167SDimitry Andric assert(CondVarExpr);
240a7dea167SDimitry Andric CondVarExpr = CondVarExpr->IgnoreImpCasts();
241a7dea167SDimitry Andric
2420b57cec5SDimitry Andric // The declaration of the value may rely on a pointer so take its l-value.
243a7dea167SDimitry Andric // FIXME: As seen in VisitCommonDeclRefExpr, sometimes DeclRefExpr may
244a7dea167SDimitry Andric // evaluate to a FieldRegion when it refers to a declaration of a lambda
245a7dea167SDimitry Andric // capture variable. We most likely need to duplicate that logic here.
246a7dea167SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(CondVarExpr))
247a7dea167SDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
248a7dea167SDimitry Andric return State->getSVal(State->getLValue(VD, LCtx));
249a7dea167SDimitry Andric
250a7dea167SDimitry Andric if (const auto *ME = dyn_cast<MemberExpr>(CondVarExpr))
251a7dea167SDimitry Andric if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
252a7dea167SDimitry Andric if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>())
253a7dea167SDimitry Andric return State->getRawSVal(*FieldL, FD->getType());
254a7dea167SDimitry Andric
255bdd1243dSDimitry Andric return std::nullopt;
2560b57cec5SDimitry Andric }
2570b57cec5SDimitry Andric
258bdd1243dSDimitry Andric static std::optional<const llvm::APSInt *>
getConcreteIntegerValue(const Expr * CondVarExpr,const ExplodedNode * N)259a7dea167SDimitry Andric getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
260a7dea167SDimitry Andric
261bdd1243dSDimitry Andric if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
262a7dea167SDimitry Andric if (auto CI = V->getAs<nonloc::ConcreteInt>())
263a7dea167SDimitry Andric return &CI->getValue();
264bdd1243dSDimitry Andric return std::nullopt;
265a7dea167SDimitry Andric }
266a7dea167SDimitry Andric
isVarAnInterestingCondition(const Expr * CondVarExpr,const ExplodedNode * N,const PathSensitiveBugReport * B)267a7dea167SDimitry Andric static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
268a7dea167SDimitry Andric const ExplodedNode *N,
269a7dea167SDimitry Andric const PathSensitiveBugReport *B) {
270a7dea167SDimitry Andric // Even if this condition is marked as interesting, it isn't *that*
271a7dea167SDimitry Andric // interesting if it didn't happen in a nested stackframe, the user could just
272a7dea167SDimitry Andric // follow the arrows.
273a7dea167SDimitry Andric if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame()))
274a7dea167SDimitry Andric return false;
275a7dea167SDimitry Andric
276bdd1243dSDimitry Andric if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
277bdd1243dSDimitry Andric if (std::optional<bugreporter::TrackingKind> K =
278bdd1243dSDimitry Andric B->getInterestingnessKind(*V))
279a7dea167SDimitry Andric return *K == bugreporter::TrackingKind::Condition;
280a7dea167SDimitry Andric
281a7dea167SDimitry Andric return false;
282a7dea167SDimitry Andric }
283a7dea167SDimitry Andric
isInterestingExpr(const Expr * E,const ExplodedNode * N,const PathSensitiveBugReport * B)284a7dea167SDimitry Andric static bool isInterestingExpr(const Expr *E, const ExplodedNode *N,
285a7dea167SDimitry Andric const PathSensitiveBugReport *B) {
286bdd1243dSDimitry Andric if (std::optional<SVal> V = getSValForVar(E, N))
28781ad6265SDimitry Andric return B->getInterestingnessKind(*V).has_value();
288a7dea167SDimitry Andric return false;
2890b57cec5SDimitry Andric }
2900b57cec5SDimitry Andric
2910b57cec5SDimitry Andric /// \return name of the macro inside the location \p Loc.
getMacroName(SourceLocation Loc,BugReporterContext & BRC)2920b57cec5SDimitry Andric static StringRef getMacroName(SourceLocation Loc,
2930b57cec5SDimitry Andric BugReporterContext &BRC) {
2940b57cec5SDimitry Andric return Lexer::getImmediateMacroName(
2950b57cec5SDimitry Andric Loc,
2960b57cec5SDimitry Andric BRC.getSourceManager(),
2970b57cec5SDimitry Andric BRC.getASTContext().getLangOpts());
2980b57cec5SDimitry Andric }
2990b57cec5SDimitry Andric
3000b57cec5SDimitry Andric /// \return Whether given spelling location corresponds to an expansion
3010b57cec5SDimitry Andric /// of a function-like macro.
isFunctionMacroExpansion(SourceLocation Loc,const SourceManager & SM)3020b57cec5SDimitry Andric static bool isFunctionMacroExpansion(SourceLocation Loc,
3030b57cec5SDimitry Andric const SourceManager &SM) {
3040b57cec5SDimitry Andric if (!Loc.isMacroID())
3050b57cec5SDimitry Andric return false;
3060b57cec5SDimitry Andric while (SM.isMacroArgExpansion(Loc))
3070b57cec5SDimitry Andric Loc = SM.getImmediateExpansionRange(Loc).getBegin();
3080b57cec5SDimitry Andric std::pair<FileID, unsigned> TLInfo = SM.getDecomposedLoc(Loc);
3090b57cec5SDimitry Andric SrcMgr::SLocEntry SE = SM.getSLocEntry(TLInfo.first);
3100b57cec5SDimitry Andric const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion();
3110b57cec5SDimitry Andric return EInfo.isFunctionMacroExpansion();
3120b57cec5SDimitry Andric }
3130b57cec5SDimitry Andric
3140b57cec5SDimitry Andric /// \return Whether \c RegionOfInterest was modified at \p N,
3150b57cec5SDimitry Andric /// where \p ValueAfter is \c RegionOfInterest's value at the end of the
3160b57cec5SDimitry Andric /// stack frame.
wasRegionOfInterestModifiedAt(const SubRegion * RegionOfInterest,const ExplodedNode * N,SVal ValueAfter)3170b57cec5SDimitry Andric static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest,
3180b57cec5SDimitry Andric const ExplodedNode *N,
3190b57cec5SDimitry Andric SVal ValueAfter) {
3200b57cec5SDimitry Andric ProgramStateRef State = N->getState();
3210b57cec5SDimitry Andric ProgramStateManager &Mgr = N->getState()->getStateManager();
3220b57cec5SDimitry Andric
3230b57cec5SDimitry Andric if (!N->getLocationAs<PostStore>() && !N->getLocationAs<PostInitializer>() &&
3240b57cec5SDimitry Andric !N->getLocationAs<PostStmt>())
3250b57cec5SDimitry Andric return false;
3260b57cec5SDimitry Andric
3270b57cec5SDimitry Andric // Writing into region of interest.
3280b57cec5SDimitry Andric if (auto PS = N->getLocationAs<PostStmt>())
3290b57cec5SDimitry Andric if (auto *BO = PS->getStmtAs<BinaryOperator>())
3300b57cec5SDimitry Andric if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
3310b57cec5SDimitry Andric N->getSVal(BO->getLHS()).getAsRegion()))
3320b57cec5SDimitry Andric return true;
3330b57cec5SDimitry Andric
3340b57cec5SDimitry Andric // SVal after the state is possibly different.
3350b57cec5SDimitry Andric SVal ValueAtN = N->getState()->getSVal(RegionOfInterest);
3360b57cec5SDimitry Andric if (!Mgr.getSValBuilder()
3370b57cec5SDimitry Andric .areEqual(State, ValueAtN, ValueAfter)
3380b57cec5SDimitry Andric .isConstrainedTrue() &&
3390b57cec5SDimitry Andric (!ValueAtN.isUndef() || !ValueAfter.isUndef()))
3400b57cec5SDimitry Andric return true;
3410b57cec5SDimitry Andric
3420b57cec5SDimitry Andric return false;
3430b57cec5SDimitry Andric }
3440b57cec5SDimitry Andric
3450b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3460b57cec5SDimitry Andric // Implementation of BugReporterVisitor.
3470b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3480b57cec5SDimitry Andric
getEndPath(BugReporterContext &,const ExplodedNode *,PathSensitiveBugReport &)349a7dea167SDimitry Andric PathDiagnosticPieceRef BugReporterVisitor::getEndPath(BugReporterContext &,
350a7dea167SDimitry Andric const ExplodedNode *,
351a7dea167SDimitry Andric PathSensitiveBugReport &) {
3520b57cec5SDimitry Andric return nullptr;
3530b57cec5SDimitry Andric }
3540b57cec5SDimitry Andric
finalizeVisitor(BugReporterContext &,const ExplodedNode *,PathSensitiveBugReport &)355a7dea167SDimitry Andric void BugReporterVisitor::finalizeVisitor(BugReporterContext &,
356a7dea167SDimitry Andric const ExplodedNode *,
357a7dea167SDimitry Andric PathSensitiveBugReport &) {}
3580b57cec5SDimitry Andric
359a7dea167SDimitry Andric PathDiagnosticPieceRef
getDefaultEndPath(const BugReporterContext & BRC,const ExplodedNode * EndPathNode,const PathSensitiveBugReport & BR)360a7dea167SDimitry Andric BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC,
361a7dea167SDimitry Andric const ExplodedNode *EndPathNode,
362a7dea167SDimitry Andric const PathSensitiveBugReport &BR) {
363a7dea167SDimitry Andric PathDiagnosticLocation L = BR.getLocation();
3640b57cec5SDimitry Andric const auto &Ranges = BR.getRanges();
3650b57cec5SDimitry Andric
3660b57cec5SDimitry Andric // Only add the statement itself as a range if we didn't specify any
3670b57cec5SDimitry Andric // special ranges for this report.
3680b57cec5SDimitry Andric auto P = std::make_shared<PathDiagnosticEventPiece>(
3690b57cec5SDimitry Andric L, BR.getDescription(), Ranges.begin() == Ranges.end());
3700b57cec5SDimitry Andric for (SourceRange Range : Ranges)
3710b57cec5SDimitry Andric P->addRange(Range);
3720b57cec5SDimitry Andric
3730b57cec5SDimitry Andric return P;
3740b57cec5SDimitry Andric }
3750b57cec5SDimitry Andric
3760b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
377349cc55cSDimitry Andric // Implementation of NoStateChangeFuncVisitor.
378349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
379349cc55cSDimitry Andric
isModifiedInFrame(const ExplodedNode * N)380349cc55cSDimitry Andric bool NoStateChangeFuncVisitor::isModifiedInFrame(const ExplodedNode *N) {
381349cc55cSDimitry Andric const LocationContext *Ctx = N->getLocationContext();
382349cc55cSDimitry Andric const StackFrameContext *SCtx = Ctx->getStackFrame();
383349cc55cSDimitry Andric if (!FramesModifyingCalculated.count(SCtx))
384349cc55cSDimitry Andric findModifyingFrames(N);
385349cc55cSDimitry Andric return FramesModifying.count(SCtx);
386349cc55cSDimitry Andric }
387349cc55cSDimitry Andric
markFrameAsModifying(const StackFrameContext * SCtx)388349cc55cSDimitry Andric void NoStateChangeFuncVisitor::markFrameAsModifying(
389349cc55cSDimitry Andric const StackFrameContext *SCtx) {
390349cc55cSDimitry Andric while (!SCtx->inTopFrame()) {
391349cc55cSDimitry Andric auto p = FramesModifying.insert(SCtx);
392349cc55cSDimitry Andric if (!p.second)
393349cc55cSDimitry Andric break; // Frame and all its parents already inserted.
394349cc55cSDimitry Andric
395349cc55cSDimitry Andric SCtx = SCtx->getParent()->getStackFrame();
396349cc55cSDimitry Andric }
397349cc55cSDimitry Andric }
398349cc55cSDimitry Andric
getMatchingCallExitEnd(const ExplodedNode * N)399349cc55cSDimitry Andric static const ExplodedNode *getMatchingCallExitEnd(const ExplodedNode *N) {
400349cc55cSDimitry Andric assert(N->getLocationAs<CallEnter>());
401349cc55cSDimitry Andric // The stackframe of the callee is only found in the nodes succeeding
402349cc55cSDimitry Andric // the CallEnter node. CallEnter's stack frame refers to the caller.
403349cc55cSDimitry Andric const StackFrameContext *OrigSCtx = N->getFirstSucc()->getStackFrame();
404349cc55cSDimitry Andric
405349cc55cSDimitry Andric // Similarly, the nodes preceding CallExitEnd refer to the callee's stack
406349cc55cSDimitry Andric // frame.
407349cc55cSDimitry Andric auto IsMatchingCallExitEnd = [OrigSCtx](const ExplodedNode *N) {
408349cc55cSDimitry Andric return N->getLocationAs<CallExitEnd>() &&
409349cc55cSDimitry Andric OrigSCtx == N->getFirstPred()->getStackFrame();
410349cc55cSDimitry Andric };
411349cc55cSDimitry Andric while (N && !IsMatchingCallExitEnd(N)) {
412349cc55cSDimitry Andric assert(N->succ_size() <= 1 &&
413349cc55cSDimitry Andric "This function is to be used on the trimmed ExplodedGraph!");
414349cc55cSDimitry Andric N = N->getFirstSucc();
415349cc55cSDimitry Andric }
416349cc55cSDimitry Andric return N;
417349cc55cSDimitry Andric }
418349cc55cSDimitry Andric
findModifyingFrames(const ExplodedNode * const CallExitBeginN)419349cc55cSDimitry Andric void NoStateChangeFuncVisitor::findModifyingFrames(
420349cc55cSDimitry Andric const ExplodedNode *const CallExitBeginN) {
421349cc55cSDimitry Andric
422349cc55cSDimitry Andric assert(CallExitBeginN->getLocationAs<CallExitBegin>());
423349cc55cSDimitry Andric
424349cc55cSDimitry Andric const StackFrameContext *const OriginalSCtx =
425349cc55cSDimitry Andric CallExitBeginN->getLocationContext()->getStackFrame();
426349cc55cSDimitry Andric
427349cc55cSDimitry Andric const ExplodedNode *CurrCallExitBeginN = CallExitBeginN;
428349cc55cSDimitry Andric const StackFrameContext *CurrentSCtx = OriginalSCtx;
429349cc55cSDimitry Andric
430349cc55cSDimitry Andric for (const ExplodedNode *CurrN = CallExitBeginN; CurrN;
431349cc55cSDimitry Andric CurrN = CurrN->getFirstPred()) {
432349cc55cSDimitry Andric // Found a new inlined call.
433349cc55cSDimitry Andric if (CurrN->getLocationAs<CallExitBegin>()) {
434349cc55cSDimitry Andric CurrCallExitBeginN = CurrN;
435349cc55cSDimitry Andric CurrentSCtx = CurrN->getStackFrame();
436349cc55cSDimitry Andric FramesModifyingCalculated.insert(CurrentSCtx);
437349cc55cSDimitry Andric // We won't see a change in between two identical exploded nodes: skip.
438349cc55cSDimitry Andric continue;
439349cc55cSDimitry Andric }
440349cc55cSDimitry Andric
441349cc55cSDimitry Andric if (auto CE = CurrN->getLocationAs<CallEnter>()) {
442349cc55cSDimitry Andric if (const ExplodedNode *CallExitEndN = getMatchingCallExitEnd(CurrN))
443349cc55cSDimitry Andric if (wasModifiedInFunction(CurrN, CallExitEndN))
444349cc55cSDimitry Andric markFrameAsModifying(CurrentSCtx);
445349cc55cSDimitry Andric
446349cc55cSDimitry Andric // We exited this inlined call, lets actualize the stack frame.
447349cc55cSDimitry Andric CurrentSCtx = CurrN->getStackFrame();
448349cc55cSDimitry Andric
449349cc55cSDimitry Andric // Stop calculating at the current function, but always regard it as
450349cc55cSDimitry Andric // modifying, so we can avoid notes like this:
451349cc55cSDimitry Andric // void f(Foo &F) {
452349cc55cSDimitry Andric // F.field = 0; // note: 0 assigned to 'F.field'
453349cc55cSDimitry Andric // // note: returning without writing to 'F.field'
454349cc55cSDimitry Andric // }
455349cc55cSDimitry Andric if (CE->getCalleeContext() == OriginalSCtx) {
456349cc55cSDimitry Andric markFrameAsModifying(CurrentSCtx);
457349cc55cSDimitry Andric break;
458349cc55cSDimitry Andric }
459349cc55cSDimitry Andric }
460349cc55cSDimitry Andric
461349cc55cSDimitry Andric if (wasModifiedBeforeCallExit(CurrN, CurrCallExitBeginN))
462349cc55cSDimitry Andric markFrameAsModifying(CurrentSCtx);
463349cc55cSDimitry Andric }
464349cc55cSDimitry Andric }
465349cc55cSDimitry Andric
VisitNode(const ExplodedNode * N,BugReporterContext & BR,PathSensitiveBugReport & R)466349cc55cSDimitry Andric PathDiagnosticPieceRef NoStateChangeFuncVisitor::VisitNode(
467349cc55cSDimitry Andric const ExplodedNode *N, BugReporterContext &BR, PathSensitiveBugReport &R) {
468349cc55cSDimitry Andric
469349cc55cSDimitry Andric const LocationContext *Ctx = N->getLocationContext();
470349cc55cSDimitry Andric const StackFrameContext *SCtx = Ctx->getStackFrame();
471349cc55cSDimitry Andric ProgramStateRef State = N->getState();
472349cc55cSDimitry Andric auto CallExitLoc = N->getLocationAs<CallExitBegin>();
473349cc55cSDimitry Andric
474349cc55cSDimitry Andric // No diagnostic if region was modified inside the frame.
475349cc55cSDimitry Andric if (!CallExitLoc || isModifiedInFrame(N))
476349cc55cSDimitry Andric return nullptr;
477349cc55cSDimitry Andric
478349cc55cSDimitry Andric CallEventRef<> Call =
479349cc55cSDimitry Andric BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
480349cc55cSDimitry Andric
481349cc55cSDimitry Andric // Optimistically suppress uninitialized value bugs that result
482349cc55cSDimitry Andric // from system headers having a chance to initialize the value
483349cc55cSDimitry Andric // but failing to do so. It's too unlikely a system header's fault.
484349cc55cSDimitry Andric // It's much more likely a situation in which the function has a failure
485349cc55cSDimitry Andric // mode that the user decided not to check. If we want to hunt such
486349cc55cSDimitry Andric // omitted checks, we should provide an explicit function-specific note
487349cc55cSDimitry Andric // describing the precondition under which the function isn't supposed to
488349cc55cSDimitry Andric // initialize its out-parameter, and additionally check that such
489349cc55cSDimitry Andric // precondition can actually be fulfilled on the current path.
490349cc55cSDimitry Andric if (Call->isInSystemHeader()) {
491349cc55cSDimitry Andric // We make an exception for system header functions that have no branches.
492349cc55cSDimitry Andric // Such functions unconditionally fail to initialize the variable.
493349cc55cSDimitry Andric // If they call other functions that have more paths within them,
494349cc55cSDimitry Andric // this suppression would still apply when we visit these inner functions.
495349cc55cSDimitry Andric // One common example of a standard function that doesn't ever initialize
496349cc55cSDimitry Andric // its out parameter is operator placement new; it's up to the follow-up
497349cc55cSDimitry Andric // constructor (if any) to initialize the memory.
498349cc55cSDimitry Andric if (!N->getStackFrame()->getCFG()->isLinear()) {
499349cc55cSDimitry Andric static int i = 0;
500349cc55cSDimitry Andric R.markInvalid(&i, nullptr);
501349cc55cSDimitry Andric }
502349cc55cSDimitry Andric return nullptr;
503349cc55cSDimitry Andric }
504349cc55cSDimitry Andric
505349cc55cSDimitry Andric if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
506349cc55cSDimitry Andric // If we failed to construct a piece for self, we still want to check
507349cc55cSDimitry Andric // whether the entity of interest is in a parameter.
508349cc55cSDimitry Andric if (PathDiagnosticPieceRef Piece = maybeEmitNoteForObjCSelf(R, *MC, N))
509349cc55cSDimitry Andric return Piece;
510349cc55cSDimitry Andric }
511349cc55cSDimitry Andric
512349cc55cSDimitry Andric if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
513349cc55cSDimitry Andric // Do not generate diagnostics for not modified parameters in
514349cc55cSDimitry Andric // constructors.
515349cc55cSDimitry Andric return maybeEmitNoteForCXXThis(R, *CCall, N);
516349cc55cSDimitry Andric }
517349cc55cSDimitry Andric
518349cc55cSDimitry Andric return maybeEmitNoteForParameters(R, *Call, N);
519349cc55cSDimitry Andric }
520349cc55cSDimitry Andric
521349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
5220b57cec5SDimitry Andric // Implementation of NoStoreFuncVisitor.
5230b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
5240b57cec5SDimitry Andric
5250b57cec5SDimitry Andric namespace {
5260b57cec5SDimitry Andric /// Put a diagnostic on return statement of all inlined functions
5270b57cec5SDimitry Andric /// for which the region of interest \p RegionOfInterest was passed into,
5280b57cec5SDimitry Andric /// but not written inside, and it has caused an undefined read or a null
5290b57cec5SDimitry Andric /// pointer dereference outside.
530349cc55cSDimitry Andric class NoStoreFuncVisitor final : public NoStateChangeFuncVisitor {
5310b57cec5SDimitry Andric const SubRegion *RegionOfInterest;
5320b57cec5SDimitry Andric MemRegionManager &MmrMgr;
5330b57cec5SDimitry Andric const SourceManager &SM;
5340b57cec5SDimitry Andric const PrintingPolicy &PP;
5350b57cec5SDimitry Andric
5360b57cec5SDimitry Andric /// Recursion limit for dereferencing fields when looking for the
5370b57cec5SDimitry Andric /// region of interest.
5380b57cec5SDimitry Andric /// The limit of two indicates that we will dereference fields only once.
5390b57cec5SDimitry Andric static const unsigned DEREFERENCE_LIMIT = 2;
5400b57cec5SDimitry Andric
5410b57cec5SDimitry Andric using RegionVector = SmallVector<const MemRegion *, 5>;
5420b57cec5SDimitry Andric
5430b57cec5SDimitry Andric public:
NoStoreFuncVisitor(const SubRegion * R,bugreporter::TrackingKind TKind)544a7dea167SDimitry Andric NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind)
545349cc55cSDimitry Andric : NoStateChangeFuncVisitor(TKind), RegionOfInterest(R),
546349cc55cSDimitry Andric MmrMgr(R->getMemRegionManager()),
5470b57cec5SDimitry Andric SM(MmrMgr.getContext().getSourceManager()),
548349cc55cSDimitry Andric PP(MmrMgr.getContext().getPrintingPolicy()) {}
5490b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const5500b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override {
5510b57cec5SDimitry Andric static int Tag = 0;
5520b57cec5SDimitry Andric ID.AddPointer(&Tag);
5530b57cec5SDimitry Andric ID.AddPointer(RegionOfInterest);
5540b57cec5SDimitry Andric }
5550b57cec5SDimitry Andric
5560b57cec5SDimitry Andric private:
557349cc55cSDimitry Andric /// \return Whether \c RegionOfInterest was modified at \p CurrN compared to
558349cc55cSDimitry Andric /// the value it holds in \p CallExitBeginN.
559972a253aSDimitry Andric bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
560349cc55cSDimitry Andric const ExplodedNode *CallExitBeginN) override;
561349cc55cSDimitry Andric
5620b57cec5SDimitry Andric /// Attempts to find the region of interest in a given record decl,
5630b57cec5SDimitry Andric /// by either following the base classes or fields.
5640b57cec5SDimitry Andric /// Dereferences fields up to a given recursion limit.
5650b57cec5SDimitry Andric /// Note that \p Vec is passed by value, leading to quadratic copying cost,
5660b57cec5SDimitry Andric /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
567bdd1243dSDimitry Andric /// \return A chain fields leading to the region of interest or std::nullopt.
568bdd1243dSDimitry Andric const std::optional<RegionVector>
5690b57cec5SDimitry Andric findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State,
5700b57cec5SDimitry Andric const MemRegion *R, const RegionVector &Vec = {},
5710b57cec5SDimitry Andric int depth = 0);
5720b57cec5SDimitry Andric
573349cc55cSDimitry Andric // Region of interest corresponds to an IVar, exiting a method
574349cc55cSDimitry Andric // which could have written into that IVar, but did not.
575972a253aSDimitry Andric PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
576349cc55cSDimitry Andric const ObjCMethodCall &Call,
577972a253aSDimitry Andric const ExplodedNode *N) final;
5780b57cec5SDimitry Andric
579972a253aSDimitry Andric PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
580349cc55cSDimitry Andric const CXXConstructorCall &Call,
581972a253aSDimitry Andric const ExplodedNode *N) final;
582349cc55cSDimitry Andric
583972a253aSDimitry Andric PathDiagnosticPieceRef
584349cc55cSDimitry Andric maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
585972a253aSDimitry Andric const ExplodedNode *N) final;
5860b57cec5SDimitry Andric
5870b57cec5SDimitry Andric /// Consume the information on the no-store stack frame in order to
5880b57cec5SDimitry Andric /// either emit a note or suppress the report enirely.
5890b57cec5SDimitry Andric /// \return Diagnostics piece for region not modified in the current function,
5900b57cec5SDimitry Andric /// if it decides to emit one.
591a7dea167SDimitry Andric PathDiagnosticPieceRef
592a7dea167SDimitry Andric maybeEmitNote(PathSensitiveBugReport &R, const CallEvent &Call,
593a7dea167SDimitry Andric const ExplodedNode *N, const RegionVector &FieldChain,
594a7dea167SDimitry Andric const MemRegion *MatchedRegion, StringRef FirstElement,
595a7dea167SDimitry Andric bool FirstIsReferenceType, unsigned IndirectionLevel);
5960b57cec5SDimitry Andric
597349cc55cSDimitry Andric bool prettyPrintRegionName(const RegionVector &FieldChain,
5980b57cec5SDimitry Andric const MemRegion *MatchedRegion,
599349cc55cSDimitry Andric StringRef FirstElement, bool FirstIsReferenceType,
600349cc55cSDimitry Andric unsigned IndirectionLevel,
6010b57cec5SDimitry Andric llvm::raw_svector_ostream &os);
6020b57cec5SDimitry Andric
603349cc55cSDimitry Andric StringRef prettyPrintFirstElement(StringRef FirstElement,
6040b57cec5SDimitry Andric bool MoreItemsExpected,
6050b57cec5SDimitry Andric int IndirectionLevel,
6060b57cec5SDimitry Andric llvm::raw_svector_ostream &os);
6070b57cec5SDimitry Andric };
608349cc55cSDimitry Andric } // namespace
6090b57cec5SDimitry Andric
6100b57cec5SDimitry Andric /// \return Whether the method declaration \p Parent
6110b57cec5SDimitry Andric /// syntactically has a binary operation writing into the ivar \p Ivar.
potentiallyWritesIntoIvar(const Decl * Parent,const ObjCIvarDecl * Ivar)6120b57cec5SDimitry Andric static bool potentiallyWritesIntoIvar(const Decl *Parent,
6130b57cec5SDimitry Andric const ObjCIvarDecl *Ivar) {
6140b57cec5SDimitry Andric using namespace ast_matchers;
6150b57cec5SDimitry Andric const char *IvarBind = "Ivar";
6160b57cec5SDimitry Andric if (!Parent || !Parent->hasBody())
6170b57cec5SDimitry Andric return false;
6180b57cec5SDimitry Andric StatementMatcher WriteIntoIvarM = binaryOperator(
6190b57cec5SDimitry Andric hasOperatorName("="),
6200b57cec5SDimitry Andric hasLHS(ignoringParenImpCasts(
6210b57cec5SDimitry Andric objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind))));
6220b57cec5SDimitry Andric StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));
6230b57cec5SDimitry Andric auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext());
6240b57cec5SDimitry Andric for (BoundNodes &Match : Matches) {
6250b57cec5SDimitry Andric auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind);
6260b57cec5SDimitry Andric if (IvarRef->isFreeIvar())
6270b57cec5SDimitry Andric return true;
6280b57cec5SDimitry Andric
6290b57cec5SDimitry Andric const Expr *Base = IvarRef->getBase();
6300b57cec5SDimitry Andric if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base))
6310b57cec5SDimitry Andric Base = ICE->getSubExpr();
6320b57cec5SDimitry Andric
6330b57cec5SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(Base))
6340b57cec5SDimitry Andric if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
6355f757f3fSDimitry Andric if (ID->getParameterKind() == ImplicitParamKind::ObjCSelf)
6360b57cec5SDimitry Andric return true;
6370b57cec5SDimitry Andric
6380b57cec5SDimitry Andric return false;
6390b57cec5SDimitry Andric }
6400b57cec5SDimitry Andric return false;
6410b57cec5SDimitry Andric }
6420b57cec5SDimitry Andric
6430b57cec5SDimitry Andric /// Attempts to find the region of interest in a given CXX decl,
6440b57cec5SDimitry Andric /// by either following the base classes or fields.
6450b57cec5SDimitry Andric /// Dereferences fields up to a given recursion limit.
6460b57cec5SDimitry Andric /// Note that \p Vec is passed by value, leading to quadratic copying cost,
6470b57cec5SDimitry Andric /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
648bdd1243dSDimitry Andric /// \return A chain fields leading to the region of interest or std::nullopt.
649bdd1243dSDimitry Andric const std::optional<NoStoreFuncVisitor::RegionVector>
findRegionOfInterestInRecord(const RecordDecl * RD,ProgramStateRef State,const MemRegion * R,const NoStoreFuncVisitor::RegionVector & Vec,int depth)6500b57cec5SDimitry Andric NoStoreFuncVisitor::findRegionOfInterestInRecord(
6510b57cec5SDimitry Andric const RecordDecl *RD, ProgramStateRef State, const MemRegion *R,
6520b57cec5SDimitry Andric const NoStoreFuncVisitor::RegionVector &Vec /* = {} */,
6530b57cec5SDimitry Andric int depth /* = 0 */) {
6540b57cec5SDimitry Andric
6550b57cec5SDimitry Andric if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
656bdd1243dSDimitry Andric return std::nullopt;
6570b57cec5SDimitry Andric
6580b57cec5SDimitry Andric if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
6590b57cec5SDimitry Andric if (!RDX->hasDefinition())
660bdd1243dSDimitry Andric return std::nullopt;
6610b57cec5SDimitry Andric
6620b57cec5SDimitry Andric // Recursively examine the base classes.
6630b57cec5SDimitry Andric // Note that following base classes does not increase the recursion depth.
6640b57cec5SDimitry Andric if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
665480093f4SDimitry Andric for (const auto &II : RDX->bases())
6660b57cec5SDimitry Andric if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
667bdd1243dSDimitry Andric if (std::optional<RegionVector> Out =
6680b57cec5SDimitry Andric findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
6690b57cec5SDimitry Andric return Out;
6700b57cec5SDimitry Andric
6710b57cec5SDimitry Andric for (const FieldDecl *I : RD->fields()) {
6720b57cec5SDimitry Andric QualType FT = I->getType();
6730b57cec5SDimitry Andric const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R));
6740b57cec5SDimitry Andric const SVal V = State->getSVal(FR);
6750b57cec5SDimitry Andric const MemRegion *VR = V.getAsRegion();
6760b57cec5SDimitry Andric
6770b57cec5SDimitry Andric RegionVector VecF = Vec;
6780b57cec5SDimitry Andric VecF.push_back(FR);
6790b57cec5SDimitry Andric
6800b57cec5SDimitry Andric if (RegionOfInterest == VR)
6810b57cec5SDimitry Andric return VecF;
6820b57cec5SDimitry Andric
6830b57cec5SDimitry Andric if (const RecordDecl *RRD = FT->getAsRecordDecl())
6840b57cec5SDimitry Andric if (auto Out =
6850b57cec5SDimitry Andric findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1))
6860b57cec5SDimitry Andric return Out;
6870b57cec5SDimitry Andric
6880b57cec5SDimitry Andric QualType PT = FT->getPointeeType();
6890b57cec5SDimitry Andric if (PT.isNull() || PT->isVoidType() || !VR)
6900b57cec5SDimitry Andric continue;
6910b57cec5SDimitry Andric
6920b57cec5SDimitry Andric if (const RecordDecl *RRD = PT->getAsRecordDecl())
693bdd1243dSDimitry Andric if (std::optional<RegionVector> Out =
6940b57cec5SDimitry Andric findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
6950b57cec5SDimitry Andric return Out;
6960b57cec5SDimitry Andric }
6970b57cec5SDimitry Andric
698bdd1243dSDimitry Andric return std::nullopt;
6990b57cec5SDimitry Andric }
7000b57cec5SDimitry Andric
701a7dea167SDimitry Andric PathDiagnosticPieceRef
maybeEmitNoteForObjCSelf(PathSensitiveBugReport & R,const ObjCMethodCall & Call,const ExplodedNode * N)702349cc55cSDimitry Andric NoStoreFuncVisitor::maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
703349cc55cSDimitry Andric const ObjCMethodCall &Call,
704349cc55cSDimitry Andric const ExplodedNode *N) {
7050b57cec5SDimitry Andric if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
706349cc55cSDimitry Andric const MemRegion *SelfRegion = Call.getReceiverSVal().getAsRegion();
7070b57cec5SDimitry Andric if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
708349cc55cSDimitry Andric potentiallyWritesIntoIvar(Call.getRuntimeDefinition().getDecl(),
7090b57cec5SDimitry Andric IvarR->getDecl()))
710349cc55cSDimitry Andric return maybeEmitNote(R, Call, N, {}, SelfRegion, "self",
7110b57cec5SDimitry Andric /*FirstIsReferenceType=*/false, 1);
7120b57cec5SDimitry Andric }
713349cc55cSDimitry Andric return nullptr;
7140b57cec5SDimitry Andric }
7150b57cec5SDimitry Andric
716349cc55cSDimitry Andric PathDiagnosticPieceRef
maybeEmitNoteForCXXThis(PathSensitiveBugReport & R,const CXXConstructorCall & Call,const ExplodedNode * N)717349cc55cSDimitry Andric NoStoreFuncVisitor::maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
718349cc55cSDimitry Andric const CXXConstructorCall &Call,
719349cc55cSDimitry Andric const ExplodedNode *N) {
720349cc55cSDimitry Andric const MemRegion *ThisR = Call.getCXXThisVal().getAsRegion();
721349cc55cSDimitry Andric if (RegionOfInterest->isSubRegionOf(ThisR) && !Call.getDecl()->isImplicit())
722349cc55cSDimitry Andric return maybeEmitNote(R, Call, N, {}, ThisR, "this",
7230b57cec5SDimitry Andric /*FirstIsReferenceType=*/false, 1);
7240b57cec5SDimitry Andric
7250b57cec5SDimitry Andric // Do not generate diagnostics for not modified parameters in
7260b57cec5SDimitry Andric // constructors.
7270b57cec5SDimitry Andric return nullptr;
7280b57cec5SDimitry Andric }
7290b57cec5SDimitry Andric
730349cc55cSDimitry Andric /// \return whether \p Ty points to a const type, or is a const reference.
isPointerToConst(QualType Ty)731349cc55cSDimitry Andric static bool isPointerToConst(QualType Ty) {
732349cc55cSDimitry Andric return !Ty->getPointeeType().isNull() &&
733349cc55cSDimitry Andric Ty->getPointeeType().getCanonicalType().isConstQualified();
734349cc55cSDimitry Andric }
735349cc55cSDimitry Andric
maybeEmitNoteForParameters(PathSensitiveBugReport & R,const CallEvent & Call,const ExplodedNode * N)736349cc55cSDimitry Andric PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters(
737349cc55cSDimitry Andric PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) {
738349cc55cSDimitry Andric ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
739349cc55cSDimitry Andric for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
740349cc55cSDimitry Andric const ParmVarDecl *PVD = Parameters[I];
741349cc55cSDimitry Andric SVal V = Call.getArgSVal(I);
7420b57cec5SDimitry Andric bool ParamIsReferenceType = PVD->getType()->isReferenceType();
7430b57cec5SDimitry Andric std::string ParamName = PVD->getNameAsString();
7440b57cec5SDimitry Andric
745349cc55cSDimitry Andric unsigned IndirectionLevel = 1;
7460b57cec5SDimitry Andric QualType T = PVD->getType();
7470b57cec5SDimitry Andric while (const MemRegion *MR = V.getAsRegion()) {
7480b57cec5SDimitry Andric if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T))
749349cc55cSDimitry Andric return maybeEmitNote(R, Call, N, {}, MR, ParamName,
7500b57cec5SDimitry Andric ParamIsReferenceType, IndirectionLevel);
7510b57cec5SDimitry Andric
7520b57cec5SDimitry Andric QualType PT = T->getPointeeType();
7530b57cec5SDimitry Andric if (PT.isNull() || PT->isVoidType())
7540b57cec5SDimitry Andric break;
7550b57cec5SDimitry Andric
756349cc55cSDimitry Andric ProgramStateRef State = N->getState();
757349cc55cSDimitry Andric
7580b57cec5SDimitry Andric if (const RecordDecl *RD = PT->getAsRecordDecl())
759bdd1243dSDimitry Andric if (std::optional<RegionVector> P =
7600b57cec5SDimitry Andric findRegionOfInterestInRecord(RD, State, MR))
761349cc55cSDimitry Andric return maybeEmitNote(R, Call, N, *P, RegionOfInterest, ParamName,
7620b57cec5SDimitry Andric ParamIsReferenceType, IndirectionLevel);
7630b57cec5SDimitry Andric
7640b57cec5SDimitry Andric V = State->getSVal(MR, PT);
7650b57cec5SDimitry Andric T = PT;
7660b57cec5SDimitry Andric IndirectionLevel++;
7670b57cec5SDimitry Andric }
7680b57cec5SDimitry Andric }
7690b57cec5SDimitry Andric
7700b57cec5SDimitry Andric return nullptr;
7710b57cec5SDimitry Andric }
7720b57cec5SDimitry Andric
wasModifiedBeforeCallExit(const ExplodedNode * CurrN,const ExplodedNode * CallExitBeginN)773349cc55cSDimitry Andric bool NoStoreFuncVisitor::wasModifiedBeforeCallExit(
774349cc55cSDimitry Andric const ExplodedNode *CurrN, const ExplodedNode *CallExitBeginN) {
775349cc55cSDimitry Andric return ::wasRegionOfInterestModifiedAt(
776349cc55cSDimitry Andric RegionOfInterest, CurrN,
777349cc55cSDimitry Andric CallExitBeginN->getState()->getSVal(RegionOfInterest));
7780b57cec5SDimitry Andric }
7790b57cec5SDimitry Andric
780a7dea167SDimitry Andric static llvm::StringLiteral WillBeUsedForACondition =
781a7dea167SDimitry Andric ", which participates in a condition later";
782a7dea167SDimitry Andric
maybeEmitNote(PathSensitiveBugReport & R,const CallEvent & Call,const ExplodedNode * N,const RegionVector & FieldChain,const MemRegion * MatchedRegion,StringRef FirstElement,bool FirstIsReferenceType,unsigned IndirectionLevel)783a7dea167SDimitry Andric PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
784a7dea167SDimitry Andric PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N,
7850b57cec5SDimitry Andric const RegionVector &FieldChain, const MemRegion *MatchedRegion,
7860b57cec5SDimitry Andric StringRef FirstElement, bool FirstIsReferenceType,
7870b57cec5SDimitry Andric unsigned IndirectionLevel) {
7880b57cec5SDimitry Andric
7890b57cec5SDimitry Andric PathDiagnosticLocation L =
7900b57cec5SDimitry Andric PathDiagnosticLocation::create(N->getLocation(), SM);
7910b57cec5SDimitry Andric
7920b57cec5SDimitry Andric // For now this shouldn't trigger, but once it does (as we add more
7930b57cec5SDimitry Andric // functions to the body farm), we'll need to decide if these reports
7940b57cec5SDimitry Andric // are worth suppressing as well.
7950b57cec5SDimitry Andric if (!L.hasValidLocation())
7960b57cec5SDimitry Andric return nullptr;
7970b57cec5SDimitry Andric
7980b57cec5SDimitry Andric SmallString<256> sbuf;
7990b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf);
8000b57cec5SDimitry Andric os << "Returning without writing to '";
8010b57cec5SDimitry Andric
8020b57cec5SDimitry Andric // Do not generate the note if failed to pretty-print.
803349cc55cSDimitry Andric if (!prettyPrintRegionName(FieldChain, MatchedRegion, FirstElement,
804349cc55cSDimitry Andric FirstIsReferenceType, IndirectionLevel, os))
8050b57cec5SDimitry Andric return nullptr;
8060b57cec5SDimitry Andric
8070b57cec5SDimitry Andric os << "'";
808a7dea167SDimitry Andric if (TKind == bugreporter::TrackingKind::Condition)
809a7dea167SDimitry Andric os << WillBeUsedForACondition;
8100b57cec5SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
8110b57cec5SDimitry Andric }
8120b57cec5SDimitry Andric
prettyPrintRegionName(const RegionVector & FieldChain,const MemRegion * MatchedRegion,StringRef FirstElement,bool FirstIsReferenceType,unsigned IndirectionLevel,llvm::raw_svector_ostream & os)813349cc55cSDimitry Andric bool NoStoreFuncVisitor::prettyPrintRegionName(const RegionVector &FieldChain,
8140b57cec5SDimitry Andric const MemRegion *MatchedRegion,
815349cc55cSDimitry Andric StringRef FirstElement,
816349cc55cSDimitry Andric bool FirstIsReferenceType,
817349cc55cSDimitry Andric unsigned IndirectionLevel,
8180b57cec5SDimitry Andric llvm::raw_svector_ostream &os) {
8190b57cec5SDimitry Andric
8200b57cec5SDimitry Andric if (FirstIsReferenceType)
8210b57cec5SDimitry Andric IndirectionLevel--;
8220b57cec5SDimitry Andric
8230b57cec5SDimitry Andric RegionVector RegionSequence;
8240b57cec5SDimitry Andric
8250b57cec5SDimitry Andric // Add the regions in the reverse order, then reverse the resulting array.
8260b57cec5SDimitry Andric assert(RegionOfInterest->isSubRegionOf(MatchedRegion));
8270b57cec5SDimitry Andric const MemRegion *R = RegionOfInterest;
8280b57cec5SDimitry Andric while (R != MatchedRegion) {
8290b57cec5SDimitry Andric RegionSequence.push_back(R);
8300b57cec5SDimitry Andric R = cast<SubRegion>(R)->getSuperRegion();
8310b57cec5SDimitry Andric }
8320b57cec5SDimitry Andric std::reverse(RegionSequence.begin(), RegionSequence.end());
8330b57cec5SDimitry Andric RegionSequence.append(FieldChain.begin(), FieldChain.end());
8340b57cec5SDimitry Andric
8350b57cec5SDimitry Andric StringRef Sep;
8360b57cec5SDimitry Andric for (const MemRegion *R : RegionSequence) {
8370b57cec5SDimitry Andric
8380b57cec5SDimitry Andric // Just keep going up to the base region.
8390b57cec5SDimitry Andric // Element regions may appear due to casts.
840349cc55cSDimitry Andric if (isa<CXXBaseObjectRegion, CXXTempObjectRegion>(R))
8410b57cec5SDimitry Andric continue;
8420b57cec5SDimitry Andric
8430b57cec5SDimitry Andric if (Sep.empty())
8440b57cec5SDimitry Andric Sep = prettyPrintFirstElement(FirstElement,
8450b57cec5SDimitry Andric /*MoreItemsExpected=*/true,
8460b57cec5SDimitry Andric IndirectionLevel, os);
8470b57cec5SDimitry Andric
8480b57cec5SDimitry Andric os << Sep;
8490b57cec5SDimitry Andric
8500b57cec5SDimitry Andric // Can only reasonably pretty-print DeclRegions.
8510b57cec5SDimitry Andric if (!isa<DeclRegion>(R))
8520b57cec5SDimitry Andric return false;
8530b57cec5SDimitry Andric
8540b57cec5SDimitry Andric const auto *DR = cast<DeclRegion>(R);
8550b57cec5SDimitry Andric Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";
8560b57cec5SDimitry Andric DR->getDecl()->getDeclName().print(os, PP);
8570b57cec5SDimitry Andric }
8580b57cec5SDimitry Andric
8590b57cec5SDimitry Andric if (Sep.empty())
8600b57cec5SDimitry Andric prettyPrintFirstElement(FirstElement,
8610b57cec5SDimitry Andric /*MoreItemsExpected=*/false, IndirectionLevel, os);
8620b57cec5SDimitry Andric return true;
8630b57cec5SDimitry Andric }
8640b57cec5SDimitry Andric
prettyPrintFirstElement(StringRef FirstElement,bool MoreItemsExpected,int IndirectionLevel,llvm::raw_svector_ostream & os)8650b57cec5SDimitry Andric StringRef NoStoreFuncVisitor::prettyPrintFirstElement(
8660b57cec5SDimitry Andric StringRef FirstElement, bool MoreItemsExpected, int IndirectionLevel,
8670b57cec5SDimitry Andric llvm::raw_svector_ostream &os) {
8680b57cec5SDimitry Andric StringRef Out = ".";
8690b57cec5SDimitry Andric
8700b57cec5SDimitry Andric if (IndirectionLevel > 0 && MoreItemsExpected) {
8710b57cec5SDimitry Andric IndirectionLevel--;
8720b57cec5SDimitry Andric Out = "->";
8730b57cec5SDimitry Andric }
8740b57cec5SDimitry Andric
8750b57cec5SDimitry Andric if (IndirectionLevel > 0 && MoreItemsExpected)
8760b57cec5SDimitry Andric os << "(";
8770b57cec5SDimitry Andric
8780b57cec5SDimitry Andric for (int i = 0; i < IndirectionLevel; i++)
8790b57cec5SDimitry Andric os << "*";
8800b57cec5SDimitry Andric os << FirstElement;
8810b57cec5SDimitry Andric
8820b57cec5SDimitry Andric if (IndirectionLevel > 0 && MoreItemsExpected)
8830b57cec5SDimitry Andric os << ")";
8840b57cec5SDimitry Andric
8850b57cec5SDimitry Andric return Out;
8860b57cec5SDimitry Andric }
8870b57cec5SDimitry Andric
8880b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8890b57cec5SDimitry Andric // Implementation of MacroNullReturnSuppressionVisitor.
8900b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8910b57cec5SDimitry Andric
8920b57cec5SDimitry Andric namespace {
8930b57cec5SDimitry Andric
8940b57cec5SDimitry Andric /// Suppress null-pointer-dereference bugs where dereferenced null was returned
8950b57cec5SDimitry Andric /// the macro.
8960b57cec5SDimitry Andric class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor {
8970b57cec5SDimitry Andric const SubRegion *RegionOfInterest;
8980b57cec5SDimitry Andric const SVal ValueAtDereference;
8990b57cec5SDimitry Andric
9000b57cec5SDimitry Andric // Do not invalidate the reports where the value was modified
9010b57cec5SDimitry Andric // after it got assigned to from the macro.
9020b57cec5SDimitry Andric bool WasModified = false;
9030b57cec5SDimitry Andric
9040b57cec5SDimitry Andric public:
MacroNullReturnSuppressionVisitor(const SubRegion * R,const SVal V)905a7dea167SDimitry Andric MacroNullReturnSuppressionVisitor(const SubRegion *R, const SVal V)
906a7dea167SDimitry Andric : RegionOfInterest(R), ValueAtDereference(V) {}
9070b57cec5SDimitry Andric
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)908a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
9090b57cec5SDimitry Andric BugReporterContext &BRC,
910a7dea167SDimitry Andric PathSensitiveBugReport &BR) override {
9110b57cec5SDimitry Andric if (WasModified)
9120b57cec5SDimitry Andric return nullptr;
9130b57cec5SDimitry Andric
9140b57cec5SDimitry Andric auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
9150b57cec5SDimitry Andric if (!BugPoint)
9160b57cec5SDimitry Andric return nullptr;
9170b57cec5SDimitry Andric
9180b57cec5SDimitry Andric const SourceManager &SMgr = BRC.getSourceManager();
9190b57cec5SDimitry Andric if (auto Loc = matchAssignment(N)) {
9200b57cec5SDimitry Andric if (isFunctionMacroExpansion(*Loc, SMgr)) {
9215ffd83dbSDimitry Andric std::string MacroName = std::string(getMacroName(*Loc, BRC));
9220b57cec5SDimitry Andric SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
9230b57cec5SDimitry Andric if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName)
9240b57cec5SDimitry Andric BR.markInvalid(getTag(), MacroName.c_str());
9250b57cec5SDimitry Andric }
9260b57cec5SDimitry Andric }
9270b57cec5SDimitry Andric
9280b57cec5SDimitry Andric if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtDereference))
9290b57cec5SDimitry Andric WasModified = true;
9300b57cec5SDimitry Andric
9310b57cec5SDimitry Andric return nullptr;
9320b57cec5SDimitry Andric }
9330b57cec5SDimitry Andric
addMacroVisitorIfNecessary(const ExplodedNode * N,const MemRegion * R,bool EnableNullFPSuppression,PathSensitiveBugReport & BR,const SVal V)9340b57cec5SDimitry Andric static void addMacroVisitorIfNecessary(
9350b57cec5SDimitry Andric const ExplodedNode *N, const MemRegion *R,
936a7dea167SDimitry Andric bool EnableNullFPSuppression, PathSensitiveBugReport &BR,
9370b57cec5SDimitry Andric const SVal V) {
9380b57cec5SDimitry Andric AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
939fe6060f1SDimitry Andric if (EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths &&
94081ad6265SDimitry Andric isa<Loc>(V))
941fe6060f1SDimitry Andric BR.addVisitor<MacroNullReturnSuppressionVisitor>(R->getAs<SubRegion>(),
942fe6060f1SDimitry Andric V);
9430b57cec5SDimitry Andric }
9440b57cec5SDimitry Andric
getTag() const9450b57cec5SDimitry Andric void* getTag() const {
9460b57cec5SDimitry Andric static int Tag = 0;
9470b57cec5SDimitry Andric return static_cast<void *>(&Tag);
9480b57cec5SDimitry Andric }
9490b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const9500b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override {
9510b57cec5SDimitry Andric ID.AddPointer(getTag());
9520b57cec5SDimitry Andric }
9530b57cec5SDimitry Andric
9540b57cec5SDimitry Andric private:
9550b57cec5SDimitry Andric /// \return Source location of right hand side of an assignment
9560b57cec5SDimitry Andric /// into \c RegionOfInterest, empty optional if none found.
matchAssignment(const ExplodedNode * N)957bdd1243dSDimitry Andric std::optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
958a7dea167SDimitry Andric const Stmt *S = N->getStmtForDiagnostics();
9590b57cec5SDimitry Andric ProgramStateRef State = N->getState();
9600b57cec5SDimitry Andric auto *LCtx = N->getLocationContext();
9610b57cec5SDimitry Andric if (!S)
962bdd1243dSDimitry Andric return std::nullopt;
9630b57cec5SDimitry Andric
9640b57cec5SDimitry Andric if (const auto *DS = dyn_cast<DeclStmt>(S)) {
9650b57cec5SDimitry Andric if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
9660b57cec5SDimitry Andric if (const Expr *RHS = VD->getInit())
9670b57cec5SDimitry Andric if (RegionOfInterest->isSubRegionOf(
9680b57cec5SDimitry Andric State->getLValue(VD, LCtx).getAsRegion()))
9690b57cec5SDimitry Andric return RHS->getBeginLoc();
9700b57cec5SDimitry Andric } else if (const auto *BO = dyn_cast<BinaryOperator>(S)) {
9710b57cec5SDimitry Andric const MemRegion *R = N->getSVal(BO->getLHS()).getAsRegion();
9720b57cec5SDimitry Andric const Expr *RHS = BO->getRHS();
9730b57cec5SDimitry Andric if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) {
9740b57cec5SDimitry Andric return RHS->getBeginLoc();
9750b57cec5SDimitry Andric }
9760b57cec5SDimitry Andric }
977bdd1243dSDimitry Andric return std::nullopt;
9780b57cec5SDimitry Andric }
9790b57cec5SDimitry Andric };
9800b57cec5SDimitry Andric
9810b57cec5SDimitry Andric } // end of anonymous namespace
9820b57cec5SDimitry Andric
9830b57cec5SDimitry Andric namespace {
9840b57cec5SDimitry Andric
9850b57cec5SDimitry Andric /// Emits an extra note at the return statement of an interesting stack frame.
9860b57cec5SDimitry Andric ///
9870b57cec5SDimitry Andric /// The returned value is marked as an interesting value, and if it's null,
9880b57cec5SDimitry Andric /// adds a visitor to track where it became null.
9890b57cec5SDimitry Andric ///
9900b57cec5SDimitry Andric /// This visitor is intended to be used when another visitor discovers that an
9910b57cec5SDimitry Andric /// interesting value comes from an inlined function call.
992fe6060f1SDimitry Andric class ReturnVisitor : public TrackingBugReporterVisitor {
993a7dea167SDimitry Andric const StackFrameContext *CalleeSFC;
9940b57cec5SDimitry Andric enum {
9950b57cec5SDimitry Andric Initial,
9960b57cec5SDimitry Andric MaybeUnsuppress,
9970b57cec5SDimitry Andric Satisfied
9980b57cec5SDimitry Andric } Mode = Initial;
9990b57cec5SDimitry Andric
10000b57cec5SDimitry Andric bool EnableNullFPSuppression;
10010b57cec5SDimitry Andric bool ShouldInvalidate = true;
10020b57cec5SDimitry Andric AnalyzerOptions& Options;
1003a7dea167SDimitry Andric bugreporter::TrackingKind TKind;
10040b57cec5SDimitry Andric
10050b57cec5SDimitry Andric public:
ReturnVisitor(TrackerRef ParentTracker,const StackFrameContext * Frame,bool Suppressed,AnalyzerOptions & Options,bugreporter::TrackingKind TKind)1006fe6060f1SDimitry Andric ReturnVisitor(TrackerRef ParentTracker, const StackFrameContext *Frame,
1007fe6060f1SDimitry Andric bool Suppressed, AnalyzerOptions &Options,
1008fe6060f1SDimitry Andric bugreporter::TrackingKind TKind)
1009fe6060f1SDimitry Andric : TrackingBugReporterVisitor(ParentTracker), CalleeSFC(Frame),
1010fe6060f1SDimitry Andric EnableNullFPSuppression(Suppressed), Options(Options), TKind(TKind) {}
10110b57cec5SDimitry Andric
getTag()10120b57cec5SDimitry Andric static void *getTag() {
10130b57cec5SDimitry Andric static int Tag = 0;
10140b57cec5SDimitry Andric return static_cast<void *>(&Tag);
10150b57cec5SDimitry Andric }
10160b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const10170b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override {
10180b57cec5SDimitry Andric ID.AddPointer(ReturnVisitor::getTag());
1019a7dea167SDimitry Andric ID.AddPointer(CalleeSFC);
10200b57cec5SDimitry Andric ID.AddBoolean(EnableNullFPSuppression);
10210b57cec5SDimitry Andric }
10220b57cec5SDimitry Andric
visitNodeInitial(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)1023a7dea167SDimitry Andric PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N,
1024a7dea167SDimitry Andric BugReporterContext &BRC,
1025a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
10260b57cec5SDimitry Andric // Only print a message at the interesting return statement.
1027a7dea167SDimitry Andric if (N->getLocationContext() != CalleeSFC)
10280b57cec5SDimitry Andric return nullptr;
10290b57cec5SDimitry Andric
1030bdd1243dSDimitry Andric std::optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
10310b57cec5SDimitry Andric if (!SP)
10320b57cec5SDimitry Andric return nullptr;
10330b57cec5SDimitry Andric
10340b57cec5SDimitry Andric const auto *Ret = dyn_cast<ReturnStmt>(SP->getStmt());
10350b57cec5SDimitry Andric if (!Ret)
10360b57cec5SDimitry Andric return nullptr;
10370b57cec5SDimitry Andric
10380b57cec5SDimitry Andric // Okay, we're at the right return statement, but do we have the return
10390b57cec5SDimitry Andric // value available?
10400b57cec5SDimitry Andric ProgramStateRef State = N->getState();
1041a7dea167SDimitry Andric SVal V = State->getSVal(Ret, CalleeSFC);
10420b57cec5SDimitry Andric if (V.isUnknownOrUndef())
10430b57cec5SDimitry Andric return nullptr;
10440b57cec5SDimitry Andric
10450b57cec5SDimitry Andric // Don't print any more notes after this one.
10460b57cec5SDimitry Andric Mode = Satisfied;
10470b57cec5SDimitry Andric
10480b57cec5SDimitry Andric const Expr *RetE = Ret->getRetValue();
10490b57cec5SDimitry Andric assert(RetE && "Tracking a return value for a void function");
10500b57cec5SDimitry Andric
10510b57cec5SDimitry Andric // Handle cases where a reference is returned and then immediately used.
1052bdd1243dSDimitry Andric std::optional<Loc> LValue;
10530b57cec5SDimitry Andric if (RetE->isGLValue()) {
10540b57cec5SDimitry Andric if ((LValue = V.getAs<Loc>())) {
10550b57cec5SDimitry Andric SVal RValue = State->getRawSVal(*LValue, RetE->getType());
105681ad6265SDimitry Andric if (isa<DefinedSVal>(RValue))
10570b57cec5SDimitry Andric V = RValue;
10580b57cec5SDimitry Andric }
10590b57cec5SDimitry Andric }
10600b57cec5SDimitry Andric
10610b57cec5SDimitry Andric // Ignore aggregate rvalues.
106281ad6265SDimitry Andric if (isa<nonloc::LazyCompoundVal, nonloc::CompoundVal>(V))
10630b57cec5SDimitry Andric return nullptr;
10640b57cec5SDimitry Andric
10650b57cec5SDimitry Andric RetE = RetE->IgnoreParenCasts();
10660b57cec5SDimitry Andric
1067a7dea167SDimitry Andric // Let's track the return value.
1068fe6060f1SDimitry Andric getParentTracker().track(RetE, N, {TKind, EnableNullFPSuppression});
10690b57cec5SDimitry Andric
10700b57cec5SDimitry Andric // Build an appropriate message based on the return value.
10710b57cec5SDimitry Andric SmallString<64> Msg;
10720b57cec5SDimitry Andric llvm::raw_svector_ostream Out(Msg);
10730b57cec5SDimitry Andric
1074a7dea167SDimitry Andric bool WouldEventBeMeaningless = false;
1075a7dea167SDimitry Andric
10760b57cec5SDimitry Andric if (State->isNull(V).isConstrainedTrue()) {
107781ad6265SDimitry Andric if (isa<Loc>(V)) {
10780b57cec5SDimitry Andric
10790b57cec5SDimitry Andric // If we have counter-suppression enabled, make sure we keep visiting
10800b57cec5SDimitry Andric // future nodes. We want to emit a path note as well, in case
10810b57cec5SDimitry Andric // the report is resurrected as valid later on.
10820b57cec5SDimitry Andric if (EnableNullFPSuppression &&
10830b57cec5SDimitry Andric Options.ShouldAvoidSuppressingNullArgumentPaths)
10840b57cec5SDimitry Andric Mode = MaybeUnsuppress;
10850b57cec5SDimitry Andric
10860b57cec5SDimitry Andric if (RetE->getType()->isObjCObjectPointerType()) {
10870b57cec5SDimitry Andric Out << "Returning nil";
10880b57cec5SDimitry Andric } else {
10890b57cec5SDimitry Andric Out << "Returning null pointer";
10900b57cec5SDimitry Andric }
10910b57cec5SDimitry Andric } else {
10920b57cec5SDimitry Andric Out << "Returning zero";
10930b57cec5SDimitry Andric }
10940b57cec5SDimitry Andric
10950b57cec5SDimitry Andric } else {
10960b57cec5SDimitry Andric if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
10970b57cec5SDimitry Andric Out << "Returning the value " << CI->getValue();
10980b57cec5SDimitry Andric } else {
1099a7dea167SDimitry Andric // There is nothing interesting about returning a value, when it is
1100a7dea167SDimitry Andric // plain value without any constraints, and the function is guaranteed
1101a7dea167SDimitry Andric // to return that every time. We could use CFG::isLinear() here, but
1102a7dea167SDimitry Andric // constexpr branches are obvious to the compiler, not necesserily to
1103a7dea167SDimitry Andric // the programmer.
1104a7dea167SDimitry Andric if (N->getCFG().size() == 3)
1105a7dea167SDimitry Andric WouldEventBeMeaningless = true;
1106a7dea167SDimitry Andric
110781ad6265SDimitry Andric Out << (isa<Loc>(V) ? "Returning pointer" : "Returning value");
11080b57cec5SDimitry Andric }
11090b57cec5SDimitry Andric }
11100b57cec5SDimitry Andric
11110b57cec5SDimitry Andric if (LValue) {
11120b57cec5SDimitry Andric if (const MemRegion *MR = LValue->getAsRegion()) {
11130b57cec5SDimitry Andric if (MR->canPrintPretty()) {
11140b57cec5SDimitry Andric Out << " (reference to ";
11150b57cec5SDimitry Andric MR->printPretty(Out);
11160b57cec5SDimitry Andric Out << ")";
11170b57cec5SDimitry Andric }
11180b57cec5SDimitry Andric }
11190b57cec5SDimitry Andric } else {
11200b57cec5SDimitry Andric // FIXME: We should have a more generalized location printing mechanism.
11210b57cec5SDimitry Andric if (const auto *DR = dyn_cast<DeclRefExpr>(RetE))
11220b57cec5SDimitry Andric if (const auto *DD = dyn_cast<DeclaratorDecl>(DR->getDecl()))
11230b57cec5SDimitry Andric Out << " (loaded from '" << *DD << "')";
11240b57cec5SDimitry Andric }
11250b57cec5SDimitry Andric
1126a7dea167SDimitry Andric PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSFC);
11270b57cec5SDimitry Andric if (!L.isValid() || !L.asLocation().isValid())
11280b57cec5SDimitry Andric return nullptr;
11290b57cec5SDimitry Andric
1130a7dea167SDimitry Andric if (TKind == bugreporter::TrackingKind::Condition)
1131a7dea167SDimitry Andric Out << WillBeUsedForACondition;
1132a7dea167SDimitry Andric
1133a7dea167SDimitry Andric auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
1134a7dea167SDimitry Andric
1135a7dea167SDimitry Andric // If we determined that the note is meaningless, make it prunable, and
1136a7dea167SDimitry Andric // don't mark the stackframe interesting.
1137a7dea167SDimitry Andric if (WouldEventBeMeaningless)
1138a7dea167SDimitry Andric EventPiece->setPrunable(true);
1139a7dea167SDimitry Andric else
1140a7dea167SDimitry Andric BR.markInteresting(CalleeSFC);
1141a7dea167SDimitry Andric
1142a7dea167SDimitry Andric return EventPiece;
11430b57cec5SDimitry Andric }
11440b57cec5SDimitry Andric
visitNodeMaybeUnsuppress(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)1145a7dea167SDimitry Andric PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N,
1146a7dea167SDimitry Andric BugReporterContext &BRC,
1147a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
11480b57cec5SDimitry Andric assert(Options.ShouldAvoidSuppressingNullArgumentPaths);
11490b57cec5SDimitry Andric
11500b57cec5SDimitry Andric // Are we at the entry node for this call?
1151bdd1243dSDimitry Andric std::optional<CallEnter> CE = N->getLocationAs<CallEnter>();
11520b57cec5SDimitry Andric if (!CE)
11530b57cec5SDimitry Andric return nullptr;
11540b57cec5SDimitry Andric
1155a7dea167SDimitry Andric if (CE->getCalleeContext() != CalleeSFC)
11560b57cec5SDimitry Andric return nullptr;
11570b57cec5SDimitry Andric
11580b57cec5SDimitry Andric Mode = Satisfied;
11590b57cec5SDimitry Andric
11600b57cec5SDimitry Andric // Don't automatically suppress a report if one of the arguments is
11610b57cec5SDimitry Andric // known to be a null pointer. Instead, start tracking /that/ null
11620b57cec5SDimitry Andric // value back to its origin.
11630b57cec5SDimitry Andric ProgramStateManager &StateMgr = BRC.getStateManager();
11640b57cec5SDimitry Andric CallEventManager &CallMgr = StateMgr.getCallEventManager();
11650b57cec5SDimitry Andric
11660b57cec5SDimitry Andric ProgramStateRef State = N->getState();
1167a7dea167SDimitry Andric CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State);
11680b57cec5SDimitry Andric for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
1169bdd1243dSDimitry Andric std::optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
11700b57cec5SDimitry Andric if (!ArgV)
11710b57cec5SDimitry Andric continue;
11720b57cec5SDimitry Andric
11730b57cec5SDimitry Andric const Expr *ArgE = Call->getArgExpr(I);
11740b57cec5SDimitry Andric if (!ArgE)
11750b57cec5SDimitry Andric continue;
11760b57cec5SDimitry Andric
11770b57cec5SDimitry Andric // Is it possible for this argument to be non-null?
11780b57cec5SDimitry Andric if (!State->isNull(*ArgV).isConstrainedTrue())
11790b57cec5SDimitry Andric continue;
11800b57cec5SDimitry Andric
1181fe6060f1SDimitry Andric if (getParentTracker()
1182fe6060f1SDimitry Andric .track(ArgE, N, {TKind, EnableNullFPSuppression})
1183fe6060f1SDimitry Andric .FoundSomethingToTrack)
11840b57cec5SDimitry Andric ShouldInvalidate = false;
11850b57cec5SDimitry Andric
11860b57cec5SDimitry Andric // If we /can't/ track the null pointer, we should err on the side of
11870b57cec5SDimitry Andric // false negatives, and continue towards marking this report invalid.
11880b57cec5SDimitry Andric // (We will still look at the other arguments, though.)
11890b57cec5SDimitry Andric }
11900b57cec5SDimitry Andric
11910b57cec5SDimitry Andric return nullptr;
11920b57cec5SDimitry Andric }
11930b57cec5SDimitry Andric
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)1194a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
11950b57cec5SDimitry Andric BugReporterContext &BRC,
1196a7dea167SDimitry Andric PathSensitiveBugReport &BR) override {
11970b57cec5SDimitry Andric switch (Mode) {
11980b57cec5SDimitry Andric case Initial:
11990b57cec5SDimitry Andric return visitNodeInitial(N, BRC, BR);
12000b57cec5SDimitry Andric case MaybeUnsuppress:
12010b57cec5SDimitry Andric return visitNodeMaybeUnsuppress(N, BRC, BR);
12020b57cec5SDimitry Andric case Satisfied:
12030b57cec5SDimitry Andric return nullptr;
12040b57cec5SDimitry Andric }
12050b57cec5SDimitry Andric
12060b57cec5SDimitry Andric llvm_unreachable("Invalid visit mode!");
12070b57cec5SDimitry Andric }
12080b57cec5SDimitry Andric
finalizeVisitor(BugReporterContext &,const ExplodedNode *,PathSensitiveBugReport & BR)12090b57cec5SDimitry Andric void finalizeVisitor(BugReporterContext &, const ExplodedNode *,
1210a7dea167SDimitry Andric PathSensitiveBugReport &BR) override {
12110b57cec5SDimitry Andric if (EnableNullFPSuppression && ShouldInvalidate)
1212a7dea167SDimitry Andric BR.markInvalid(ReturnVisitor::getTag(), CalleeSFC);
12130b57cec5SDimitry Andric }
12140b57cec5SDimitry Andric };
12150b57cec5SDimitry Andric
12160b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1217fe6060f1SDimitry Andric // StoreSiteFinder
12180b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12190b57cec5SDimitry Andric
1220fe6060f1SDimitry Andric /// Finds last store into the given region,
1221fe6060f1SDimitry Andric /// which is different from a given symbolic value.
1222fe6060f1SDimitry Andric class StoreSiteFinder final : public TrackingBugReporterVisitor {
1223fe6060f1SDimitry Andric const MemRegion *R;
1224fe6060f1SDimitry Andric SVal V;
1225fe6060f1SDimitry Andric bool Satisfied = false;
1226fe6060f1SDimitry Andric
1227fe6060f1SDimitry Andric TrackingOptions Options;
1228fe6060f1SDimitry Andric const StackFrameContext *OriginSFC;
1229fe6060f1SDimitry Andric
1230fe6060f1SDimitry Andric public:
1231fe6060f1SDimitry Andric /// \param V We're searching for the store where \c R received this value.
1232fe6060f1SDimitry Andric /// \param R The region we're tracking.
1233349cc55cSDimitry Andric /// \param Options Tracking behavior options.
1234fe6060f1SDimitry Andric /// \param OriginSFC Only adds notes when the last store happened in a
1235fe6060f1SDimitry Andric /// different stackframe to this one. Disregarded if the tracking kind
1236fe6060f1SDimitry Andric /// is thorough.
1237fe6060f1SDimitry Andric /// This is useful, because for non-tracked regions, notes about
1238fe6060f1SDimitry Andric /// changes to its value in a nested stackframe could be pruned, and
1239fe6060f1SDimitry Andric /// this visitor can prevent that without polluting the bugpath too
1240fe6060f1SDimitry Andric /// much.
StoreSiteFinder(bugreporter::TrackerRef ParentTracker,KnownSVal V,const MemRegion * R,TrackingOptions Options,const StackFrameContext * OriginSFC=nullptr)1241fe6060f1SDimitry Andric StoreSiteFinder(bugreporter::TrackerRef ParentTracker, KnownSVal V,
1242fe6060f1SDimitry Andric const MemRegion *R, TrackingOptions Options,
1243fe6060f1SDimitry Andric const StackFrameContext *OriginSFC = nullptr)
1244fe6060f1SDimitry Andric : TrackingBugReporterVisitor(ParentTracker), R(R), V(V), Options(Options),
1245fe6060f1SDimitry Andric OriginSFC(OriginSFC) {
1246fe6060f1SDimitry Andric assert(R);
1247fe6060f1SDimitry Andric }
1248fe6060f1SDimitry Andric
1249fe6060f1SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override;
1250fe6060f1SDimitry Andric
1251fe6060f1SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
1252fe6060f1SDimitry Andric BugReporterContext &BRC,
1253fe6060f1SDimitry Andric PathSensitiveBugReport &BR) override;
1254fe6060f1SDimitry Andric };
1255bdd1243dSDimitry Andric } // namespace
1256fe6060f1SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const1257fe6060f1SDimitry Andric void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const {
12580b57cec5SDimitry Andric static int tag = 0;
12590b57cec5SDimitry Andric ID.AddPointer(&tag);
12600b57cec5SDimitry Andric ID.AddPointer(R);
12610b57cec5SDimitry Andric ID.Add(V);
1262fe6060f1SDimitry Andric ID.AddInteger(static_cast<int>(Options.Kind));
1263fe6060f1SDimitry Andric ID.AddBoolean(Options.EnableNullFPSuppression);
12640b57cec5SDimitry Andric }
12650b57cec5SDimitry Andric
12660b57cec5SDimitry Andric /// Returns true if \p N represents the DeclStmt declaring and initializing
12670b57cec5SDimitry Andric /// \p VR.
isInitializationOfVar(const ExplodedNode * N,const VarRegion * VR)12680b57cec5SDimitry Andric static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
1269bdd1243dSDimitry Andric std::optional<PostStmt> P = N->getLocationAs<PostStmt>();
12700b57cec5SDimitry Andric if (!P)
12710b57cec5SDimitry Andric return false;
12720b57cec5SDimitry Andric
12730b57cec5SDimitry Andric const DeclStmt *DS = P->getStmtAs<DeclStmt>();
12740b57cec5SDimitry Andric if (!DS)
12750b57cec5SDimitry Andric return false;
12760b57cec5SDimitry Andric
12770b57cec5SDimitry Andric if (DS->getSingleDecl() != VR->getDecl())
12780b57cec5SDimitry Andric return false;
12790b57cec5SDimitry Andric
12800b57cec5SDimitry Andric const MemSpaceRegion *VarSpace = VR->getMemorySpace();
12810b57cec5SDimitry Andric const auto *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace);
12820b57cec5SDimitry Andric if (!FrameSpace) {
12830b57cec5SDimitry Andric // If we ever directly evaluate global DeclStmts, this assertion will be
12840b57cec5SDimitry Andric // invalid, but this still seems preferable to silently accepting an
12850b57cec5SDimitry Andric // initialization that may be for a path-sensitive variable.
12860b57cec5SDimitry Andric assert(VR->getDecl()->isStaticLocal() && "non-static stackless VarRegion");
12870b57cec5SDimitry Andric return true;
12880b57cec5SDimitry Andric }
12890b57cec5SDimitry Andric
12900b57cec5SDimitry Andric assert(VR->getDecl()->hasLocalStorage());
12910b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
12920b57cec5SDimitry Andric return FrameSpace->getStackFrame() == LCtx->getStackFrame();
12930b57cec5SDimitry Andric }
12940b57cec5SDimitry Andric
isObjCPointer(const MemRegion * R)1295fe6060f1SDimitry Andric static bool isObjCPointer(const MemRegion *R) {
1296fe6060f1SDimitry Andric if (R->isBoundable())
1297fe6060f1SDimitry Andric if (const auto *TR = dyn_cast<TypedValueRegion>(R))
1298fe6060f1SDimitry Andric return TR->getValueType()->isObjCObjectPointerType();
1299fe6060f1SDimitry Andric
1300fe6060f1SDimitry Andric return false;
1301fe6060f1SDimitry Andric }
1302fe6060f1SDimitry Andric
isObjCPointer(const ValueDecl * D)1303fe6060f1SDimitry Andric static bool isObjCPointer(const ValueDecl *D) {
1304fe6060f1SDimitry Andric return D->getType()->isObjCObjectPointerType();
1305fe6060f1SDimitry Andric }
1306fe6060f1SDimitry Andric
13070b57cec5SDimitry Andric /// Show diagnostics for initializing or declaring a region \p R with a bad value.
showBRDiagnostics(llvm::raw_svector_ostream & OS,StoreInfo SI)1308fe6060f1SDimitry Andric static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
1309fe6060f1SDimitry Andric const bool HasPrefix = SI.Dest->canPrintPretty();
1310fe6060f1SDimitry Andric
1311fe6060f1SDimitry Andric if (HasPrefix) {
1312fe6060f1SDimitry Andric SI.Dest->printPretty(OS);
1313fe6060f1SDimitry Andric OS << " ";
13140b57cec5SDimitry Andric }
13150b57cec5SDimitry Andric
1316fe6060f1SDimitry Andric const char *Action = nullptr;
13170b57cec5SDimitry Andric
1318fe6060f1SDimitry Andric switch (SI.StoreKind) {
1319fe6060f1SDimitry Andric case StoreInfo::Initialization:
1320fe6060f1SDimitry Andric Action = HasPrefix ? "initialized to " : "Initializing to ";
1321fe6060f1SDimitry Andric break;
1322fe6060f1SDimitry Andric case StoreInfo::BlockCapture:
1323fe6060f1SDimitry Andric Action = HasPrefix ? "captured by block as " : "Captured by block as ";
1324fe6060f1SDimitry Andric break;
1325fe6060f1SDimitry Andric default:
1326fe6060f1SDimitry Andric llvm_unreachable("Unexpected store kind");
1327fe6060f1SDimitry Andric }
1328fe6060f1SDimitry Andric
132981ad6265SDimitry Andric if (isa<loc::ConcreteInt>(SI.Value)) {
1330fe6060f1SDimitry Andric OS << Action << (isObjCPointer(SI.Dest) ? "nil" : "a null pointer value");
1331fe6060f1SDimitry Andric
1332fe6060f1SDimitry Andric } else if (auto CVal = SI.Value.getAs<nonloc::ConcreteInt>()) {
1333fe6060f1SDimitry Andric OS << Action << CVal->getValue();
1334fe6060f1SDimitry Andric
1335fe6060f1SDimitry Andric } else if (SI.Origin && SI.Origin->canPrintPretty()) {
1336fe6060f1SDimitry Andric OS << Action << "the value of ";
1337fe6060f1SDimitry Andric SI.Origin->printPretty(OS);
1338fe6060f1SDimitry Andric
1339fe6060f1SDimitry Andric } else if (SI.StoreKind == StoreInfo::Initialization) {
1340fe6060f1SDimitry Andric // We don't need to check here, all these conditions were
1341fe6060f1SDimitry Andric // checked by StoreSiteFinder, when it figured out that it is
1342fe6060f1SDimitry Andric // initialization.
1343fe6060f1SDimitry Andric const auto *DS =
1344fe6060f1SDimitry Andric cast<DeclStmt>(SI.StoreSite->getLocationAs<PostStmt>()->getStmt());
1345fe6060f1SDimitry Andric
1346fe6060f1SDimitry Andric if (SI.Value.isUndef()) {
1347fe6060f1SDimitry Andric if (isa<VarRegion>(SI.Dest)) {
13480b57cec5SDimitry Andric const auto *VD = cast<VarDecl>(DS->getSingleDecl());
1349fe6060f1SDimitry Andric
13500b57cec5SDimitry Andric if (VD->getInit()) {
1351fe6060f1SDimitry Andric OS << (HasPrefix ? "initialized" : "Initializing")
13520b57cec5SDimitry Andric << " to a garbage value";
13530b57cec5SDimitry Andric } else {
1354fe6060f1SDimitry Andric OS << (HasPrefix ? "declared" : "Declaring")
13550b57cec5SDimitry Andric << " without an initial value";
13560b57cec5SDimitry Andric }
13570b57cec5SDimitry Andric }
13580b57cec5SDimitry Andric } else {
1359fe6060f1SDimitry Andric OS << (HasPrefix ? "initialized" : "Initialized") << " here";
13600b57cec5SDimitry Andric }
13610b57cec5SDimitry Andric }
13620b57cec5SDimitry Andric }
13630b57cec5SDimitry Andric
13640b57cec5SDimitry Andric /// Display diagnostics for passing bad region as a parameter.
showBRParamDiagnostics(llvm::raw_svector_ostream & OS,StoreInfo SI)1365fe6060f1SDimitry Andric static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS,
1366fe6060f1SDimitry Andric StoreInfo SI) {
1367fe6060f1SDimitry Andric const auto *VR = cast<VarRegion>(SI.Dest);
1368bdd1243dSDimitry Andric const auto *D = VR->getDecl();
13690b57cec5SDimitry Andric
1370fe6060f1SDimitry Andric OS << "Passing ";
13710b57cec5SDimitry Andric
137281ad6265SDimitry Andric if (isa<loc::ConcreteInt>(SI.Value)) {
1373bdd1243dSDimitry Andric OS << (isObjCPointer(D) ? "nil object reference" : "null pointer value");
1374fe6060f1SDimitry Andric
1375fe6060f1SDimitry Andric } else if (SI.Value.isUndef()) {
1376fe6060f1SDimitry Andric OS << "uninitialized value";
1377fe6060f1SDimitry Andric
1378fe6060f1SDimitry Andric } else if (auto CI = SI.Value.getAs<nonloc::ConcreteInt>()) {
1379fe6060f1SDimitry Andric OS << "the value " << CI->getValue();
1380fe6060f1SDimitry Andric
1381fe6060f1SDimitry Andric } else if (SI.Origin && SI.Origin->canPrintPretty()) {
1382fe6060f1SDimitry Andric SI.Origin->printPretty(OS);
1383fe6060f1SDimitry Andric
13840b57cec5SDimitry Andric } else {
1385fe6060f1SDimitry Andric OS << "value";
13860b57cec5SDimitry Andric }
13870b57cec5SDimitry Andric
1388bdd1243dSDimitry Andric if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
13890b57cec5SDimitry Andric // Printed parameter indexes are 1-based, not 0-based.
13900b57cec5SDimitry Andric unsigned Idx = Param->getFunctionScopeIndex() + 1;
1391fe6060f1SDimitry Andric OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
13920b57cec5SDimitry Andric if (VR->canPrintPretty()) {
1393fe6060f1SDimitry Andric OS << " ";
1394fe6060f1SDimitry Andric VR->printPretty(OS);
13950b57cec5SDimitry Andric }
1396bdd1243dSDimitry Andric } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(D)) {
13975f757f3fSDimitry Andric if (ImplParam->getParameterKind() == ImplicitParamKind::ObjCSelf) {
1398bdd1243dSDimitry Andric OS << " via implicit parameter 'self'";
1399bdd1243dSDimitry Andric }
1400bdd1243dSDimitry Andric }
14010b57cec5SDimitry Andric }
14020b57cec5SDimitry Andric
14030b57cec5SDimitry Andric /// Show default diagnostics for storing bad region.
showBRDefaultDiagnostics(llvm::raw_svector_ostream & OS,StoreInfo SI)1404fe6060f1SDimitry Andric static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS,
1405fe6060f1SDimitry Andric StoreInfo SI) {
1406fe6060f1SDimitry Andric const bool HasSuffix = SI.Dest->canPrintPretty();
14070b57cec5SDimitry Andric
140881ad6265SDimitry Andric if (isa<loc::ConcreteInt>(SI.Value)) {
1409fe6060f1SDimitry Andric OS << (isObjCPointer(SI.Dest) ? "nil object reference stored"
1410fe6060f1SDimitry Andric : (HasSuffix ? "Null pointer value stored"
1411fe6060f1SDimitry Andric : "Storing null pointer value"));
14120b57cec5SDimitry Andric
1413fe6060f1SDimitry Andric } else if (SI.Value.isUndef()) {
1414fe6060f1SDimitry Andric OS << (HasSuffix ? "Uninitialized value stored"
1415fe6060f1SDimitry Andric : "Storing uninitialized value");
1416fe6060f1SDimitry Andric
1417fe6060f1SDimitry Andric } else if (auto CV = SI.Value.getAs<nonloc::ConcreteInt>()) {
1418fe6060f1SDimitry Andric if (HasSuffix)
1419fe6060f1SDimitry Andric OS << "The value " << CV->getValue() << " is assigned";
14200b57cec5SDimitry Andric else
1421fe6060f1SDimitry Andric OS << "Assigning " << CV->getValue();
1422fe6060f1SDimitry Andric
1423fe6060f1SDimitry Andric } else if (SI.Origin && SI.Origin->canPrintPretty()) {
1424fe6060f1SDimitry Andric if (HasSuffix) {
1425fe6060f1SDimitry Andric OS << "The value of ";
1426fe6060f1SDimitry Andric SI.Origin->printPretty(OS);
1427fe6060f1SDimitry Andric OS << " is assigned";
1428fe6060f1SDimitry Andric } else {
1429fe6060f1SDimitry Andric OS << "Assigning the value of ";
1430fe6060f1SDimitry Andric SI.Origin->printPretty(OS);
1431fe6060f1SDimitry Andric }
14320b57cec5SDimitry Andric
14330b57cec5SDimitry Andric } else {
1434fe6060f1SDimitry Andric OS << (HasSuffix ? "Value assigned" : "Assigning value");
14350b57cec5SDimitry Andric }
14360b57cec5SDimitry Andric
1437fe6060f1SDimitry Andric if (HasSuffix) {
1438fe6060f1SDimitry Andric OS << " to ";
1439fe6060f1SDimitry Andric SI.Dest->printPretty(OS);
14400b57cec5SDimitry Andric }
14410b57cec5SDimitry Andric }
14420b57cec5SDimitry Andric
isTrivialCopyOrMoveCtor(const CXXConstructExpr * CE)1443bdd1243dSDimitry Andric static bool isTrivialCopyOrMoveCtor(const CXXConstructExpr *CE) {
1444bdd1243dSDimitry Andric if (!CE)
1445bdd1243dSDimitry Andric return false;
1446bdd1243dSDimitry Andric
1447bdd1243dSDimitry Andric const auto *CtorDecl = CE->getConstructor();
1448bdd1243dSDimitry Andric
1449bdd1243dSDimitry Andric return CtorDecl->isCopyOrMoveConstructor() && CtorDecl->isTrivial();
1450bdd1243dSDimitry Andric }
1451bdd1243dSDimitry Andric
tryExtractInitializerFromList(const InitListExpr * ILE,const MemRegion * R)1452bdd1243dSDimitry Andric static const Expr *tryExtractInitializerFromList(const InitListExpr *ILE,
1453bdd1243dSDimitry Andric const MemRegion *R) {
1454bdd1243dSDimitry Andric
1455bdd1243dSDimitry Andric const auto *TVR = dyn_cast_or_null<TypedValueRegion>(R);
1456bdd1243dSDimitry Andric
1457bdd1243dSDimitry Andric if (!TVR)
1458bdd1243dSDimitry Andric return nullptr;
1459bdd1243dSDimitry Andric
1460bdd1243dSDimitry Andric const auto ITy = ILE->getType().getCanonicalType();
1461bdd1243dSDimitry Andric
1462bdd1243dSDimitry Andric // Push each sub-region onto the stack.
1463bdd1243dSDimitry Andric std::stack<const TypedValueRegion *> TVRStack;
1464bdd1243dSDimitry Andric while (isa<FieldRegion>(TVR) || isa<ElementRegion>(TVR)) {
1465bdd1243dSDimitry Andric // We found a region that matches the type of the init list,
1466bdd1243dSDimitry Andric // so we assume this is the outer-most region. This can happen
1467bdd1243dSDimitry Andric // if the initializer list is inside a class. If our assumption
1468bdd1243dSDimitry Andric // is wrong, we return a nullptr in the end.
1469bdd1243dSDimitry Andric if (ITy == TVR->getValueType().getCanonicalType())
1470bdd1243dSDimitry Andric break;
1471bdd1243dSDimitry Andric
1472bdd1243dSDimitry Andric TVRStack.push(TVR);
1473bdd1243dSDimitry Andric TVR = cast<TypedValueRegion>(TVR->getSuperRegion());
1474bdd1243dSDimitry Andric }
1475bdd1243dSDimitry Andric
1476bdd1243dSDimitry Andric // If the type of the outer most region doesn't match the type
1477bdd1243dSDimitry Andric // of the ILE, we can't match the ILE and the region.
1478bdd1243dSDimitry Andric if (ITy != TVR->getValueType().getCanonicalType())
1479bdd1243dSDimitry Andric return nullptr;
1480bdd1243dSDimitry Andric
1481bdd1243dSDimitry Andric const Expr *Init = ILE;
1482bdd1243dSDimitry Andric while (!TVRStack.empty()) {
1483bdd1243dSDimitry Andric TVR = TVRStack.top();
1484bdd1243dSDimitry Andric TVRStack.pop();
1485bdd1243dSDimitry Andric
1486bdd1243dSDimitry Andric // We hit something that's not an init list before
1487bdd1243dSDimitry Andric // running out of regions, so we most likely failed.
1488bdd1243dSDimitry Andric if (!isa<InitListExpr>(Init))
1489bdd1243dSDimitry Andric return nullptr;
1490bdd1243dSDimitry Andric
1491bdd1243dSDimitry Andric ILE = cast<InitListExpr>(Init);
1492bdd1243dSDimitry Andric auto NumInits = ILE->getNumInits();
1493bdd1243dSDimitry Andric
1494bdd1243dSDimitry Andric if (const auto *FR = dyn_cast<FieldRegion>(TVR)) {
1495bdd1243dSDimitry Andric const auto *FD = FR->getDecl();
1496bdd1243dSDimitry Andric
1497bdd1243dSDimitry Andric if (FD->getFieldIndex() >= NumInits)
1498bdd1243dSDimitry Andric return nullptr;
1499bdd1243dSDimitry Andric
1500bdd1243dSDimitry Andric Init = ILE->getInit(FD->getFieldIndex());
1501bdd1243dSDimitry Andric } else if (const auto *ER = dyn_cast<ElementRegion>(TVR)) {
1502bdd1243dSDimitry Andric const auto Ind = ER->getIndex();
1503bdd1243dSDimitry Andric
1504bdd1243dSDimitry Andric // If index is symbolic, we can't figure out which expression
1505bdd1243dSDimitry Andric // belongs to the region.
1506bdd1243dSDimitry Andric if (!Ind.isConstant())
1507bdd1243dSDimitry Andric return nullptr;
1508bdd1243dSDimitry Andric
1509bdd1243dSDimitry Andric const auto IndVal = Ind.getAsInteger()->getLimitedValue();
1510bdd1243dSDimitry Andric if (IndVal >= NumInits)
1511bdd1243dSDimitry Andric return nullptr;
1512bdd1243dSDimitry Andric
1513bdd1243dSDimitry Andric Init = ILE->getInit(IndVal);
1514bdd1243dSDimitry Andric }
1515bdd1243dSDimitry Andric }
1516bdd1243dSDimitry Andric
1517bdd1243dSDimitry Andric return Init;
1518bdd1243dSDimitry Andric }
1519bdd1243dSDimitry Andric
VisitNode(const ExplodedNode * Succ,BugReporterContext & BRC,PathSensitiveBugReport & BR)1520fe6060f1SDimitry Andric PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
1521a7dea167SDimitry Andric BugReporterContext &BRC,
1522a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
15230b57cec5SDimitry Andric if (Satisfied)
15240b57cec5SDimitry Andric return nullptr;
15250b57cec5SDimitry Andric
15260b57cec5SDimitry Andric const ExplodedNode *StoreSite = nullptr;
15270b57cec5SDimitry Andric const ExplodedNode *Pred = Succ->getFirstPred();
15280b57cec5SDimitry Andric const Expr *InitE = nullptr;
15290b57cec5SDimitry Andric bool IsParam = false;
15300b57cec5SDimitry Andric
15310b57cec5SDimitry Andric // First see if we reached the declaration of the region.
15320b57cec5SDimitry Andric if (const auto *VR = dyn_cast<VarRegion>(R)) {
15330b57cec5SDimitry Andric if (isInitializationOfVar(Pred, VR)) {
15340b57cec5SDimitry Andric StoreSite = Pred;
15350b57cec5SDimitry Andric InitE = VR->getDecl()->getInit();
15360b57cec5SDimitry Andric }
15370b57cec5SDimitry Andric }
15380b57cec5SDimitry Andric
15390b57cec5SDimitry Andric // If this is a post initializer expression, initializing the region, we
15400b57cec5SDimitry Andric // should track the initializer expression.
1541bdd1243dSDimitry Andric if (std::optional<PostInitializer> PIP =
1542bdd1243dSDimitry Andric Pred->getLocationAs<PostInitializer>()) {
15430b57cec5SDimitry Andric const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue();
1544a7dea167SDimitry Andric if (FieldReg == R) {
15450b57cec5SDimitry Andric StoreSite = Pred;
15460b57cec5SDimitry Andric InitE = PIP->getInitializer()->getInit();
15470b57cec5SDimitry Andric }
15480b57cec5SDimitry Andric }
15490b57cec5SDimitry Andric
15500b57cec5SDimitry Andric // Otherwise, see if this is the store site:
15510b57cec5SDimitry Andric // (1) Succ has this binding and Pred does not, i.e. this is
15520b57cec5SDimitry Andric // where the binding first occurred.
15530b57cec5SDimitry Andric // (2) Succ has this binding and is a PostStore node for this region, i.e.
15540b57cec5SDimitry Andric // the same binding was re-assigned here.
15550b57cec5SDimitry Andric if (!StoreSite) {
15560b57cec5SDimitry Andric if (Succ->getState()->getSVal(R) != V)
15570b57cec5SDimitry Andric return nullptr;
15580b57cec5SDimitry Andric
15590b57cec5SDimitry Andric if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) {
1560bdd1243dSDimitry Andric std::optional<PostStore> PS = Succ->getLocationAs<PostStore>();
15610b57cec5SDimitry Andric if (!PS || PS->getLocationValue() != R)
15620b57cec5SDimitry Andric return nullptr;
15630b57cec5SDimitry Andric }
15640b57cec5SDimitry Andric
15650b57cec5SDimitry Andric StoreSite = Succ;
15660b57cec5SDimitry Andric
1567bdd1243dSDimitry Andric if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) {
15680b57cec5SDimitry Andric // If this is an assignment expression, we can track the value
15690b57cec5SDimitry Andric // being assigned.
1570bdd1243dSDimitry Andric if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
15710b57cec5SDimitry Andric if (BO->isAssignmentOp())
15720b57cec5SDimitry Andric InitE = BO->getRHS();
1573bdd1243dSDimitry Andric }
1574bdd1243dSDimitry Andric // If we have a declaration like 'S s{1,2}' that needs special
1575bdd1243dSDimitry Andric // handling, we handle it here.
1576bdd1243dSDimitry Andric else if (const auto *DS = P->getStmtAs<DeclStmt>()) {
1577bdd1243dSDimitry Andric const auto *Decl = DS->getSingleDecl();
1578bdd1243dSDimitry Andric if (isa<VarDecl>(Decl)) {
1579bdd1243dSDimitry Andric const auto *VD = cast<VarDecl>(Decl);
1580bdd1243dSDimitry Andric
1581bdd1243dSDimitry Andric // FIXME: Here we only track the inner most region, so we lose
1582bdd1243dSDimitry Andric // information, but it's still better than a crash or no information
1583bdd1243dSDimitry Andric // at all.
1584bdd1243dSDimitry Andric //
1585bdd1243dSDimitry Andric // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y',
1586bdd1243dSDimitry Andric // and throw away the rest.
1587bdd1243dSDimitry Andric if (const auto *ILE = dyn_cast<InitListExpr>(VD->getInit()))
1588bdd1243dSDimitry Andric InitE = tryExtractInitializerFromList(ILE, R);
1589bdd1243dSDimitry Andric }
1590bdd1243dSDimitry Andric } else if (const auto *CE = P->getStmtAs<CXXConstructExpr>()) {
1591bdd1243dSDimitry Andric
1592bdd1243dSDimitry Andric const auto State = Succ->getState();
1593bdd1243dSDimitry Andric
1594bdd1243dSDimitry Andric if (isTrivialCopyOrMoveCtor(CE) && isa<SubRegion>(R)) {
1595bdd1243dSDimitry Andric // Migrate the field regions from the current object to
1596bdd1243dSDimitry Andric // the parent object. If we track 'a.y.e' and encounter
1597bdd1243dSDimitry Andric // 'S a = b' then we need to track 'b.y.e'.
1598bdd1243dSDimitry Andric
1599bdd1243dSDimitry Andric // Push the regions to a stack, from last to first, so
1600bdd1243dSDimitry Andric // considering the example above the stack will look like
1601bdd1243dSDimitry Andric // (bottom) 'e' -> 'y' (top).
1602bdd1243dSDimitry Andric
1603bdd1243dSDimitry Andric std::stack<const SubRegion *> SRStack;
1604bdd1243dSDimitry Andric const SubRegion *SR = cast<SubRegion>(R);
1605bdd1243dSDimitry Andric while (isa<FieldRegion>(SR) || isa<ElementRegion>(SR)) {
1606bdd1243dSDimitry Andric SRStack.push(SR);
1607bdd1243dSDimitry Andric SR = cast<SubRegion>(SR->getSuperRegion());
1608bdd1243dSDimitry Andric }
1609bdd1243dSDimitry Andric
1610bdd1243dSDimitry Andric // Get the region for the object we copied/moved from.
1611bdd1243dSDimitry Andric const auto *OriginEx = CE->getArg(0);
1612bdd1243dSDimitry Andric const auto OriginVal =
1613bdd1243dSDimitry Andric State->getSVal(OriginEx, Succ->getLocationContext());
1614bdd1243dSDimitry Andric
1615bdd1243dSDimitry Andric // Pop the stored field regions and apply them to the origin
1616bdd1243dSDimitry Andric // object in the same order we had them on the copy.
1617bdd1243dSDimitry Andric // OriginField will evolve like 'b' -> 'b.y' -> 'b.y.e'.
1618bdd1243dSDimitry Andric SVal OriginField = OriginVal;
1619bdd1243dSDimitry Andric while (!SRStack.empty()) {
1620bdd1243dSDimitry Andric const auto *TopR = SRStack.top();
1621bdd1243dSDimitry Andric SRStack.pop();
1622bdd1243dSDimitry Andric
1623bdd1243dSDimitry Andric if (const auto *FR = dyn_cast<FieldRegion>(TopR)) {
1624bdd1243dSDimitry Andric OriginField = State->getLValue(FR->getDecl(), OriginField);
1625bdd1243dSDimitry Andric } else if (const auto *ER = dyn_cast<ElementRegion>(TopR)) {
1626bdd1243dSDimitry Andric OriginField = State->getLValue(ER->getElementType(),
1627bdd1243dSDimitry Andric ER->getIndex(), OriginField);
1628bdd1243dSDimitry Andric } else {
1629bdd1243dSDimitry Andric // FIXME: handle other region type
1630bdd1243dSDimitry Andric }
1631bdd1243dSDimitry Andric }
1632bdd1243dSDimitry Andric
1633bdd1243dSDimitry Andric // Track 'b.y.e'.
1634bdd1243dSDimitry Andric getParentTracker().track(V, OriginField.getAsRegion(), Options);
1635bdd1243dSDimitry Andric InitE = OriginEx;
1636bdd1243dSDimitry Andric }
1637bdd1243dSDimitry Andric }
1638bdd1243dSDimitry Andric // This branch can occur in cases like `Ctor() : field{ x, y } {}'.
1639bdd1243dSDimitry Andric else if (const auto *ILE = P->getStmtAs<InitListExpr>()) {
1640bdd1243dSDimitry Andric // FIXME: Here we only track the top level region, so we lose
1641bdd1243dSDimitry Andric // information, but it's still better than a crash or no information
1642bdd1243dSDimitry Andric // at all.
1643bdd1243dSDimitry Andric //
1644bdd1243dSDimitry Andric // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', and
1645bdd1243dSDimitry Andric // throw away the rest.
1646bdd1243dSDimitry Andric InitE = tryExtractInitializerFromList(ILE, R);
1647bdd1243dSDimitry Andric }
1648bdd1243dSDimitry Andric }
16490b57cec5SDimitry Andric
16500b57cec5SDimitry Andric // If this is a call entry, the variable should be a parameter.
16510b57cec5SDimitry Andric // FIXME: Handle CXXThisRegion as well. (This is not a priority because
16520b57cec5SDimitry Andric // 'this' should never be NULL, but this visitor isn't just for NULL and
16530b57cec5SDimitry Andric // UndefinedVal.)
1654bdd1243dSDimitry Andric if (std::optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
16550b57cec5SDimitry Andric if (const auto *VR = dyn_cast<VarRegion>(R)) {
16560b57cec5SDimitry Andric
1657a7dea167SDimitry Andric if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
16580b57cec5SDimitry Andric ProgramStateManager &StateMgr = BRC.getStateManager();
16590b57cec5SDimitry Andric CallEventManager &CallMgr = StateMgr.getCallEventManager();
16600b57cec5SDimitry Andric
16610b57cec5SDimitry Andric CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(),
16620b57cec5SDimitry Andric Succ->getState());
16630b57cec5SDimitry Andric InitE = Call->getArgExpr(Param->getFunctionScopeIndex());
1664a7dea167SDimitry Andric } else {
1665a7dea167SDimitry Andric // Handle Objective-C 'self'.
1666a7dea167SDimitry Andric assert(isa<ImplicitParamDecl>(VR->getDecl()));
1667a7dea167SDimitry Andric InitE = cast<ObjCMessageExpr>(CE->getCalleeContext()->getCallSite())
1668a7dea167SDimitry Andric ->getInstanceReceiver()->IgnoreParenCasts();
1669a7dea167SDimitry Andric }
16700b57cec5SDimitry Andric IsParam = true;
16710b57cec5SDimitry Andric }
16720b57cec5SDimitry Andric }
16730b57cec5SDimitry Andric
16740b57cec5SDimitry Andric // If this is a CXXTempObjectRegion, the Expr responsible for its creation
16750b57cec5SDimitry Andric // is wrapped inside of it.
16760b57cec5SDimitry Andric if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(R))
16770b57cec5SDimitry Andric InitE = TmpR->getExpr();
16780b57cec5SDimitry Andric }
16790b57cec5SDimitry Andric
16800b57cec5SDimitry Andric if (!StoreSite)
16810b57cec5SDimitry Andric return nullptr;
1682a7dea167SDimitry Andric
16830b57cec5SDimitry Andric Satisfied = true;
16840b57cec5SDimitry Andric
16850b57cec5SDimitry Andric // If we have an expression that provided the value, try to track where it
16860b57cec5SDimitry Andric // came from.
16870b57cec5SDimitry Andric if (InitE) {
16880b57cec5SDimitry Andric if (!IsParam)
16890b57cec5SDimitry Andric InitE = InitE->IgnoreParenCasts();
1690a7dea167SDimitry Andric
1691fe6060f1SDimitry Andric getParentTracker().track(InitE, StoreSite, Options);
16920b57cec5SDimitry Andric }
1693a7dea167SDimitry Andric
1694fe6060f1SDimitry Andric // Let's try to find the region where the value came from.
1695fe6060f1SDimitry Andric const MemRegion *OldRegion = nullptr;
1696fe6060f1SDimitry Andric
1697fe6060f1SDimitry Andric // If we have init expression, it might be simply a reference
1698fe6060f1SDimitry Andric // to a variable, so we can use it.
1699fe6060f1SDimitry Andric if (InitE) {
1700fe6060f1SDimitry Andric // That region might still be not exactly what we are looking for.
1701fe6060f1SDimitry Andric // In situations like `int &ref = val;`, we can't say that
1702fe6060f1SDimitry Andric // `ref` is initialized with `val`, rather refers to `val`.
1703fe6060f1SDimitry Andric //
1704fe6060f1SDimitry Andric // In order, to mitigate situations like this, we check if the last
1705fe6060f1SDimitry Andric // stored value in that region is the value that we track.
1706fe6060f1SDimitry Andric //
1707fe6060f1SDimitry Andric // TODO: support other situations better.
1708fe6060f1SDimitry Andric if (const MemRegion *Candidate =
1709fe6060f1SDimitry Andric getLocationRegionIfReference(InitE, Succ, false)) {
1710fe6060f1SDimitry Andric const StoreManager &SM = BRC.getStateManager().getStoreManager();
1711fe6060f1SDimitry Andric
1712fe6060f1SDimitry Andric // Here we traverse the graph up to find the last node where the
1713fe6060f1SDimitry Andric // candidate region is still in the store.
1714fe6060f1SDimitry Andric for (const ExplodedNode *N = StoreSite; N; N = N->getFirstPred()) {
1715fe6060f1SDimitry Andric if (SM.includedInBindings(N->getState()->getStore(), Candidate)) {
1716fe6060f1SDimitry Andric // And if it was bound to the target value, we can use it.
1717fe6060f1SDimitry Andric if (N->getState()->getSVal(Candidate) == V) {
1718fe6060f1SDimitry Andric OldRegion = Candidate;
1719fe6060f1SDimitry Andric }
1720fe6060f1SDimitry Andric break;
1721fe6060f1SDimitry Andric }
1722fe6060f1SDimitry Andric }
1723fe6060f1SDimitry Andric }
1724fe6060f1SDimitry Andric }
1725fe6060f1SDimitry Andric
1726fe6060f1SDimitry Andric // Otherwise, if the current region does indeed contain the value
1727fe6060f1SDimitry Andric // we are looking for, we can look for a region where this value
1728fe6060f1SDimitry Andric // was before.
1729fe6060f1SDimitry Andric //
1730fe6060f1SDimitry Andric // It can be useful for situations like:
1731fe6060f1SDimitry Andric // new = identity(old)
1732fe6060f1SDimitry Andric // where the analyzer knows that 'identity' returns the value of its
1733fe6060f1SDimitry Andric // first argument.
1734fe6060f1SDimitry Andric //
1735fe6060f1SDimitry Andric // NOTE: If the region R is not a simple var region, it can contain
1736fe6060f1SDimitry Andric // V in one of its subregions.
1737fe6060f1SDimitry Andric if (!OldRegion && StoreSite->getState()->getSVal(R) == V) {
1738fe6060f1SDimitry Andric // Let's go up the graph to find the node where the region is
1739fe6060f1SDimitry Andric // bound to V.
1740fe6060f1SDimitry Andric const ExplodedNode *NodeWithoutBinding = StoreSite->getFirstPred();
1741fe6060f1SDimitry Andric for (;
1742fe6060f1SDimitry Andric NodeWithoutBinding && NodeWithoutBinding->getState()->getSVal(R) == V;
1743fe6060f1SDimitry Andric NodeWithoutBinding = NodeWithoutBinding->getFirstPred()) {
1744fe6060f1SDimitry Andric }
1745fe6060f1SDimitry Andric
1746fe6060f1SDimitry Andric if (NodeWithoutBinding) {
1747fe6060f1SDimitry Andric // Let's try to find a unique binding for the value in that node.
1748fe6060f1SDimitry Andric // We want to use this to find unique bindings because of the following
1749fe6060f1SDimitry Andric // situations:
1750fe6060f1SDimitry Andric // b = a;
1751fe6060f1SDimitry Andric // c = identity(b);
1752fe6060f1SDimitry Andric //
1753fe6060f1SDimitry Andric // Telling the user that the value of 'a' is assigned to 'c', while
1754fe6060f1SDimitry Andric // correct, can be confusing.
1755fe6060f1SDimitry Andric StoreManager::FindUniqueBinding FB(V.getAsLocSymbol());
1756fe6060f1SDimitry Andric BRC.getStateManager().iterBindings(NodeWithoutBinding->getState(), FB);
1757fe6060f1SDimitry Andric if (FB)
1758fe6060f1SDimitry Andric OldRegion = FB.getRegion();
1759fe6060f1SDimitry Andric }
1760fe6060f1SDimitry Andric }
1761fe6060f1SDimitry Andric
1762fe6060f1SDimitry Andric if (Options.Kind == TrackingKind::Condition && OriginSFC &&
1763a7dea167SDimitry Andric !OriginSFC->isParentOf(StoreSite->getStackFrame()))
1764a7dea167SDimitry Andric return nullptr;
17650b57cec5SDimitry Andric
17660b57cec5SDimitry Andric // Okay, we've found the binding. Emit an appropriate message.
17670b57cec5SDimitry Andric SmallString<256> sbuf;
17680b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf);
17690b57cec5SDimitry Andric
1770fe6060f1SDimitry Andric StoreInfo SI = {StoreInfo::Assignment, // default kind
1771fe6060f1SDimitry Andric StoreSite,
1772fe6060f1SDimitry Andric InitE,
1773fe6060f1SDimitry Andric V,
1774fe6060f1SDimitry Andric R,
1775fe6060f1SDimitry Andric OldRegion};
1776fe6060f1SDimitry Andric
1777bdd1243dSDimitry Andric if (std::optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
17780b57cec5SDimitry Andric const Stmt *S = PS->getStmt();
17790b57cec5SDimitry Andric const auto *DS = dyn_cast<DeclStmt>(S);
17800b57cec5SDimitry Andric const auto *VR = dyn_cast<VarRegion>(R);
17810b57cec5SDimitry Andric
17820b57cec5SDimitry Andric if (DS) {
1783fe6060f1SDimitry Andric SI.StoreKind = StoreInfo::Initialization;
17840b57cec5SDimitry Andric } else if (isa<BlockExpr>(S)) {
1785fe6060f1SDimitry Andric SI.StoreKind = StoreInfo::BlockCapture;
17860b57cec5SDimitry Andric if (VR) {
17870b57cec5SDimitry Andric // See if we can get the BlockVarRegion.
17880b57cec5SDimitry Andric ProgramStateRef State = StoreSite->getState();
17890b57cec5SDimitry Andric SVal V = StoreSite->getSVal(S);
17900b57cec5SDimitry Andric if (const auto *BDR =
17910b57cec5SDimitry Andric dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
17920b57cec5SDimitry Andric if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) {
1793fe6060f1SDimitry Andric getParentTracker().track(State->getSVal(OriginalR), OriginalR,
1794fe6060f1SDimitry Andric Options, OriginSFC);
17950b57cec5SDimitry Andric }
17960b57cec5SDimitry Andric }
17970b57cec5SDimitry Andric }
17980b57cec5SDimitry Andric }
1799fe6060f1SDimitry Andric } else if (SI.StoreSite->getLocation().getAs<CallEnter>() &&
1800fe6060f1SDimitry Andric isa<VarRegion>(SI.Dest)) {
1801fe6060f1SDimitry Andric SI.StoreKind = StoreInfo::CallArgument;
18020b57cec5SDimitry Andric }
18030b57cec5SDimitry Andric
1804fe6060f1SDimitry Andric return getParentTracker().handle(SI, BRC, Options);
18050b57cec5SDimitry Andric }
18060b57cec5SDimitry Andric
18070b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18080b57cec5SDimitry Andric // Implementation of TrackConstraintBRVisitor.
18090b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18100b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const18110b57cec5SDimitry Andric void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
18120b57cec5SDimitry Andric static int tag = 0;
18130b57cec5SDimitry Andric ID.AddPointer(&tag);
181406c3fb27SDimitry Andric ID.AddString(Message);
18150b57cec5SDimitry Andric ID.AddBoolean(Assumption);
18160b57cec5SDimitry Andric ID.Add(Constraint);
18170b57cec5SDimitry Andric }
18180b57cec5SDimitry Andric
18190b57cec5SDimitry Andric /// Return the tag associated with this visitor. This tag will be used
18200b57cec5SDimitry Andric /// to make all PathDiagnosticPieces created by this visitor.
getTag()18210b57cec5SDimitry Andric const char *TrackConstraintBRVisitor::getTag() {
18220b57cec5SDimitry Andric return "TrackConstraintBRVisitor";
18230b57cec5SDimitry Andric }
18240b57cec5SDimitry Andric
isZeroCheck() const182506c3fb27SDimitry Andric bool TrackConstraintBRVisitor::isZeroCheck() const {
182606c3fb27SDimitry Andric return !Assumption && Constraint.getAs<Loc>();
182706c3fb27SDimitry Andric }
182806c3fb27SDimitry Andric
isUnderconstrained(const ExplodedNode * N) const18290b57cec5SDimitry Andric bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const {
183006c3fb27SDimitry Andric if (isZeroCheck())
18310b57cec5SDimitry Andric return N->getState()->isNull(Constraint).isUnderconstrained();
18320b57cec5SDimitry Andric return (bool)N->getState()->assume(Constraint, !Assumption);
18330b57cec5SDimitry Andric }
18340b57cec5SDimitry Andric
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport &)1835a7dea167SDimitry Andric PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode(
1836a7dea167SDimitry Andric const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
18370b57cec5SDimitry Andric const ExplodedNode *PrevN = N->getFirstPred();
18380b57cec5SDimitry Andric if (IsSatisfied)
18390b57cec5SDimitry Andric return nullptr;
18400b57cec5SDimitry Andric
18410b57cec5SDimitry Andric // Start tracking after we see the first state in which the value is
18420b57cec5SDimitry Andric // constrained.
18430b57cec5SDimitry Andric if (!IsTrackingTurnedOn)
18440b57cec5SDimitry Andric if (!isUnderconstrained(N))
18450b57cec5SDimitry Andric IsTrackingTurnedOn = true;
18460b57cec5SDimitry Andric if (!IsTrackingTurnedOn)
18470b57cec5SDimitry Andric return nullptr;
18480b57cec5SDimitry Andric
18490b57cec5SDimitry Andric // Check if in the previous state it was feasible for this constraint
18500b57cec5SDimitry Andric // to *not* be true.
18510b57cec5SDimitry Andric if (isUnderconstrained(PrevN)) {
18520b57cec5SDimitry Andric IsSatisfied = true;
18530b57cec5SDimitry Andric
18545e801ac6SDimitry Andric // At this point, the negation of the constraint should be infeasible. If it
18555e801ac6SDimitry Andric // is feasible, make sure that the negation of the constrainti was
18565e801ac6SDimitry Andric // infeasible in the current state. If it is feasible, we somehow missed
18575e801ac6SDimitry Andric // the transition point.
18580b57cec5SDimitry Andric assert(!isUnderconstrained(N));
18590b57cec5SDimitry Andric
18600b57cec5SDimitry Andric // Construct a new PathDiagnosticPiece.
18610b57cec5SDimitry Andric ProgramPoint P = N->getLocation();
186281ad6265SDimitry Andric
186381ad6265SDimitry Andric // If this node already have a specialized note, it's probably better
186481ad6265SDimitry Andric // than our generic note.
186581ad6265SDimitry Andric // FIXME: This only looks for note tags, not for other ways to add a note.
186681ad6265SDimitry Andric if (isa_and_nonnull<NoteTag>(P.getTag()))
186781ad6265SDimitry Andric return nullptr;
186881ad6265SDimitry Andric
18690b57cec5SDimitry Andric PathDiagnosticLocation L =
18700b57cec5SDimitry Andric PathDiagnosticLocation::create(P, BRC.getSourceManager());
18710b57cec5SDimitry Andric if (!L.isValid())
18720b57cec5SDimitry Andric return nullptr;
18730b57cec5SDimitry Andric
187406c3fb27SDimitry Andric auto X = std::make_shared<PathDiagnosticEventPiece>(L, Message);
18750b57cec5SDimitry Andric X->setTag(getTag());
18760b57cec5SDimitry Andric return std::move(X);
18770b57cec5SDimitry Andric }
18780b57cec5SDimitry Andric
18790b57cec5SDimitry Andric return nullptr;
18800b57cec5SDimitry Andric }
18810b57cec5SDimitry Andric
18820b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18830b57cec5SDimitry Andric // Implementation of SuppressInlineDefensiveChecksVisitor.
18840b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18850b57cec5SDimitry Andric
18860b57cec5SDimitry Andric SuppressInlineDefensiveChecksVisitor::
SuppressInlineDefensiveChecksVisitor(DefinedSVal Value,const ExplodedNode * N)18870b57cec5SDimitry Andric SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
18880b57cec5SDimitry Andric : V(Value) {
18890b57cec5SDimitry Andric // Check if the visitor is disabled.
18900b57cec5SDimitry Andric AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
18910b57cec5SDimitry Andric if (!Options.ShouldSuppressInlinedDefensiveChecks)
18920b57cec5SDimitry Andric IsSatisfied = true;
18930b57cec5SDimitry Andric }
18940b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const18950b57cec5SDimitry Andric void SuppressInlineDefensiveChecksVisitor::Profile(
18960b57cec5SDimitry Andric llvm::FoldingSetNodeID &ID) const {
18970b57cec5SDimitry Andric static int id = 0;
18980b57cec5SDimitry Andric ID.AddPointer(&id);
18990b57cec5SDimitry Andric ID.Add(V);
19000b57cec5SDimitry Andric }
19010b57cec5SDimitry Andric
getTag()19020b57cec5SDimitry Andric const char *SuppressInlineDefensiveChecksVisitor::getTag() {
19030b57cec5SDimitry Andric return "IDCVisitor";
19040b57cec5SDimitry Andric }
19050b57cec5SDimitry Andric
1906a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitNode(const ExplodedNode * Succ,BugReporterContext & BRC,PathSensitiveBugReport & BR)19070b57cec5SDimitry Andric SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
19080b57cec5SDimitry Andric BugReporterContext &BRC,
1909a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
19100b57cec5SDimitry Andric const ExplodedNode *Pred = Succ->getFirstPred();
19110b57cec5SDimitry Andric if (IsSatisfied)
19120b57cec5SDimitry Andric return nullptr;
19130b57cec5SDimitry Andric
19140b57cec5SDimitry Andric // Start tracking after we see the first state in which the value is null.
19150b57cec5SDimitry Andric if (!IsTrackingTurnedOn)
19160b57cec5SDimitry Andric if (Succ->getState()->isNull(V).isConstrainedTrue())
19170b57cec5SDimitry Andric IsTrackingTurnedOn = true;
19180b57cec5SDimitry Andric if (!IsTrackingTurnedOn)
19190b57cec5SDimitry Andric return nullptr;
19200b57cec5SDimitry Andric
19210b57cec5SDimitry Andric // Check if in the previous state it was feasible for this value
19220b57cec5SDimitry Andric // to *not* be null.
1923480093f4SDimitry Andric if (!Pred->getState()->isNull(V).isConstrainedTrue() &&
1924480093f4SDimitry Andric Succ->getState()->isNull(V).isConstrainedTrue()) {
19250b57cec5SDimitry Andric IsSatisfied = true;
19260b57cec5SDimitry Andric
19270b57cec5SDimitry Andric // Check if this is inlined defensive checks.
19280b57cec5SDimitry Andric const LocationContext *CurLC = Succ->getLocationContext();
19290b57cec5SDimitry Andric const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext();
19300b57cec5SDimitry Andric if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) {
19310b57cec5SDimitry Andric BR.markInvalid("Suppress IDC", CurLC);
19320b57cec5SDimitry Andric return nullptr;
19330b57cec5SDimitry Andric }
19340b57cec5SDimitry Andric
19350b57cec5SDimitry Andric // Treat defensive checks in function-like macros as if they were an inlined
19360b57cec5SDimitry Andric // defensive check. If the bug location is not in a macro and the
19370b57cec5SDimitry Andric // terminator for the current location is in a macro then suppress the
19380b57cec5SDimitry Andric // warning.
19390b57cec5SDimitry Andric auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
19400b57cec5SDimitry Andric
19410b57cec5SDimitry Andric if (!BugPoint)
19420b57cec5SDimitry Andric return nullptr;
19430b57cec5SDimitry Andric
19440b57cec5SDimitry Andric ProgramPoint CurPoint = Succ->getLocation();
19450b57cec5SDimitry Andric const Stmt *CurTerminatorStmt = nullptr;
19460b57cec5SDimitry Andric if (auto BE = CurPoint.getAs<BlockEdge>()) {
19470b57cec5SDimitry Andric CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt();
19480b57cec5SDimitry Andric } else if (auto SP = CurPoint.getAs<StmtPoint>()) {
19490b57cec5SDimitry Andric const Stmt *CurStmt = SP->getStmt();
19500b57cec5SDimitry Andric if (!CurStmt->getBeginLoc().isMacroID())
19510b57cec5SDimitry Andric return nullptr;
19520b57cec5SDimitry Andric
19530b57cec5SDimitry Andric CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap();
19540b57cec5SDimitry Andric CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminatorStmt();
19550b57cec5SDimitry Andric } else {
19560b57cec5SDimitry Andric return nullptr;
19570b57cec5SDimitry Andric }
19580b57cec5SDimitry Andric
19590b57cec5SDimitry Andric if (!CurTerminatorStmt)
19600b57cec5SDimitry Andric return nullptr;
19610b57cec5SDimitry Andric
19620b57cec5SDimitry Andric SourceLocation TerminatorLoc = CurTerminatorStmt->getBeginLoc();
19630b57cec5SDimitry Andric if (TerminatorLoc.isMacroID()) {
19640b57cec5SDimitry Andric SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
19650b57cec5SDimitry Andric
19660b57cec5SDimitry Andric // Suppress reports unless we are in that same macro.
19670b57cec5SDimitry Andric if (!BugLoc.isMacroID() ||
19680b57cec5SDimitry Andric getMacroName(BugLoc, BRC) != getMacroName(TerminatorLoc, BRC)) {
19690b57cec5SDimitry Andric BR.markInvalid("Suppress Macro IDC", CurLC);
19700b57cec5SDimitry Andric }
19710b57cec5SDimitry Andric return nullptr;
19720b57cec5SDimitry Andric }
19730b57cec5SDimitry Andric }
19740b57cec5SDimitry Andric return nullptr;
19750b57cec5SDimitry Andric }
19760b57cec5SDimitry Andric
19770b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
19780b57cec5SDimitry Andric // TrackControlDependencyCondBRVisitor.
19790b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
19800b57cec5SDimitry Andric
19810b57cec5SDimitry Andric namespace {
19820b57cec5SDimitry Andric /// Tracks the expressions that are a control dependency of the node that was
19830b57cec5SDimitry Andric /// supplied to the constructor.
19840b57cec5SDimitry Andric /// For example:
19850b57cec5SDimitry Andric ///
19860b57cec5SDimitry Andric /// cond = 1;
19870b57cec5SDimitry Andric /// if (cond)
19880b57cec5SDimitry Andric /// 10 / 0;
19890b57cec5SDimitry Andric ///
19900b57cec5SDimitry Andric /// An error is emitted at line 3. This visitor realizes that the branch
19910b57cec5SDimitry Andric /// on line 2 is a control dependency of line 3, and tracks it's condition via
19920b57cec5SDimitry Andric /// trackExpressionValue().
1993fe6060f1SDimitry Andric class TrackControlDependencyCondBRVisitor final
1994fe6060f1SDimitry Andric : public TrackingBugReporterVisitor {
19950b57cec5SDimitry Andric const ExplodedNode *Origin;
19960b57cec5SDimitry Andric ControlDependencyCalculator ControlDeps;
19970b57cec5SDimitry Andric llvm::SmallSet<const CFGBlock *, 32> VisitedBlocks;
19980b57cec5SDimitry Andric
19990b57cec5SDimitry Andric public:
TrackControlDependencyCondBRVisitor(TrackerRef ParentTracker,const ExplodedNode * O)2000fe6060f1SDimitry Andric TrackControlDependencyCondBRVisitor(TrackerRef ParentTracker,
2001fe6060f1SDimitry Andric const ExplodedNode *O)
2002fe6060f1SDimitry Andric : TrackingBugReporterVisitor(ParentTracker), Origin(O),
2003fe6060f1SDimitry Andric ControlDeps(&O->getCFG()) {}
20040b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const20050b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override {
20060b57cec5SDimitry Andric static int x = 0;
20070b57cec5SDimitry Andric ID.AddPointer(&x);
20080b57cec5SDimitry Andric }
20090b57cec5SDimitry Andric
2010a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
20110b57cec5SDimitry Andric BugReporterContext &BRC,
2012a7dea167SDimitry Andric PathSensitiveBugReport &BR) override;
20130b57cec5SDimitry Andric };
20140b57cec5SDimitry Andric } // end of anonymous namespace
20150b57cec5SDimitry Andric
20160b57cec5SDimitry Andric static std::shared_ptr<PathDiagnosticEventPiece>
constructDebugPieceForTrackedCondition(const Expr * Cond,const ExplodedNode * N,BugReporterContext & BRC)20170b57cec5SDimitry Andric constructDebugPieceForTrackedCondition(const Expr *Cond,
20180b57cec5SDimitry Andric const ExplodedNode *N,
20190b57cec5SDimitry Andric BugReporterContext &BRC) {
20200b57cec5SDimitry Andric
20210b57cec5SDimitry Andric if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE ||
20220b57cec5SDimitry Andric !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug)
20230b57cec5SDimitry Andric return nullptr;
20240b57cec5SDimitry Andric
20255ffd83dbSDimitry Andric std::string ConditionText = std::string(Lexer::getSourceText(
20260b57cec5SDimitry Andric CharSourceRange::getTokenRange(Cond->getSourceRange()),
20275ffd83dbSDimitry Andric BRC.getSourceManager(), BRC.getASTContext().getLangOpts()));
20280b57cec5SDimitry Andric
20290b57cec5SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(
20300b57cec5SDimitry Andric PathDiagnosticLocation::createBegin(
20310b57cec5SDimitry Andric Cond, BRC.getSourceManager(), N->getLocationContext()),
20320b57cec5SDimitry Andric (Twine() + "Tracking condition '" + ConditionText + "'").str());
20330b57cec5SDimitry Andric }
20340b57cec5SDimitry Andric
isAssertlikeBlock(const CFGBlock * B,ASTContext & Context)2035a7dea167SDimitry Andric static bool isAssertlikeBlock(const CFGBlock *B, ASTContext &Context) {
2036a7dea167SDimitry Andric if (B->succ_size() != 2)
2037a7dea167SDimitry Andric return false;
2038a7dea167SDimitry Andric
2039a7dea167SDimitry Andric const CFGBlock *Then = B->succ_begin()->getReachableBlock();
2040a7dea167SDimitry Andric const CFGBlock *Else = (B->succ_begin() + 1)->getReachableBlock();
2041a7dea167SDimitry Andric
2042a7dea167SDimitry Andric if (!Then || !Else)
2043a7dea167SDimitry Andric return false;
2044a7dea167SDimitry Andric
2045a7dea167SDimitry Andric if (Then->isInevitablySinking() != Else->isInevitablySinking())
2046a7dea167SDimitry Andric return true;
2047a7dea167SDimitry Andric
2048a7dea167SDimitry Andric // For the following condition the following CFG would be built:
2049a7dea167SDimitry Andric //
2050a7dea167SDimitry Andric // ------------->
2051a7dea167SDimitry Andric // / \
2052a7dea167SDimitry Andric // [B1] -> [B2] -> [B3] -> [sink]
2053a7dea167SDimitry Andric // assert(A && B || C); \ \
2054a7dea167SDimitry Andric // -----------> [go on with the execution]
2055a7dea167SDimitry Andric //
2056a7dea167SDimitry Andric // It so happens that CFGBlock::getTerminatorCondition returns 'A' for block
2057a7dea167SDimitry Andric // B1, 'A && B' for B2, and 'A && B || C' for B3. Let's check whether we
2058a7dea167SDimitry Andric // reached the end of the condition!
2059a7dea167SDimitry Andric if (const Stmt *ElseCond = Else->getTerminatorCondition())
2060a7dea167SDimitry Andric if (const auto *BinOp = dyn_cast<BinaryOperator>(ElseCond))
2061a7dea167SDimitry Andric if (BinOp->isLogicalOp())
2062a7dea167SDimitry Andric return isAssertlikeBlock(Else, Context);
2063a7dea167SDimitry Andric
2064a7dea167SDimitry Andric return false;
2065a7dea167SDimitry Andric }
2066a7dea167SDimitry Andric
2067a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)20680b57cec5SDimitry Andric TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N,
20690b57cec5SDimitry Andric BugReporterContext &BRC,
2070a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
20710b57cec5SDimitry Andric // We can only reason about control dependencies within the same stack frame.
20720b57cec5SDimitry Andric if (Origin->getStackFrame() != N->getStackFrame())
20730b57cec5SDimitry Andric return nullptr;
20740b57cec5SDimitry Andric
2075a7dea167SDimitry Andric CFGBlock *NB = const_cast<CFGBlock *>(N->getCFGBlock());
20760b57cec5SDimitry Andric
20770b57cec5SDimitry Andric // Skip if we already inspected this block.
20780b57cec5SDimitry Andric if (!VisitedBlocks.insert(NB).second)
20790b57cec5SDimitry Andric return nullptr;
20800b57cec5SDimitry Andric
2081a7dea167SDimitry Andric CFGBlock *OriginB = const_cast<CFGBlock *>(Origin->getCFGBlock());
20820b57cec5SDimitry Andric
20830b57cec5SDimitry Andric // TODO: Cache CFGBlocks for each ExplodedNode.
20840b57cec5SDimitry Andric if (!OriginB || !NB)
20850b57cec5SDimitry Andric return nullptr;
20860b57cec5SDimitry Andric
2087a7dea167SDimitry Andric if (isAssertlikeBlock(NB, BRC.getASTContext()))
2088a7dea167SDimitry Andric return nullptr;
2089a7dea167SDimitry Andric
20900b57cec5SDimitry Andric if (ControlDeps.isControlDependent(OriginB, NB)) {
2091a7dea167SDimitry Andric // We don't really want to explain for range loops. Evidence suggests that
2092a7dea167SDimitry Andric // the only thing that leads to is the addition of calls to operator!=.
2093a7dea167SDimitry Andric if (llvm::isa_and_nonnull<CXXForRangeStmt>(NB->getTerminatorStmt()))
2094a7dea167SDimitry Andric return nullptr;
2095a7dea167SDimitry Andric
20960b57cec5SDimitry Andric if (const Expr *Condition = NB->getLastCondition()) {
209781ad6265SDimitry Andric
209881ad6265SDimitry Andric // If we can't retrieve a sensible condition, just bail out.
209981ad6265SDimitry Andric const Expr *InnerExpr = peelOffOuterExpr(Condition, N);
210081ad6265SDimitry Andric if (!InnerExpr)
210181ad6265SDimitry Andric return nullptr;
210281ad6265SDimitry Andric
210381ad6265SDimitry Andric // If the condition was a function call, we likely won't gain much from
210481ad6265SDimitry Andric // tracking it either. Evidence suggests that it will mostly trigger in
210581ad6265SDimitry Andric // scenarios like this:
210681ad6265SDimitry Andric //
210781ad6265SDimitry Andric // void f(int *x) {
210881ad6265SDimitry Andric // x = nullptr;
210981ad6265SDimitry Andric // if (alwaysTrue()) // We don't need a whole lot of explanation
211081ad6265SDimitry Andric // // here, the function name is good enough.
211181ad6265SDimitry Andric // *x = 5;
211281ad6265SDimitry Andric // }
211381ad6265SDimitry Andric //
211481ad6265SDimitry Andric // Its easy to create a counterexample where this heuristic would make us
211581ad6265SDimitry Andric // lose valuable information, but we've never really seen one in practice.
211681ad6265SDimitry Andric if (isa<CallExpr>(InnerExpr))
211781ad6265SDimitry Andric return nullptr;
211881ad6265SDimitry Andric
21190b57cec5SDimitry Andric // Keeping track of the already tracked conditions on a visitor level
21200b57cec5SDimitry Andric // isn't sufficient, because a new visitor is created for each tracked
21210b57cec5SDimitry Andric // expression, hence the BugReport level set.
21220b57cec5SDimitry Andric if (BR.addTrackedCondition(N)) {
212381ad6265SDimitry Andric getParentTracker().track(InnerExpr, N,
2124fe6060f1SDimitry Andric {bugreporter::TrackingKind::Condition,
2125fe6060f1SDimitry Andric /*EnableNullFPSuppression=*/false});
21260b57cec5SDimitry Andric return constructDebugPieceForTrackedCondition(Condition, N, BRC);
21270b57cec5SDimitry Andric }
21280b57cec5SDimitry Andric }
21290b57cec5SDimitry Andric }
21300b57cec5SDimitry Andric
21310b57cec5SDimitry Andric return nullptr;
21320b57cec5SDimitry Andric }
21330b57cec5SDimitry Andric
21340b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
21350b57cec5SDimitry Andric // Implementation of trackExpressionValue.
21360b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
21370b57cec5SDimitry Andric
peelOffOuterExpr(const Expr * Ex,const ExplodedNode * N)213881ad6265SDimitry Andric static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) {
213981ad6265SDimitry Andric
21400b57cec5SDimitry Andric Ex = Ex->IgnoreParenCasts();
21410b57cec5SDimitry Andric if (const auto *FE = dyn_cast<FullExpr>(Ex))
21420b57cec5SDimitry Andric return peelOffOuterExpr(FE->getSubExpr(), N);
21430b57cec5SDimitry Andric if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ex))
21440b57cec5SDimitry Andric return peelOffOuterExpr(OVE->getSourceExpr(), N);
21450b57cec5SDimitry Andric if (const auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) {
21460b57cec5SDimitry Andric const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm());
21470b57cec5SDimitry Andric if (PropRef && PropRef->isMessagingGetter()) {
21480b57cec5SDimitry Andric const Expr *GetterMessageSend =
21490b57cec5SDimitry Andric POE->getSemanticExpr(POE->getNumSemanticExprs() - 1);
21500b57cec5SDimitry Andric assert(isa<ObjCMessageExpr>(GetterMessageSend->IgnoreParenCasts()));
21510b57cec5SDimitry Andric return peelOffOuterExpr(GetterMessageSend, N);
21520b57cec5SDimitry Andric }
21530b57cec5SDimitry Andric }
21540b57cec5SDimitry Andric
21550b57cec5SDimitry Andric // Peel off the ternary operator.
21560b57cec5SDimitry Andric if (const auto *CO = dyn_cast<ConditionalOperator>(Ex)) {
21570b57cec5SDimitry Andric // Find a node where the branching occurred and find out which branch
21580b57cec5SDimitry Andric // we took (true/false) by looking at the ExplodedGraph.
21590b57cec5SDimitry Andric const ExplodedNode *NI = N;
21600b57cec5SDimitry Andric do {
21610b57cec5SDimitry Andric ProgramPoint ProgPoint = NI->getLocation();
2162bdd1243dSDimitry Andric if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
21630b57cec5SDimitry Andric const CFGBlock *srcBlk = BE->getSrc();
21640b57cec5SDimitry Andric if (const Stmt *term = srcBlk->getTerminatorStmt()) {
21650b57cec5SDimitry Andric if (term == CO) {
21660b57cec5SDimitry Andric bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst());
21670b57cec5SDimitry Andric if (TookTrueBranch)
21680b57cec5SDimitry Andric return peelOffOuterExpr(CO->getTrueExpr(), N);
21690b57cec5SDimitry Andric else
21700b57cec5SDimitry Andric return peelOffOuterExpr(CO->getFalseExpr(), N);
21710b57cec5SDimitry Andric }
21720b57cec5SDimitry Andric }
21730b57cec5SDimitry Andric }
21740b57cec5SDimitry Andric NI = NI->getFirstPred();
21750b57cec5SDimitry Andric } while (NI);
21760b57cec5SDimitry Andric }
21770b57cec5SDimitry Andric
21780b57cec5SDimitry Andric if (auto *BO = dyn_cast<BinaryOperator>(Ex))
21790b57cec5SDimitry Andric if (const Expr *SubEx = peelOffPointerArithmetic(BO))
21800b57cec5SDimitry Andric return peelOffOuterExpr(SubEx, N);
21810b57cec5SDimitry Andric
21820b57cec5SDimitry Andric if (auto *UO = dyn_cast<UnaryOperator>(Ex)) {
21830b57cec5SDimitry Andric if (UO->getOpcode() == UO_LNot)
21840b57cec5SDimitry Andric return peelOffOuterExpr(UO->getSubExpr(), N);
21850b57cec5SDimitry Andric
21860b57cec5SDimitry Andric // FIXME: There's a hack in our Store implementation that always computes
21870b57cec5SDimitry Andric // field offsets around null pointers as if they are always equal to 0.
21880b57cec5SDimitry Andric // The idea here is to report accesses to fields as null dereferences
21890b57cec5SDimitry Andric // even though the pointer value that's being dereferenced is actually
21900b57cec5SDimitry Andric // the offset of the field rather than exactly 0.
21910b57cec5SDimitry Andric // See the FIXME in StoreManager's getLValueFieldOrIvar() method.
21920b57cec5SDimitry Andric // This code interacts heavily with this hack; otherwise the value
21930b57cec5SDimitry Andric // would not be null at all for most fields, so we'd be unable to track it.
21940b57cec5SDimitry Andric if (UO->getOpcode() == UO_AddrOf && UO->getSubExpr()->isLValue())
21950b57cec5SDimitry Andric if (const Expr *DerefEx = bugreporter::getDerefExpr(UO->getSubExpr()))
21960b57cec5SDimitry Andric return peelOffOuterExpr(DerefEx, N);
21970b57cec5SDimitry Andric }
21980b57cec5SDimitry Andric
21990b57cec5SDimitry Andric return Ex;
22000b57cec5SDimitry Andric }
22010b57cec5SDimitry Andric
22020b57cec5SDimitry Andric /// Find the ExplodedNode where the lvalue (the value of 'Ex')
22030b57cec5SDimitry Andric /// was computed.
findNodeForExpression(const ExplodedNode * N,const Expr * Inner)22040b57cec5SDimitry Andric static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
22050b57cec5SDimitry Andric const Expr *Inner) {
22060b57cec5SDimitry Andric while (N) {
2207a7dea167SDimitry Andric if (N->getStmtForDiagnostics() == Inner)
22080b57cec5SDimitry Andric return N;
22090b57cec5SDimitry Andric N = N->getFirstPred();
22100b57cec5SDimitry Andric }
22110b57cec5SDimitry Andric return N;
22120b57cec5SDimitry Andric }
22130b57cec5SDimitry Andric
2214fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
2215fe6060f1SDimitry Andric // Tracker implementation
2216fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
2217a7dea167SDimitry Andric
constructNote(StoreInfo SI,BugReporterContext & BRC,StringRef NodeText)2218fe6060f1SDimitry Andric PathDiagnosticPieceRef StoreHandler::constructNote(StoreInfo SI,
2219fe6060f1SDimitry Andric BugReporterContext &BRC,
2220fe6060f1SDimitry Andric StringRef NodeText) {
2221fe6060f1SDimitry Andric // Construct a new PathDiagnosticPiece.
2222fe6060f1SDimitry Andric ProgramPoint P = SI.StoreSite->getLocation();
2223fe6060f1SDimitry Andric PathDiagnosticLocation L;
2224fe6060f1SDimitry Andric if (P.getAs<CallEnter>() && SI.SourceOfTheValue)
2225fe6060f1SDimitry Andric L = PathDiagnosticLocation(SI.SourceOfTheValue, BRC.getSourceManager(),
2226fe6060f1SDimitry Andric P.getLocationContext());
22270b57cec5SDimitry Andric
2228fe6060f1SDimitry Andric if (!L.isValid() || !L.asLocation().isValid())
2229fe6060f1SDimitry Andric L = PathDiagnosticLocation::create(P, BRC.getSourceManager());
22300b57cec5SDimitry Andric
2231fe6060f1SDimitry Andric if (!L.isValid() || !L.asLocation().isValid())
2232fe6060f1SDimitry Andric return nullptr;
2233fe6060f1SDimitry Andric
2234fe6060f1SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(L, NodeText);
2235fe6060f1SDimitry Andric }
2236fe6060f1SDimitry Andric
2237bdd1243dSDimitry Andric namespace {
2238fe6060f1SDimitry Andric class DefaultStoreHandler final : public StoreHandler {
2239fe6060f1SDimitry Andric public:
2240fe6060f1SDimitry Andric using StoreHandler::StoreHandler;
2241fe6060f1SDimitry Andric
handle(StoreInfo SI,BugReporterContext & BRC,TrackingOptions Opts)2242fe6060f1SDimitry Andric PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC,
2243fe6060f1SDimitry Andric TrackingOptions Opts) override {
2244fe6060f1SDimitry Andric // Okay, we've found the binding. Emit an appropriate message.
2245fe6060f1SDimitry Andric SmallString<256> Buffer;
2246fe6060f1SDimitry Andric llvm::raw_svector_ostream OS(Buffer);
2247fe6060f1SDimitry Andric
2248fe6060f1SDimitry Andric switch (SI.StoreKind) {
2249fe6060f1SDimitry Andric case StoreInfo::Initialization:
2250fe6060f1SDimitry Andric case StoreInfo::BlockCapture:
2251fe6060f1SDimitry Andric showBRDiagnostics(OS, SI);
2252fe6060f1SDimitry Andric break;
2253fe6060f1SDimitry Andric case StoreInfo::CallArgument:
2254fe6060f1SDimitry Andric showBRParamDiagnostics(OS, SI);
2255fe6060f1SDimitry Andric break;
2256fe6060f1SDimitry Andric case StoreInfo::Assignment:
2257fe6060f1SDimitry Andric showBRDefaultDiagnostics(OS, SI);
2258fe6060f1SDimitry Andric break;
2259fe6060f1SDimitry Andric }
2260fe6060f1SDimitry Andric
2261fe6060f1SDimitry Andric if (Opts.Kind == bugreporter::TrackingKind::Condition)
2262fe6060f1SDimitry Andric OS << WillBeUsedForACondition;
2263fe6060f1SDimitry Andric
2264fe6060f1SDimitry Andric return constructNote(SI, BRC, OS.str());
2265fe6060f1SDimitry Andric }
2266fe6060f1SDimitry Andric };
2267fe6060f1SDimitry Andric
2268fe6060f1SDimitry Andric class ControlDependencyHandler final : public ExpressionHandler {
2269fe6060f1SDimitry Andric public:
2270fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2271fe6060f1SDimitry Andric
handle(const Expr * Inner,const ExplodedNode * InputNode,const ExplodedNode * LVNode,TrackingOptions Opts)2272fe6060f1SDimitry Andric Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2273fe6060f1SDimitry Andric const ExplodedNode *LVNode,
2274fe6060f1SDimitry Andric TrackingOptions Opts) override {
2275fe6060f1SDimitry Andric PathSensitiveBugReport &Report = getParentTracker().getReport();
22760b57cec5SDimitry Andric
22770b57cec5SDimitry Andric // We only track expressions if we believe that they are important. Chances
2278fe6060f1SDimitry Andric // are good that control dependencies to the tracking point are also
2279fe6060f1SDimitry Andric // important because of this, let's explain why we believe control reached
2280fe6060f1SDimitry Andric // this point.
2281fe6060f1SDimitry Andric // TODO: Shouldn't we track control dependencies of every bug location,
2282fe6060f1SDimitry Andric // rather than only tracked expressions?
2283fe6060f1SDimitry Andric if (LVNode->getState()
2284fe6060f1SDimitry Andric ->getAnalysisManager()
2285fe6060f1SDimitry Andric .getAnalyzerOptions()
2286fe6060f1SDimitry Andric .ShouldTrackConditions) {
2287fe6060f1SDimitry Andric Report.addVisitor<TrackControlDependencyCondBRVisitor>(
2288fe6060f1SDimitry Andric &getParentTracker(), InputNode);
2289fe6060f1SDimitry Andric return {/*FoundSomethingToTrack=*/true};
2290fe6060f1SDimitry Andric }
22910b57cec5SDimitry Andric
2292fe6060f1SDimitry Andric return {};
2293fe6060f1SDimitry Andric }
2294fe6060f1SDimitry Andric };
2295fe6060f1SDimitry Andric
2296fe6060f1SDimitry Andric class NilReceiverHandler final : public ExpressionHandler {
2297fe6060f1SDimitry Andric public:
2298fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2299fe6060f1SDimitry Andric
handle(const Expr * Inner,const ExplodedNode * InputNode,const ExplodedNode * LVNode,TrackingOptions Opts)2300fe6060f1SDimitry Andric Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2301fe6060f1SDimitry Andric const ExplodedNode *LVNode,
2302fe6060f1SDimitry Andric TrackingOptions Opts) override {
23030b57cec5SDimitry Andric // The message send could be nil due to the receiver being nil.
2304fe6060f1SDimitry Andric // At this point in the path, the receiver should be live since we are at
2305fe6060f1SDimitry Andric // the message send expr. If it is nil, start tracking it.
2306fe6060f1SDimitry Andric if (const Expr *Receiver =
2307fe6060f1SDimitry Andric NilReceiverBRVisitor::getNilReceiver(Inner, LVNode))
2308fe6060f1SDimitry Andric return getParentTracker().track(Receiver, LVNode, Opts);
23090b57cec5SDimitry Andric
2310fe6060f1SDimitry Andric return {};
2311fe6060f1SDimitry Andric }
2312fe6060f1SDimitry Andric };
2313fe6060f1SDimitry Andric
2314fe6060f1SDimitry Andric class ArrayIndexHandler final : public ExpressionHandler {
2315fe6060f1SDimitry Andric public:
2316fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2317fe6060f1SDimitry Andric
handle(const Expr * Inner,const ExplodedNode * InputNode,const ExplodedNode * LVNode,TrackingOptions Opts)2318fe6060f1SDimitry Andric Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2319fe6060f1SDimitry Andric const ExplodedNode *LVNode,
2320fe6060f1SDimitry Andric TrackingOptions Opts) override {
23210b57cec5SDimitry Andric // Track the index if this is an array subscript.
23220b57cec5SDimitry Andric if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner))
2323fe6060f1SDimitry Andric return getParentTracker().track(
2324fe6060f1SDimitry Andric Arr->getIdx(), LVNode,
2325fe6060f1SDimitry Andric {Opts.Kind, /*EnableNullFPSuppression*/ false});
2326fe6060f1SDimitry Andric
2327fe6060f1SDimitry Andric return {};
2328fe6060f1SDimitry Andric }
2329fe6060f1SDimitry Andric };
2330fe6060f1SDimitry Andric
2331fe6060f1SDimitry Andric // TODO: extract it into more handlers
2332fe6060f1SDimitry Andric class InterestingLValueHandler final : public ExpressionHandler {
2333fe6060f1SDimitry Andric public:
2334fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2335fe6060f1SDimitry Andric
handle(const Expr * Inner,const ExplodedNode * InputNode,const ExplodedNode * LVNode,TrackingOptions Opts)2336fe6060f1SDimitry Andric Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2337fe6060f1SDimitry Andric const ExplodedNode *LVNode,
2338fe6060f1SDimitry Andric TrackingOptions Opts) override {
2339fe6060f1SDimitry Andric ProgramStateRef LVState = LVNode->getState();
2340fe6060f1SDimitry Andric const StackFrameContext *SFC = LVNode->getStackFrame();
2341fe6060f1SDimitry Andric PathSensitiveBugReport &Report = getParentTracker().getReport();
2342fe6060f1SDimitry Andric Tracker::Result Result;
23430b57cec5SDimitry Andric
23440b57cec5SDimitry Andric // See if the expression we're interested refers to a variable.
23450b57cec5SDimitry Andric // If so, we can track both its contents and constraints on its value.
23460b57cec5SDimitry Andric if (ExplodedGraph::isInterestingLValueExpr(Inner)) {
23470b57cec5SDimitry Andric SVal LVal = LVNode->getSVal(Inner);
23480b57cec5SDimitry Andric
23490b57cec5SDimitry Andric const MemRegion *RR = getLocationRegionIfReference(Inner, LVNode);
23500b57cec5SDimitry Andric bool LVIsNull = LVState->isNull(LVal).isConstrainedTrue();
23510b57cec5SDimitry Andric
23520b57cec5SDimitry Andric // If this is a C++ reference to a null pointer, we are tracking the
23530b57cec5SDimitry Andric // pointer. In addition, we should find the store at which the reference
23540b57cec5SDimitry Andric // got initialized.
23550b57cec5SDimitry Andric if (RR && !LVIsNull)
2356fe6060f1SDimitry Andric Result.combineWith(getParentTracker().track(LVal, RR, Opts, SFC));
23570b57cec5SDimitry Andric
23580b57cec5SDimitry Andric // In case of C++ references, we want to differentiate between a null
23590b57cec5SDimitry Andric // reference and reference to null pointer.
23600b57cec5SDimitry Andric // If the LVal is null, check if we are dealing with null reference.
23610b57cec5SDimitry Andric // For those, we want to track the location of the reference.
2362fe6060f1SDimitry Andric const MemRegion *R =
2363fe6060f1SDimitry Andric (RR && LVIsNull) ? RR : LVNode->getSVal(Inner).getAsRegion();
23640b57cec5SDimitry Andric
23650b57cec5SDimitry Andric if (R) {
23660b57cec5SDimitry Andric
23670b57cec5SDimitry Andric // Mark both the variable region and its contents as interesting.
23680b57cec5SDimitry Andric SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
2369fe6060f1SDimitry Andric Report.addVisitor<NoStoreFuncVisitor>(cast<SubRegion>(R), Opts.Kind);
2370fe6060f1SDimitry Andric
2371fe6060f1SDimitry Andric // When we got here, we do have something to track, and we will
2372fe6060f1SDimitry Andric // interrupt.
2373fe6060f1SDimitry Andric Result.FoundSomethingToTrack = true;
2374fe6060f1SDimitry Andric Result.WasInterrupted = true;
23750b57cec5SDimitry Andric
23760b57cec5SDimitry Andric MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
2377fe6060f1SDimitry Andric LVNode, R, Opts.EnableNullFPSuppression, Report, V);
23780b57cec5SDimitry Andric
2379fe6060f1SDimitry Andric Report.markInteresting(V, Opts.Kind);
2380fe6060f1SDimitry Andric Report.addVisitor<UndefOrNullArgVisitor>(R);
23810b57cec5SDimitry Andric
2382fe6060f1SDimitry Andric // If the contents are symbolic and null, find out when they became
2383fe6060f1SDimitry Andric // null.
2384a7dea167SDimitry Andric if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true))
2385a7dea167SDimitry Andric if (LVState->isNull(V).isConstrainedTrue())
238606c3fb27SDimitry Andric Report.addVisitor<TrackConstraintBRVisitor>(
238706c3fb27SDimitry Andric V.castAs<DefinedSVal>(),
238806c3fb27SDimitry Andric /*Assumption=*/false, "Assuming pointer value is null");
23890b57cec5SDimitry Andric
23900b57cec5SDimitry Andric // Add visitor, which will suppress inline defensive checks.
23910b57cec5SDimitry Andric if (auto DV = V.getAs<DefinedSVal>())
2392fe6060f1SDimitry Andric if (!DV->isZeroConstant() && Opts.EnableNullFPSuppression)
2393fe6060f1SDimitry Andric // Note that LVNode may be too late (i.e., too far from the
2394fe6060f1SDimitry Andric // InputNode) because the lvalue may have been computed before the
2395fe6060f1SDimitry Andric // inlined call was evaluated. InputNode may as well be too early
2396fe6060f1SDimitry Andric // here, because the symbol is already dead; this, however, is fine
2397fe6060f1SDimitry Andric // because we can still find the node in which it collapsed to null
2398fe6060f1SDimitry Andric // previously.
2399fe6060f1SDimitry Andric Report.addVisitor<SuppressInlineDefensiveChecksVisitor>(*DV,
2400fe6060f1SDimitry Andric InputNode);
2401fe6060f1SDimitry Andric getParentTracker().track(V, R, Opts, SFC);
2402fe6060f1SDimitry Andric }
2403480093f4SDimitry Andric }
24040b57cec5SDimitry Andric
2405fe6060f1SDimitry Andric return Result;
24060b57cec5SDimitry Andric }
2407fe6060f1SDimitry Andric };
2408fe6060f1SDimitry Andric
2409fe6060f1SDimitry Andric /// Adds a ReturnVisitor if the given statement represents a call that was
2410fe6060f1SDimitry Andric /// inlined.
2411fe6060f1SDimitry Andric ///
2412fe6060f1SDimitry Andric /// This will search back through the ExplodedGraph, starting from the given
2413fe6060f1SDimitry Andric /// node, looking for when the given statement was processed. If it turns out
2414fe6060f1SDimitry Andric /// the statement is a call that was inlined, we add the visitor to the
2415fe6060f1SDimitry Andric /// bug report, so it can print a note later.
2416fe6060f1SDimitry Andric class InlinedFunctionCallHandler final : public ExpressionHandler {
2417fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2418fe6060f1SDimitry Andric
handle(const Expr * E,const ExplodedNode * InputNode,const ExplodedNode * ExprNode,TrackingOptions Opts)2419fe6060f1SDimitry Andric Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode,
2420fe6060f1SDimitry Andric const ExplodedNode *ExprNode,
2421fe6060f1SDimitry Andric TrackingOptions Opts) override {
2422fe6060f1SDimitry Andric if (!CallEvent::isCallStmt(E))
2423fe6060f1SDimitry Andric return {};
2424fe6060f1SDimitry Andric
2425fe6060f1SDimitry Andric // First, find when we processed the statement.
2426fe6060f1SDimitry Andric // If we work with a 'CXXNewExpr' that is going to be purged away before
2427fe6060f1SDimitry Andric // its call take place. We would catch that purge in the last condition
2428fe6060f1SDimitry Andric // as a 'StmtPoint' so we have to bypass it.
2429fe6060f1SDimitry Andric const bool BypassCXXNewExprEval = isa<CXXNewExpr>(E);
2430fe6060f1SDimitry Andric
2431fe6060f1SDimitry Andric // This is moving forward when we enter into another context.
2432fe6060f1SDimitry Andric const StackFrameContext *CurrentSFC = ExprNode->getStackFrame();
2433fe6060f1SDimitry Andric
2434fe6060f1SDimitry Andric do {
2435fe6060f1SDimitry Andric // If that is satisfied we found our statement as an inlined call.
2436bdd1243dSDimitry Andric if (std::optional<CallExitEnd> CEE =
2437bdd1243dSDimitry Andric ExprNode->getLocationAs<CallExitEnd>())
2438fe6060f1SDimitry Andric if (CEE->getCalleeContext()->getCallSite() == E)
2439fe6060f1SDimitry Andric break;
2440fe6060f1SDimitry Andric
2441fe6060f1SDimitry Andric // Try to move forward to the end of the call-chain.
2442fe6060f1SDimitry Andric ExprNode = ExprNode->getFirstPred();
2443fe6060f1SDimitry Andric if (!ExprNode)
2444fe6060f1SDimitry Andric break;
2445fe6060f1SDimitry Andric
2446fe6060f1SDimitry Andric const StackFrameContext *PredSFC = ExprNode->getStackFrame();
2447fe6060f1SDimitry Andric
2448fe6060f1SDimitry Andric // If that is satisfied we found our statement.
2449fe6060f1SDimitry Andric // FIXME: This code currently bypasses the call site for the
2450fe6060f1SDimitry Andric // conservatively evaluated allocator.
2451fe6060f1SDimitry Andric if (!BypassCXXNewExprEval)
2452bdd1243dSDimitry Andric if (std::optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>())
2453fe6060f1SDimitry Andric // See if we do not enter into another context.
2454fe6060f1SDimitry Andric if (SP->getStmt() == E && CurrentSFC == PredSFC)
2455fe6060f1SDimitry Andric break;
2456fe6060f1SDimitry Andric
2457fe6060f1SDimitry Andric CurrentSFC = PredSFC;
2458fe6060f1SDimitry Andric } while (ExprNode->getStackFrame() == CurrentSFC);
2459fe6060f1SDimitry Andric
2460fe6060f1SDimitry Andric // Next, step over any post-statement checks.
2461fe6060f1SDimitry Andric while (ExprNode && ExprNode->getLocation().getAs<PostStmt>())
2462fe6060f1SDimitry Andric ExprNode = ExprNode->getFirstPred();
2463fe6060f1SDimitry Andric if (!ExprNode)
2464fe6060f1SDimitry Andric return {};
2465fe6060f1SDimitry Andric
2466fe6060f1SDimitry Andric // Finally, see if we inlined the call.
2467bdd1243dSDimitry Andric std::optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>();
2468fe6060f1SDimitry Andric if (!CEE)
2469fe6060f1SDimitry Andric return {};
2470fe6060f1SDimitry Andric
2471fe6060f1SDimitry Andric const StackFrameContext *CalleeContext = CEE->getCalleeContext();
2472fe6060f1SDimitry Andric if (CalleeContext->getCallSite() != E)
2473fe6060f1SDimitry Andric return {};
2474fe6060f1SDimitry Andric
2475fe6060f1SDimitry Andric // Check the return value.
2476fe6060f1SDimitry Andric ProgramStateRef State = ExprNode->getState();
2477fe6060f1SDimitry Andric SVal RetVal = ExprNode->getSVal(E);
2478fe6060f1SDimitry Andric
2479fe6060f1SDimitry Andric // Handle cases where a reference is returned and then immediately used.
2480fe6060f1SDimitry Andric if (cast<Expr>(E)->isGLValue())
2481bdd1243dSDimitry Andric if (std::optional<Loc> LValue = RetVal.getAs<Loc>())
2482fe6060f1SDimitry Andric RetVal = State->getSVal(*LValue);
2483fe6060f1SDimitry Andric
2484fe6060f1SDimitry Andric // See if the return value is NULL. If so, suppress the report.
2485fe6060f1SDimitry Andric AnalyzerOptions &Options = State->getAnalysisManager().options;
2486fe6060f1SDimitry Andric
2487fe6060f1SDimitry Andric bool EnableNullFPSuppression = false;
2488fe6060f1SDimitry Andric if (Opts.EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths)
2489bdd1243dSDimitry Andric if (std::optional<Loc> RetLoc = RetVal.getAs<Loc>())
2490fe6060f1SDimitry Andric EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue();
2491fe6060f1SDimitry Andric
2492fe6060f1SDimitry Andric PathSensitiveBugReport &Report = getParentTracker().getReport();
2493fe6060f1SDimitry Andric Report.addVisitor<ReturnVisitor>(&getParentTracker(), CalleeContext,
2494fe6060f1SDimitry Andric EnableNullFPSuppression, Options,
2495fe6060f1SDimitry Andric Opts.Kind);
2496fe6060f1SDimitry Andric return {true};
24970b57cec5SDimitry Andric }
2498fe6060f1SDimitry Andric };
2499fe6060f1SDimitry Andric
2500fe6060f1SDimitry Andric class DefaultExpressionHandler final : public ExpressionHandler {
2501fe6060f1SDimitry Andric public:
2502fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2503fe6060f1SDimitry Andric
handle(const Expr * Inner,const ExplodedNode * InputNode,const ExplodedNode * LVNode,TrackingOptions Opts)2504fe6060f1SDimitry Andric Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode,
2505fe6060f1SDimitry Andric const ExplodedNode *LVNode,
2506fe6060f1SDimitry Andric TrackingOptions Opts) override {
2507fe6060f1SDimitry Andric ProgramStateRef LVState = LVNode->getState();
2508fe6060f1SDimitry Andric const StackFrameContext *SFC = LVNode->getStackFrame();
2509fe6060f1SDimitry Andric PathSensitiveBugReport &Report = getParentTracker().getReport();
2510fe6060f1SDimitry Andric Tracker::Result Result;
25110b57cec5SDimitry Andric
25120b57cec5SDimitry Andric // If the expression is not an "lvalue expression", we can still
25130b57cec5SDimitry Andric // track the constraints on its contents.
25140b57cec5SDimitry Andric SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext());
25150b57cec5SDimitry Andric
25160b57cec5SDimitry Andric // Is it a symbolic value?
25170b57cec5SDimitry Andric if (auto L = V.getAs<loc::MemRegionVal>()) {
25180b57cec5SDimitry Andric // FIXME: this is a hack for fixing a later crash when attempting to
25190b57cec5SDimitry Andric // dereference a void* pointer.
25200b57cec5SDimitry Andric // We should not try to dereference pointers at all when we don't care
25210b57cec5SDimitry Andric // what is written inside the pointer.
25220b57cec5SDimitry Andric bool CanDereference = true;
2523a7dea167SDimitry Andric if (const auto *SR = L->getRegionAs<SymbolicRegion>()) {
2524bdd1243dSDimitry Andric if (SR->getPointeeStaticType()->isVoidType())
25250b57cec5SDimitry Andric CanDereference = false;
2526a7dea167SDimitry Andric } else if (L->getRegionAs<AllocaRegion>())
2527a7dea167SDimitry Andric CanDereference = false;
25280b57cec5SDimitry Andric
25290b57cec5SDimitry Andric // At this point we are dealing with the region's LValue.
2530fe6060f1SDimitry Andric // However, if the rvalue is a symbolic region, we should track it as
2531fe6060f1SDimitry Andric // well. Try to use the correct type when looking up the value.
25320b57cec5SDimitry Andric SVal RVal;
2533a7dea167SDimitry Andric if (ExplodedGraph::isInterestingLValueExpr(Inner))
253481ad6265SDimitry Andric RVal = LVState->getRawSVal(*L, Inner->getType());
2535a7dea167SDimitry Andric else if (CanDereference)
25360b57cec5SDimitry Andric RVal = LVState->getSVal(L->getRegion());
25370b57cec5SDimitry Andric
2538a7dea167SDimitry Andric if (CanDereference) {
2539fe6060f1SDimitry Andric Report.addVisitor<UndefOrNullArgVisitor>(L->getRegion());
2540fe6060f1SDimitry Andric Result.FoundSomethingToTrack = true;
2541a7dea167SDimitry Andric
25420b57cec5SDimitry Andric if (auto KV = RVal.getAs<KnownSVal>())
2543fe6060f1SDimitry Andric Result.combineWith(
2544fe6060f1SDimitry Andric getParentTracker().track(*KV, L->getRegion(), Opts, SFC));
2545a7dea167SDimitry Andric }
25460b57cec5SDimitry Andric
25470b57cec5SDimitry Andric const MemRegion *RegionRVal = RVal.getAsRegion();
2548fe6060f1SDimitry Andric if (isa_and_nonnull<SymbolicRegion>(RegionRVal)) {
2549fe6060f1SDimitry Andric Report.markInteresting(RegionRVal, Opts.Kind);
2550fe6060f1SDimitry Andric Report.addVisitor<TrackConstraintBRVisitor>(
2551fe6060f1SDimitry Andric loc::MemRegionVal(RegionRVal),
255206c3fb27SDimitry Andric /*Assumption=*/false, "Assuming pointer value is null");
2553fe6060f1SDimitry Andric Result.FoundSomethingToTrack = true;
25540b57cec5SDimitry Andric }
25550b57cec5SDimitry Andric }
2556fe6060f1SDimitry Andric
2557fe6060f1SDimitry Andric return Result;
2558fe6060f1SDimitry Andric }
2559fe6060f1SDimitry Andric };
2560fe6060f1SDimitry Andric
2561fe6060f1SDimitry Andric /// Attempts to add visitors to track an RValue expression back to its point of
2562fe6060f1SDimitry Andric /// origin.
2563fe6060f1SDimitry Andric class PRValueHandler final : public ExpressionHandler {
2564fe6060f1SDimitry Andric public:
2565fe6060f1SDimitry Andric using ExpressionHandler::ExpressionHandler;
2566fe6060f1SDimitry Andric
handle(const Expr * E,const ExplodedNode * InputNode,const ExplodedNode * ExprNode,TrackingOptions Opts)2567fe6060f1SDimitry Andric Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode,
2568fe6060f1SDimitry Andric const ExplodedNode *ExprNode,
2569fe6060f1SDimitry Andric TrackingOptions Opts) override {
2570fe6060f1SDimitry Andric if (!E->isPRValue())
2571fe6060f1SDimitry Andric return {};
2572fe6060f1SDimitry Andric
2573fe6060f1SDimitry Andric const ExplodedNode *RVNode = findNodeForExpression(ExprNode, E);
2574fe6060f1SDimitry Andric if (!RVNode)
2575fe6060f1SDimitry Andric return {};
2576fe6060f1SDimitry Andric
2577bdd1243dSDimitry Andric Tracker::Result CombinedResult;
2578bdd1243dSDimitry Andric Tracker &Parent = getParentTracker();
2579bdd1243dSDimitry Andric
2580bdd1243dSDimitry Andric const auto track = [&CombinedResult, &Parent, ExprNode,
2581bdd1243dSDimitry Andric Opts](const Expr *Inner) {
2582bdd1243dSDimitry Andric CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts));
2583bdd1243dSDimitry Andric };
2584bdd1243dSDimitry Andric
2585bdd1243dSDimitry Andric // FIXME: Initializer lists can appear in many different contexts
2586bdd1243dSDimitry Andric // and most of them needs a special handling. For now let's handle
2587bdd1243dSDimitry Andric // what we can. If the initializer list only has 1 element, we track
2588bdd1243dSDimitry Andric // that.
2589bdd1243dSDimitry Andric // This snippet even handles nesting, e.g.: int *x{{{{{y}}}}};
2590bdd1243dSDimitry Andric if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
2591bdd1243dSDimitry Andric if (ILE->getNumInits() == 1) {
2592bdd1243dSDimitry Andric track(ILE->getInit(0));
2593bdd1243dSDimitry Andric
2594bdd1243dSDimitry Andric return CombinedResult;
2595bdd1243dSDimitry Andric }
2596bdd1243dSDimitry Andric
2597bdd1243dSDimitry Andric return {};
2598bdd1243dSDimitry Andric }
2599bdd1243dSDimitry Andric
2600fe6060f1SDimitry Andric ProgramStateRef RVState = RVNode->getState();
2601fe6060f1SDimitry Andric SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext());
2602fe6060f1SDimitry Andric const auto *BO = dyn_cast<BinaryOperator>(E);
2603fe6060f1SDimitry Andric
2604fe6060f1SDimitry Andric if (!BO || !BO->isMultiplicativeOp() || !V.isZeroConstant())
2605fe6060f1SDimitry Andric return {};
2606fe6060f1SDimitry Andric
2607fe6060f1SDimitry Andric SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext());
2608fe6060f1SDimitry Andric SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext());
2609fe6060f1SDimitry Andric
2610fe6060f1SDimitry Andric // Track both LHS and RHS of a multiplication.
2611fe6060f1SDimitry Andric if (BO->getOpcode() == BO_Mul) {
2612fe6060f1SDimitry Andric if (LHSV.isZeroConstant())
2613fe6060f1SDimitry Andric track(BO->getLHS());
2614fe6060f1SDimitry Andric if (RHSV.isZeroConstant())
2615fe6060f1SDimitry Andric track(BO->getRHS());
2616fe6060f1SDimitry Andric } else { // Track only the LHS of a division or a modulo.
2617fe6060f1SDimitry Andric if (LHSV.isZeroConstant())
2618fe6060f1SDimitry Andric track(BO->getLHS());
2619fe6060f1SDimitry Andric }
2620fe6060f1SDimitry Andric
2621fe6060f1SDimitry Andric return CombinedResult;
2622fe6060f1SDimitry Andric }
2623fe6060f1SDimitry Andric };
2624bdd1243dSDimitry Andric } // namespace
2625fe6060f1SDimitry Andric
Tracker(PathSensitiveBugReport & Report)2626fe6060f1SDimitry Andric Tracker::Tracker(PathSensitiveBugReport &Report) : Report(Report) {
2627fe6060f1SDimitry Andric // Default expression handlers.
2628fe6060f1SDimitry Andric addLowPriorityHandler<ControlDependencyHandler>();
2629fe6060f1SDimitry Andric addLowPriorityHandler<NilReceiverHandler>();
2630fe6060f1SDimitry Andric addLowPriorityHandler<ArrayIndexHandler>();
2631fe6060f1SDimitry Andric addLowPriorityHandler<InterestingLValueHandler>();
2632fe6060f1SDimitry Andric addLowPriorityHandler<InlinedFunctionCallHandler>();
2633fe6060f1SDimitry Andric addLowPriorityHandler<DefaultExpressionHandler>();
2634fe6060f1SDimitry Andric addLowPriorityHandler<PRValueHandler>();
2635fe6060f1SDimitry Andric // Default store handlers.
2636fe6060f1SDimitry Andric addHighPriorityHandler<DefaultStoreHandler>();
2637fe6060f1SDimitry Andric }
2638fe6060f1SDimitry Andric
track(const Expr * E,const ExplodedNode * N,TrackingOptions Opts)2639fe6060f1SDimitry Andric Tracker::Result Tracker::track(const Expr *E, const ExplodedNode *N,
2640fe6060f1SDimitry Andric TrackingOptions Opts) {
2641fe6060f1SDimitry Andric if (!E || !N)
2642fe6060f1SDimitry Andric return {};
2643fe6060f1SDimitry Andric
2644fe6060f1SDimitry Andric const Expr *Inner = peelOffOuterExpr(E, N);
2645fe6060f1SDimitry Andric const ExplodedNode *LVNode = findNodeForExpression(N, Inner);
2646fe6060f1SDimitry Andric if (!LVNode)
2647fe6060f1SDimitry Andric return {};
2648fe6060f1SDimitry Andric
2649fe6060f1SDimitry Andric Result CombinedResult;
2650fe6060f1SDimitry Andric // Iterate through the handlers in the order according to their priorities.
2651fe6060f1SDimitry Andric for (ExpressionHandlerPtr &Handler : ExpressionHandlers) {
2652fe6060f1SDimitry Andric CombinedResult.combineWith(Handler->handle(Inner, N, LVNode, Opts));
2653fe6060f1SDimitry Andric if (CombinedResult.WasInterrupted) {
2654fe6060f1SDimitry Andric // There is no need to confuse our users here.
2655fe6060f1SDimitry Andric // We got interrupted, but our users don't need to know about it.
2656fe6060f1SDimitry Andric CombinedResult.WasInterrupted = false;
2657fe6060f1SDimitry Andric break;
2658fe6060f1SDimitry Andric }
2659fe6060f1SDimitry Andric }
2660fe6060f1SDimitry Andric
2661fe6060f1SDimitry Andric return CombinedResult;
2662fe6060f1SDimitry Andric }
2663fe6060f1SDimitry Andric
track(SVal V,const MemRegion * R,TrackingOptions Opts,const StackFrameContext * Origin)2664fe6060f1SDimitry Andric Tracker::Result Tracker::track(SVal V, const MemRegion *R, TrackingOptions Opts,
2665fe6060f1SDimitry Andric const StackFrameContext *Origin) {
2666fe6060f1SDimitry Andric if (auto KV = V.getAs<KnownSVal>()) {
2667fe6060f1SDimitry Andric Report.addVisitor<StoreSiteFinder>(this, *KV, R, Opts, Origin);
2668fe6060f1SDimitry Andric return {true};
2669fe6060f1SDimitry Andric }
2670fe6060f1SDimitry Andric return {};
2671fe6060f1SDimitry Andric }
2672fe6060f1SDimitry Andric
handle(StoreInfo SI,BugReporterContext & BRC,TrackingOptions Opts)2673fe6060f1SDimitry Andric PathDiagnosticPieceRef Tracker::handle(StoreInfo SI, BugReporterContext &BRC,
2674fe6060f1SDimitry Andric TrackingOptions Opts) {
2675fe6060f1SDimitry Andric // Iterate through the handlers in the order according to their priorities.
2676fe6060f1SDimitry Andric for (StoreHandlerPtr &Handler : StoreHandlers) {
2677fe6060f1SDimitry Andric if (PathDiagnosticPieceRef Result = Handler->handle(SI, BRC, Opts))
2678fe6060f1SDimitry Andric // If the handler produced a non-null piece, return it.
2679fe6060f1SDimitry Andric // There is no need in asking other handlers.
2680fe6060f1SDimitry Andric return Result;
2681fe6060f1SDimitry Andric }
2682fe6060f1SDimitry Andric return {};
2683fe6060f1SDimitry Andric }
2684fe6060f1SDimitry Andric
trackExpressionValue(const ExplodedNode * InputNode,const Expr * E,PathSensitiveBugReport & Report,TrackingOptions Opts)2685fe6060f1SDimitry Andric bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
2686fe6060f1SDimitry Andric const Expr *E,
2687fe6060f1SDimitry Andric
2688fe6060f1SDimitry Andric PathSensitiveBugReport &Report,
2689fe6060f1SDimitry Andric TrackingOptions Opts) {
2690fe6060f1SDimitry Andric return Tracker::create(Report)
2691fe6060f1SDimitry Andric ->track(E, InputNode, Opts)
2692fe6060f1SDimitry Andric .FoundSomethingToTrack;
2693fe6060f1SDimitry Andric }
2694fe6060f1SDimitry Andric
trackStoredValue(KnownSVal V,const MemRegion * R,PathSensitiveBugReport & Report,TrackingOptions Opts,const StackFrameContext * Origin)2695fe6060f1SDimitry Andric void bugreporter::trackStoredValue(KnownSVal V, const MemRegion *R,
2696fe6060f1SDimitry Andric PathSensitiveBugReport &Report,
2697fe6060f1SDimitry Andric TrackingOptions Opts,
2698fe6060f1SDimitry Andric const StackFrameContext *Origin) {
2699fe6060f1SDimitry Andric Tracker::create(Report)->track(V, R, Opts, Origin);
27000b57cec5SDimitry Andric }
27010b57cec5SDimitry Andric
27020b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
27030b57cec5SDimitry Andric // Implementation of NulReceiverBRVisitor.
27040b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
27050b57cec5SDimitry Andric
getNilReceiver(const Stmt * S,const ExplodedNode * N)27060b57cec5SDimitry Andric const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
27070b57cec5SDimitry Andric const ExplodedNode *N) {
27080b57cec5SDimitry Andric const auto *ME = dyn_cast<ObjCMessageExpr>(S);
27090b57cec5SDimitry Andric if (!ME)
27100b57cec5SDimitry Andric return nullptr;
27110b57cec5SDimitry Andric if (const Expr *Receiver = ME->getInstanceReceiver()) {
27120b57cec5SDimitry Andric ProgramStateRef state = N->getState();
27130b57cec5SDimitry Andric SVal V = N->getSVal(Receiver);
27140b57cec5SDimitry Andric if (state->isNull(V).isConstrainedTrue())
27150b57cec5SDimitry Andric return Receiver;
27160b57cec5SDimitry Andric }
27170b57cec5SDimitry Andric return nullptr;
27180b57cec5SDimitry Andric }
27190b57cec5SDimitry Andric
2720a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)2721a7dea167SDimitry Andric NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
2722a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
2723bdd1243dSDimitry Andric std::optional<PreStmt> P = N->getLocationAs<PreStmt>();
27240b57cec5SDimitry Andric if (!P)
27250b57cec5SDimitry Andric return nullptr;
27260b57cec5SDimitry Andric
27270b57cec5SDimitry Andric const Stmt *S = P->getStmt();
27280b57cec5SDimitry Andric const Expr *Receiver = getNilReceiver(S, N);
27290b57cec5SDimitry Andric if (!Receiver)
27300b57cec5SDimitry Andric return nullptr;
27310b57cec5SDimitry Andric
27320b57cec5SDimitry Andric llvm::SmallString<256> Buf;
27330b57cec5SDimitry Andric llvm::raw_svector_ostream OS(Buf);
27340b57cec5SDimitry Andric
27350b57cec5SDimitry Andric if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) {
27360b57cec5SDimitry Andric OS << "'";
27370b57cec5SDimitry Andric ME->getSelector().print(OS);
27380b57cec5SDimitry Andric OS << "' not called";
27390b57cec5SDimitry Andric }
27400b57cec5SDimitry Andric else {
27410b57cec5SDimitry Andric OS << "No method is called";
27420b57cec5SDimitry Andric }
27430b57cec5SDimitry Andric OS << " because the receiver is nil";
27440b57cec5SDimitry Andric
27450b57cec5SDimitry Andric // The receiver was nil, and hence the method was skipped.
27460b57cec5SDimitry Andric // Register a BugReporterVisitor to issue a message telling us how
27470b57cec5SDimitry Andric // the receiver was null.
2748fe6060f1SDimitry Andric bugreporter::trackExpressionValue(N, Receiver, BR,
2749fe6060f1SDimitry Andric {bugreporter::TrackingKind::Thorough,
2750fe6060f1SDimitry Andric /*EnableNullFPSuppression*/ false});
27510b57cec5SDimitry Andric // Issue a message saying that the method was skipped.
27520b57cec5SDimitry Andric PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
27530b57cec5SDimitry Andric N->getLocationContext());
27540b57cec5SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(L, OS.str());
27550b57cec5SDimitry Andric }
27560b57cec5SDimitry Andric
27570b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
27580b57cec5SDimitry Andric // Visitor that tries to report interesting diagnostics from conditions.
27590b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
27600b57cec5SDimitry Andric
27610b57cec5SDimitry Andric /// Return the tag associated with this visitor. This tag will be used
27620b57cec5SDimitry Andric /// to make all PathDiagnosticPieces created by this visitor.
getTag()2763a7dea167SDimitry Andric const char *ConditionBRVisitor::getTag() { return "ConditionBRVisitor"; }
27640b57cec5SDimitry Andric
2765a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)2766a7dea167SDimitry Andric ConditionBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
2767a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
27680b57cec5SDimitry Andric auto piece = VisitNodeImpl(N, BRC, BR);
27690b57cec5SDimitry Andric if (piece) {
27700b57cec5SDimitry Andric piece->setTag(getTag());
27710b57cec5SDimitry Andric if (auto *ev = dyn_cast<PathDiagnosticEventPiece>(piece.get()))
27720b57cec5SDimitry Andric ev->setPrunable(true, /* override */ false);
27730b57cec5SDimitry Andric }
27740b57cec5SDimitry Andric return piece;
27750b57cec5SDimitry Andric }
27760b57cec5SDimitry Andric
2777a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitNodeImpl(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)27780b57cec5SDimitry Andric ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
2779a7dea167SDimitry Andric BugReporterContext &BRC,
2780a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
27810b57cec5SDimitry Andric ProgramPoint ProgPoint = N->getLocation();
27820b57cec5SDimitry Andric const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags =
27830b57cec5SDimitry Andric ExprEngine::geteagerlyAssumeBinOpBifurcationTags();
27840b57cec5SDimitry Andric
27850b57cec5SDimitry Andric // If an assumption was made on a branch, it should be caught
27860b57cec5SDimitry Andric // here by looking at the state transition.
2787bdd1243dSDimitry Andric if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
27880b57cec5SDimitry Andric const CFGBlock *SrcBlock = BE->getSrc();
27890b57cec5SDimitry Andric if (const Stmt *Term = SrcBlock->getTerminatorStmt()) {
27900b57cec5SDimitry Andric // If the tag of the previous node is 'Eagerly Assume...' the current
27910b57cec5SDimitry Andric // 'BlockEdge' has the same constraint information. We do not want to
27920b57cec5SDimitry Andric // report the value as it is just an assumption on the predecessor node
27930b57cec5SDimitry Andric // which will be caught in the next VisitNode() iteration as a 'PostStmt'.
27940b57cec5SDimitry Andric const ProgramPointTag *PreviousNodeTag =
27950b57cec5SDimitry Andric N->getFirstPred()->getLocation().getTag();
27960b57cec5SDimitry Andric if (PreviousNodeTag == Tags.first || PreviousNodeTag == Tags.second)
27970b57cec5SDimitry Andric return nullptr;
27980b57cec5SDimitry Andric
27990b57cec5SDimitry Andric return VisitTerminator(Term, N, SrcBlock, BE->getDst(), BR, BRC);
28000b57cec5SDimitry Andric }
28010b57cec5SDimitry Andric return nullptr;
28020b57cec5SDimitry Andric }
28030b57cec5SDimitry Andric
2804bdd1243dSDimitry Andric if (std::optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
28050b57cec5SDimitry Andric const ProgramPointTag *CurrentNodeTag = PS->getTag();
28060b57cec5SDimitry Andric if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second)
28070b57cec5SDimitry Andric return nullptr;
28080b57cec5SDimitry Andric
28090b57cec5SDimitry Andric bool TookTrue = CurrentNodeTag == Tags.first;
28100b57cec5SDimitry Andric return VisitTrueTest(cast<Expr>(PS->getStmt()), BRC, BR, N, TookTrue);
28110b57cec5SDimitry Andric }
28120b57cec5SDimitry Andric
28130b57cec5SDimitry Andric return nullptr;
28140b57cec5SDimitry Andric }
28150b57cec5SDimitry Andric
VisitTerminator(const Stmt * Term,const ExplodedNode * N,const CFGBlock * srcBlk,const CFGBlock * dstBlk,PathSensitiveBugReport & R,BugReporterContext & BRC)2816a7dea167SDimitry Andric PathDiagnosticPieceRef ConditionBRVisitor::VisitTerminator(
28170b57cec5SDimitry Andric const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk,
2818a7dea167SDimitry Andric const CFGBlock *dstBlk, PathSensitiveBugReport &R,
2819a7dea167SDimitry Andric BugReporterContext &BRC) {
28200b57cec5SDimitry Andric const Expr *Cond = nullptr;
28210b57cec5SDimitry Andric
28220b57cec5SDimitry Andric // In the code below, Term is a CFG terminator and Cond is a branch condition
28230b57cec5SDimitry Andric // expression upon which the decision is made on this terminator.
28240b57cec5SDimitry Andric //
28250b57cec5SDimitry Andric // For example, in "if (x == 0)", the "if (x == 0)" statement is a terminator,
28260b57cec5SDimitry Andric // and "x == 0" is the respective condition.
28270b57cec5SDimitry Andric //
28280b57cec5SDimitry Andric // Another example: in "if (x && y)", we've got two terminators and two
28290b57cec5SDimitry Andric // conditions due to short-circuit nature of operator "&&":
28300b57cec5SDimitry Andric // 1. The "if (x && y)" statement is a terminator,
28310b57cec5SDimitry Andric // and "y" is the respective condition.
28320b57cec5SDimitry Andric // 2. Also "x && ..." is another terminator,
28330b57cec5SDimitry Andric // and "x" is its condition.
28340b57cec5SDimitry Andric
28350b57cec5SDimitry Andric switch (Term->getStmtClass()) {
28360b57cec5SDimitry Andric // FIXME: Stmt::SwitchStmtClass is worth handling, however it is a bit
28370b57cec5SDimitry Andric // more tricky because there are more than two branches to account for.
28380b57cec5SDimitry Andric default:
28390b57cec5SDimitry Andric return nullptr;
28400b57cec5SDimitry Andric case Stmt::IfStmtClass:
28410b57cec5SDimitry Andric Cond = cast<IfStmt>(Term)->getCond();
28420b57cec5SDimitry Andric break;
28430b57cec5SDimitry Andric case Stmt::ConditionalOperatorClass:
28440b57cec5SDimitry Andric Cond = cast<ConditionalOperator>(Term)->getCond();
28450b57cec5SDimitry Andric break;
28460b57cec5SDimitry Andric case Stmt::BinaryOperatorClass:
28470b57cec5SDimitry Andric // When we encounter a logical operator (&& or ||) as a CFG terminator,
28480b57cec5SDimitry Andric // then the condition is actually its LHS; otherwise, we'd encounter
28490b57cec5SDimitry Andric // the parent, such as if-statement, as a terminator.
28500b57cec5SDimitry Andric const auto *BO = cast<BinaryOperator>(Term);
28510b57cec5SDimitry Andric assert(BO->isLogicalOp() &&
28520b57cec5SDimitry Andric "CFG terminator is not a short-circuit operator!");
28530b57cec5SDimitry Andric Cond = BO->getLHS();
28540b57cec5SDimitry Andric break;
28550b57cec5SDimitry Andric }
28560b57cec5SDimitry Andric
28570b57cec5SDimitry Andric Cond = Cond->IgnoreParens();
28580b57cec5SDimitry Andric
28590b57cec5SDimitry Andric // However, when we encounter a logical operator as a branch condition,
28600b57cec5SDimitry Andric // then the condition is actually its RHS, because LHS would be
28610b57cec5SDimitry Andric // the condition for the logical operator terminator.
28620b57cec5SDimitry Andric while (const auto *InnerBO = dyn_cast<BinaryOperator>(Cond)) {
28630b57cec5SDimitry Andric if (!InnerBO->isLogicalOp())
28640b57cec5SDimitry Andric break;
28650b57cec5SDimitry Andric Cond = InnerBO->getRHS()->IgnoreParens();
28660b57cec5SDimitry Andric }
28670b57cec5SDimitry Andric
28680b57cec5SDimitry Andric assert(Cond);
28690b57cec5SDimitry Andric assert(srcBlk->succ_size() == 2);
28700b57cec5SDimitry Andric const bool TookTrue = *(srcBlk->succ_begin()) == dstBlk;
28710b57cec5SDimitry Andric return VisitTrueTest(Cond, BRC, R, N, TookTrue);
28720b57cec5SDimitry Andric }
28730b57cec5SDimitry Andric
2874a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitTrueTest(const Expr * Cond,BugReporterContext & BRC,PathSensitiveBugReport & R,const ExplodedNode * N,bool TookTrue)28750b57cec5SDimitry Andric ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
2876a7dea167SDimitry Andric PathSensitiveBugReport &R,
2877a7dea167SDimitry Andric const ExplodedNode *N, bool TookTrue) {
28780b57cec5SDimitry Andric ProgramStateRef CurrentState = N->getState();
28790b57cec5SDimitry Andric ProgramStateRef PrevState = N->getFirstPred()->getState();
28800b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
28810b57cec5SDimitry Andric
28820b57cec5SDimitry Andric // If the constraint information is changed between the current and the
28830b57cec5SDimitry Andric // previous program state we assuming the newly seen constraint information.
28840b57cec5SDimitry Andric // If we cannot evaluate the condition (and the constraints are the same)
28850b57cec5SDimitry Andric // the analyzer has no information about the value and just assuming it.
28860b57cec5SDimitry Andric bool IsAssuming =
28870b57cec5SDimitry Andric !BRC.getStateManager().haveEqualConstraints(CurrentState, PrevState) ||
28880b57cec5SDimitry Andric CurrentState->getSVal(Cond, LCtx).isUnknownOrUndef();
28890b57cec5SDimitry Andric
28900b57cec5SDimitry Andric // These will be modified in code below, but we need to preserve the original
28910b57cec5SDimitry Andric // values in case we want to throw the generic message.
28920b57cec5SDimitry Andric const Expr *CondTmp = Cond;
28930b57cec5SDimitry Andric bool TookTrueTmp = TookTrue;
28940b57cec5SDimitry Andric
28950b57cec5SDimitry Andric while (true) {
28960b57cec5SDimitry Andric CondTmp = CondTmp->IgnoreParenCasts();
28970b57cec5SDimitry Andric switch (CondTmp->getStmtClass()) {
28980b57cec5SDimitry Andric default:
28990b57cec5SDimitry Andric break;
29000b57cec5SDimitry Andric case Stmt::BinaryOperatorClass:
29010b57cec5SDimitry Andric if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp),
29020b57cec5SDimitry Andric BRC, R, N, TookTrueTmp, IsAssuming))
29030b57cec5SDimitry Andric return P;
29040b57cec5SDimitry Andric break;
29050b57cec5SDimitry Andric case Stmt::DeclRefExprClass:
29060b57cec5SDimitry Andric if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp),
29070b57cec5SDimitry Andric BRC, R, N, TookTrueTmp, IsAssuming))
29080b57cec5SDimitry Andric return P;
29090b57cec5SDimitry Andric break;
29100b57cec5SDimitry Andric case Stmt::MemberExprClass:
29110b57cec5SDimitry Andric if (auto P = VisitTrueTest(Cond, cast<MemberExpr>(CondTmp),
29120b57cec5SDimitry Andric BRC, R, N, TookTrueTmp, IsAssuming))
29130b57cec5SDimitry Andric return P;
29140b57cec5SDimitry Andric break;
29150b57cec5SDimitry Andric case Stmt::UnaryOperatorClass: {
29160b57cec5SDimitry Andric const auto *UO = cast<UnaryOperator>(CondTmp);
29170b57cec5SDimitry Andric if (UO->getOpcode() == UO_LNot) {
29180b57cec5SDimitry Andric TookTrueTmp = !TookTrueTmp;
29190b57cec5SDimitry Andric CondTmp = UO->getSubExpr();
29200b57cec5SDimitry Andric continue;
29210b57cec5SDimitry Andric }
29220b57cec5SDimitry Andric break;
29230b57cec5SDimitry Andric }
29240b57cec5SDimitry Andric }
29250b57cec5SDimitry Andric break;
29260b57cec5SDimitry Andric }
29270b57cec5SDimitry Andric
29280b57cec5SDimitry Andric // Condition too complex to explain? Just say something so that the user
29290b57cec5SDimitry Andric // knew we've made some path decision at this point.
29300b57cec5SDimitry Andric // If it is too complex and we know the evaluation of the condition do not
29310b57cec5SDimitry Andric // repeat the note from 'BugReporter.cpp'
29320b57cec5SDimitry Andric if (!IsAssuming)
29330b57cec5SDimitry Andric return nullptr;
29340b57cec5SDimitry Andric
29350b57cec5SDimitry Andric PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
29360b57cec5SDimitry Andric if (!Loc.isValid() || !Loc.asLocation().isValid())
29370b57cec5SDimitry Andric return nullptr;
29380b57cec5SDimitry Andric
29390b57cec5SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(
29400b57cec5SDimitry Andric Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage);
29410b57cec5SDimitry Andric }
29420b57cec5SDimitry Andric
patternMatch(const Expr * Ex,const Expr * ParentEx,raw_ostream & Out,BugReporterContext & BRC,PathSensitiveBugReport & report,const ExplodedNode * N,std::optional<bool> & prunable,bool IsSameFieldName)2943bdd1243dSDimitry Andric bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx,
2944bdd1243dSDimitry Andric raw_ostream &Out, BugReporterContext &BRC,
2945a7dea167SDimitry Andric PathSensitiveBugReport &report,
29460b57cec5SDimitry Andric const ExplodedNode *N,
2947bdd1243dSDimitry Andric std::optional<bool> &prunable,
29480b57cec5SDimitry Andric bool IsSameFieldName) {
29490b57cec5SDimitry Andric const Expr *OriginalExpr = Ex;
29500b57cec5SDimitry Andric Ex = Ex->IgnoreParenCasts();
29510b57cec5SDimitry Andric
2952349cc55cSDimitry Andric if (isa<GNUNullExpr, ObjCBoolLiteralExpr, CXXBoolLiteralExpr, IntegerLiteral,
2953349cc55cSDimitry Andric FloatingLiteral>(Ex)) {
29540b57cec5SDimitry Andric // Use heuristics to determine if the expression is a macro
29550b57cec5SDimitry Andric // expanding to a literal and if so, use the macro's name.
29560b57cec5SDimitry Andric SourceLocation BeginLoc = OriginalExpr->getBeginLoc();
29570b57cec5SDimitry Andric SourceLocation EndLoc = OriginalExpr->getEndLoc();
29580b57cec5SDimitry Andric if (BeginLoc.isMacroID() && EndLoc.isMacroID()) {
2959a7dea167SDimitry Andric const SourceManager &SM = BRC.getSourceManager();
29600b57cec5SDimitry Andric const LangOptions &LO = BRC.getASTContext().getLangOpts();
29610b57cec5SDimitry Andric if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) &&
29620b57cec5SDimitry Andric Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) {
29630b57cec5SDimitry Andric CharSourceRange R = Lexer::getAsCharRange({BeginLoc, EndLoc}, SM, LO);
29640b57cec5SDimitry Andric Out << Lexer::getSourceText(R, SM, LO);
29650b57cec5SDimitry Andric return false;
29660b57cec5SDimitry Andric }
29670b57cec5SDimitry Andric }
29680b57cec5SDimitry Andric }
29690b57cec5SDimitry Andric
29700b57cec5SDimitry Andric if (const auto *DR = dyn_cast<DeclRefExpr>(Ex)) {
29710b57cec5SDimitry Andric const bool quotes = isa<VarDecl>(DR->getDecl());
29720b57cec5SDimitry Andric if (quotes) {
29730b57cec5SDimitry Andric Out << '\'';
29740b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
29750b57cec5SDimitry Andric const ProgramState *state = N->getState().get();
29760b57cec5SDimitry Andric if (const MemRegion *R = state->getLValue(cast<VarDecl>(DR->getDecl()),
29770b57cec5SDimitry Andric LCtx).getAsRegion()) {
29780b57cec5SDimitry Andric if (report.isInteresting(R))
29790b57cec5SDimitry Andric prunable = false;
29800b57cec5SDimitry Andric else {
29810b57cec5SDimitry Andric const ProgramState *state = N->getState().get();
29820b57cec5SDimitry Andric SVal V = state->getSVal(R);
29830b57cec5SDimitry Andric if (report.isInteresting(V))
29840b57cec5SDimitry Andric prunable = false;
29850b57cec5SDimitry Andric }
29860b57cec5SDimitry Andric }
29870b57cec5SDimitry Andric }
29880b57cec5SDimitry Andric Out << DR->getDecl()->getDeclName().getAsString();
29890b57cec5SDimitry Andric if (quotes)
29900b57cec5SDimitry Andric Out << '\'';
29910b57cec5SDimitry Andric return quotes;
29920b57cec5SDimitry Andric }
29930b57cec5SDimitry Andric
29940b57cec5SDimitry Andric if (const auto *IL = dyn_cast<IntegerLiteral>(Ex)) {
29950b57cec5SDimitry Andric QualType OriginalTy = OriginalExpr->getType();
29960b57cec5SDimitry Andric if (OriginalTy->isPointerType()) {
29970b57cec5SDimitry Andric if (IL->getValue() == 0) {
29980b57cec5SDimitry Andric Out << "null";
29990b57cec5SDimitry Andric return false;
30000b57cec5SDimitry Andric }
30010b57cec5SDimitry Andric }
30020b57cec5SDimitry Andric else if (OriginalTy->isObjCObjectPointerType()) {
30030b57cec5SDimitry Andric if (IL->getValue() == 0) {
30040b57cec5SDimitry Andric Out << "nil";
30050b57cec5SDimitry Andric return false;
30060b57cec5SDimitry Andric }
30070b57cec5SDimitry Andric }
30080b57cec5SDimitry Andric
30090b57cec5SDimitry Andric Out << IL->getValue();
30100b57cec5SDimitry Andric return false;
30110b57cec5SDimitry Andric }
30120b57cec5SDimitry Andric
30130b57cec5SDimitry Andric if (const auto *ME = dyn_cast<MemberExpr>(Ex)) {
30140b57cec5SDimitry Andric if (!IsSameFieldName)
30150b57cec5SDimitry Andric Out << "field '" << ME->getMemberDecl()->getName() << '\'';
30160b57cec5SDimitry Andric else
30170b57cec5SDimitry Andric Out << '\''
30180b57cec5SDimitry Andric << Lexer::getSourceText(
30190b57cec5SDimitry Andric CharSourceRange::getTokenRange(Ex->getSourceRange()),
302004eeddc0SDimitry Andric BRC.getSourceManager(), BRC.getASTContext().getLangOpts(),
302104eeddc0SDimitry Andric nullptr)
30220b57cec5SDimitry Andric << '\'';
30230b57cec5SDimitry Andric }
30240b57cec5SDimitry Andric
30250b57cec5SDimitry Andric return false;
30260b57cec5SDimitry Andric }
30270b57cec5SDimitry Andric
VisitTrueTest(const Expr * Cond,const BinaryOperator * BExpr,BugReporterContext & BRC,PathSensitiveBugReport & R,const ExplodedNode * N,bool TookTrue,bool IsAssuming)3028a7dea167SDimitry Andric PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
30290b57cec5SDimitry Andric const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC,
3030a7dea167SDimitry Andric PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue,
3031a7dea167SDimitry Andric bool IsAssuming) {
30320b57cec5SDimitry Andric bool shouldInvert = false;
3033bdd1243dSDimitry Andric std::optional<bool> shouldPrune;
30340b57cec5SDimitry Andric
30350b57cec5SDimitry Andric // Check if the field name of the MemberExprs is ambiguous. Example:
30360b57cec5SDimitry Andric // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'.
30370b57cec5SDimitry Andric bool IsSameFieldName = false;
3038a7dea167SDimitry Andric const auto *LhsME = dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts());
3039a7dea167SDimitry Andric const auto *RhsME = dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts());
3040a7dea167SDimitry Andric
3041a7dea167SDimitry Andric if (LhsME && RhsME)
3042a7dea167SDimitry Andric IsSameFieldName =
3043a7dea167SDimitry Andric LhsME->getMemberDecl()->getName() == RhsME->getMemberDecl()->getName();
30440b57cec5SDimitry Andric
30450b57cec5SDimitry Andric SmallString<128> LhsString, RhsString;
30460b57cec5SDimitry Andric {
30470b57cec5SDimitry Andric llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
30480b57cec5SDimitry Andric const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, BRC, R,
30490b57cec5SDimitry Andric N, shouldPrune, IsSameFieldName);
30500b57cec5SDimitry Andric const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, BRC, R,
30510b57cec5SDimitry Andric N, shouldPrune, IsSameFieldName);
30520b57cec5SDimitry Andric
30530b57cec5SDimitry Andric shouldInvert = !isVarLHS && isVarRHS;
30540b57cec5SDimitry Andric }
30550b57cec5SDimitry Andric
30560b57cec5SDimitry Andric BinaryOperator::Opcode Op = BExpr->getOpcode();
30570b57cec5SDimitry Andric
30580b57cec5SDimitry Andric if (BinaryOperator::isAssignmentOp(Op)) {
30590b57cec5SDimitry Andric // For assignment operators, all that we care about is that the LHS
30600b57cec5SDimitry Andric // evaluates to "true" or "false".
30610b57cec5SDimitry Andric return VisitConditionVariable(LhsString, BExpr->getLHS(), BRC, R, N,
30620b57cec5SDimitry Andric TookTrue);
30630b57cec5SDimitry Andric }
30640b57cec5SDimitry Andric
30650b57cec5SDimitry Andric // For non-assignment operations, we require that we can understand
30660b57cec5SDimitry Andric // both the LHS and RHS.
30670b57cec5SDimitry Andric if (LhsString.empty() || RhsString.empty() ||
30680b57cec5SDimitry Andric !BinaryOperator::isComparisonOp(Op) || Op == BO_Cmp)
30690b57cec5SDimitry Andric return nullptr;
30700b57cec5SDimitry Andric
30710b57cec5SDimitry Andric // Should we invert the strings if the LHS is not a variable name?
30720b57cec5SDimitry Andric SmallString<256> buf;
30730b57cec5SDimitry Andric llvm::raw_svector_ostream Out(buf);
30740b57cec5SDimitry Andric Out << (IsAssuming ? "Assuming " : "")
30750b57cec5SDimitry Andric << (shouldInvert ? RhsString : LhsString) << " is ";
30760b57cec5SDimitry Andric
30770b57cec5SDimitry Andric // Do we need to invert the opcode?
30780b57cec5SDimitry Andric if (shouldInvert)
30790b57cec5SDimitry Andric switch (Op) {
30800b57cec5SDimitry Andric default: break;
30810b57cec5SDimitry Andric case BO_LT: Op = BO_GT; break;
30820b57cec5SDimitry Andric case BO_GT: Op = BO_LT; break;
30830b57cec5SDimitry Andric case BO_LE: Op = BO_GE; break;
30840b57cec5SDimitry Andric case BO_GE: Op = BO_LE; break;
30850b57cec5SDimitry Andric }
30860b57cec5SDimitry Andric
30870b57cec5SDimitry Andric if (!TookTrue)
30880b57cec5SDimitry Andric switch (Op) {
30890b57cec5SDimitry Andric case BO_EQ: Op = BO_NE; break;
30900b57cec5SDimitry Andric case BO_NE: Op = BO_EQ; break;
30910b57cec5SDimitry Andric case BO_LT: Op = BO_GE; break;
30920b57cec5SDimitry Andric case BO_GT: Op = BO_LE; break;
30930b57cec5SDimitry Andric case BO_LE: Op = BO_GT; break;
30940b57cec5SDimitry Andric case BO_GE: Op = BO_LT; break;
30950b57cec5SDimitry Andric default:
30960b57cec5SDimitry Andric return nullptr;
30970b57cec5SDimitry Andric }
30980b57cec5SDimitry Andric
30990b57cec5SDimitry Andric switch (Op) {
31000b57cec5SDimitry Andric case BO_EQ:
31010b57cec5SDimitry Andric Out << "equal to ";
31020b57cec5SDimitry Andric break;
31030b57cec5SDimitry Andric case BO_NE:
31040b57cec5SDimitry Andric Out << "not equal to ";
31050b57cec5SDimitry Andric break;
31060b57cec5SDimitry Andric default:
31070b57cec5SDimitry Andric Out << BinaryOperator::getOpcodeStr(Op) << ' ';
31080b57cec5SDimitry Andric break;
31090b57cec5SDimitry Andric }
31100b57cec5SDimitry Andric
31110b57cec5SDimitry Andric Out << (shouldInvert ? LhsString : RhsString);
31120b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
3113a7dea167SDimitry Andric const SourceManager &SM = BRC.getSourceManager();
3114a7dea167SDimitry Andric
3115a7dea167SDimitry Andric if (isVarAnInterestingCondition(BExpr->getLHS(), N, &R) ||
3116a7dea167SDimitry Andric isVarAnInterestingCondition(BExpr->getRHS(), N, &R))
3117a7dea167SDimitry Andric Out << WillBeUsedForACondition;
31180b57cec5SDimitry Andric
31190b57cec5SDimitry Andric // Convert 'field ...' to 'Field ...' if it is a MemberExpr.
31205ffd83dbSDimitry Andric std::string Message = std::string(Out.str());
31210b57cec5SDimitry Andric Message[0] = toupper(Message[0]);
31220b57cec5SDimitry Andric
3123a7dea167SDimitry Andric // If we know the value create a pop-up note to the value part of 'BExpr'.
3124a7dea167SDimitry Andric if (!IsAssuming) {
3125a7dea167SDimitry Andric PathDiagnosticLocation Loc;
3126a7dea167SDimitry Andric if (!shouldInvert) {
3127a7dea167SDimitry Andric if (LhsME && LhsME->getMemberLoc().isValid())
3128a7dea167SDimitry Andric Loc = PathDiagnosticLocation(LhsME->getMemberLoc(), SM);
3129a7dea167SDimitry Andric else
3130a7dea167SDimitry Andric Loc = PathDiagnosticLocation(BExpr->getLHS(), SM, LCtx);
3131a7dea167SDimitry Andric } else {
3132a7dea167SDimitry Andric if (RhsME && RhsME->getMemberLoc().isValid())
3133a7dea167SDimitry Andric Loc = PathDiagnosticLocation(RhsME->getMemberLoc(), SM);
3134a7dea167SDimitry Andric else
3135a7dea167SDimitry Andric Loc = PathDiagnosticLocation(BExpr->getRHS(), SM, LCtx);
3136a7dea167SDimitry Andric }
31370b57cec5SDimitry Andric
3138a7dea167SDimitry Andric return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message);
3139a7dea167SDimitry Andric }
3140a7dea167SDimitry Andric
3141a7dea167SDimitry Andric PathDiagnosticLocation Loc(Cond, SM, LCtx);
31420b57cec5SDimitry Andric auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message);
314381ad6265SDimitry Andric if (shouldPrune)
3144bdd1243dSDimitry Andric event->setPrunable(*shouldPrune);
31450b57cec5SDimitry Andric return event;
31460b57cec5SDimitry Andric }
31470b57cec5SDimitry Andric
VisitConditionVariable(StringRef LhsString,const Expr * CondVarExpr,BugReporterContext & BRC,PathSensitiveBugReport & report,const ExplodedNode * N,bool TookTrue)3148a7dea167SDimitry Andric PathDiagnosticPieceRef ConditionBRVisitor::VisitConditionVariable(
31490b57cec5SDimitry Andric StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC,
3150a7dea167SDimitry Andric PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue) {
31510b57cec5SDimitry Andric // FIXME: If there's already a constraint tracker for this variable,
31520b57cec5SDimitry Andric // we shouldn't emit anything here (c.f. the double note in
31530b57cec5SDimitry Andric // test/Analysis/inlining/path-notes.c)
31540b57cec5SDimitry Andric SmallString<256> buf;
31550b57cec5SDimitry Andric llvm::raw_svector_ostream Out(buf);
31560b57cec5SDimitry Andric Out << "Assuming " << LhsString << " is ";
31570b57cec5SDimitry Andric
31580b57cec5SDimitry Andric if (!printValue(CondVarExpr, Out, N, TookTrue, /*IsAssuming=*/true))
31590b57cec5SDimitry Andric return nullptr;
31600b57cec5SDimitry Andric
31610b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
31620b57cec5SDimitry Andric PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx);
3163a7dea167SDimitry Andric
3164a7dea167SDimitry Andric if (isVarAnInterestingCondition(CondVarExpr, N, &report))
3165a7dea167SDimitry Andric Out << WillBeUsedForACondition;
3166a7dea167SDimitry Andric
31670b57cec5SDimitry Andric auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
31680b57cec5SDimitry Andric
3169a7dea167SDimitry Andric if (isInterestingExpr(CondVarExpr, N, &report))
31700b57cec5SDimitry Andric event->setPrunable(false);
31710b57cec5SDimitry Andric
31720b57cec5SDimitry Andric return event;
31730b57cec5SDimitry Andric }
31740b57cec5SDimitry Andric
VisitTrueTest(const Expr * Cond,const DeclRefExpr * DRE,BugReporterContext & BRC,PathSensitiveBugReport & report,const ExplodedNode * N,bool TookTrue,bool IsAssuming)3175a7dea167SDimitry Andric PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
31760b57cec5SDimitry Andric const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC,
3177a7dea167SDimitry Andric PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue,
3178a7dea167SDimitry Andric bool IsAssuming) {
31790b57cec5SDimitry Andric const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
31800b57cec5SDimitry Andric if (!VD)
31810b57cec5SDimitry Andric return nullptr;
31820b57cec5SDimitry Andric
31830b57cec5SDimitry Andric SmallString<256> Buf;
31840b57cec5SDimitry Andric llvm::raw_svector_ostream Out(Buf);
31850b57cec5SDimitry Andric
31860b57cec5SDimitry Andric Out << (IsAssuming ? "Assuming '" : "'") << VD->getDeclName() << "' is ";
31870b57cec5SDimitry Andric
31880b57cec5SDimitry Andric if (!printValue(DRE, Out, N, TookTrue, IsAssuming))
31890b57cec5SDimitry Andric return nullptr;
31900b57cec5SDimitry Andric
31910b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
31920b57cec5SDimitry Andric
3193a7dea167SDimitry Andric if (isVarAnInterestingCondition(DRE, N, &report))
3194a7dea167SDimitry Andric Out << WillBeUsedForACondition;
3195a7dea167SDimitry Andric
3196a7dea167SDimitry Andric // If we know the value create a pop-up note to the 'DRE'.
3197a7dea167SDimitry Andric if (!IsAssuming) {
3198a7dea167SDimitry Andric PathDiagnosticLocation Loc(DRE, BRC.getSourceManager(), LCtx);
31990b57cec5SDimitry Andric return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str());
3200a7dea167SDimitry Andric }
32010b57cec5SDimitry Andric
3202a7dea167SDimitry Andric PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
32030b57cec5SDimitry Andric auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
3204a7dea167SDimitry Andric
3205a7dea167SDimitry Andric if (isInterestingExpr(DRE, N, &report))
32060b57cec5SDimitry Andric event->setPrunable(false);
3207a7dea167SDimitry Andric
32080b57cec5SDimitry Andric return std::move(event);
32090b57cec5SDimitry Andric }
32100b57cec5SDimitry Andric
VisitTrueTest(const Expr * Cond,const MemberExpr * ME,BugReporterContext & BRC,PathSensitiveBugReport & report,const ExplodedNode * N,bool TookTrue,bool IsAssuming)3211a7dea167SDimitry Andric PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
32120b57cec5SDimitry Andric const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC,
3213a7dea167SDimitry Andric PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue,
3214a7dea167SDimitry Andric bool IsAssuming) {
32150b57cec5SDimitry Andric SmallString<256> Buf;
32160b57cec5SDimitry Andric llvm::raw_svector_ostream Out(Buf);
32170b57cec5SDimitry Andric
32180b57cec5SDimitry Andric Out << (IsAssuming ? "Assuming field '" : "Field '")
32190b57cec5SDimitry Andric << ME->getMemberDecl()->getName() << "' is ";
32200b57cec5SDimitry Andric
32210b57cec5SDimitry Andric if (!printValue(ME, Out, N, TookTrue, IsAssuming))
32220b57cec5SDimitry Andric return nullptr;
32230b57cec5SDimitry Andric
32240b57cec5SDimitry Andric const LocationContext *LCtx = N->getLocationContext();
3225a7dea167SDimitry Andric PathDiagnosticLocation Loc;
3226a7dea167SDimitry Andric
3227a7dea167SDimitry Andric // If we know the value create a pop-up note to the member of the MemberExpr.
3228a7dea167SDimitry Andric if (!IsAssuming && ME->getMemberLoc().isValid())
3229a7dea167SDimitry Andric Loc = PathDiagnosticLocation(ME->getMemberLoc(), BRC.getSourceManager());
3230a7dea167SDimitry Andric else
3231a7dea167SDimitry Andric Loc = PathDiagnosticLocation(Cond, BRC.getSourceManager(), LCtx);
3232a7dea167SDimitry Andric
32330b57cec5SDimitry Andric if (!Loc.isValid() || !Loc.asLocation().isValid())
32340b57cec5SDimitry Andric return nullptr;
32350b57cec5SDimitry Andric
3236a7dea167SDimitry Andric if (isVarAnInterestingCondition(ME, N, &report))
3237a7dea167SDimitry Andric Out << WillBeUsedForACondition;
3238a7dea167SDimitry Andric
32390b57cec5SDimitry Andric // If we know the value create a pop-up note.
32400b57cec5SDimitry Andric if (!IsAssuming)
32410b57cec5SDimitry Andric return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str());
32420b57cec5SDimitry Andric
3243a7dea167SDimitry Andric auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
3244a7dea167SDimitry Andric if (isInterestingExpr(ME, N, &report))
3245a7dea167SDimitry Andric event->setPrunable(false);
3246a7dea167SDimitry Andric return event;
32470b57cec5SDimitry Andric }
32480b57cec5SDimitry Andric
printValue(const Expr * CondVarExpr,raw_ostream & Out,const ExplodedNode * N,bool TookTrue,bool IsAssuming)32490b57cec5SDimitry Andric bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
32500b57cec5SDimitry Andric const ExplodedNode *N, bool TookTrue,
32510b57cec5SDimitry Andric bool IsAssuming) {
32520b57cec5SDimitry Andric QualType Ty = CondVarExpr->getType();
32530b57cec5SDimitry Andric
32540b57cec5SDimitry Andric if (Ty->isPointerType()) {
32550b57cec5SDimitry Andric Out << (TookTrue ? "non-null" : "null");
32560b57cec5SDimitry Andric return true;
32570b57cec5SDimitry Andric }
32580b57cec5SDimitry Andric
32590b57cec5SDimitry Andric if (Ty->isObjCObjectPointerType()) {
32600b57cec5SDimitry Andric Out << (TookTrue ? "non-nil" : "nil");
32610b57cec5SDimitry Andric return true;
32620b57cec5SDimitry Andric }
32630b57cec5SDimitry Andric
32640b57cec5SDimitry Andric if (!Ty->isIntegralOrEnumerationType())
32650b57cec5SDimitry Andric return false;
32660b57cec5SDimitry Andric
3267bdd1243dSDimitry Andric std::optional<const llvm::APSInt *> IntValue;
32680b57cec5SDimitry Andric if (!IsAssuming)
32690b57cec5SDimitry Andric IntValue = getConcreteIntegerValue(CondVarExpr, N);
32700b57cec5SDimitry Andric
327181ad6265SDimitry Andric if (IsAssuming || !IntValue) {
32720b57cec5SDimitry Andric if (Ty->isBooleanType())
32730b57cec5SDimitry Andric Out << (TookTrue ? "true" : "false");
32740b57cec5SDimitry Andric else
32750b57cec5SDimitry Andric Out << (TookTrue ? "not equal to 0" : "0");
32760b57cec5SDimitry Andric } else {
32770b57cec5SDimitry Andric if (Ty->isBooleanType())
3278bdd1243dSDimitry Andric Out << ((*IntValue)->getBoolValue() ? "true" : "false");
32790b57cec5SDimitry Andric else
3280bdd1243dSDimitry Andric Out << **IntValue;
32810b57cec5SDimitry Andric }
32820b57cec5SDimitry Andric
32830b57cec5SDimitry Andric return true;
32840b57cec5SDimitry Andric }
32850b57cec5SDimitry Andric
3286a7dea167SDimitry Andric constexpr llvm::StringLiteral ConditionBRVisitor::GenericTrueMessage;
3287a7dea167SDimitry Andric constexpr llvm::StringLiteral ConditionBRVisitor::GenericFalseMessage;
32880b57cec5SDimitry Andric
isPieceMessageGeneric(const PathDiagnosticPiece * Piece)32890b57cec5SDimitry Andric bool ConditionBRVisitor::isPieceMessageGeneric(
32900b57cec5SDimitry Andric const PathDiagnosticPiece *Piece) {
32910b57cec5SDimitry Andric return Piece->getString() == GenericTrueMessage ||
32920b57cec5SDimitry Andric Piece->getString() == GenericFalseMessage;
32930b57cec5SDimitry Andric }
32940b57cec5SDimitry Andric
32950b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
32960b57cec5SDimitry Andric // Implementation of LikelyFalsePositiveSuppressionBRVisitor.
32970b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
32980b57cec5SDimitry Andric
finalizeVisitor(BugReporterContext & BRC,const ExplodedNode * N,PathSensitiveBugReport & BR)32990b57cec5SDimitry Andric void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
3300a7dea167SDimitry Andric BugReporterContext &BRC, const ExplodedNode *N,
3301a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
33020b57cec5SDimitry Andric // Here we suppress false positives coming from system headers. This list is
33030b57cec5SDimitry Andric // based on known issues.
3304a7dea167SDimitry Andric const AnalyzerOptions &Options = BRC.getAnalyzerOptions();
33050b57cec5SDimitry Andric const Decl *D = N->getLocationContext()->getDecl();
33060b57cec5SDimitry Andric
33070b57cec5SDimitry Andric if (AnalysisDeclContext::isInStdNamespace(D)) {
33080b57cec5SDimitry Andric // Skip reports within the 'std' namespace. Although these can sometimes be
33090b57cec5SDimitry Andric // the user's fault, we currently don't report them very well, and
33100b57cec5SDimitry Andric // Note that this will not help for any other data structure libraries, like
33110b57cec5SDimitry Andric // TR1, Boost, or llvm/ADT.
33120b57cec5SDimitry Andric if (Options.ShouldSuppressFromCXXStandardLibrary) {
33130b57cec5SDimitry Andric BR.markInvalid(getTag(), nullptr);
33140b57cec5SDimitry Andric return;
33150b57cec5SDimitry Andric } else {
33160b57cec5SDimitry Andric // If the complete 'std' suppression is not enabled, suppress reports
33170b57cec5SDimitry Andric // from the 'std' namespace that are known to produce false positives.
33180b57cec5SDimitry Andric
33190b57cec5SDimitry Andric // The analyzer issues a false use-after-free when std::list::pop_front
33200b57cec5SDimitry Andric // or std::list::pop_back are called multiple times because we cannot
33210b57cec5SDimitry Andric // reason about the internal invariants of the data structure.
33220b57cec5SDimitry Andric if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
33230b57cec5SDimitry Andric const CXXRecordDecl *CD = MD->getParent();
33240b57cec5SDimitry Andric if (CD->getName() == "list") {
33250b57cec5SDimitry Andric BR.markInvalid(getTag(), nullptr);
33260b57cec5SDimitry Andric return;
33270b57cec5SDimitry Andric }
33280b57cec5SDimitry Andric }
33290b57cec5SDimitry Andric
33300b57cec5SDimitry Andric // The analyzer issues a false positive when the constructor of
33310b57cec5SDimitry Andric // std::__independent_bits_engine from algorithms is used.
33320b57cec5SDimitry Andric if (const auto *MD = dyn_cast<CXXConstructorDecl>(D)) {
33330b57cec5SDimitry Andric const CXXRecordDecl *CD = MD->getParent();
33340b57cec5SDimitry Andric if (CD->getName() == "__independent_bits_engine") {
33350b57cec5SDimitry Andric BR.markInvalid(getTag(), nullptr);
33360b57cec5SDimitry Andric return;
33370b57cec5SDimitry Andric }
33380b57cec5SDimitry Andric }
33390b57cec5SDimitry Andric
33400b57cec5SDimitry Andric for (const LocationContext *LCtx = N->getLocationContext(); LCtx;
33410b57cec5SDimitry Andric LCtx = LCtx->getParent()) {
33420b57cec5SDimitry Andric const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
33430b57cec5SDimitry Andric if (!MD)
33440b57cec5SDimitry Andric continue;
33450b57cec5SDimitry Andric
33460b57cec5SDimitry Andric const CXXRecordDecl *CD = MD->getParent();
33470b57cec5SDimitry Andric // The analyzer issues a false positive on
33480b57cec5SDimitry Andric // std::basic_string<uint8_t> v; v.push_back(1);
33490b57cec5SDimitry Andric // and
33500b57cec5SDimitry Andric // std::u16string s; s += u'a';
33510b57cec5SDimitry Andric // because we cannot reason about the internal invariants of the
33520b57cec5SDimitry Andric // data structure.
33530b57cec5SDimitry Andric if (CD->getName() == "basic_string") {
33540b57cec5SDimitry Andric BR.markInvalid(getTag(), nullptr);
33550b57cec5SDimitry Andric return;
33560b57cec5SDimitry Andric }
33570b57cec5SDimitry Andric
33580b57cec5SDimitry Andric // The analyzer issues a false positive on
33590b57cec5SDimitry Andric // std::shared_ptr<int> p(new int(1)); p = nullptr;
33600b57cec5SDimitry Andric // because it does not reason properly about temporary destructors.
33610b57cec5SDimitry Andric if (CD->getName() == "shared_ptr") {
33620b57cec5SDimitry Andric BR.markInvalid(getTag(), nullptr);
33630b57cec5SDimitry Andric return;
33640b57cec5SDimitry Andric }
33650b57cec5SDimitry Andric }
33660b57cec5SDimitry Andric }
33670b57cec5SDimitry Andric }
33680b57cec5SDimitry Andric
33690b57cec5SDimitry Andric // Skip reports within the sys/queue.h macros as we do not have the ability to
33700b57cec5SDimitry Andric // reason about data structure shapes.
3371a7dea167SDimitry Andric const SourceManager &SM = BRC.getSourceManager();
3372a7dea167SDimitry Andric FullSourceLoc Loc = BR.getLocation().asLocation();
33730b57cec5SDimitry Andric while (Loc.isMacroID()) {
33740b57cec5SDimitry Andric Loc = Loc.getSpellingLoc();
33755f757f3fSDimitry Andric if (SM.getFilename(Loc).ends_with("sys/queue.h")) {
33760b57cec5SDimitry Andric BR.markInvalid(getTag(), nullptr);
33770b57cec5SDimitry Andric return;
33780b57cec5SDimitry Andric }
33790b57cec5SDimitry Andric }
33800b57cec5SDimitry Andric }
33810b57cec5SDimitry Andric
33820b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
33830b57cec5SDimitry Andric // Implementation of UndefOrNullArgVisitor.
33840b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
33850b57cec5SDimitry Andric
3386a7dea167SDimitry Andric PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)3387a7dea167SDimitry Andric UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
3388a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
33890b57cec5SDimitry Andric ProgramStateRef State = N->getState();
33900b57cec5SDimitry Andric ProgramPoint ProgLoc = N->getLocation();
33910b57cec5SDimitry Andric
33920b57cec5SDimitry Andric // We are only interested in visiting CallEnter nodes.
3393bdd1243dSDimitry Andric std::optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
33940b57cec5SDimitry Andric if (!CEnter)
33950b57cec5SDimitry Andric return nullptr;
33960b57cec5SDimitry Andric
33970b57cec5SDimitry Andric // Check if one of the arguments is the region the visitor is tracking.
33980b57cec5SDimitry Andric CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
33990b57cec5SDimitry Andric CallEventRef<> Call = CEMgr.getCaller(CEnter->getCalleeContext(), State);
34000b57cec5SDimitry Andric unsigned Idx = 0;
34010b57cec5SDimitry Andric ArrayRef<ParmVarDecl *> parms = Call->parameters();
34020b57cec5SDimitry Andric
34030b57cec5SDimitry Andric for (const auto ParamDecl : parms) {
34040b57cec5SDimitry Andric const MemRegion *ArgReg = Call->getArgSVal(Idx).getAsRegion();
34050b57cec5SDimitry Andric ++Idx;
34060b57cec5SDimitry Andric
34070b57cec5SDimitry Andric // Are we tracking the argument or its subregion?
34080b57cec5SDimitry Andric if ( !ArgReg || !R->isSubRegionOf(ArgReg->StripCasts()))
34090b57cec5SDimitry Andric continue;
34100b57cec5SDimitry Andric
34110b57cec5SDimitry Andric // Check the function parameter type.
34120b57cec5SDimitry Andric assert(ParamDecl && "Formal parameter has no decl?");
34130b57cec5SDimitry Andric QualType T = ParamDecl->getType();
34140b57cec5SDimitry Andric
34150b57cec5SDimitry Andric if (!(T->isAnyPointerType() || T->isReferenceType())) {
34160b57cec5SDimitry Andric // Function can only change the value passed in by address.
34170b57cec5SDimitry Andric continue;
34180b57cec5SDimitry Andric }
34190b57cec5SDimitry Andric
34200b57cec5SDimitry Andric // If it is a const pointer value, the function does not intend to
34210b57cec5SDimitry Andric // change the value.
34220b57cec5SDimitry Andric if (T->getPointeeType().isConstQualified())
34230b57cec5SDimitry Andric continue;
34240b57cec5SDimitry Andric
34250b57cec5SDimitry Andric // Mark the call site (LocationContext) as interesting if the value of the
34260b57cec5SDimitry Andric // argument is undefined or '0'/'NULL'.
34270b57cec5SDimitry Andric SVal BoundVal = State->getSVal(R);
34280b57cec5SDimitry Andric if (BoundVal.isUndef() || BoundVal.isZeroConstant()) {
34290b57cec5SDimitry Andric BR.markInteresting(CEnter->getCalleeContext());
34300b57cec5SDimitry Andric return nullptr;
34310b57cec5SDimitry Andric }
34320b57cec5SDimitry Andric }
34330b57cec5SDimitry Andric return nullptr;
34340b57cec5SDimitry Andric }
34350b57cec5SDimitry Andric
34360b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
34370b57cec5SDimitry Andric // Implementation of FalsePositiveRefutationBRVisitor.
34380b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
34390b57cec5SDimitry Andric
FalsePositiveRefutationBRVisitor()34400b57cec5SDimitry Andric FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
3441e8d8bef9SDimitry Andric : Constraints(ConstraintMap::Factory().getEmptyMap()) {}
34420b57cec5SDimitry Andric
finalizeVisitor(BugReporterContext & BRC,const ExplodedNode * EndPathNode,PathSensitiveBugReport & BR)34430b57cec5SDimitry Andric void FalsePositiveRefutationBRVisitor::finalizeVisitor(
3444a7dea167SDimitry Andric BugReporterContext &BRC, const ExplodedNode *EndPathNode,
3445a7dea167SDimitry Andric PathSensitiveBugReport &BR) {
34460b57cec5SDimitry Andric // Collect new constraints
34475ffd83dbSDimitry Andric addConstraints(EndPathNode, /*OverwriteConstraintsOnExistingSyms=*/true);
34480b57cec5SDimitry Andric
34490b57cec5SDimitry Andric // Create a refutation manager
34500b57cec5SDimitry Andric llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver();
34510b57cec5SDimitry Andric ASTContext &Ctx = BRC.getASTContext();
34520b57cec5SDimitry Andric
34530b57cec5SDimitry Andric // Add constraints to the solver
34540b57cec5SDimitry Andric for (const auto &I : Constraints) {
34550b57cec5SDimitry Andric const SymbolRef Sym = I.first;
34560b57cec5SDimitry Andric auto RangeIt = I.second.begin();
34570b57cec5SDimitry Andric
34585ffd83dbSDimitry Andric llvm::SMTExprRef SMTConstraints = SMTConv::getRangeExpr(
34590b57cec5SDimitry Andric RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(),
34600b57cec5SDimitry Andric /*InRange=*/true);
34610b57cec5SDimitry Andric while ((++RangeIt) != I.second.end()) {
34625ffd83dbSDimitry Andric SMTConstraints = RefutationSolver->mkOr(
34635ffd83dbSDimitry Andric SMTConstraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym,
34640b57cec5SDimitry Andric RangeIt->From(), RangeIt->To(),
34650b57cec5SDimitry Andric /*InRange=*/true));
34660b57cec5SDimitry Andric }
34670b57cec5SDimitry Andric
34685ffd83dbSDimitry Andric RefutationSolver->addConstraint(SMTConstraints);
34690b57cec5SDimitry Andric }
34700b57cec5SDimitry Andric
34710b57cec5SDimitry Andric // And check for satisfiability
3472bdd1243dSDimitry Andric std::optional<bool> IsSAT = RefutationSolver->check();
347381ad6265SDimitry Andric if (!IsSAT)
34740b57cec5SDimitry Andric return;
34750b57cec5SDimitry Andric
3476bdd1243dSDimitry Andric if (!*IsSAT)
34770b57cec5SDimitry Andric BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
34780b57cec5SDimitry Andric }
34790b57cec5SDimitry Andric
addConstraints(const ExplodedNode * N,bool OverwriteConstraintsOnExistingSyms)34805ffd83dbSDimitry Andric void FalsePositiveRefutationBRVisitor::addConstraints(
34815ffd83dbSDimitry Andric const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) {
34820b57cec5SDimitry Andric // Collect new constraints
3483e8d8bef9SDimitry Andric ConstraintMap NewCs = getConstraintMap(N->getState());
3484e8d8bef9SDimitry Andric ConstraintMap::Factory &CF = N->getState()->get_context<ConstraintMap>();
34850b57cec5SDimitry Andric
34860b57cec5SDimitry Andric // Add constraints if we don't have them yet
34870b57cec5SDimitry Andric for (auto const &C : NewCs) {
34880b57cec5SDimitry Andric const SymbolRef &Sym = C.first;
34890b57cec5SDimitry Andric if (!Constraints.contains(Sym)) {
34905ffd83dbSDimitry Andric // This symbol is new, just add the constraint.
34915ffd83dbSDimitry Andric Constraints = CF.add(Constraints, Sym, C.second);
34925ffd83dbSDimitry Andric } else if (OverwriteConstraintsOnExistingSyms) {
34935ffd83dbSDimitry Andric // Overwrite the associated constraint of the Symbol.
34945ffd83dbSDimitry Andric Constraints = CF.remove(Constraints, Sym);
34950b57cec5SDimitry Andric Constraints = CF.add(Constraints, Sym, C.second);
34960b57cec5SDimitry Andric }
34970b57cec5SDimitry Andric }
34985ffd83dbSDimitry Andric }
34990b57cec5SDimitry Andric
VisitNode(const ExplodedNode * N,BugReporterContext &,PathSensitiveBugReport &)35005ffd83dbSDimitry Andric PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
35015ffd83dbSDimitry Andric const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) {
35025ffd83dbSDimitry Andric addConstraints(N, /*OverwriteConstraintsOnExistingSyms=*/false);
35030b57cec5SDimitry Andric return nullptr;
35040b57cec5SDimitry Andric }
35050b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const35060b57cec5SDimitry Andric void FalsePositiveRefutationBRVisitor::Profile(
35070b57cec5SDimitry Andric llvm::FoldingSetNodeID &ID) const {
35080b57cec5SDimitry Andric static int Tag = 0;
35090b57cec5SDimitry Andric ID.AddPointer(&Tag);
35100b57cec5SDimitry Andric }
35110b57cec5SDimitry Andric
35120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
35130b57cec5SDimitry Andric // Implementation of TagVisitor.
35140b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
35150b57cec5SDimitry Andric
35160b57cec5SDimitry Andric int NoteTag::Kind = 0;
35170b57cec5SDimitry Andric
Profile(llvm::FoldingSetNodeID & ID) const35180b57cec5SDimitry Andric void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
35190b57cec5SDimitry Andric static int Tag = 0;
35200b57cec5SDimitry Andric ID.AddPointer(&Tag);
35210b57cec5SDimitry Andric }
35220b57cec5SDimitry Andric
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & R)3523a7dea167SDimitry Andric PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N,
3524a7dea167SDimitry Andric BugReporterContext &BRC,
3525a7dea167SDimitry Andric PathSensitiveBugReport &R) {
35260b57cec5SDimitry Andric ProgramPoint PP = N->getLocation();
35270b57cec5SDimitry Andric const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag());
35280b57cec5SDimitry Andric if (!T)
35290b57cec5SDimitry Andric return nullptr;
35300b57cec5SDimitry Andric
3531bdd1243dSDimitry Andric if (std::optional<std::string> Msg = T->generateMessage(BRC, R)) {
35320b57cec5SDimitry Andric PathDiagnosticLocation Loc =
35330b57cec5SDimitry Andric PathDiagnosticLocation::create(PP, BRC.getSourceManager());
35340b57cec5SDimitry Andric auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
35350b57cec5SDimitry Andric Piece->setPrunable(T->isPrunable());
35360b57cec5SDimitry Andric return Piece;
35370b57cec5SDimitry Andric }
35380b57cec5SDimitry Andric
35390b57cec5SDimitry Andric return nullptr;
35400b57cec5SDimitry Andric }
3541