1 | //===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines a set of BugReporter "visitors" which can be used to |
10 | // enhance the diagnostics reported for a bug. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" |
15 | #include "clang/AST/ASTContext.h" |
16 | #include "clang/AST/Decl.h" |
17 | #include "clang/AST/DeclBase.h" |
18 | #include "clang/AST/DeclCXX.h" |
19 | #include "clang/AST/Expr.h" |
20 | #include "clang/AST/ExprCXX.h" |
21 | #include "clang/AST/ExprObjC.h" |
22 | #include "clang/AST/Stmt.h" |
23 | #include "clang/AST/Type.h" |
24 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
25 | #include "clang/Analysis/Analyses/Dominators.h" |
26 | #include "clang/Analysis/AnalysisDeclContext.h" |
27 | #include "clang/Analysis/CFG.h" |
28 | #include "clang/Analysis/CFGStmtMap.h" |
29 | #include "clang/Analysis/PathDiagnostic.h" |
30 | #include "clang/Analysis/ProgramPoint.h" |
31 | #include "clang/Basic/IdentifierTable.h" |
32 | #include "clang/Basic/LLVM.h" |
33 | #include "clang/Basic/SourceLocation.h" |
34 | #include "clang/Basic/SourceManager.h" |
35 | #include "clang/Lex/Lexer.h" |
36 | #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" |
37 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
38 | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
39 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
40 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
41 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
42 | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
43 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
44 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" |
45 | #include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" |
46 | #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" |
47 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
48 | #include "llvm/ADT/ArrayRef.h" |
49 | #include "llvm/ADT/STLExtras.h" |
50 | #include "llvm/ADT/SmallPtrSet.h" |
51 | #include "llvm/ADT/SmallString.h" |
52 | #include "llvm/ADT/SmallVector.h" |
53 | #include "llvm/ADT/StringExtras.h" |
54 | #include "llvm/ADT/StringRef.h" |
55 | #include "llvm/Support/Casting.h" |
56 | #include "llvm/Support/ErrorHandling.h" |
57 | #include "llvm/Support/raw_ostream.h" |
58 | #include <cassert> |
59 | #include <deque> |
60 | #include <memory> |
61 | #include <optional> |
62 | #include <string> |
63 | #include <utility> |
64 | |
65 | using namespace clang; |
66 | using namespace ento; |
67 | using namespace bugreporter; |
68 | |
69 | //===----------------------------------------------------------------------===// |
70 | // Utility functions. |
71 | //===----------------------------------------------------------------------===// |
72 | |
73 | static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) { |
74 | if (B->isAdditiveOp() && B->getType()->isPointerType()) { |
75 | if (B->getLHS()->getType()->isPointerType()) { |
76 | return B->getLHS(); |
77 | } else if (B->getRHS()->getType()->isPointerType()) { |
78 | return B->getRHS(); |
79 | } |
80 | } |
81 | return nullptr; |
82 | } |
83 | |
84 | /// \return A subexpression of @c Ex which represents the |
85 | /// expression-of-interest. |
86 | static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N); |
87 | |
88 | /// Given that expression S represents a pointer that would be dereferenced, |
89 | /// try to find a sub-expression from which the pointer came from. |
90 | /// This is used for tracking down origins of a null or undefined value: |
91 | /// "this is null because that is null because that is null" etc. |
92 | /// We wipe away field and element offsets because they merely add offsets. |
93 | /// We also wipe away all casts except lvalue-to-rvalue casts, because the |
94 | /// latter represent an actual pointer dereference; however, we remove |
95 | /// the final lvalue-to-rvalue cast before returning from this function |
96 | /// because it demonstrates more clearly from where the pointer rvalue was |
97 | /// loaded. Examples: |
98 | /// x->y.z ==> x (lvalue) |
99 | /// foo()->y.z ==> foo() (rvalue) |
100 | const Expr *bugreporter::getDerefExpr(const Stmt *S) { |
101 | const auto *E = dyn_cast<Expr>(Val: S); |
102 | if (!E) |
103 | return nullptr; |
104 | |
105 | while (true) { |
106 | if (const auto *CE = dyn_cast<CastExpr>(Val: E)) { |
107 | if (CE->getCastKind() == CK_LValueToRValue) { |
108 | // This cast represents the load we're looking for. |
109 | break; |
110 | } |
111 | E = CE->getSubExpr(); |
112 | } else if (const auto *B = dyn_cast<BinaryOperator>(Val: E)) { |
113 | // Pointer arithmetic: '*(x + 2)' -> 'x') etc. |
114 | if (const Expr *Inner = peelOffPointerArithmetic(B)) { |
115 | E = Inner; |
116 | } else { |
117 | // Probably more arithmetic can be pattern-matched here, |
118 | // but for now give up. |
119 | break; |
120 | } |
121 | } else if (const auto *U = dyn_cast<UnaryOperator>(Val: E)) { |
122 | if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf || |
123 | (U->isIncrementDecrementOp() && U->getType()->isPointerType())) { |
124 | // Operators '*' and '&' don't actually mean anything. |
125 | // We look at casts instead. |
126 | E = U->getSubExpr(); |
127 | } else { |
128 | // Probably more arithmetic can be pattern-matched here, |
129 | // but for now give up. |
130 | break; |
131 | } |
132 | } |
133 | // Pattern match for a few useful cases: a[0], p->f, *p etc. |
134 | else if (const auto *ME = dyn_cast<MemberExpr>(Val: E)) { |
135 | // This handles the case when the dereferencing of a member reference |
136 | // happens. This is needed, because the AST for dereferencing a |
137 | // member reference looks like the following: |
138 | // |-MemberExpr |
139 | // `-DeclRefExpr |
140 | // Without this special case the notes would refer to the whole object |
141 | // (struct, class or union variable) instead of just the relevant member. |
142 | |
143 | if (ME->getMemberDecl()->getType()->isReferenceType()) |
144 | break; |
145 | E = ME->getBase(); |
146 | } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(Val: E)) { |
147 | E = IvarRef->getBase(); |
148 | } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(Val: E)) { |
149 | E = AE->getBase(); |
150 | } else if (const auto *PE = dyn_cast<ParenExpr>(Val: E)) { |
151 | E = PE->getSubExpr(); |
152 | } else if (const auto *FE = dyn_cast<FullExpr>(Val: E)) { |
153 | E = FE->getSubExpr(); |
154 | } else { |
155 | // Other arbitrary stuff. |
156 | break; |
157 | } |
158 | } |
159 | |
160 | // Special case: remove the final lvalue-to-rvalue cast, but do not recurse |
161 | // deeper into the sub-expression. This way we return the lvalue from which |
162 | // our pointer rvalue was loaded. |
163 | if (const auto *CE = dyn_cast<ImplicitCastExpr>(Val: E)) |
164 | if (CE->getCastKind() == CK_LValueToRValue) |
165 | E = CE->getSubExpr(); |
166 | |
167 | return E; |
168 | } |
169 | |
170 | static const VarDecl *getVarDeclForExpression(const Expr *E) { |
171 | if (const auto *DR = dyn_cast<DeclRefExpr>(Val: E)) |
172 | return dyn_cast<VarDecl>(Val: DR->getDecl()); |
173 | return nullptr; |
174 | } |
175 | |
176 | static const MemRegion * |
177 | getLocationRegionIfReference(const Expr *E, const ExplodedNode *N, |
178 | bool LookingForReference = true) { |
179 | if (const auto *ME = dyn_cast<MemberExpr>(Val: E)) { |
180 | // This handles null references from FieldRegions, for example: |
181 | // struct Wrapper { int &ref; }; |
182 | // Wrapper w = { *(int *)0 }; |
183 | // w.ref = 1; |
184 | const Expr *Base = ME->getBase(); |
185 | const VarDecl *VD = getVarDeclForExpression(E: Base); |
186 | if (!VD) |
187 | return nullptr; |
188 | |
189 | const auto *FD = dyn_cast<FieldDecl>(Val: ME->getMemberDecl()); |
190 | if (!FD) |
191 | return nullptr; |
192 | |
193 | if (FD->getType()->isReferenceType()) { |
194 | SVal StructSVal = N->getState()->getLValue(VD, LC: N->getLocationContext()); |
195 | return N->getState()->getLValue(decl: FD, Base: StructSVal).getAsRegion(); |
196 | } |
197 | return nullptr; |
198 | } |
199 | |
200 | const VarDecl *VD = getVarDeclForExpression(E); |
201 | if (!VD) |
202 | return nullptr; |
203 | if (LookingForReference && !VD->getType()->isReferenceType()) |
204 | return nullptr; |
205 | return N->getState()->getLValue(VD, LC: N->getLocationContext()).getAsRegion(); |
206 | } |
207 | |
208 | /// Comparing internal representations of symbolic values (via |
209 | /// SVal::operator==()) is a valid way to check if the value was updated, |
210 | /// unless it's a LazyCompoundVal that may have a different internal |
211 | /// representation every time it is loaded from the state. In this function we |
212 | /// do an approximate comparison for lazy compound values, checking that they |
213 | /// are the immediate snapshots of the tracked region's bindings within the |
214 | /// node's respective states but not really checking that these snapshots |
215 | /// actually contain the same set of bindings. |
216 | static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, |
217 | const ExplodedNode *RightNode, SVal RightVal) { |
218 | if (LeftVal == RightVal) |
219 | return true; |
220 | |
221 | const auto LLCV = LeftVal.getAs<nonloc::LazyCompoundVal>(); |
222 | if (!LLCV) |
223 | return false; |
224 | |
225 | const auto RLCV = RightVal.getAs<nonloc::LazyCompoundVal>(); |
226 | if (!RLCV) |
227 | return false; |
228 | |
229 | return LLCV->getRegion() == RLCV->getRegion() && |
230 | LLCV->getStore() == LeftNode->getState()->getStore() && |
231 | RLCV->getStore() == RightNode->getState()->getStore(); |
232 | } |
233 | |
234 | static std::optional<SVal> getSValForVar(const Expr *CondVarExpr, |
235 | const ExplodedNode *N) { |
236 | ProgramStateRef State = N->getState(); |
237 | const LocationContext *LCtx = N->getLocationContext(); |
238 | |
239 | assert(CondVarExpr); |
240 | CondVarExpr = CondVarExpr->IgnoreImpCasts(); |
241 | |
242 | // The declaration of the value may rely on a pointer so take its l-value. |
243 | // FIXME: As seen in VisitCommonDeclRefExpr, sometimes DeclRefExpr may |
244 | // evaluate to a FieldRegion when it refers to a declaration of a lambda |
245 | // capture variable. We most likely need to duplicate that logic here. |
246 | if (const auto *DRE = dyn_cast<DeclRefExpr>(Val: CondVarExpr)) |
247 | if (const auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl())) |
248 | return State->getSVal(LV: State->getLValue(VD, LC: LCtx)); |
249 | |
250 | if (const auto *ME = dyn_cast<MemberExpr>(Val: CondVarExpr)) |
251 | if (const auto *FD = dyn_cast<FieldDecl>(Val: ME->getMemberDecl())) |
252 | if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>()) |
253 | return State->getRawSVal(LV: *FieldL, T: FD->getType()); |
254 | |
255 | return std::nullopt; |
256 | } |
257 | |
258 | static std::optional<const llvm::APSInt *> |
259 | getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { |
260 | |
261 | if (std::optional<SVal> V = getSValForVar(CondVarExpr, N)) |
262 | if (auto CI = V->getAs<nonloc::ConcreteInt>()) |
263 | return &CI->getValue(); |
264 | return std::nullopt; |
265 | } |
266 | |
267 | static bool isVarAnInterestingCondition(const Expr *CondVarExpr, |
268 | const ExplodedNode *N, |
269 | const PathSensitiveBugReport *B) { |
270 | // Even if this condition is marked as interesting, it isn't *that* |
271 | // interesting if it didn't happen in a nested stackframe, the user could just |
272 | // follow the arrows. |
273 | if (!B->getErrorNode()->getStackFrame()->isParentOf(LC: N->getStackFrame())) |
274 | return false; |
275 | |
276 | if (std::optional<SVal> V = getSValForVar(CondVarExpr, N)) |
277 | if (std::optional<bugreporter::TrackingKind> K = |
278 | B->getInterestingnessKind(V: *V)) |
279 | return *K == bugreporter::TrackingKind::Condition; |
280 | |
281 | return false; |
282 | } |
283 | |
284 | static bool isInterestingExpr(const Expr *E, const ExplodedNode *N, |
285 | const PathSensitiveBugReport *B) { |
286 | if (std::optional<SVal> V = getSValForVar(CondVarExpr: E, N)) |
287 | return B->getInterestingnessKind(V: *V).has_value(); |
288 | return false; |
289 | } |
290 | |
291 | /// \return name of the macro inside the location \p Loc. |
292 | static StringRef getMacroName(SourceLocation Loc, |
293 | BugReporterContext &BRC) { |
294 | return Lexer::getImmediateMacroName( |
295 | Loc, |
296 | SM: BRC.getSourceManager(), |
297 | LangOpts: BRC.getASTContext().getLangOpts()); |
298 | } |
299 | |
300 | /// \return Whether given spelling location corresponds to an expansion |
301 | /// of a function-like macro. |
302 | static bool isFunctionMacroExpansion(SourceLocation Loc, |
303 | const SourceManager &SM) { |
304 | if (!Loc.isMacroID()) |
305 | return false; |
306 | while (SM.isMacroArgExpansion(Loc)) |
307 | Loc = SM.getImmediateExpansionRange(Loc).getBegin(); |
308 | std::pair<FileID, unsigned> TLInfo = SM.getDecomposedLoc(Loc); |
309 | SrcMgr::SLocEntry SE = SM.getSLocEntry(FID: TLInfo.first); |
310 | const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); |
311 | return EInfo.isFunctionMacroExpansion(); |
312 | } |
313 | |
314 | /// \return Whether \c RegionOfInterest was modified at \p N, |
315 | /// where \p ValueAfter is \c RegionOfInterest's value at the end of the |
316 | /// stack frame. |
317 | static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest, |
318 | const ExplodedNode *N, |
319 | SVal ValueAfter) { |
320 | ProgramStateRef State = N->getState(); |
321 | ProgramStateManager &Mgr = N->getState()->getStateManager(); |
322 | |
323 | if (!N->getLocationAs<PostStore>() && !N->getLocationAs<PostInitializer>() && |
324 | !N->getLocationAs<PostStmt>()) |
325 | return false; |
326 | |
327 | // Writing into region of interest. |
328 | if (auto PS = N->getLocationAs<PostStmt>()) |
329 | if (auto *BO = PS->getStmtAs<BinaryOperator>()) |
330 | if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf( |
331 | R: N->getSVal(BO->getLHS()).getAsRegion())) |
332 | return true; |
333 | |
334 | // SVal after the state is possibly different. |
335 | SVal ValueAtN = N->getState()->getSVal(R: RegionOfInterest); |
336 | if (!Mgr.getSValBuilder() |
337 | .areEqual(state: State, lhs: ValueAtN, rhs: ValueAfter) |
338 | .isConstrainedTrue() && |
339 | (!ValueAtN.isUndef() || !ValueAfter.isUndef())) |
340 | return true; |
341 | |
342 | return false; |
343 | } |
344 | |
345 | //===----------------------------------------------------------------------===// |
346 | // Implementation of BugReporterVisitor. |
347 | //===----------------------------------------------------------------------===// |
348 | |
349 | PathDiagnosticPieceRef BugReporterVisitor::getEndPath(BugReporterContext &, |
350 | const ExplodedNode *, |
351 | PathSensitiveBugReport &) { |
352 | return nullptr; |
353 | } |
354 | |
355 | void BugReporterVisitor::finalizeVisitor(BugReporterContext &, |
356 | const ExplodedNode *, |
357 | PathSensitiveBugReport &) {} |
358 | |
359 | PathDiagnosticPieceRef |
360 | BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC, |
361 | const ExplodedNode *EndPathNode, |
362 | const PathSensitiveBugReport &BR) { |
363 | PathDiagnosticLocation L = BR.getLocation(); |
364 | const auto &Ranges = BR.getRanges(); |
365 | |
366 | // Only add the statement itself as a range if we didn't specify any |
367 | // special ranges for this report. |
368 | auto P = std::make_shared<PathDiagnosticEventPiece>( |
369 | args&: L, args: BR.getDescription(), args: Ranges.begin() == Ranges.end()); |
370 | for (SourceRange Range : Ranges) |
371 | P->addRange(R: Range); |
372 | |
373 | return P; |
374 | } |
375 | |
376 | //===----------------------------------------------------------------------===// |
377 | // Implementation of NoStateChangeFuncVisitor. |
378 | //===----------------------------------------------------------------------===// |
379 | |
380 | bool NoStateChangeFuncVisitor::isModifiedInFrame(const ExplodedNode *N) { |
381 | const LocationContext *Ctx = N->getLocationContext(); |
382 | const StackFrameContext *SCtx = Ctx->getStackFrame(); |
383 | if (!FramesModifyingCalculated.count(Ptr: SCtx)) |
384 | findModifyingFrames(CallExitBeginN: N); |
385 | return FramesModifying.count(Ptr: SCtx); |
386 | } |
387 | |
388 | void NoStateChangeFuncVisitor::markFrameAsModifying( |
389 | const StackFrameContext *SCtx) { |
390 | while (!SCtx->inTopFrame()) { |
391 | auto p = FramesModifying.insert(Ptr: SCtx); |
392 | if (!p.second) |
393 | break; // Frame and all its parents already inserted. |
394 | |
395 | SCtx = SCtx->getParent()->getStackFrame(); |
396 | } |
397 | } |
398 | |
399 | static const ExplodedNode *getMatchingCallExitEnd(const ExplodedNode *N) { |
400 | assert(N->getLocationAs<CallEnter>()); |
401 | // The stackframe of the callee is only found in the nodes succeeding |
402 | // the CallEnter node. CallEnter's stack frame refers to the caller. |
403 | const StackFrameContext *OrigSCtx = N->getFirstSucc()->getStackFrame(); |
404 | |
405 | // Similarly, the nodes preceding CallExitEnd refer to the callee's stack |
406 | // frame. |
407 | auto IsMatchingCallExitEnd = [OrigSCtx](const ExplodedNode *N) { |
408 | return N->getLocationAs<CallExitEnd>() && |
409 | OrigSCtx == N->getFirstPred()->getStackFrame(); |
410 | }; |
411 | while (N && !IsMatchingCallExitEnd(N)) { |
412 | assert(N->succ_size() <= 1 && |
413 | "This function is to be used on the trimmed ExplodedGraph!" ); |
414 | N = N->getFirstSucc(); |
415 | } |
416 | return N; |
417 | } |
418 | |
419 | void NoStateChangeFuncVisitor::findModifyingFrames( |
420 | const ExplodedNode *const CallExitBeginN) { |
421 | |
422 | assert(CallExitBeginN->getLocationAs<CallExitBegin>()); |
423 | |
424 | const StackFrameContext *const OriginalSCtx = |
425 | CallExitBeginN->getLocationContext()->getStackFrame(); |
426 | |
427 | const ExplodedNode *CurrCallExitBeginN = CallExitBeginN; |
428 | const StackFrameContext *CurrentSCtx = OriginalSCtx; |
429 | |
430 | for (const ExplodedNode *CurrN = CallExitBeginN; CurrN; |
431 | CurrN = CurrN->getFirstPred()) { |
432 | // Found a new inlined call. |
433 | if (CurrN->getLocationAs<CallExitBegin>()) { |
434 | CurrCallExitBeginN = CurrN; |
435 | CurrentSCtx = CurrN->getStackFrame(); |
436 | FramesModifyingCalculated.insert(Ptr: CurrentSCtx); |
437 | // We won't see a change in between two identical exploded nodes: skip. |
438 | continue; |
439 | } |
440 | |
441 | if (auto CE = CurrN->getLocationAs<CallEnter>()) { |
442 | if (const ExplodedNode *CallExitEndN = getMatchingCallExitEnd(N: CurrN)) |
443 | if (wasModifiedInFunction(CallEnterN: CurrN, CallExitEndN)) |
444 | markFrameAsModifying(SCtx: CurrentSCtx); |
445 | |
446 | // We exited this inlined call, lets actualize the stack frame. |
447 | CurrentSCtx = CurrN->getStackFrame(); |
448 | |
449 | // Stop calculating at the current function, but always regard it as |
450 | // modifying, so we can avoid notes like this: |
451 | // void f(Foo &F) { |
452 | // F.field = 0; // note: 0 assigned to 'F.field' |
453 | // // note: returning without writing to 'F.field' |
454 | // } |
455 | if (CE->getCalleeContext() == OriginalSCtx) { |
456 | markFrameAsModifying(SCtx: CurrentSCtx); |
457 | break; |
458 | } |
459 | } |
460 | |
461 | if (wasModifiedBeforeCallExit(CurrN, CallExitBeginN: CurrCallExitBeginN)) |
462 | markFrameAsModifying(SCtx: CurrentSCtx); |
463 | } |
464 | } |
465 | |
466 | PathDiagnosticPieceRef NoStateChangeFuncVisitor::VisitNode( |
467 | const ExplodedNode *N, BugReporterContext &BR, PathSensitiveBugReport &R) { |
468 | |
469 | const LocationContext *Ctx = N->getLocationContext(); |
470 | const StackFrameContext *SCtx = Ctx->getStackFrame(); |
471 | ProgramStateRef State = N->getState(); |
472 | auto CallExitLoc = N->getLocationAs<CallExitBegin>(); |
473 | |
474 | // No diagnostic if region was modified inside the frame. |
475 | if (!CallExitLoc || isModifiedInFrame(N)) |
476 | return nullptr; |
477 | |
478 | CallEventRef<> Call = |
479 | BR.getStateManager().getCallEventManager().getCaller(CalleeCtx: SCtx, State); |
480 | |
481 | // Optimistically suppress uninitialized value bugs that result |
482 | // from system headers having a chance to initialize the value |
483 | // but failing to do so. It's too unlikely a system header's fault. |
484 | // It's much more likely a situation in which the function has a failure |
485 | // mode that the user decided not to check. If we want to hunt such |
486 | // omitted checks, we should provide an explicit function-specific note |
487 | // describing the precondition under which the function isn't supposed to |
488 | // initialize its out-parameter, and additionally check that such |
489 | // precondition can actually be fulfilled on the current path. |
490 | if (Call->isInSystemHeader()) { |
491 | // We make an exception for system header functions that have no branches. |
492 | // Such functions unconditionally fail to initialize the variable. |
493 | // If they call other functions that have more paths within them, |
494 | // this suppression would still apply when we visit these inner functions. |
495 | // One common example of a standard function that doesn't ever initialize |
496 | // its out parameter is operator placement new; it's up to the follow-up |
497 | // constructor (if any) to initialize the memory. |
498 | if (!N->getStackFrame()->getCFG()->isLinear()) { |
499 | static int i = 0; |
500 | R.markInvalid(Tag: &i, Data: nullptr); |
501 | } |
502 | return nullptr; |
503 | } |
504 | |
505 | if (const auto *MC = dyn_cast<ObjCMethodCall>(Val&: Call)) { |
506 | // If we failed to construct a piece for self, we still want to check |
507 | // whether the entity of interest is in a parameter. |
508 | if (PathDiagnosticPieceRef Piece = maybeEmitNoteForObjCSelf(R, Call: *MC, N)) |
509 | return Piece; |
510 | } |
511 | |
512 | if (const auto *CCall = dyn_cast<CXXConstructorCall>(Val&: Call)) { |
513 | // Do not generate diagnostics for not modified parameters in |
514 | // constructors. |
515 | return maybeEmitNoteForCXXThis(R, Call: *CCall, N); |
516 | } |
517 | |
518 | return maybeEmitNoteForParameters(R, Call: *Call, N); |
519 | } |
520 | |
521 | //===----------------------------------------------------------------------===// |
522 | // Implementation of NoStoreFuncVisitor. |
523 | //===----------------------------------------------------------------------===// |
524 | |
525 | namespace { |
526 | /// Put a diagnostic on return statement of all inlined functions |
527 | /// for which the region of interest \p RegionOfInterest was passed into, |
528 | /// but not written inside, and it has caused an undefined read or a null |
529 | /// pointer dereference outside. |
530 | class NoStoreFuncVisitor final : public NoStateChangeFuncVisitor { |
531 | const SubRegion *RegionOfInterest; |
532 | MemRegionManager &MmrMgr; |
533 | const SourceManager &SM; |
534 | const PrintingPolicy &PP; |
535 | |
536 | /// Recursion limit for dereferencing fields when looking for the |
537 | /// region of interest. |
538 | /// The limit of two indicates that we will dereference fields only once. |
539 | static const unsigned DEREFERENCE_LIMIT = 2; |
540 | |
541 | using RegionVector = SmallVector<const MemRegion *, 5>; |
542 | |
543 | public: |
544 | NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind) |
545 | : NoStateChangeFuncVisitor(TKind), RegionOfInterest(R), |
546 | MmrMgr(R->getMemRegionManager()), |
547 | SM(MmrMgr.getContext().getSourceManager()), |
548 | PP(MmrMgr.getContext().getPrintingPolicy()) {} |
549 | |
550 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
551 | static int Tag = 0; |
552 | ID.AddPointer(Ptr: &Tag); |
553 | ID.AddPointer(Ptr: RegionOfInterest); |
554 | } |
555 | |
556 | private: |
557 | /// \return Whether \c RegionOfInterest was modified at \p CurrN compared to |
558 | /// the value it holds in \p CallExitBeginN. |
559 | bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN, |
560 | const ExplodedNode *CallExitBeginN) override; |
561 | |
562 | /// Attempts to find the region of interest in a given record decl, |
563 | /// by either following the base classes or fields. |
564 | /// Dereferences fields up to a given recursion limit. |
565 | /// Note that \p Vec is passed by value, leading to quadratic copying cost, |
566 | /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. |
567 | /// \return A chain fields leading to the region of interest or std::nullopt. |
568 | const std::optional<RegionVector> |
569 | findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, |
570 | const MemRegion *R, const RegionVector &Vec = {}, |
571 | int depth = 0); |
572 | |
573 | // Region of interest corresponds to an IVar, exiting a method |
574 | // which could have written into that IVar, but did not. |
575 | PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, |
576 | const ObjCMethodCall &Call, |
577 | const ExplodedNode *N) final; |
578 | |
579 | PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, |
580 | const CXXConstructorCall &Call, |
581 | const ExplodedNode *N) final; |
582 | |
583 | PathDiagnosticPieceRef |
584 | maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, |
585 | const ExplodedNode *N) final; |
586 | |
587 | /// Consume the information on the no-store stack frame in order to |
588 | /// either emit a note or suppress the report enirely. |
589 | /// \return Diagnostics piece for region not modified in the current function, |
590 | /// if it decides to emit one. |
591 | PathDiagnosticPieceRef |
592 | maybeEmitNote(PathSensitiveBugReport &R, const CallEvent &Call, |
593 | const ExplodedNode *N, const RegionVector &FieldChain, |
594 | const MemRegion *MatchedRegion, StringRef FirstElement, |
595 | bool FirstIsReferenceType, unsigned IndirectionLevel); |
596 | |
597 | bool prettyPrintRegionName(const RegionVector &FieldChain, |
598 | const MemRegion *MatchedRegion, |
599 | StringRef FirstElement, bool FirstIsReferenceType, |
600 | unsigned IndirectionLevel, |
601 | llvm::raw_svector_ostream &os); |
602 | |
603 | StringRef prettyPrintFirstElement(StringRef FirstElement, |
604 | bool MoreItemsExpected, |
605 | int IndirectionLevel, |
606 | llvm::raw_svector_ostream &os); |
607 | }; |
608 | } // namespace |
609 | |
610 | /// \return Whether the method declaration \p Parent |
611 | /// syntactically has a binary operation writing into the ivar \p Ivar. |
612 | static bool potentiallyWritesIntoIvar(const Decl *Parent, |
613 | const ObjCIvarDecl *Ivar) { |
614 | using namespace ast_matchers; |
615 | const char *IvarBind = "Ivar" ; |
616 | if (!Parent || !Parent->hasBody()) |
617 | return false; |
618 | StatementMatcher WriteIntoIvarM = binaryOperator( |
619 | hasOperatorName(Name: "=" ), |
620 | hasLHS(ignoringParenImpCasts( |
621 | objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind)))); |
622 | StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); |
623 | auto Matches = match(Matcher: ParentM, Node: *Parent->getBody(), Context&: Parent->getASTContext()); |
624 | for (BoundNodes &Match : Matches) { |
625 | auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind); |
626 | if (IvarRef->isFreeIvar()) |
627 | return true; |
628 | |
629 | const Expr *Base = IvarRef->getBase(); |
630 | if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) |
631 | Base = ICE->getSubExpr(); |
632 | |
633 | if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) |
634 | if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) |
635 | if (ID->getParameterKind() == ImplicitParamKind::ObjCSelf) |
636 | return true; |
637 | |
638 | return false; |
639 | } |
640 | return false; |
641 | } |
642 | |
643 | /// Attempts to find the region of interest in a given CXX decl, |
644 | /// by either following the base classes or fields. |
645 | /// Dereferences fields up to a given recursion limit. |
646 | /// Note that \p Vec is passed by value, leading to quadratic copying cost, |
647 | /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. |
648 | /// \return A chain fields leading to the region of interest or std::nullopt. |
649 | const std::optional<NoStoreFuncVisitor::RegionVector> |
650 | NoStoreFuncVisitor::findRegionOfInterestInRecord( |
651 | const RecordDecl *RD, ProgramStateRef State, const MemRegion *R, |
652 | const NoStoreFuncVisitor::RegionVector &Vec /* = {} */, |
653 | int depth /* = 0 */) { |
654 | |
655 | if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. |
656 | return std::nullopt; |
657 | |
658 | if (const auto *RDX = dyn_cast<CXXRecordDecl>(Val: RD)) |
659 | if (!RDX->hasDefinition()) |
660 | return std::nullopt; |
661 | |
662 | // Recursively examine the base classes. |
663 | // Note that following base classes does not increase the recursion depth. |
664 | if (const auto *RDX = dyn_cast<CXXRecordDecl>(Val: RD)) |
665 | for (const auto &II : RDX->bases()) |
666 | if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) |
667 | if (std::optional<RegionVector> Out = |
668 | findRegionOfInterestInRecord(RD: RRD, State, R, Vec, depth)) |
669 | return Out; |
670 | |
671 | for (const FieldDecl *I : RD->fields()) { |
672 | QualType FT = I->getType(); |
673 | const FieldRegion *FR = MmrMgr.getFieldRegion(fd: I, superRegion: cast<SubRegion>(Val: R)); |
674 | const SVal V = State->getSVal(R: FR); |
675 | const MemRegion *VR = V.getAsRegion(); |
676 | |
677 | RegionVector VecF = Vec; |
678 | VecF.push_back(Elt: FR); |
679 | |
680 | if (RegionOfInterest == VR) |
681 | return VecF; |
682 | |
683 | if (const RecordDecl *RRD = FT->getAsRecordDecl()) |
684 | if (auto Out = |
685 | findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1)) |
686 | return Out; |
687 | |
688 | QualType PT = FT->getPointeeType(); |
689 | if (PT.isNull() || PT->isVoidType() || !VR) |
690 | continue; |
691 | |
692 | if (const RecordDecl *RRD = PT->getAsRecordDecl()) |
693 | if (std::optional<RegionVector> Out = |
694 | findRegionOfInterestInRecord(RD: RRD, State, R: VR, Vec: VecF, depth: depth + 1)) |
695 | return Out; |
696 | } |
697 | |
698 | return std::nullopt; |
699 | } |
700 | |
701 | PathDiagnosticPieceRef |
702 | NoStoreFuncVisitor::maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, |
703 | const ObjCMethodCall &Call, |
704 | const ExplodedNode *N) { |
705 | if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(Val: RegionOfInterest)) { |
706 | const MemRegion *SelfRegion = Call.getReceiverSVal().getAsRegion(); |
707 | if (RegionOfInterest->isSubRegionOf(R: SelfRegion) && |
708 | potentiallyWritesIntoIvar(Parent: Call.getRuntimeDefinition().getDecl(), |
709 | Ivar: IvarR->getDecl())) |
710 | return maybeEmitNote(R, Call, N, FieldChain: {}, MatchedRegion: SelfRegion, FirstElement: "self" , |
711 | /*FirstIsReferenceType=*/false, IndirectionLevel: 1); |
712 | } |
713 | return nullptr; |
714 | } |
715 | |
716 | PathDiagnosticPieceRef |
717 | NoStoreFuncVisitor::maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, |
718 | const CXXConstructorCall &Call, |
719 | const ExplodedNode *N) { |
720 | const MemRegion *ThisR = Call.getCXXThisVal().getAsRegion(); |
721 | if (RegionOfInterest->isSubRegionOf(R: ThisR) && !Call.getDecl()->isImplicit()) |
722 | return maybeEmitNote(R, Call, N, FieldChain: {}, MatchedRegion: ThisR, FirstElement: "this" , |
723 | /*FirstIsReferenceType=*/false, IndirectionLevel: 1); |
724 | |
725 | // Do not generate diagnostics for not modified parameters in |
726 | // constructors. |
727 | return nullptr; |
728 | } |
729 | |
730 | /// \return whether \p Ty points to a const type, or is a const reference. |
731 | static bool isPointerToConst(QualType Ty) { |
732 | return !Ty->getPointeeType().isNull() && |
733 | Ty->getPointeeType().getCanonicalType().isConstQualified(); |
734 | } |
735 | |
736 | PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters( |
737 | PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) { |
738 | ArrayRef<ParmVarDecl *> Parameters = Call.parameters(); |
739 | for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) { |
740 | const ParmVarDecl *PVD = Parameters[I]; |
741 | SVal V = Call.getArgSVal(Index: I); |
742 | bool ParamIsReferenceType = PVD->getType()->isReferenceType(); |
743 | std::string ParamName = PVD->getNameAsString(); |
744 | |
745 | unsigned IndirectionLevel = 1; |
746 | QualType T = PVD->getType(); |
747 | while (const MemRegion *MR = V.getAsRegion()) { |
748 | if (RegionOfInterest->isSubRegionOf(R: MR) && !isPointerToConst(Ty: T)) |
749 | return maybeEmitNote(R, Call, N, FieldChain: {}, MatchedRegion: MR, FirstElement: ParamName, |
750 | FirstIsReferenceType: ParamIsReferenceType, IndirectionLevel); |
751 | |
752 | QualType PT = T->getPointeeType(); |
753 | if (PT.isNull() || PT->isVoidType()) |
754 | break; |
755 | |
756 | ProgramStateRef State = N->getState(); |
757 | |
758 | if (const RecordDecl *RD = PT->getAsRecordDecl()) |
759 | if (std::optional<RegionVector> P = |
760 | findRegionOfInterestInRecord(RD, State, R: MR)) |
761 | return maybeEmitNote(R, Call, N, FieldChain: *P, MatchedRegion: RegionOfInterest, FirstElement: ParamName, |
762 | FirstIsReferenceType: ParamIsReferenceType, IndirectionLevel); |
763 | |
764 | V = State->getSVal(R: MR, T: PT); |
765 | T = PT; |
766 | IndirectionLevel++; |
767 | } |
768 | } |
769 | |
770 | return nullptr; |
771 | } |
772 | |
773 | bool NoStoreFuncVisitor::wasModifiedBeforeCallExit( |
774 | const ExplodedNode *CurrN, const ExplodedNode *CallExitBeginN) { |
775 | return ::wasRegionOfInterestModifiedAt( |
776 | RegionOfInterest, N: CurrN, |
777 | ValueAfter: CallExitBeginN->getState()->getSVal(R: RegionOfInterest)); |
778 | } |
779 | |
780 | static llvm::StringLiteral WillBeUsedForACondition = |
781 | ", which participates in a condition later" ; |
782 | |
783 | PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote( |
784 | PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N, |
785 | const RegionVector &FieldChain, const MemRegion *MatchedRegion, |
786 | StringRef FirstElement, bool FirstIsReferenceType, |
787 | unsigned IndirectionLevel) { |
788 | |
789 | PathDiagnosticLocation L = |
790 | PathDiagnosticLocation::create(P: N->getLocation(), SMng: SM); |
791 | |
792 | // For now this shouldn't trigger, but once it does (as we add more |
793 | // functions to the body farm), we'll need to decide if these reports |
794 | // are worth suppressing as well. |
795 | if (!L.hasValidLocation()) |
796 | return nullptr; |
797 | |
798 | SmallString<256> sbuf; |
799 | llvm::raw_svector_ostream os(sbuf); |
800 | os << "Returning without writing to '" ; |
801 | |
802 | // Do not generate the note if failed to pretty-print. |
803 | if (!prettyPrintRegionName(FieldChain, MatchedRegion, FirstElement, |
804 | FirstIsReferenceType, IndirectionLevel, os)) |
805 | return nullptr; |
806 | |
807 | os << "'" ; |
808 | if (TKind == bugreporter::TrackingKind::Condition) |
809 | os << WillBeUsedForACondition; |
810 | return std::make_shared<PathDiagnosticEventPiece>(args&: L, args: os.str()); |
811 | } |
812 | |
813 | bool NoStoreFuncVisitor::prettyPrintRegionName(const RegionVector &FieldChain, |
814 | const MemRegion *MatchedRegion, |
815 | StringRef FirstElement, |
816 | bool FirstIsReferenceType, |
817 | unsigned IndirectionLevel, |
818 | llvm::raw_svector_ostream &os) { |
819 | |
820 | if (FirstIsReferenceType) |
821 | IndirectionLevel--; |
822 | |
823 | RegionVector RegionSequence; |
824 | |
825 | // Add the regions in the reverse order, then reverse the resulting array. |
826 | assert(RegionOfInterest->isSubRegionOf(MatchedRegion)); |
827 | const MemRegion *R = RegionOfInterest; |
828 | while (R != MatchedRegion) { |
829 | RegionSequence.push_back(Elt: R); |
830 | R = cast<SubRegion>(Val: R)->getSuperRegion(); |
831 | } |
832 | std::reverse(first: RegionSequence.begin(), last: RegionSequence.end()); |
833 | RegionSequence.append(in_start: FieldChain.begin(), in_end: FieldChain.end()); |
834 | |
835 | StringRef Sep; |
836 | for (const MemRegion *R : RegionSequence) { |
837 | |
838 | // Just keep going up to the base region. |
839 | // Element regions may appear due to casts. |
840 | if (isa<CXXBaseObjectRegion, CXXTempObjectRegion>(Val: R)) |
841 | continue; |
842 | |
843 | if (Sep.empty()) |
844 | Sep = prettyPrintFirstElement(FirstElement, |
845 | /*MoreItemsExpected=*/true, |
846 | IndirectionLevel, os); |
847 | |
848 | os << Sep; |
849 | |
850 | // Can only reasonably pretty-print DeclRegions. |
851 | if (!isa<DeclRegion>(Val: R)) |
852 | return false; |
853 | |
854 | const auto *DR = cast<DeclRegion>(Val: R); |
855 | Sep = DR->getValueType()->isAnyPointerType() ? "->" : "." ; |
856 | DR->getDecl()->getDeclName().print(os, PP); |
857 | } |
858 | |
859 | if (Sep.empty()) |
860 | prettyPrintFirstElement(FirstElement, |
861 | /*MoreItemsExpected=*/false, IndirectionLevel, os); |
862 | return true; |
863 | } |
864 | |
865 | StringRef NoStoreFuncVisitor::prettyPrintFirstElement( |
866 | StringRef FirstElement, bool MoreItemsExpected, int IndirectionLevel, |
867 | llvm::raw_svector_ostream &os) { |
868 | StringRef Out = "." ; |
869 | |
870 | if (IndirectionLevel > 0 && MoreItemsExpected) { |
871 | IndirectionLevel--; |
872 | Out = "->" ; |
873 | } |
874 | |
875 | if (IndirectionLevel > 0 && MoreItemsExpected) |
876 | os << "(" ; |
877 | |
878 | for (int i = 0; i < IndirectionLevel; i++) |
879 | os << "*" ; |
880 | os << FirstElement; |
881 | |
882 | if (IndirectionLevel > 0 && MoreItemsExpected) |
883 | os << ")" ; |
884 | |
885 | return Out; |
886 | } |
887 | |
888 | //===----------------------------------------------------------------------===// |
889 | // Implementation of MacroNullReturnSuppressionVisitor. |
890 | //===----------------------------------------------------------------------===// |
891 | |
892 | namespace { |
893 | |
894 | /// Suppress null-pointer-dereference bugs where dereferenced null was returned |
895 | /// the macro. |
896 | class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor { |
897 | const SubRegion *RegionOfInterest; |
898 | const SVal ValueAtDereference; |
899 | |
900 | // Do not invalidate the reports where the value was modified |
901 | // after it got assigned to from the macro. |
902 | bool WasModified = false; |
903 | |
904 | public: |
905 | MacroNullReturnSuppressionVisitor(const SubRegion *R, const SVal V) |
906 | : RegionOfInterest(R), ValueAtDereference(V) {} |
907 | |
908 | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
909 | BugReporterContext &BRC, |
910 | PathSensitiveBugReport &BR) override { |
911 | if (WasModified) |
912 | return nullptr; |
913 | |
914 | auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>(); |
915 | if (!BugPoint) |
916 | return nullptr; |
917 | |
918 | const SourceManager &SMgr = BRC.getSourceManager(); |
919 | if (auto Loc = matchAssignment(N)) { |
920 | if (isFunctionMacroExpansion(Loc: *Loc, SM: SMgr)) { |
921 | std::string MacroName = std::string(getMacroName(Loc: *Loc, BRC)); |
922 | SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc(); |
923 | if (!BugLoc.isMacroID() || getMacroName(Loc: BugLoc, BRC) != MacroName) |
924 | BR.markInvalid(Tag: getTag(), Data: MacroName.c_str()); |
925 | } |
926 | } |
927 | |
928 | if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAfter: ValueAtDereference)) |
929 | WasModified = true; |
930 | |
931 | return nullptr; |
932 | } |
933 | |
934 | static void addMacroVisitorIfNecessary( |
935 | const ExplodedNode *N, const MemRegion *R, |
936 | bool EnableNullFPSuppression, PathSensitiveBugReport &BR, |
937 | const SVal V) { |
938 | AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; |
939 | if (EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths && |
940 | isa<Loc>(Val: V)) |
941 | BR.addVisitor<MacroNullReturnSuppressionVisitor>(ConstructorArgs: R->getAs<SubRegion>(), |
942 | ConstructorArgs: V); |
943 | } |
944 | |
945 | void* getTag() const { |
946 | static int Tag = 0; |
947 | return static_cast<void *>(&Tag); |
948 | } |
949 | |
950 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
951 | ID.AddPointer(Ptr: getTag()); |
952 | } |
953 | |
954 | private: |
955 | /// \return Source location of right hand side of an assignment |
956 | /// into \c RegionOfInterest, empty optional if none found. |
957 | std::optional<SourceLocation> matchAssignment(const ExplodedNode *N) { |
958 | const Stmt *S = N->getStmtForDiagnostics(); |
959 | ProgramStateRef State = N->getState(); |
960 | auto *LCtx = N->getLocationContext(); |
961 | if (!S) |
962 | return std::nullopt; |
963 | |
964 | if (const auto *DS = dyn_cast<DeclStmt>(Val: S)) { |
965 | if (const auto *VD = dyn_cast<VarDecl>(Val: DS->getSingleDecl())) |
966 | if (const Expr *RHS = VD->getInit()) |
967 | if (RegionOfInterest->isSubRegionOf( |
968 | R: State->getLValue(VD, LC: LCtx).getAsRegion())) |
969 | return RHS->getBeginLoc(); |
970 | } else if (const auto *BO = dyn_cast<BinaryOperator>(Val: S)) { |
971 | const MemRegion *R = N->getSVal(BO->getLHS()).getAsRegion(); |
972 | const Expr *RHS = BO->getRHS(); |
973 | if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) { |
974 | return RHS->getBeginLoc(); |
975 | } |
976 | } |
977 | return std::nullopt; |
978 | } |
979 | }; |
980 | |
981 | } // end of anonymous namespace |
982 | |
983 | namespace { |
984 | |
985 | /// Emits an extra note at the return statement of an interesting stack frame. |
986 | /// |
987 | /// The returned value is marked as an interesting value, and if it's null, |
988 | /// adds a visitor to track where it became null. |
989 | /// |
990 | /// This visitor is intended to be used when another visitor discovers that an |
991 | /// interesting value comes from an inlined function call. |
992 | class ReturnVisitor : public TrackingBugReporterVisitor { |
993 | const StackFrameContext *CalleeSFC; |
994 | enum { |
995 | Initial, |
996 | MaybeUnsuppress, |
997 | Satisfied |
998 | } Mode = Initial; |
999 | |
1000 | bool EnableNullFPSuppression; |
1001 | bool ShouldInvalidate = true; |
1002 | AnalyzerOptions& Options; |
1003 | bugreporter::TrackingKind TKind; |
1004 | |
1005 | public: |
1006 | ReturnVisitor(TrackerRef ParentTracker, const StackFrameContext *Frame, |
1007 | bool Suppressed, AnalyzerOptions &Options, |
1008 | bugreporter::TrackingKind TKind) |
1009 | : TrackingBugReporterVisitor(ParentTracker), CalleeSFC(Frame), |
1010 | EnableNullFPSuppression(Suppressed), Options(Options), TKind(TKind) {} |
1011 | |
1012 | static void *getTag() { |
1013 | static int Tag = 0; |
1014 | return static_cast<void *>(&Tag); |
1015 | } |
1016 | |
1017 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
1018 | ID.AddPointer(Ptr: ReturnVisitor::getTag()); |
1019 | ID.AddPointer(Ptr: CalleeSFC); |
1020 | ID.AddBoolean(B: EnableNullFPSuppression); |
1021 | } |
1022 | |
1023 | PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N, |
1024 | BugReporterContext &BRC, |
1025 | PathSensitiveBugReport &BR) { |
1026 | // Only print a message at the interesting return statement. |
1027 | if (N->getLocationContext() != CalleeSFC) |
1028 | return nullptr; |
1029 | |
1030 | std::optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); |
1031 | if (!SP) |
1032 | return nullptr; |
1033 | |
1034 | const auto *Ret = dyn_cast<ReturnStmt>(Val: SP->getStmt()); |
1035 | if (!Ret) |
1036 | return nullptr; |
1037 | |
1038 | // Okay, we're at the right return statement, but do we have the return |
1039 | // value available? |
1040 | ProgramStateRef State = N->getState(); |
1041 | SVal V = State->getSVal(Ret, CalleeSFC); |
1042 | if (V.isUnknownOrUndef()) |
1043 | return nullptr; |
1044 | |
1045 | // Don't print any more notes after this one. |
1046 | Mode = Satisfied; |
1047 | |
1048 | const Expr *RetE = Ret->getRetValue(); |
1049 | assert(RetE && "Tracking a return value for a void function" ); |
1050 | |
1051 | // Handle cases where a reference is returned and then immediately used. |
1052 | std::optional<Loc> LValue; |
1053 | if (RetE->isGLValue()) { |
1054 | if ((LValue = V.getAs<Loc>())) { |
1055 | SVal RValue = State->getRawSVal(LV: *LValue, T: RetE->getType()); |
1056 | if (isa<DefinedSVal>(Val: RValue)) |
1057 | V = RValue; |
1058 | } |
1059 | } |
1060 | |
1061 | // Ignore aggregate rvalues. |
1062 | if (isa<nonloc::LazyCompoundVal, nonloc::CompoundVal>(Val: V)) |
1063 | return nullptr; |
1064 | |
1065 | RetE = RetE->IgnoreParenCasts(); |
1066 | |
1067 | // Let's track the return value. |
1068 | getParentTracker().track(E: RetE, N, Opts: {.Kind: TKind, .EnableNullFPSuppression: EnableNullFPSuppression}); |
1069 | |
1070 | // Build an appropriate message based on the return value. |
1071 | SmallString<64> Msg; |
1072 | llvm::raw_svector_ostream Out(Msg); |
1073 | |
1074 | bool WouldEventBeMeaningless = false; |
1075 | |
1076 | if (State->isNull(V).isConstrainedTrue()) { |
1077 | if (isa<Loc>(Val: V)) { |
1078 | |
1079 | // If we have counter-suppression enabled, make sure we keep visiting |
1080 | // future nodes. We want to emit a path note as well, in case |
1081 | // the report is resurrected as valid later on. |
1082 | if (EnableNullFPSuppression && |
1083 | Options.ShouldAvoidSuppressingNullArgumentPaths) |
1084 | Mode = MaybeUnsuppress; |
1085 | |
1086 | if (RetE->getType()->isObjCObjectPointerType()) { |
1087 | Out << "Returning nil" ; |
1088 | } else { |
1089 | Out << "Returning null pointer" ; |
1090 | } |
1091 | } else { |
1092 | Out << "Returning zero" ; |
1093 | } |
1094 | |
1095 | } else { |
1096 | if (auto CI = V.getAs<nonloc::ConcreteInt>()) { |
1097 | Out << "Returning the value " << CI->getValue(); |
1098 | } else { |
1099 | // There is nothing interesting about returning a value, when it is |
1100 | // plain value without any constraints, and the function is guaranteed |
1101 | // to return that every time. We could use CFG::isLinear() here, but |
1102 | // constexpr branches are obvious to the compiler, not necesserily to |
1103 | // the programmer. |
1104 | if (N->getCFG().size() == 3) |
1105 | WouldEventBeMeaningless = true; |
1106 | |
1107 | Out << (isa<Loc>(Val: V) ? "Returning pointer" : "Returning value" ); |
1108 | } |
1109 | } |
1110 | |
1111 | if (LValue) { |
1112 | if (const MemRegion *MR = LValue->getAsRegion()) { |
1113 | if (MR->canPrintPretty()) { |
1114 | Out << " (reference to " ; |
1115 | MR->printPretty(os&: Out); |
1116 | Out << ")" ; |
1117 | } |
1118 | } |
1119 | } else { |
1120 | // FIXME: We should have a more generalized location printing mechanism. |
1121 | if (const auto *DR = dyn_cast<DeclRefExpr>(Val: RetE)) |
1122 | if (const auto *DD = dyn_cast<DeclaratorDecl>(Val: DR->getDecl())) |
1123 | Out << " (loaded from '" << *DD << "')" ; |
1124 | } |
1125 | |
1126 | PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSFC); |
1127 | if (!L.isValid() || !L.asLocation().isValid()) |
1128 | return nullptr; |
1129 | |
1130 | if (TKind == bugreporter::TrackingKind::Condition) |
1131 | Out << WillBeUsedForACondition; |
1132 | |
1133 | auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(args&: L, args: Out.str()); |
1134 | |
1135 | // If we determined that the note is meaningless, make it prunable, and |
1136 | // don't mark the stackframe interesting. |
1137 | if (WouldEventBeMeaningless) |
1138 | EventPiece->setPrunable(true); |
1139 | else |
1140 | BR.markInteresting(LC: CalleeSFC); |
1141 | |
1142 | return EventPiece; |
1143 | } |
1144 | |
1145 | PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N, |
1146 | BugReporterContext &BRC, |
1147 | PathSensitiveBugReport &BR) { |
1148 | assert(Options.ShouldAvoidSuppressingNullArgumentPaths); |
1149 | |
1150 | // Are we at the entry node for this call? |
1151 | std::optional<CallEnter> CE = N->getLocationAs<CallEnter>(); |
1152 | if (!CE) |
1153 | return nullptr; |
1154 | |
1155 | if (CE->getCalleeContext() != CalleeSFC) |
1156 | return nullptr; |
1157 | |
1158 | Mode = Satisfied; |
1159 | |
1160 | // Don't automatically suppress a report if one of the arguments is |
1161 | // known to be a null pointer. Instead, start tracking /that/ null |
1162 | // value back to its origin. |
1163 | ProgramStateManager &StateMgr = BRC.getStateManager(); |
1164 | CallEventManager &CallMgr = StateMgr.getCallEventManager(); |
1165 | |
1166 | ProgramStateRef State = N->getState(); |
1167 | CallEventRef<> Call = CallMgr.getCaller(CalleeCtx: CalleeSFC, State); |
1168 | for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { |
1169 | std::optional<Loc> ArgV = Call->getArgSVal(Index: I).getAs<Loc>(); |
1170 | if (!ArgV) |
1171 | continue; |
1172 | |
1173 | const Expr *ArgE = Call->getArgExpr(Index: I); |
1174 | if (!ArgE) |
1175 | continue; |
1176 | |
1177 | // Is it possible for this argument to be non-null? |
1178 | if (!State->isNull(V: *ArgV).isConstrainedTrue()) |
1179 | continue; |
1180 | |
1181 | if (getParentTracker() |
1182 | .track(E: ArgE, N, Opts: {.Kind: TKind, .EnableNullFPSuppression: EnableNullFPSuppression}) |
1183 | .FoundSomethingToTrack) |
1184 | ShouldInvalidate = false; |
1185 | |
1186 | // If we /can't/ track the null pointer, we should err on the side of |
1187 | // false negatives, and continue towards marking this report invalid. |
1188 | // (We will still look at the other arguments, though.) |
1189 | } |
1190 | |
1191 | return nullptr; |
1192 | } |
1193 | |
1194 | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
1195 | BugReporterContext &BRC, |
1196 | PathSensitiveBugReport &BR) override { |
1197 | switch (Mode) { |
1198 | case Initial: |
1199 | return visitNodeInitial(N, BRC, BR); |
1200 | case MaybeUnsuppress: |
1201 | return visitNodeMaybeUnsuppress(N, BRC, BR); |
1202 | case Satisfied: |
1203 | return nullptr; |
1204 | } |
1205 | |
1206 | llvm_unreachable("Invalid visit mode!" ); |
1207 | } |
1208 | |
1209 | void finalizeVisitor(BugReporterContext &, const ExplodedNode *, |
1210 | PathSensitiveBugReport &BR) override { |
1211 | if (EnableNullFPSuppression && ShouldInvalidate) |
1212 | BR.markInvalid(Tag: ReturnVisitor::getTag(), Data: CalleeSFC); |
1213 | } |
1214 | }; |
1215 | |
1216 | //===----------------------------------------------------------------------===// |
1217 | // StoreSiteFinder |
1218 | //===----------------------------------------------------------------------===// |
1219 | |
1220 | /// Finds last store into the given region, |
1221 | /// which is different from a given symbolic value. |
1222 | class StoreSiteFinder final : public TrackingBugReporterVisitor { |
1223 | const MemRegion *R; |
1224 | SVal V; |
1225 | bool Satisfied = false; |
1226 | |
1227 | TrackingOptions Options; |
1228 | const StackFrameContext *OriginSFC; |
1229 | |
1230 | public: |
1231 | /// \param V We're searching for the store where \c R received this value. |
1232 | /// \param R The region we're tracking. |
1233 | /// \param Options Tracking behavior options. |
1234 | /// \param OriginSFC Only adds notes when the last store happened in a |
1235 | /// different stackframe to this one. Disregarded if the tracking kind |
1236 | /// is thorough. |
1237 | /// This is useful, because for non-tracked regions, notes about |
1238 | /// changes to its value in a nested stackframe could be pruned, and |
1239 | /// this visitor can prevent that without polluting the bugpath too |
1240 | /// much. |
1241 | StoreSiteFinder(bugreporter::TrackerRef ParentTracker, SVal V, |
1242 | const MemRegion *R, TrackingOptions Options, |
1243 | const StackFrameContext *OriginSFC = nullptr) |
1244 | : TrackingBugReporterVisitor(ParentTracker), R(R), V(V), Options(Options), |
1245 | OriginSFC(OriginSFC) { |
1246 | assert(R); |
1247 | } |
1248 | |
1249 | void Profile(llvm::FoldingSetNodeID &ID) const override; |
1250 | |
1251 | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
1252 | BugReporterContext &BRC, |
1253 | PathSensitiveBugReport &BR) override; |
1254 | }; |
1255 | } // namespace |
1256 | |
1257 | void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const { |
1258 | static int tag = 0; |
1259 | ID.AddPointer(Ptr: &tag); |
1260 | ID.AddPointer(Ptr: R); |
1261 | ID.Add(x: V); |
1262 | ID.AddInteger(I: static_cast<int>(Options.Kind)); |
1263 | ID.AddBoolean(B: Options.EnableNullFPSuppression); |
1264 | } |
1265 | |
1266 | /// Returns true if \p N represents the DeclStmt declaring and initializing |
1267 | /// \p VR. |
1268 | static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { |
1269 | std::optional<PostStmt> P = N->getLocationAs<PostStmt>(); |
1270 | if (!P) |
1271 | return false; |
1272 | |
1273 | const DeclStmt *DS = P->getStmtAs<DeclStmt>(); |
1274 | if (!DS) |
1275 | return false; |
1276 | |
1277 | if (DS->getSingleDecl() != VR->getDecl()) |
1278 | return false; |
1279 | |
1280 | const MemSpaceRegion *VarSpace = VR->getMemorySpace(); |
1281 | const auto *FrameSpace = dyn_cast<StackSpaceRegion>(Val: VarSpace); |
1282 | if (!FrameSpace) { |
1283 | // If we ever directly evaluate global DeclStmts, this assertion will be |
1284 | // invalid, but this still seems preferable to silently accepting an |
1285 | // initialization that may be for a path-sensitive variable. |
1286 | assert(VR->getDecl()->isStaticLocal() && "non-static stackless VarRegion" ); |
1287 | return true; |
1288 | } |
1289 | |
1290 | assert(VR->getDecl()->hasLocalStorage()); |
1291 | const LocationContext *LCtx = N->getLocationContext(); |
1292 | return FrameSpace->getStackFrame() == LCtx->getStackFrame(); |
1293 | } |
1294 | |
1295 | static bool isObjCPointer(const MemRegion *R) { |
1296 | if (R->isBoundable()) |
1297 | if (const auto *TR = dyn_cast<TypedValueRegion>(Val: R)) |
1298 | return TR->getValueType()->isObjCObjectPointerType(); |
1299 | |
1300 | return false; |
1301 | } |
1302 | |
1303 | static bool isObjCPointer(const ValueDecl *D) { |
1304 | return D->getType()->isObjCObjectPointerType(); |
1305 | } |
1306 | |
1307 | /// Show diagnostics for initializing or declaring a region \p R with a bad value. |
1308 | static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) { |
1309 | const bool HasPrefix = SI.Dest->canPrintPretty(); |
1310 | |
1311 | if (HasPrefix) { |
1312 | SI.Dest->printPretty(os&: OS); |
1313 | OS << " " ; |
1314 | } |
1315 | |
1316 | const char *Action = nullptr; |
1317 | |
1318 | switch (SI.StoreKind) { |
1319 | case StoreInfo::Initialization: |
1320 | Action = HasPrefix ? "initialized to " : "Initializing to " ; |
1321 | break; |
1322 | case StoreInfo::BlockCapture: |
1323 | Action = HasPrefix ? "captured by block as " : "Captured by block as " ; |
1324 | break; |
1325 | default: |
1326 | llvm_unreachable("Unexpected store kind" ); |
1327 | } |
1328 | |
1329 | if (isa<loc::ConcreteInt>(Val: SI.Value)) { |
1330 | OS << Action << (isObjCPointer(R: SI.Dest) ? "nil" : "a null pointer value" ); |
1331 | |
1332 | } else if (auto CVal = SI.Value.getAs<nonloc::ConcreteInt>()) { |
1333 | OS << Action << CVal->getValue(); |
1334 | |
1335 | } else if (SI.Origin && SI.Origin->canPrintPretty()) { |
1336 | OS << Action << "the value of " ; |
1337 | SI.Origin->printPretty(os&: OS); |
1338 | |
1339 | } else if (SI.StoreKind == StoreInfo::Initialization) { |
1340 | // We don't need to check here, all these conditions were |
1341 | // checked by StoreSiteFinder, when it figured out that it is |
1342 | // initialization. |
1343 | const auto *DS = |
1344 | cast<DeclStmt>(Val: SI.StoreSite->getLocationAs<PostStmt>()->getStmt()); |
1345 | |
1346 | if (SI.Value.isUndef()) { |
1347 | if (isa<VarRegion>(Val: SI.Dest)) { |
1348 | const auto *VD = cast<VarDecl>(Val: DS->getSingleDecl()); |
1349 | |
1350 | if (VD->getInit()) { |
1351 | OS << (HasPrefix ? "initialized" : "Initializing" ) |
1352 | << " to a garbage value" ; |
1353 | } else { |
1354 | OS << (HasPrefix ? "declared" : "Declaring" ) |
1355 | << " without an initial value" ; |
1356 | } |
1357 | } |
1358 | } else { |
1359 | OS << (HasPrefix ? "initialized" : "Initialized" ) << " here" ; |
1360 | } |
1361 | } |
1362 | } |
1363 | |
1364 | /// Display diagnostics for passing bad region as a parameter. |
1365 | static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS, |
1366 | StoreInfo SI) { |
1367 | const auto *VR = cast<VarRegion>(Val: SI.Dest); |
1368 | const auto *D = VR->getDecl(); |
1369 | |
1370 | OS << "Passing " ; |
1371 | |
1372 | if (isa<loc::ConcreteInt>(Val: SI.Value)) { |
1373 | OS << (isObjCPointer(D) ? "nil object reference" : "null pointer value" ); |
1374 | |
1375 | } else if (SI.Value.isUndef()) { |
1376 | OS << "uninitialized value" ; |
1377 | |
1378 | } else if (auto CI = SI.Value.getAs<nonloc::ConcreteInt>()) { |
1379 | OS << "the value " << CI->getValue(); |
1380 | |
1381 | } else if (SI.Origin && SI.Origin->canPrintPretty()) { |
1382 | SI.Origin->printPretty(os&: OS); |
1383 | |
1384 | } else { |
1385 | OS << "value" ; |
1386 | } |
1387 | |
1388 | if (const auto *Param = dyn_cast<ParmVarDecl>(Val: VR->getDecl())) { |
1389 | // Printed parameter indexes are 1-based, not 0-based. |
1390 | unsigned Idx = Param->getFunctionScopeIndex() + 1; |
1391 | OS << " via " << Idx << llvm::getOrdinalSuffix(Val: Idx) << " parameter" ; |
1392 | if (VR->canPrintPretty()) { |
1393 | OS << " " ; |
1394 | VR->printPretty(os&: OS); |
1395 | } |
1396 | } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(Val: D)) { |
1397 | if (ImplParam->getParameterKind() == ImplicitParamKind::ObjCSelf) { |
1398 | OS << " via implicit parameter 'self'" ; |
1399 | } |
1400 | } |
1401 | } |
1402 | |
1403 | /// Show default diagnostics for storing bad region. |
1404 | static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS, |
1405 | StoreInfo SI) { |
1406 | const bool HasSuffix = SI.Dest->canPrintPretty(); |
1407 | |
1408 | if (isa<loc::ConcreteInt>(Val: SI.Value)) { |
1409 | OS << (isObjCPointer(R: SI.Dest) ? "nil object reference stored" |
1410 | : (HasSuffix ? "Null pointer value stored" |
1411 | : "Storing null pointer value" )); |
1412 | |
1413 | } else if (SI.Value.isUndef()) { |
1414 | OS << (HasSuffix ? "Uninitialized value stored" |
1415 | : "Storing uninitialized value" ); |
1416 | |
1417 | } else if (auto CV = SI.Value.getAs<nonloc::ConcreteInt>()) { |
1418 | if (HasSuffix) |
1419 | OS << "The value " << CV->getValue() << " is assigned" ; |
1420 | else |
1421 | OS << "Assigning " << CV->getValue(); |
1422 | |
1423 | } else if (SI.Origin && SI.Origin->canPrintPretty()) { |
1424 | if (HasSuffix) { |
1425 | OS << "The value of " ; |
1426 | SI.Origin->printPretty(os&: OS); |
1427 | OS << " is assigned" ; |
1428 | } else { |
1429 | OS << "Assigning the value of " ; |
1430 | SI.Origin->printPretty(os&: OS); |
1431 | } |
1432 | |
1433 | } else { |
1434 | OS << (HasSuffix ? "Value assigned" : "Assigning value" ); |
1435 | } |
1436 | |
1437 | if (HasSuffix) { |
1438 | OS << " to " ; |
1439 | SI.Dest->printPretty(os&: OS); |
1440 | } |
1441 | } |
1442 | |
1443 | static bool isTrivialCopyOrMoveCtor(const CXXConstructExpr *CE) { |
1444 | if (!CE) |
1445 | return false; |
1446 | |
1447 | const auto *CtorDecl = CE->getConstructor(); |
1448 | |
1449 | return CtorDecl->isCopyOrMoveConstructor() && CtorDecl->isTrivial(); |
1450 | } |
1451 | |
1452 | static const Expr *(const InitListExpr *ILE, |
1453 | const MemRegion *R) { |
1454 | |
1455 | const auto *TVR = dyn_cast_or_null<TypedValueRegion>(Val: R); |
1456 | |
1457 | if (!TVR) |
1458 | return nullptr; |
1459 | |
1460 | const auto ITy = ILE->getType().getCanonicalType(); |
1461 | |
1462 | // Push each sub-region onto the stack. |
1463 | std::stack<const TypedValueRegion *> TVRStack; |
1464 | while (isa<FieldRegion>(Val: TVR) || isa<ElementRegion>(Val: TVR)) { |
1465 | // We found a region that matches the type of the init list, |
1466 | // so we assume this is the outer-most region. This can happen |
1467 | // if the initializer list is inside a class. If our assumption |
1468 | // is wrong, we return a nullptr in the end. |
1469 | if (ITy == TVR->getValueType().getCanonicalType()) |
1470 | break; |
1471 | |
1472 | TVRStack.push(x: TVR); |
1473 | TVR = cast<TypedValueRegion>(Val: TVR->getSuperRegion()); |
1474 | } |
1475 | |
1476 | // If the type of the outer most region doesn't match the type |
1477 | // of the ILE, we can't match the ILE and the region. |
1478 | if (ITy != TVR->getValueType().getCanonicalType()) |
1479 | return nullptr; |
1480 | |
1481 | const Expr *Init = ILE; |
1482 | while (!TVRStack.empty()) { |
1483 | TVR = TVRStack.top(); |
1484 | TVRStack.pop(); |
1485 | |
1486 | // We hit something that's not an init list before |
1487 | // running out of regions, so we most likely failed. |
1488 | if (!isa<InitListExpr>(Val: Init)) |
1489 | return nullptr; |
1490 | |
1491 | ILE = cast<InitListExpr>(Val: Init); |
1492 | auto NumInits = ILE->getNumInits(); |
1493 | |
1494 | if (const auto *FR = dyn_cast<FieldRegion>(Val: TVR)) { |
1495 | const auto *FD = FR->getDecl(); |
1496 | |
1497 | if (FD->getFieldIndex() >= NumInits) |
1498 | return nullptr; |
1499 | |
1500 | Init = ILE->getInit(Init: FD->getFieldIndex()); |
1501 | } else if (const auto *ER = dyn_cast<ElementRegion>(Val: TVR)) { |
1502 | const auto Ind = ER->getIndex(); |
1503 | |
1504 | // If index is symbolic, we can't figure out which expression |
1505 | // belongs to the region. |
1506 | if (!Ind.isConstant()) |
1507 | return nullptr; |
1508 | |
1509 | const auto IndVal = Ind.getAsInteger()->getLimitedValue(); |
1510 | if (IndVal >= NumInits) |
1511 | return nullptr; |
1512 | |
1513 | Init = ILE->getInit(Init: IndVal); |
1514 | } |
1515 | } |
1516 | |
1517 | return Init; |
1518 | } |
1519 | |
1520 | PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ, |
1521 | BugReporterContext &BRC, |
1522 | PathSensitiveBugReport &BR) { |
1523 | if (Satisfied) |
1524 | return nullptr; |
1525 | |
1526 | const ExplodedNode *StoreSite = nullptr; |
1527 | const ExplodedNode *Pred = Succ->getFirstPred(); |
1528 | const Expr *InitE = nullptr; |
1529 | bool IsParam = false; |
1530 | |
1531 | // First see if we reached the declaration of the region. |
1532 | if (const auto *VR = dyn_cast<VarRegion>(Val: R)) { |
1533 | if (isInitializationOfVar(N: Pred, VR)) { |
1534 | StoreSite = Pred; |
1535 | InitE = VR->getDecl()->getInit(); |
1536 | } |
1537 | } |
1538 | |
1539 | // If this is a post initializer expression, initializing the region, we |
1540 | // should track the initializer expression. |
1541 | if (std::optional<PostInitializer> PIP = |
1542 | Pred->getLocationAs<PostInitializer>()) { |
1543 | const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); |
1544 | if (FieldReg == R) { |
1545 | StoreSite = Pred; |
1546 | InitE = PIP->getInitializer()->getInit(); |
1547 | } |
1548 | } |
1549 | |
1550 | // Otherwise, see if this is the store site: |
1551 | // (1) Succ has this binding and Pred does not, i.e. this is |
1552 | // where the binding first occurred. |
1553 | // (2) Succ has this binding and is a PostStore node for this region, i.e. |
1554 | // the same binding was re-assigned here. |
1555 | if (!StoreSite) { |
1556 | if (Succ->getState()->getSVal(R) != V) |
1557 | return nullptr; |
1558 | |
1559 | if (hasVisibleUpdate(LeftNode: Pred, LeftVal: Pred->getState()->getSVal(R), RightNode: Succ, RightVal: V)) { |
1560 | std::optional<PostStore> PS = Succ->getLocationAs<PostStore>(); |
1561 | if (!PS || PS->getLocationValue() != R) |
1562 | return nullptr; |
1563 | } |
1564 | |
1565 | StoreSite = Succ; |
1566 | |
1567 | if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) { |
1568 | // If this is an assignment expression, we can track the value |
1569 | // being assigned. |
1570 | if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { |
1571 | if (BO->isAssignmentOp()) |
1572 | InitE = BO->getRHS(); |
1573 | } |
1574 | // If we have a declaration like 'S s{1,2}' that needs special |
1575 | // handling, we handle it here. |
1576 | else if (const auto *DS = P->getStmtAs<DeclStmt>()) { |
1577 | const auto *Decl = DS->getSingleDecl(); |
1578 | if (isa<VarDecl>(Val: Decl)) { |
1579 | const auto *VD = cast<VarDecl>(Val: Decl); |
1580 | |
1581 | // FIXME: Here we only track the inner most region, so we lose |
1582 | // information, but it's still better than a crash or no information |
1583 | // at all. |
1584 | // |
1585 | // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', |
1586 | // and throw away the rest. |
1587 | if (const auto *ILE = dyn_cast<InitListExpr>(Val: VD->getInit())) |
1588 | InitE = tryExtractInitializerFromList(ILE, R); |
1589 | } |
1590 | } else if (const auto *CE = P->getStmtAs<CXXConstructExpr>()) { |
1591 | |
1592 | const auto State = Succ->getState(); |
1593 | |
1594 | if (isTrivialCopyOrMoveCtor(CE) && isa<SubRegion>(Val: R)) { |
1595 | // Migrate the field regions from the current object to |
1596 | // the parent object. If we track 'a.y.e' and encounter |
1597 | // 'S a = b' then we need to track 'b.y.e'. |
1598 | |
1599 | // Push the regions to a stack, from last to first, so |
1600 | // considering the example above the stack will look like |
1601 | // (bottom) 'e' -> 'y' (top). |
1602 | |
1603 | std::stack<const SubRegion *> SRStack; |
1604 | const SubRegion *SR = cast<SubRegion>(Val: R); |
1605 | while (isa<FieldRegion>(Val: SR) || isa<ElementRegion>(Val: SR)) { |
1606 | SRStack.push(x: SR); |
1607 | SR = cast<SubRegion>(Val: SR->getSuperRegion()); |
1608 | } |
1609 | |
1610 | // Get the region for the object we copied/moved from. |
1611 | const auto *OriginEx = CE->getArg(Arg: 0); |
1612 | const auto OriginVal = |
1613 | State->getSVal(OriginEx, Succ->getLocationContext()); |
1614 | |
1615 | // Pop the stored field regions and apply them to the origin |
1616 | // object in the same order we had them on the copy. |
1617 | // OriginField will evolve like 'b' -> 'b.y' -> 'b.y.e'. |
1618 | SVal OriginField = OriginVal; |
1619 | while (!SRStack.empty()) { |
1620 | const auto *TopR = SRStack.top(); |
1621 | SRStack.pop(); |
1622 | |
1623 | if (const auto *FR = dyn_cast<FieldRegion>(Val: TopR)) { |
1624 | OriginField = State->getLValue(decl: FR->getDecl(), Base: OriginField); |
1625 | } else if (const auto *ER = dyn_cast<ElementRegion>(Val: TopR)) { |
1626 | OriginField = State->getLValue(ElementType: ER->getElementType(), |
1627 | Idx: ER->getIndex(), Base: OriginField); |
1628 | } else { |
1629 | // FIXME: handle other region type |
1630 | } |
1631 | } |
1632 | |
1633 | // Track 'b.y.e'. |
1634 | getParentTracker().track(V, R: OriginField.getAsRegion(), Opts: Options); |
1635 | InitE = OriginEx; |
1636 | } |
1637 | } |
1638 | // This branch can occur in cases like `Ctor() : field{ x, y } {}'. |
1639 | else if (const auto *ILE = P->getStmtAs<InitListExpr>()) { |
1640 | // FIXME: Here we only track the top level region, so we lose |
1641 | // information, but it's still better than a crash or no information |
1642 | // at all. |
1643 | // |
1644 | // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', and |
1645 | // throw away the rest. |
1646 | InitE = tryExtractInitializerFromList(ILE, R); |
1647 | } |
1648 | } |
1649 | |
1650 | // If this is a call entry, the variable should be a parameter. |
1651 | // FIXME: Handle CXXThisRegion as well. (This is not a priority because |
1652 | // 'this' should never be NULL, but this visitor isn't just for NULL and |
1653 | // UndefinedVal.) |
1654 | if (std::optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { |
1655 | if (const auto *VR = dyn_cast<VarRegion>(Val: R)) { |
1656 | |
1657 | if (const auto *Param = dyn_cast<ParmVarDecl>(Val: VR->getDecl())) { |
1658 | ProgramStateManager &StateMgr = BRC.getStateManager(); |
1659 | CallEventManager &CallMgr = StateMgr.getCallEventManager(); |
1660 | |
1661 | CallEventRef<> Call = CallMgr.getCaller(CalleeCtx: CE->getCalleeContext(), |
1662 | State: Succ->getState()); |
1663 | InitE = Call->getArgExpr(Index: Param->getFunctionScopeIndex()); |
1664 | } else { |
1665 | // Handle Objective-C 'self'. |
1666 | assert(isa<ImplicitParamDecl>(VR->getDecl())); |
1667 | InitE = cast<ObjCMessageExpr>(Val: CE->getCalleeContext()->getCallSite()) |
1668 | ->getInstanceReceiver()->IgnoreParenCasts(); |
1669 | } |
1670 | IsParam = true; |
1671 | } |
1672 | } |
1673 | |
1674 | // If this is a CXXTempObjectRegion, the Expr responsible for its creation |
1675 | // is wrapped inside of it. |
1676 | if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(Val: R)) |
1677 | InitE = TmpR->getExpr(); |
1678 | } |
1679 | |
1680 | if (!StoreSite) |
1681 | return nullptr; |
1682 | |
1683 | Satisfied = true; |
1684 | |
1685 | // If we have an expression that provided the value, try to track where it |
1686 | // came from. |
1687 | if (InitE) { |
1688 | if (!IsParam) |
1689 | InitE = InitE->IgnoreParenCasts(); |
1690 | |
1691 | getParentTracker().track(E: InitE, N: StoreSite, Opts: Options); |
1692 | } |
1693 | |
1694 | // Let's try to find the region where the value came from. |
1695 | const MemRegion *OldRegion = nullptr; |
1696 | |
1697 | // If we have init expression, it might be simply a reference |
1698 | // to a variable, so we can use it. |
1699 | if (InitE) { |
1700 | // That region might still be not exactly what we are looking for. |
1701 | // In situations like `int &ref = val;`, we can't say that |
1702 | // `ref` is initialized with `val`, rather refers to `val`. |
1703 | // |
1704 | // In order, to mitigate situations like this, we check if the last |
1705 | // stored value in that region is the value that we track. |
1706 | // |
1707 | // TODO: support other situations better. |
1708 | if (const MemRegion *Candidate = |
1709 | getLocationRegionIfReference(E: InitE, N: Succ, LookingForReference: false)) { |
1710 | const StoreManager &SM = BRC.getStateManager().getStoreManager(); |
1711 | |
1712 | // Here we traverse the graph up to find the last node where the |
1713 | // candidate region is still in the store. |
1714 | for (const ExplodedNode *N = StoreSite; N; N = N->getFirstPred()) { |
1715 | if (SM.includedInBindings(store: N->getState()->getStore(), region: Candidate)) { |
1716 | // And if it was bound to the target value, we can use it. |
1717 | if (N->getState()->getSVal(R: Candidate) == V) { |
1718 | OldRegion = Candidate; |
1719 | } |
1720 | break; |
1721 | } |
1722 | } |
1723 | } |
1724 | } |
1725 | |
1726 | // Otherwise, if the current region does indeed contain the value |
1727 | // we are looking for, we can look for a region where this value |
1728 | // was before. |
1729 | // |
1730 | // It can be useful for situations like: |
1731 | // new = identity(old) |
1732 | // where the analyzer knows that 'identity' returns the value of its |
1733 | // first argument. |
1734 | // |
1735 | // NOTE: If the region R is not a simple var region, it can contain |
1736 | // V in one of its subregions. |
1737 | if (!OldRegion && StoreSite->getState()->getSVal(R) == V) { |
1738 | // Let's go up the graph to find the node where the region is |
1739 | // bound to V. |
1740 | const ExplodedNode *NodeWithoutBinding = StoreSite->getFirstPred(); |
1741 | for (; |
1742 | NodeWithoutBinding && NodeWithoutBinding->getState()->getSVal(R) == V; |
1743 | NodeWithoutBinding = NodeWithoutBinding->getFirstPred()) { |
1744 | } |
1745 | |
1746 | if (NodeWithoutBinding) { |
1747 | // Let's try to find a unique binding for the value in that node. |
1748 | // We want to use this to find unique bindings because of the following |
1749 | // situations: |
1750 | // b = a; |
1751 | // c = identity(b); |
1752 | // |
1753 | // Telling the user that the value of 'a' is assigned to 'c', while |
1754 | // correct, can be confusing. |
1755 | StoreManager::FindUniqueBinding FB(V.getAsLocSymbol()); |
1756 | BRC.getStateManager().iterBindings(state: NodeWithoutBinding->getState(), F&: FB); |
1757 | if (FB) |
1758 | OldRegion = FB.getRegion(); |
1759 | } |
1760 | } |
1761 | |
1762 | if (Options.Kind == TrackingKind::Condition && OriginSFC && |
1763 | !OriginSFC->isParentOf(LC: StoreSite->getStackFrame())) |
1764 | return nullptr; |
1765 | |
1766 | // Okay, we've found the binding. Emit an appropriate message. |
1767 | SmallString<256> sbuf; |
1768 | llvm::raw_svector_ostream os(sbuf); |
1769 | |
1770 | StoreInfo SI = {.StoreKind: StoreInfo::Assignment, // default kind |
1771 | .StoreSite: StoreSite, |
1772 | .SourceOfTheValue: InitE, |
1773 | .Value: V, |
1774 | .Dest: R, |
1775 | .Origin: OldRegion}; |
1776 | |
1777 | if (std::optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { |
1778 | const Stmt *S = PS->getStmt(); |
1779 | const auto *DS = dyn_cast<DeclStmt>(Val: S); |
1780 | const auto *VR = dyn_cast<VarRegion>(Val: R); |
1781 | |
1782 | if (DS) { |
1783 | SI.StoreKind = StoreInfo::Initialization; |
1784 | } else if (isa<BlockExpr>(Val: S)) { |
1785 | SI.StoreKind = StoreInfo::BlockCapture; |
1786 | if (VR) { |
1787 | // See if we can get the BlockVarRegion. |
1788 | ProgramStateRef State = StoreSite->getState(); |
1789 | SVal V = StoreSite->getSVal(S); |
1790 | if (const auto *BDR = |
1791 | dyn_cast_or_null<BlockDataRegion>(Val: V.getAsRegion())) { |
1792 | if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { |
1793 | getParentTracker().track(V: State->getSVal(R: OriginalR), R: OriginalR, |
1794 | Opts: Options, Origin: OriginSFC); |
1795 | } |
1796 | } |
1797 | } |
1798 | } |
1799 | } else if (SI.StoreSite->getLocation().getAs<CallEnter>() && |
1800 | isa<VarRegion>(Val: SI.Dest)) { |
1801 | SI.StoreKind = StoreInfo::CallArgument; |
1802 | } |
1803 | |
1804 | return getParentTracker().handle(SI, BRC, Opts: Options); |
1805 | } |
1806 | |
1807 | //===----------------------------------------------------------------------===// |
1808 | // Implementation of TrackConstraintBRVisitor. |
1809 | //===----------------------------------------------------------------------===// |
1810 | |
1811 | void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { |
1812 | static int tag = 0; |
1813 | ID.AddPointer(Ptr: &tag); |
1814 | ID.AddString(String: Message); |
1815 | ID.AddBoolean(B: Assumption); |
1816 | ID.Add(x: Constraint); |
1817 | } |
1818 | |
1819 | /// Return the tag associated with this visitor. This tag will be used |
1820 | /// to make all PathDiagnosticPieces created by this visitor. |
1821 | const char *TrackConstraintBRVisitor::getTag() { |
1822 | return "TrackConstraintBRVisitor" ; |
1823 | } |
1824 | |
1825 | bool TrackConstraintBRVisitor::isZeroCheck() const { |
1826 | return !Assumption && Constraint.getAs<Loc>(); |
1827 | } |
1828 | |
1829 | bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const { |
1830 | if (isZeroCheck()) |
1831 | return N->getState()->isNull(V: Constraint).isUnderconstrained(); |
1832 | return (bool)N->getState()->assume(Cond: Constraint, Assumption: !Assumption); |
1833 | } |
1834 | |
1835 | PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode( |
1836 | const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { |
1837 | const ExplodedNode *PrevN = N->getFirstPred(); |
1838 | if (IsSatisfied) |
1839 | return nullptr; |
1840 | |
1841 | // Start tracking after we see the first state in which the value is |
1842 | // constrained. |
1843 | if (!IsTrackingTurnedOn) |
1844 | if (!isUnderconstrained(N)) |
1845 | IsTrackingTurnedOn = true; |
1846 | if (!IsTrackingTurnedOn) |
1847 | return nullptr; |
1848 | |
1849 | // Check if in the previous state it was feasible for this constraint |
1850 | // to *not* be true. |
1851 | if (isUnderconstrained(N: PrevN)) { |
1852 | IsSatisfied = true; |
1853 | |
1854 | // At this point, the negation of the constraint should be infeasible. If it |
1855 | // is feasible, make sure that the negation of the constrainti was |
1856 | // infeasible in the current state. If it is feasible, we somehow missed |
1857 | // the transition point. |
1858 | assert(!isUnderconstrained(N)); |
1859 | |
1860 | // Construct a new PathDiagnosticPiece. |
1861 | ProgramPoint P = N->getLocation(); |
1862 | |
1863 | // If this node already have a specialized note, it's probably better |
1864 | // than our generic note. |
1865 | // FIXME: This only looks for note tags, not for other ways to add a note. |
1866 | if (isa_and_nonnull<NoteTag>(Val: P.getTag())) |
1867 | return nullptr; |
1868 | |
1869 | PathDiagnosticLocation L = |
1870 | PathDiagnosticLocation::create(P, SMng: BRC.getSourceManager()); |
1871 | if (!L.isValid()) |
1872 | return nullptr; |
1873 | |
1874 | auto X = std::make_shared<PathDiagnosticEventPiece>(args&: L, args: Message); |
1875 | X->setTag(getTag()); |
1876 | return std::move(X); |
1877 | } |
1878 | |
1879 | return nullptr; |
1880 | } |
1881 | |
1882 | //===----------------------------------------------------------------------===// |
1883 | // Implementation of SuppressInlineDefensiveChecksVisitor. |
1884 | //===----------------------------------------------------------------------===// |
1885 | |
1886 | SuppressInlineDefensiveChecksVisitor:: |
1887 | SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) |
1888 | : V(Value) { |
1889 | // Check if the visitor is disabled. |
1890 | AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; |
1891 | if (!Options.ShouldSuppressInlinedDefensiveChecks) |
1892 | IsSatisfied = true; |
1893 | } |
1894 | |
1895 | void SuppressInlineDefensiveChecksVisitor::Profile( |
1896 | llvm::FoldingSetNodeID &ID) const { |
1897 | static int id = 0; |
1898 | ID.AddPointer(Ptr: &id); |
1899 | ID.Add(x: V); |
1900 | } |
1901 | |
1902 | const char *SuppressInlineDefensiveChecksVisitor::getTag() { |
1903 | return "IDCVisitor" ; |
1904 | } |
1905 | |
1906 | PathDiagnosticPieceRef |
1907 | SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, |
1908 | BugReporterContext &BRC, |
1909 | PathSensitiveBugReport &BR) { |
1910 | const ExplodedNode *Pred = Succ->getFirstPred(); |
1911 | if (IsSatisfied) |
1912 | return nullptr; |
1913 | |
1914 | // Start tracking after we see the first state in which the value is null. |
1915 | if (!IsTrackingTurnedOn) |
1916 | if (Succ->getState()->isNull(V).isConstrainedTrue()) |
1917 | IsTrackingTurnedOn = true; |
1918 | if (!IsTrackingTurnedOn) |
1919 | return nullptr; |
1920 | |
1921 | // Check if in the previous state it was feasible for this value |
1922 | // to *not* be null. |
1923 | if (!Pred->getState()->isNull(V).isConstrainedTrue() && |
1924 | Succ->getState()->isNull(V).isConstrainedTrue()) { |
1925 | IsSatisfied = true; |
1926 | |
1927 | // Check if this is inlined defensive checks. |
1928 | const LocationContext *CurLC = Succ->getLocationContext(); |
1929 | const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); |
1930 | if (CurLC != ReportLC && !CurLC->isParentOf(LC: ReportLC)) { |
1931 | BR.markInvalid(Tag: "Suppress IDC" , Data: CurLC); |
1932 | return nullptr; |
1933 | } |
1934 | |
1935 | // Treat defensive checks in function-like macros as if they were an inlined |
1936 | // defensive check. If the bug location is not in a macro and the |
1937 | // terminator for the current location is in a macro then suppress the |
1938 | // warning. |
1939 | auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>(); |
1940 | |
1941 | if (!BugPoint) |
1942 | return nullptr; |
1943 | |
1944 | ProgramPoint CurPoint = Succ->getLocation(); |
1945 | const Stmt *CurTerminatorStmt = nullptr; |
1946 | if (auto BE = CurPoint.getAs<BlockEdge>()) { |
1947 | CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt(); |
1948 | } else if (auto SP = CurPoint.getAs<StmtPoint>()) { |
1949 | const Stmt *CurStmt = SP->getStmt(); |
1950 | if (!CurStmt->getBeginLoc().isMacroID()) |
1951 | return nullptr; |
1952 | |
1953 | CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap(); |
1954 | CurTerminatorStmt = Map->getBlock(S: CurStmt)->getTerminatorStmt(); |
1955 | } else { |
1956 | return nullptr; |
1957 | } |
1958 | |
1959 | if (!CurTerminatorStmt) |
1960 | return nullptr; |
1961 | |
1962 | SourceLocation TerminatorLoc = CurTerminatorStmt->getBeginLoc(); |
1963 | if (TerminatorLoc.isMacroID()) { |
1964 | SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc(); |
1965 | |
1966 | // Suppress reports unless we are in that same macro. |
1967 | if (!BugLoc.isMacroID() || |
1968 | getMacroName(Loc: BugLoc, BRC) != getMacroName(Loc: TerminatorLoc, BRC)) { |
1969 | BR.markInvalid(Tag: "Suppress Macro IDC" , Data: CurLC); |
1970 | } |
1971 | return nullptr; |
1972 | } |
1973 | } |
1974 | return nullptr; |
1975 | } |
1976 | |
1977 | //===----------------------------------------------------------------------===// |
1978 | // TrackControlDependencyCondBRVisitor. |
1979 | //===----------------------------------------------------------------------===// |
1980 | |
1981 | namespace { |
1982 | /// Tracks the expressions that are a control dependency of the node that was |
1983 | /// supplied to the constructor. |
1984 | /// For example: |
1985 | /// |
1986 | /// cond = 1; |
1987 | /// if (cond) |
1988 | /// 10 / 0; |
1989 | /// |
1990 | /// An error is emitted at line 3. This visitor realizes that the branch |
1991 | /// on line 2 is a control dependency of line 3, and tracks it's condition via |
1992 | /// trackExpressionValue(). |
1993 | class TrackControlDependencyCondBRVisitor final |
1994 | : public TrackingBugReporterVisitor { |
1995 | const ExplodedNode *Origin; |
1996 | ControlDependencyCalculator ControlDeps; |
1997 | llvm::SmallSet<const CFGBlock *, 32> VisitedBlocks; |
1998 | |
1999 | public: |
2000 | TrackControlDependencyCondBRVisitor(TrackerRef ParentTracker, |
2001 | const ExplodedNode *O) |
2002 | : TrackingBugReporterVisitor(ParentTracker), Origin(O), |
2003 | ControlDeps(&O->getCFG()) {} |
2004 | |
2005 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
2006 | static int x = 0; |
2007 | ID.AddPointer(Ptr: &x); |
2008 | } |
2009 | |
2010 | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
2011 | BugReporterContext &BRC, |
2012 | PathSensitiveBugReport &BR) override; |
2013 | }; |
2014 | } // end of anonymous namespace |
2015 | |
2016 | static std::shared_ptr<PathDiagnosticEventPiece> |
2017 | constructDebugPieceForTrackedCondition(const Expr *Cond, |
2018 | const ExplodedNode *N, |
2019 | BugReporterContext &BRC) { |
2020 | |
2021 | if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE || |
2022 | !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug) |
2023 | return nullptr; |
2024 | |
2025 | std::string ConditionText = std::string(Lexer::getSourceText( |
2026 | Range: CharSourceRange::getTokenRange(Cond->getSourceRange()), |
2027 | SM: BRC.getSourceManager(), LangOpts: BRC.getASTContext().getLangOpts())); |
2028 | |
2029 | return std::make_shared<PathDiagnosticEventPiece>( |
2030 | args: PathDiagnosticLocation::createBegin( |
2031 | Cond, BRC.getSourceManager(), N->getLocationContext()), |
2032 | args: (Twine() + "Tracking condition '" + ConditionText + "'" ).str()); |
2033 | } |
2034 | |
2035 | static bool isAssertlikeBlock(const CFGBlock *B, ASTContext &Context) { |
2036 | if (B->succ_size() != 2) |
2037 | return false; |
2038 | |
2039 | const CFGBlock *Then = B->succ_begin()->getReachableBlock(); |
2040 | const CFGBlock *Else = (B->succ_begin() + 1)->getReachableBlock(); |
2041 | |
2042 | if (!Then || !Else) |
2043 | return false; |
2044 | |
2045 | if (Then->isInevitablySinking() != Else->isInevitablySinking()) |
2046 | return true; |
2047 | |
2048 | // For the following condition the following CFG would be built: |
2049 | // |
2050 | // -------------> |
2051 | // / \ |
2052 | // [B1] -> [B2] -> [B3] -> [sink] |
2053 | // assert(A && B || C); \ \ |
2054 | // -----------> [go on with the execution] |
2055 | // |
2056 | // It so happens that CFGBlock::getTerminatorCondition returns 'A' for block |
2057 | // B1, 'A && B' for B2, and 'A && B || C' for B3. Let's check whether we |
2058 | // reached the end of the condition! |
2059 | if (const Stmt *ElseCond = Else->getTerminatorCondition()) |
2060 | if (const auto *BinOp = dyn_cast<BinaryOperator>(Val: ElseCond)) |
2061 | if (BinOp->isLogicalOp()) |
2062 | return isAssertlikeBlock(B: Else, Context); |
2063 | |
2064 | return false; |
2065 | } |
2066 | |
2067 | PathDiagnosticPieceRef |
2068 | TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, |
2069 | BugReporterContext &BRC, |
2070 | PathSensitiveBugReport &BR) { |
2071 | // We can only reason about control dependencies within the same stack frame. |
2072 | if (Origin->getStackFrame() != N->getStackFrame()) |
2073 | return nullptr; |
2074 | |
2075 | CFGBlock *NB = const_cast<CFGBlock *>(N->getCFGBlock()); |
2076 | |
2077 | // Skip if we already inspected this block. |
2078 | if (!VisitedBlocks.insert(Ptr: NB).second) |
2079 | return nullptr; |
2080 | |
2081 | CFGBlock *OriginB = const_cast<CFGBlock *>(Origin->getCFGBlock()); |
2082 | |
2083 | // TODO: Cache CFGBlocks for each ExplodedNode. |
2084 | if (!OriginB || !NB) |
2085 | return nullptr; |
2086 | |
2087 | if (isAssertlikeBlock(B: NB, Context&: BRC.getASTContext())) |
2088 | return nullptr; |
2089 | |
2090 | if (ControlDeps.isControlDependent(A: OriginB, B: NB)) { |
2091 | // We don't really want to explain for range loops. Evidence suggests that |
2092 | // the only thing that leads to is the addition of calls to operator!=. |
2093 | if (llvm::isa_and_nonnull<CXXForRangeStmt>(Val: NB->getTerminatorStmt())) |
2094 | return nullptr; |
2095 | |
2096 | if (const Expr *Condition = NB->getLastCondition()) { |
2097 | |
2098 | // If we can't retrieve a sensible condition, just bail out. |
2099 | const Expr *InnerExpr = peelOffOuterExpr(Ex: Condition, N); |
2100 | if (!InnerExpr) |
2101 | return nullptr; |
2102 | |
2103 | // If the condition was a function call, we likely won't gain much from |
2104 | // tracking it either. Evidence suggests that it will mostly trigger in |
2105 | // scenarios like this: |
2106 | // |
2107 | // void f(int *x) { |
2108 | // x = nullptr; |
2109 | // if (alwaysTrue()) // We don't need a whole lot of explanation |
2110 | // // here, the function name is good enough. |
2111 | // *x = 5; |
2112 | // } |
2113 | // |
2114 | // Its easy to create a counterexample where this heuristic would make us |
2115 | // lose valuable information, but we've never really seen one in practice. |
2116 | if (isa<CallExpr>(Val: InnerExpr)) |
2117 | return nullptr; |
2118 | |
2119 | // Keeping track of the already tracked conditions on a visitor level |
2120 | // isn't sufficient, because a new visitor is created for each tracked |
2121 | // expression, hence the BugReport level set. |
2122 | if (BR.addTrackedCondition(Cond: N)) { |
2123 | getParentTracker().track(E: InnerExpr, N, |
2124 | Opts: {.Kind: bugreporter::TrackingKind::Condition, |
2125 | /*EnableNullFPSuppression=*/false}); |
2126 | return constructDebugPieceForTrackedCondition(Cond: Condition, N, BRC); |
2127 | } |
2128 | } |
2129 | } |
2130 | |
2131 | return nullptr; |
2132 | } |
2133 | |
2134 | //===----------------------------------------------------------------------===// |
2135 | // Implementation of trackExpressionValue. |
2136 | //===----------------------------------------------------------------------===// |
2137 | |
2138 | static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) { |
2139 | |
2140 | Ex = Ex->IgnoreParenCasts(); |
2141 | if (const auto *FE = dyn_cast<FullExpr>(Val: Ex)) |
2142 | return peelOffOuterExpr(Ex: FE->getSubExpr(), N); |
2143 | if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Val: Ex)) |
2144 | return peelOffOuterExpr(Ex: OVE->getSourceExpr(), N); |
2145 | if (const auto *POE = dyn_cast<PseudoObjectExpr>(Val: Ex)) { |
2146 | const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(Val: POE->getSyntacticForm()); |
2147 | if (PropRef && PropRef->isMessagingGetter()) { |
2148 | const Expr *GetterMessageSend = |
2149 | POE->getSemanticExpr(index: POE->getNumSemanticExprs() - 1); |
2150 | assert(isa<ObjCMessageExpr>(GetterMessageSend->IgnoreParenCasts())); |
2151 | return peelOffOuterExpr(Ex: GetterMessageSend, N); |
2152 | } |
2153 | } |
2154 | |
2155 | // Peel off the ternary operator. |
2156 | if (const auto *CO = dyn_cast<ConditionalOperator>(Val: Ex)) { |
2157 | // Find a node where the branching occurred and find out which branch |
2158 | // we took (true/false) by looking at the ExplodedGraph. |
2159 | const ExplodedNode *NI = N; |
2160 | do { |
2161 | ProgramPoint ProgPoint = NI->getLocation(); |
2162 | if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { |
2163 | const CFGBlock *srcBlk = BE->getSrc(); |
2164 | if (const Stmt *term = srcBlk->getTerminatorStmt()) { |
2165 | if (term == CO) { |
2166 | bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst()); |
2167 | if (TookTrueBranch) |
2168 | return peelOffOuterExpr(Ex: CO->getTrueExpr(), N); |
2169 | else |
2170 | return peelOffOuterExpr(Ex: CO->getFalseExpr(), N); |
2171 | } |
2172 | } |
2173 | } |
2174 | NI = NI->getFirstPred(); |
2175 | } while (NI); |
2176 | } |
2177 | |
2178 | if (auto *BO = dyn_cast<BinaryOperator>(Val: Ex)) |
2179 | if (const Expr *SubEx = peelOffPointerArithmetic(B: BO)) |
2180 | return peelOffOuterExpr(Ex: SubEx, N); |
2181 | |
2182 | if (auto *UO = dyn_cast<UnaryOperator>(Val: Ex)) { |
2183 | if (UO->getOpcode() == UO_LNot) |
2184 | return peelOffOuterExpr(Ex: UO->getSubExpr(), N); |
2185 | |
2186 | // FIXME: There's a hack in our Store implementation that always computes |
2187 | // field offsets around null pointers as if they are always equal to 0. |
2188 | // The idea here is to report accesses to fields as null dereferences |
2189 | // even though the pointer value that's being dereferenced is actually |
2190 | // the offset of the field rather than exactly 0. |
2191 | // See the FIXME in StoreManager's getLValueFieldOrIvar() method. |
2192 | // This code interacts heavily with this hack; otherwise the value |
2193 | // would not be null at all for most fields, so we'd be unable to track it. |
2194 | if (UO->getOpcode() == UO_AddrOf && UO->getSubExpr()->isLValue()) |
2195 | if (const Expr *DerefEx = bugreporter::getDerefExpr(UO->getSubExpr())) |
2196 | return peelOffOuterExpr(Ex: DerefEx, N); |
2197 | } |
2198 | |
2199 | return Ex; |
2200 | } |
2201 | |
2202 | /// Find the ExplodedNode where the lvalue (the value of 'Ex') |
2203 | /// was computed. |
2204 | static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, |
2205 | const Expr *Inner) { |
2206 | while (N) { |
2207 | if (N->getStmtForDiagnostics() == Inner) |
2208 | return N; |
2209 | N = N->getFirstPred(); |
2210 | } |
2211 | return N; |
2212 | } |
2213 | |
2214 | //===----------------------------------------------------------------------===// |
2215 | // Tracker implementation |
2216 | //===----------------------------------------------------------------------===// |
2217 | |
2218 | PathDiagnosticPieceRef StoreHandler::constructNote(StoreInfo SI, |
2219 | BugReporterContext &BRC, |
2220 | StringRef NodeText) { |
2221 | // Construct a new PathDiagnosticPiece. |
2222 | ProgramPoint P = SI.StoreSite->getLocation(); |
2223 | PathDiagnosticLocation L; |
2224 | if (P.getAs<CallEnter>() && SI.SourceOfTheValue) |
2225 | L = PathDiagnosticLocation(SI.SourceOfTheValue, BRC.getSourceManager(), |
2226 | P.getLocationContext()); |
2227 | |
2228 | if (!L.isValid() || !L.asLocation().isValid()) |
2229 | L = PathDiagnosticLocation::create(P, SMng: BRC.getSourceManager()); |
2230 | |
2231 | if (!L.isValid() || !L.asLocation().isValid()) |
2232 | return nullptr; |
2233 | |
2234 | return std::make_shared<PathDiagnosticEventPiece>(args&: L, args&: NodeText); |
2235 | } |
2236 | |
2237 | namespace { |
2238 | class DefaultStoreHandler final : public StoreHandler { |
2239 | public: |
2240 | using StoreHandler::StoreHandler; |
2241 | |
2242 | PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext &BRC, |
2243 | TrackingOptions Opts) override { |
2244 | // Okay, we've found the binding. Emit an appropriate message. |
2245 | SmallString<256> Buffer; |
2246 | llvm::raw_svector_ostream OS(Buffer); |
2247 | |
2248 | switch (SI.StoreKind) { |
2249 | case StoreInfo::Initialization: |
2250 | case StoreInfo::BlockCapture: |
2251 | showBRDiagnostics(OS, SI); |
2252 | break; |
2253 | case StoreInfo::CallArgument: |
2254 | showBRParamDiagnostics(OS, SI); |
2255 | break; |
2256 | case StoreInfo::Assignment: |
2257 | showBRDefaultDiagnostics(OS, SI); |
2258 | break; |
2259 | } |
2260 | |
2261 | if (Opts.Kind == bugreporter::TrackingKind::Condition) |
2262 | OS << WillBeUsedForACondition; |
2263 | |
2264 | return constructNote(SI, BRC, NodeText: OS.str()); |
2265 | } |
2266 | }; |
2267 | |
2268 | class ControlDependencyHandler final : public ExpressionHandler { |
2269 | public: |
2270 | using ExpressionHandler::ExpressionHandler; |
2271 | |
2272 | Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, |
2273 | const ExplodedNode *LVNode, |
2274 | TrackingOptions Opts) override { |
2275 | PathSensitiveBugReport &Report = getParentTracker().getReport(); |
2276 | |
2277 | // We only track expressions if we believe that they are important. Chances |
2278 | // are good that control dependencies to the tracking point are also |
2279 | // important because of this, let's explain why we believe control reached |
2280 | // this point. |
2281 | // TODO: Shouldn't we track control dependencies of every bug location, |
2282 | // rather than only tracked expressions? |
2283 | if (LVNode->getState() |
2284 | ->getAnalysisManager() |
2285 | .getAnalyzerOptions() |
2286 | .ShouldTrackConditions) { |
2287 | Report.addVisitor<TrackControlDependencyCondBRVisitor>( |
2288 | ConstructorArgs: &getParentTracker(), ConstructorArgs&: InputNode); |
2289 | return {/*FoundSomethingToTrack=*/true}; |
2290 | } |
2291 | |
2292 | return {}; |
2293 | } |
2294 | }; |
2295 | |
2296 | class NilReceiverHandler final : public ExpressionHandler { |
2297 | public: |
2298 | using ExpressionHandler::ExpressionHandler; |
2299 | |
2300 | Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, |
2301 | const ExplodedNode *LVNode, |
2302 | TrackingOptions Opts) override { |
2303 | // The message send could be nil due to the receiver being nil. |
2304 | // At this point in the path, the receiver should be live since we are at |
2305 | // the message send expr. If it is nil, start tracking it. |
2306 | if (const Expr *Receiver = |
2307 | NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) |
2308 | return getParentTracker().track(E: Receiver, N: LVNode, Opts); |
2309 | |
2310 | return {}; |
2311 | } |
2312 | }; |
2313 | |
2314 | class ArrayIndexHandler final : public ExpressionHandler { |
2315 | public: |
2316 | using ExpressionHandler::ExpressionHandler; |
2317 | |
2318 | Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, |
2319 | const ExplodedNode *LVNode, |
2320 | TrackingOptions Opts) override { |
2321 | // Track the index if this is an array subscript. |
2322 | if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Val: Inner)) |
2323 | return getParentTracker().track( |
2324 | E: Arr->getIdx(), N: LVNode, |
2325 | Opts: {.Kind: Opts.Kind, /*EnableNullFPSuppression*/ false}); |
2326 | |
2327 | return {}; |
2328 | } |
2329 | }; |
2330 | |
2331 | // TODO: extract it into more handlers |
2332 | class InterestingLValueHandler final : public ExpressionHandler { |
2333 | public: |
2334 | using ExpressionHandler::ExpressionHandler; |
2335 | |
2336 | Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, |
2337 | const ExplodedNode *LVNode, |
2338 | TrackingOptions Opts) override { |
2339 | ProgramStateRef LVState = LVNode->getState(); |
2340 | const StackFrameContext *SFC = LVNode->getStackFrame(); |
2341 | PathSensitiveBugReport &Report = getParentTracker().getReport(); |
2342 | Tracker::Result Result; |
2343 | |
2344 | // See if the expression we're interested refers to a variable. |
2345 | // If so, we can track both its contents and constraints on its value. |
2346 | if (ExplodedGraph::isInterestingLValueExpr(Ex: Inner)) { |
2347 | SVal LVal = LVNode->getSVal(Inner); |
2348 | |
2349 | const MemRegion *RR = getLocationRegionIfReference(E: Inner, N: LVNode); |
2350 | bool LVIsNull = LVState->isNull(V: LVal).isConstrainedTrue(); |
2351 | |
2352 | // If this is a C++ reference to a null pointer, we are tracking the |
2353 | // pointer. In addition, we should find the store at which the reference |
2354 | // got initialized. |
2355 | if (RR && !LVIsNull) |
2356 | Result.combineWith(Other: getParentTracker().track(V: LVal, R: RR, Opts, Origin: SFC)); |
2357 | |
2358 | // In case of C++ references, we want to differentiate between a null |
2359 | // reference and reference to null pointer. |
2360 | // If the LVal is null, check if we are dealing with null reference. |
2361 | // For those, we want to track the location of the reference. |
2362 | const MemRegion *R = |
2363 | (RR && LVIsNull) ? RR : LVNode->getSVal(Inner).getAsRegion(); |
2364 | |
2365 | if (R) { |
2366 | |
2367 | // Mark both the variable region and its contents as interesting. |
2368 | SVal V = LVState->getRawSVal(LV: loc::MemRegionVal(R)); |
2369 | Report.addVisitor<NoStoreFuncVisitor>(ConstructorArgs: cast<SubRegion>(Val: R), ConstructorArgs&: Opts.Kind); |
2370 | |
2371 | // When we got here, we do have something to track, and we will |
2372 | // interrupt. |
2373 | Result.FoundSomethingToTrack = true; |
2374 | Result.WasInterrupted = true; |
2375 | |
2376 | MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary( |
2377 | N: LVNode, R, EnableNullFPSuppression: Opts.EnableNullFPSuppression, BR&: Report, V); |
2378 | |
2379 | Report.markInteresting(V, TKind: Opts.Kind); |
2380 | Report.addVisitor<UndefOrNullArgVisitor>(ConstructorArgs&: R); |
2381 | |
2382 | // If the contents are symbolic and null, find out when they became |
2383 | // null. |
2384 | if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true)) |
2385 | if (LVState->isNull(V).isConstrainedTrue()) |
2386 | Report.addVisitor<TrackConstraintBRVisitor>( |
2387 | ConstructorArgs: V.castAs<DefinedSVal>(), |
2388 | /*Assumption=*/ConstructorArgs: false, ConstructorArgs: "Assuming pointer value is null" ); |
2389 | |
2390 | // Add visitor, which will suppress inline defensive checks. |
2391 | if (auto DV = V.getAs<DefinedSVal>()) |
2392 | if (!DV->isZeroConstant() && Opts.EnableNullFPSuppression) |
2393 | // Note that LVNode may be too late (i.e., too far from the |
2394 | // InputNode) because the lvalue may have been computed before the |
2395 | // inlined call was evaluated. InputNode may as well be too early |
2396 | // here, because the symbol is already dead; this, however, is fine |
2397 | // because we can still find the node in which it collapsed to null |
2398 | // previously. |
2399 | Report.addVisitor<SuppressInlineDefensiveChecksVisitor>(*DV, |
2400 | InputNode); |
2401 | getParentTracker().track(V, R, Opts, Origin: SFC); |
2402 | } |
2403 | } |
2404 | |
2405 | return Result; |
2406 | } |
2407 | }; |
2408 | |
2409 | /// Adds a ReturnVisitor if the given statement represents a call that was |
2410 | /// inlined. |
2411 | /// |
2412 | /// This will search back through the ExplodedGraph, starting from the given |
2413 | /// node, looking for when the given statement was processed. If it turns out |
2414 | /// the statement is a call that was inlined, we add the visitor to the |
2415 | /// bug report, so it can print a note later. |
2416 | class InlinedFunctionCallHandler final : public ExpressionHandler { |
2417 | using ExpressionHandler::ExpressionHandler; |
2418 | |
2419 | Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode, |
2420 | const ExplodedNode *ExprNode, |
2421 | TrackingOptions Opts) override { |
2422 | if (!CallEvent::isCallStmt(E)) |
2423 | return {}; |
2424 | |
2425 | // First, find when we processed the statement. |
2426 | // If we work with a 'CXXNewExpr' that is going to be purged away before |
2427 | // its call take place. We would catch that purge in the last condition |
2428 | // as a 'StmtPoint' so we have to bypass it. |
2429 | const bool BypassCXXNewExprEval = isa<CXXNewExpr>(Val: E); |
2430 | |
2431 | // This is moving forward when we enter into another context. |
2432 | const StackFrameContext *CurrentSFC = ExprNode->getStackFrame(); |
2433 | |
2434 | do { |
2435 | // If that is satisfied we found our statement as an inlined call. |
2436 | if (std::optional<CallExitEnd> CEE = |
2437 | ExprNode->getLocationAs<CallExitEnd>()) |
2438 | if (CEE->getCalleeContext()->getCallSite() == E) |
2439 | break; |
2440 | |
2441 | // Try to move forward to the end of the call-chain. |
2442 | ExprNode = ExprNode->getFirstPred(); |
2443 | if (!ExprNode) |
2444 | break; |
2445 | |
2446 | const StackFrameContext *PredSFC = ExprNode->getStackFrame(); |
2447 | |
2448 | // If that is satisfied we found our statement. |
2449 | // FIXME: This code currently bypasses the call site for the |
2450 | // conservatively evaluated allocator. |
2451 | if (!BypassCXXNewExprEval) |
2452 | if (std::optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>()) |
2453 | // See if we do not enter into another context. |
2454 | if (SP->getStmt() == E && CurrentSFC == PredSFC) |
2455 | break; |
2456 | |
2457 | CurrentSFC = PredSFC; |
2458 | } while (ExprNode->getStackFrame() == CurrentSFC); |
2459 | |
2460 | // Next, step over any post-statement checks. |
2461 | while (ExprNode && ExprNode->getLocation().getAs<PostStmt>()) |
2462 | ExprNode = ExprNode->getFirstPred(); |
2463 | if (!ExprNode) |
2464 | return {}; |
2465 | |
2466 | // Finally, see if we inlined the call. |
2467 | std::optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>(); |
2468 | if (!CEE) |
2469 | return {}; |
2470 | |
2471 | const StackFrameContext *CalleeContext = CEE->getCalleeContext(); |
2472 | if (CalleeContext->getCallSite() != E) |
2473 | return {}; |
2474 | |
2475 | // Check the return value. |
2476 | ProgramStateRef State = ExprNode->getState(); |
2477 | SVal RetVal = ExprNode->getSVal(E); |
2478 | |
2479 | // Handle cases where a reference is returned and then immediately used. |
2480 | if (cast<Expr>(Val: E)->isGLValue()) |
2481 | if (std::optional<Loc> LValue = RetVal.getAs<Loc>()) |
2482 | RetVal = State->getSVal(LV: *LValue); |
2483 | |
2484 | // See if the return value is NULL. If so, suppress the report. |
2485 | AnalyzerOptions &Options = State->getAnalysisManager().options; |
2486 | |
2487 | bool EnableNullFPSuppression = false; |
2488 | if (Opts.EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths) |
2489 | if (std::optional<Loc> RetLoc = RetVal.getAs<Loc>()) |
2490 | EnableNullFPSuppression = State->isNull(V: *RetLoc).isConstrainedTrue(); |
2491 | |
2492 | PathSensitiveBugReport &Report = getParentTracker().getReport(); |
2493 | Report.addVisitor<ReturnVisitor>(ConstructorArgs: &getParentTracker(), ConstructorArgs&: CalleeContext, |
2494 | ConstructorArgs&: EnableNullFPSuppression, ConstructorArgs&: Options, |
2495 | ConstructorArgs&: Opts.Kind); |
2496 | return {.FoundSomethingToTrack: true}; |
2497 | } |
2498 | }; |
2499 | |
2500 | class DefaultExpressionHandler final : public ExpressionHandler { |
2501 | public: |
2502 | using ExpressionHandler::ExpressionHandler; |
2503 | |
2504 | Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, |
2505 | const ExplodedNode *LVNode, |
2506 | TrackingOptions Opts) override { |
2507 | ProgramStateRef LVState = LVNode->getState(); |
2508 | const StackFrameContext *SFC = LVNode->getStackFrame(); |
2509 | PathSensitiveBugReport &Report = getParentTracker().getReport(); |
2510 | Tracker::Result Result; |
2511 | |
2512 | // If the expression is not an "lvalue expression", we can still |
2513 | // track the constraints on its contents. |
2514 | SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext()); |
2515 | |
2516 | // Is it a symbolic value? |
2517 | if (auto L = V.getAs<loc::MemRegionVal>()) { |
2518 | // FIXME: this is a hack for fixing a later crash when attempting to |
2519 | // dereference a void* pointer. |
2520 | // We should not try to dereference pointers at all when we don't care |
2521 | // what is written inside the pointer. |
2522 | bool CanDereference = true; |
2523 | if (const auto *SR = L->getRegionAs<SymbolicRegion>()) { |
2524 | if (SR->getPointeeStaticType()->isVoidType()) |
2525 | CanDereference = false; |
2526 | } else if (L->getRegionAs<AllocaRegion>()) |
2527 | CanDereference = false; |
2528 | |
2529 | // At this point we are dealing with the region's LValue. |
2530 | // However, if the rvalue is a symbolic region, we should track it as |
2531 | // well. Try to use the correct type when looking up the value. |
2532 | SVal RVal; |
2533 | if (ExplodedGraph::isInterestingLValueExpr(Ex: Inner)) |
2534 | RVal = LVState->getRawSVal(LV: *L, T: Inner->getType()); |
2535 | else if (CanDereference) |
2536 | RVal = LVState->getSVal(L->getRegion()); |
2537 | |
2538 | if (CanDereference) { |
2539 | Report.addVisitor<UndefOrNullArgVisitor>(L->getRegion()); |
2540 | Result.FoundSomethingToTrack = true; |
2541 | |
2542 | if (!RVal.isUnknown()) |
2543 | Result.combineWith( |
2544 | Other: getParentTracker().track(RVal, L->getRegion(), Opts, SFC)); |
2545 | } |
2546 | |
2547 | const MemRegion *RegionRVal = RVal.getAsRegion(); |
2548 | if (isa_and_nonnull<SymbolicRegion>(Val: RegionRVal)) { |
2549 | Report.markInteresting(R: RegionRVal, TKind: Opts.Kind); |
2550 | Report.addVisitor<TrackConstraintBRVisitor>( |
2551 | ConstructorArgs: loc::MemRegionVal(RegionRVal), |
2552 | /*Assumption=*/ConstructorArgs: false, ConstructorArgs: "Assuming pointer value is null" ); |
2553 | Result.FoundSomethingToTrack = true; |
2554 | } |
2555 | } |
2556 | |
2557 | return Result; |
2558 | } |
2559 | }; |
2560 | |
2561 | /// Attempts to add visitors to track an RValue expression back to its point of |
2562 | /// origin. |
2563 | class PRValueHandler final : public ExpressionHandler { |
2564 | public: |
2565 | using ExpressionHandler::ExpressionHandler; |
2566 | |
2567 | Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode, |
2568 | const ExplodedNode *ExprNode, |
2569 | TrackingOptions Opts) override { |
2570 | if (!E->isPRValue()) |
2571 | return {}; |
2572 | |
2573 | const ExplodedNode *RVNode = findNodeForExpression(N: ExprNode, Inner: E); |
2574 | if (!RVNode) |
2575 | return {}; |
2576 | |
2577 | Tracker::Result CombinedResult; |
2578 | Tracker &Parent = getParentTracker(); |
2579 | |
2580 | const auto track = [&CombinedResult, &Parent, ExprNode, |
2581 | Opts](const Expr *Inner) { |
2582 | CombinedResult.combineWith(Other: Parent.track(E: Inner, N: ExprNode, Opts)); |
2583 | }; |
2584 | |
2585 | // FIXME: Initializer lists can appear in many different contexts |
2586 | // and most of them needs a special handling. For now let's handle |
2587 | // what we can. If the initializer list only has 1 element, we track |
2588 | // that. |
2589 | // This snippet even handles nesting, e.g.: int *x{{{{{y}}}}}; |
2590 | if (const auto *ILE = dyn_cast<InitListExpr>(Val: E)) { |
2591 | if (ILE->getNumInits() == 1) { |
2592 | track(ILE->getInit(Init: 0)); |
2593 | |
2594 | return CombinedResult; |
2595 | } |
2596 | |
2597 | return {}; |
2598 | } |
2599 | |
2600 | ProgramStateRef RVState = RVNode->getState(); |
2601 | SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext()); |
2602 | const auto *BO = dyn_cast<BinaryOperator>(Val: E); |
2603 | |
2604 | if (!BO || !BO->isMultiplicativeOp() || !V.isZeroConstant()) |
2605 | return {}; |
2606 | |
2607 | SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext()); |
2608 | SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext()); |
2609 | |
2610 | // Track both LHS and RHS of a multiplication. |
2611 | if (BO->getOpcode() == BO_Mul) { |
2612 | if (LHSV.isZeroConstant()) |
2613 | track(BO->getLHS()); |
2614 | if (RHSV.isZeroConstant()) |
2615 | track(BO->getRHS()); |
2616 | } else { // Track only the LHS of a division or a modulo. |
2617 | if (LHSV.isZeroConstant()) |
2618 | track(BO->getLHS()); |
2619 | } |
2620 | |
2621 | return CombinedResult; |
2622 | } |
2623 | }; |
2624 | } // namespace |
2625 | |
2626 | Tracker::Tracker(PathSensitiveBugReport &Report) : Report(Report) { |
2627 | // Default expression handlers. |
2628 | addLowPriorityHandler<ControlDependencyHandler>(); |
2629 | addLowPriorityHandler<NilReceiverHandler>(); |
2630 | addLowPriorityHandler<ArrayIndexHandler>(); |
2631 | addLowPriorityHandler<InterestingLValueHandler>(); |
2632 | addLowPriorityHandler<InlinedFunctionCallHandler>(); |
2633 | addLowPriorityHandler<DefaultExpressionHandler>(); |
2634 | addLowPriorityHandler<PRValueHandler>(); |
2635 | // Default store handlers. |
2636 | addHighPriorityHandler<DefaultStoreHandler>(); |
2637 | } |
2638 | |
2639 | Tracker::Result Tracker::track(const Expr *E, const ExplodedNode *N, |
2640 | TrackingOptions Opts) { |
2641 | if (!E || !N) |
2642 | return {}; |
2643 | |
2644 | const Expr *Inner = peelOffOuterExpr(Ex: E, N); |
2645 | const ExplodedNode *LVNode = findNodeForExpression(N, Inner); |
2646 | if (!LVNode) |
2647 | return {}; |
2648 | |
2649 | Result CombinedResult; |
2650 | // Iterate through the handlers in the order according to their priorities. |
2651 | for (ExpressionHandlerPtr &Handler : ExpressionHandlers) { |
2652 | CombinedResult.combineWith(Other: Handler->handle(E: Inner, Original: N, ExprNode: LVNode, Opts)); |
2653 | if (CombinedResult.WasInterrupted) { |
2654 | // There is no need to confuse our users here. |
2655 | // We got interrupted, but our users don't need to know about it. |
2656 | CombinedResult.WasInterrupted = false; |
2657 | break; |
2658 | } |
2659 | } |
2660 | |
2661 | return CombinedResult; |
2662 | } |
2663 | |
2664 | Tracker::Result Tracker::track(SVal V, const MemRegion *R, TrackingOptions Opts, |
2665 | const StackFrameContext *Origin) { |
2666 | if (!V.isUnknown()) { |
2667 | Report.addVisitor<StoreSiteFinder>(ConstructorArgs: this, ConstructorArgs&: V, ConstructorArgs&: R, ConstructorArgs&: Opts, ConstructorArgs&: Origin); |
2668 | return {.FoundSomethingToTrack: true}; |
2669 | } |
2670 | return {}; |
2671 | } |
2672 | |
2673 | PathDiagnosticPieceRef Tracker::handle(StoreInfo SI, BugReporterContext &BRC, |
2674 | TrackingOptions Opts) { |
2675 | // Iterate through the handlers in the order according to their priorities. |
2676 | for (StoreHandlerPtr &Handler : StoreHandlers) { |
2677 | if (PathDiagnosticPieceRef Result = Handler->handle(SI, BRC, Opts)) |
2678 | // If the handler produced a non-null piece, return it. |
2679 | // There is no need in asking other handlers. |
2680 | return Result; |
2681 | } |
2682 | return {}; |
2683 | } |
2684 | |
2685 | bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, |
2686 | const Expr *E, |
2687 | |
2688 | PathSensitiveBugReport &Report, |
2689 | TrackingOptions Opts) { |
2690 | return Tracker::create(Report) |
2691 | ->track(E, N: InputNode, Opts) |
2692 | .FoundSomethingToTrack; |
2693 | } |
2694 | |
2695 | void bugreporter::trackStoredValue(SVal V, const MemRegion *R, |
2696 | PathSensitiveBugReport &Report, |
2697 | TrackingOptions Opts, |
2698 | const StackFrameContext *Origin) { |
2699 | Tracker::create(Report)->track(V, R, Opts, Origin); |
2700 | } |
2701 | |
2702 | //===----------------------------------------------------------------------===// |
2703 | // Implementation of NulReceiverBRVisitor. |
2704 | //===----------------------------------------------------------------------===// |
2705 | |
2706 | const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, |
2707 | const ExplodedNode *N) { |
2708 | const auto *ME = dyn_cast<ObjCMessageExpr>(Val: S); |
2709 | if (!ME) |
2710 | return nullptr; |
2711 | if (const Expr *Receiver = ME->getInstanceReceiver()) { |
2712 | ProgramStateRef state = N->getState(); |
2713 | SVal V = N->getSVal(Receiver); |
2714 | if (state->isNull(V).isConstrainedTrue()) |
2715 | return Receiver; |
2716 | } |
2717 | return nullptr; |
2718 | } |
2719 | |
2720 | PathDiagnosticPieceRef |
2721 | NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, |
2722 | PathSensitiveBugReport &BR) { |
2723 | std::optional<PreStmt> P = N->getLocationAs<PreStmt>(); |
2724 | if (!P) |
2725 | return nullptr; |
2726 | |
2727 | const Stmt *S = P->getStmt(); |
2728 | const Expr *Receiver = getNilReceiver(S, N); |
2729 | if (!Receiver) |
2730 | return nullptr; |
2731 | |
2732 | llvm::SmallString<256> Buf; |
2733 | llvm::raw_svector_ostream OS(Buf); |
2734 | |
2735 | if (const auto *ME = dyn_cast<ObjCMessageExpr>(Val: S)) { |
2736 | OS << "'" ; |
2737 | ME->getSelector().print(OS); |
2738 | OS << "' not called" ; |
2739 | } |
2740 | else { |
2741 | OS << "No method is called" ; |
2742 | } |
2743 | OS << " because the receiver is nil" ; |
2744 | |
2745 | // The receiver was nil, and hence the method was skipped. |
2746 | // Register a BugReporterVisitor to issue a message telling us how |
2747 | // the receiver was null. |
2748 | bugreporter::trackExpressionValue(InputNode: N, E: Receiver, Report&: BR, |
2749 | Opts: {.Kind: bugreporter::TrackingKind::Thorough, |
2750 | /*EnableNullFPSuppression*/ false}); |
2751 | // Issue a message saying that the method was skipped. |
2752 | PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), |
2753 | N->getLocationContext()); |
2754 | return std::make_shared<PathDiagnosticEventPiece>(args&: L, args: OS.str()); |
2755 | } |
2756 | |
2757 | //===----------------------------------------------------------------------===// |
2758 | // Visitor that tries to report interesting diagnostics from conditions. |
2759 | //===----------------------------------------------------------------------===// |
2760 | |
2761 | /// Return the tag associated with this visitor. This tag will be used |
2762 | /// to make all PathDiagnosticPieces created by this visitor. |
2763 | const char *ConditionBRVisitor::getTag() { return "ConditionBRVisitor" ; } |
2764 | |
2765 | PathDiagnosticPieceRef |
2766 | ConditionBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, |
2767 | PathSensitiveBugReport &BR) { |
2768 | auto piece = VisitNodeImpl(N, BRC, BR); |
2769 | if (piece) { |
2770 | piece->setTag(getTag()); |
2771 | if (auto *ev = dyn_cast<PathDiagnosticEventPiece>(Val: piece.get())) |
2772 | ev->setPrunable(isPrunable: true, /* override */ false); |
2773 | } |
2774 | return piece; |
2775 | } |
2776 | |
2777 | PathDiagnosticPieceRef |
2778 | ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, |
2779 | BugReporterContext &BRC, |
2780 | PathSensitiveBugReport &BR) { |
2781 | ProgramPoint ProgPoint = N->getLocation(); |
2782 | const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags = |
2783 | ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); |
2784 | |
2785 | // If an assumption was made on a branch, it should be caught |
2786 | // here by looking at the state transition. |
2787 | if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { |
2788 | const CFGBlock *SrcBlock = BE->getSrc(); |
2789 | if (const Stmt *Term = SrcBlock->getTerminatorStmt()) { |
2790 | // If the tag of the previous node is 'Eagerly Assume...' the current |
2791 | // 'BlockEdge' has the same constraint information. We do not want to |
2792 | // report the value as it is just an assumption on the predecessor node |
2793 | // which will be caught in the next VisitNode() iteration as a 'PostStmt'. |
2794 | const ProgramPointTag *PreviousNodeTag = |
2795 | N->getFirstPred()->getLocation().getTag(); |
2796 | if (PreviousNodeTag == Tags.first || PreviousNodeTag == Tags.second) |
2797 | return nullptr; |
2798 | |
2799 | return VisitTerminator(Term, N, SrcBlk: SrcBlock, DstBlk: BE->getDst(), R&: BR, BRC); |
2800 | } |
2801 | return nullptr; |
2802 | } |
2803 | |
2804 | if (std::optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) { |
2805 | const ProgramPointTag *CurrentNodeTag = PS->getTag(); |
2806 | if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second) |
2807 | return nullptr; |
2808 | |
2809 | bool TookTrue = CurrentNodeTag == Tags.first; |
2810 | return VisitTrueTest(Cond: cast<Expr>(Val: PS->getStmt()), BRC, R&: BR, N, TookTrue); |
2811 | } |
2812 | |
2813 | return nullptr; |
2814 | } |
2815 | |
2816 | PathDiagnosticPieceRef ConditionBRVisitor::VisitTerminator( |
2817 | const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk, |
2818 | const CFGBlock *dstBlk, PathSensitiveBugReport &R, |
2819 | BugReporterContext &BRC) { |
2820 | const Expr *Cond = nullptr; |
2821 | |
2822 | // In the code below, Term is a CFG terminator and Cond is a branch condition |
2823 | // expression upon which the decision is made on this terminator. |
2824 | // |
2825 | // For example, in "if (x == 0)", the "if (x == 0)" statement is a terminator, |
2826 | // and "x == 0" is the respective condition. |
2827 | // |
2828 | // Another example: in "if (x && y)", we've got two terminators and two |
2829 | // conditions due to short-circuit nature of operator "&&": |
2830 | // 1. The "if (x && y)" statement is a terminator, |
2831 | // and "y" is the respective condition. |
2832 | // 2. Also "x && ..." is another terminator, |
2833 | // and "x" is its condition. |
2834 | |
2835 | switch (Term->getStmtClass()) { |
2836 | // FIXME: Stmt::SwitchStmtClass is worth handling, however it is a bit |
2837 | // more tricky because there are more than two branches to account for. |
2838 | default: |
2839 | return nullptr; |
2840 | case Stmt::IfStmtClass: |
2841 | Cond = cast<IfStmt>(Val: Term)->getCond(); |
2842 | break; |
2843 | case Stmt::ConditionalOperatorClass: |
2844 | Cond = cast<ConditionalOperator>(Val: Term)->getCond(); |
2845 | break; |
2846 | case Stmt::BinaryOperatorClass: |
2847 | // When we encounter a logical operator (&& or ||) as a CFG terminator, |
2848 | // then the condition is actually its LHS; otherwise, we'd encounter |
2849 | // the parent, such as if-statement, as a terminator. |
2850 | const auto *BO = cast<BinaryOperator>(Val: Term); |
2851 | assert(BO->isLogicalOp() && |
2852 | "CFG terminator is not a short-circuit operator!" ); |
2853 | Cond = BO->getLHS(); |
2854 | break; |
2855 | } |
2856 | |
2857 | Cond = Cond->IgnoreParens(); |
2858 | |
2859 | // However, when we encounter a logical operator as a branch condition, |
2860 | // then the condition is actually its RHS, because LHS would be |
2861 | // the condition for the logical operator terminator. |
2862 | while (const auto *InnerBO = dyn_cast<BinaryOperator>(Val: Cond)) { |
2863 | if (!InnerBO->isLogicalOp()) |
2864 | break; |
2865 | Cond = InnerBO->getRHS()->IgnoreParens(); |
2866 | } |
2867 | |
2868 | assert(Cond); |
2869 | assert(srcBlk->succ_size() == 2); |
2870 | const bool TookTrue = *(srcBlk->succ_begin()) == dstBlk; |
2871 | return VisitTrueTest(Cond, BRC, R, N, TookTrue); |
2872 | } |
2873 | |
2874 | PathDiagnosticPieceRef |
2875 | ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, |
2876 | PathSensitiveBugReport &R, |
2877 | const ExplodedNode *N, bool TookTrue) { |
2878 | ProgramStateRef CurrentState = N->getState(); |
2879 | ProgramStateRef PrevState = N->getFirstPred()->getState(); |
2880 | const LocationContext *LCtx = N->getLocationContext(); |
2881 | |
2882 | // If the constraint information is changed between the current and the |
2883 | // previous program state we assuming the newly seen constraint information. |
2884 | // If we cannot evaluate the condition (and the constraints are the same) |
2885 | // the analyzer has no information about the value and just assuming it. |
2886 | // FIXME: This logic is not entirely correct, because e.g. in code like |
2887 | // void f(unsigned arg) { |
2888 | // if (arg >= 0) { |
2889 | // // ... |
2890 | // } |
2891 | // } |
2892 | // it will say that the "arg >= 0" check is _assuming_ something new because |
2893 | // the constraint that "$arg >= 0" is 1 was added to the list of known |
2894 | // constraints. However, the unsigned value is always >= 0 so semantically |
2895 | // this is not a "real" assumption. |
2896 | bool IsAssuming = |
2897 | !BRC.getStateManager().haveEqualConstraints(S1: CurrentState, S2: PrevState) || |
2898 | CurrentState->getSVal(Cond, LCtx).isUnknownOrUndef(); |
2899 | |
2900 | // These will be modified in code below, but we need to preserve the original |
2901 | // values in case we want to throw the generic message. |
2902 | const Expr *CondTmp = Cond; |
2903 | bool TookTrueTmp = TookTrue; |
2904 | |
2905 | while (true) { |
2906 | CondTmp = CondTmp->IgnoreParenCasts(); |
2907 | switch (CondTmp->getStmtClass()) { |
2908 | default: |
2909 | break; |
2910 | case Stmt::BinaryOperatorClass: |
2911 | if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp), |
2912 | BRC, R, N, TookTrueTmp, IsAssuming)) |
2913 | return P; |
2914 | break; |
2915 | case Stmt::DeclRefExprClass: |
2916 | if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp), |
2917 | BRC, R, N, TookTrueTmp, IsAssuming)) |
2918 | return P; |
2919 | break; |
2920 | case Stmt::MemberExprClass: |
2921 | if (auto P = VisitTrueTest(Cond, cast<MemberExpr>(CondTmp), |
2922 | BRC, R, N, TookTrueTmp, IsAssuming)) |
2923 | return P; |
2924 | break; |
2925 | case Stmt::UnaryOperatorClass: { |
2926 | const auto *UO = cast<UnaryOperator>(Val: CondTmp); |
2927 | if (UO->getOpcode() == UO_LNot) { |
2928 | TookTrueTmp = !TookTrueTmp; |
2929 | CondTmp = UO->getSubExpr(); |
2930 | continue; |
2931 | } |
2932 | break; |
2933 | } |
2934 | } |
2935 | break; |
2936 | } |
2937 | |
2938 | // Condition too complex to explain? Just say something so that the user |
2939 | // knew we've made some path decision at this point. |
2940 | // If it is too complex and we know the evaluation of the condition do not |
2941 | // repeat the note from 'BugReporter.cpp' |
2942 | if (!IsAssuming) |
2943 | return nullptr; |
2944 | |
2945 | PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); |
2946 | if (!Loc.isValid() || !Loc.asLocation().isValid()) |
2947 | return nullptr; |
2948 | |
2949 | return std::make_shared<PathDiagnosticEventPiece>( |
2950 | args&: Loc, args: TookTrue ? GenericTrueMessage : GenericFalseMessage); |
2951 | } |
2952 | |
2953 | bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx, |
2954 | raw_ostream &Out, BugReporterContext &BRC, |
2955 | PathSensitiveBugReport &report, |
2956 | const ExplodedNode *N, |
2957 | std::optional<bool> &prunable, |
2958 | bool IsSameFieldName) { |
2959 | const Expr *OriginalExpr = Ex; |
2960 | Ex = Ex->IgnoreParenCasts(); |
2961 | |
2962 | if (isa<GNUNullExpr, ObjCBoolLiteralExpr, CXXBoolLiteralExpr, IntegerLiteral, |
2963 | FloatingLiteral>(Val: Ex)) { |
2964 | // Use heuristics to determine if the expression is a macro |
2965 | // expanding to a literal and if so, use the macro's name. |
2966 | SourceLocation BeginLoc = OriginalExpr->getBeginLoc(); |
2967 | SourceLocation EndLoc = OriginalExpr->getEndLoc(); |
2968 | if (BeginLoc.isMacroID() && EndLoc.isMacroID()) { |
2969 | const SourceManager &SM = BRC.getSourceManager(); |
2970 | const LangOptions &LO = BRC.getASTContext().getLangOpts(); |
2971 | if (Lexer::isAtStartOfMacroExpansion(loc: BeginLoc, SM, LangOpts: LO) && |
2972 | Lexer::isAtEndOfMacroExpansion(loc: EndLoc, SM, LangOpts: LO)) { |
2973 | CharSourceRange R = Lexer::getAsCharRange(Range: {BeginLoc, EndLoc}, SM, LangOpts: LO); |
2974 | Out << Lexer::getSourceText(Range: R, SM, LangOpts: LO); |
2975 | return false; |
2976 | } |
2977 | } |
2978 | } |
2979 | |
2980 | if (const auto *DR = dyn_cast<DeclRefExpr>(Val: Ex)) { |
2981 | const bool quotes = isa<VarDecl>(Val: DR->getDecl()); |
2982 | if (quotes) { |
2983 | Out << '\''; |
2984 | const LocationContext *LCtx = N->getLocationContext(); |
2985 | const ProgramState *state = N->getState().get(); |
2986 | if (const MemRegion *R = state->getLValue(VD: cast<VarDecl>(Val: DR->getDecl()), |
2987 | LC: LCtx).getAsRegion()) { |
2988 | if (report.isInteresting(R)) |
2989 | prunable = false; |
2990 | else { |
2991 | const ProgramState *state = N->getState().get(); |
2992 | SVal V = state->getSVal(R); |
2993 | if (report.isInteresting(V)) |
2994 | prunable = false; |
2995 | } |
2996 | } |
2997 | } |
2998 | Out << DR->getDecl()->getDeclName().getAsString(); |
2999 | if (quotes) |
3000 | Out << '\''; |
3001 | return quotes; |
3002 | } |
3003 | |
3004 | if (const auto *IL = dyn_cast<IntegerLiteral>(Val: Ex)) { |
3005 | QualType OriginalTy = OriginalExpr->getType(); |
3006 | if (OriginalTy->isPointerType()) { |
3007 | if (IL->getValue() == 0) { |
3008 | Out << "null" ; |
3009 | return false; |
3010 | } |
3011 | } |
3012 | else if (OriginalTy->isObjCObjectPointerType()) { |
3013 | if (IL->getValue() == 0) { |
3014 | Out << "nil" ; |
3015 | return false; |
3016 | } |
3017 | } |
3018 | |
3019 | Out << IL->getValue(); |
3020 | return false; |
3021 | } |
3022 | |
3023 | if (const auto *ME = dyn_cast<MemberExpr>(Val: Ex)) { |
3024 | if (!IsSameFieldName) |
3025 | Out << "field '" << ME->getMemberDecl()->getName() << '\''; |
3026 | else |
3027 | Out << '\'' |
3028 | << Lexer::getSourceText( |
3029 | Range: CharSourceRange::getTokenRange(Ex->getSourceRange()), |
3030 | SM: BRC.getSourceManager(), LangOpts: BRC.getASTContext().getLangOpts(), |
3031 | Invalid: nullptr) |
3032 | << '\''; |
3033 | } |
3034 | |
3035 | return false; |
3036 | } |
3037 | |
3038 | PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( |
3039 | const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC, |
3040 | PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue, |
3041 | bool IsAssuming) { |
3042 | bool shouldInvert = false; |
3043 | std::optional<bool> shouldPrune; |
3044 | |
3045 | // Check if the field name of the MemberExprs is ambiguous. Example: |
3046 | // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'. |
3047 | bool IsSameFieldName = false; |
3048 | const auto *LhsME = dyn_cast<MemberExpr>(Val: BExpr->getLHS()->IgnoreParenCasts()); |
3049 | const auto *RhsME = dyn_cast<MemberExpr>(Val: BExpr->getRHS()->IgnoreParenCasts()); |
3050 | |
3051 | if (LhsME && RhsME) |
3052 | IsSameFieldName = |
3053 | LhsME->getMemberDecl()->getName() == RhsME->getMemberDecl()->getName(); |
3054 | |
3055 | SmallString<128> LhsString, RhsString; |
3056 | { |
3057 | llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); |
3058 | const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, BRC, R, |
3059 | N, shouldPrune, IsSameFieldName); |
3060 | const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, BRC, R, |
3061 | N, shouldPrune, IsSameFieldName); |
3062 | |
3063 | shouldInvert = !isVarLHS && isVarRHS; |
3064 | } |
3065 | |
3066 | BinaryOperator::Opcode Op = BExpr->getOpcode(); |
3067 | |
3068 | if (BinaryOperator::isAssignmentOp(Opc: Op)) { |
3069 | // For assignment operators, all that we care about is that the LHS |
3070 | // evaluates to "true" or "false". |
3071 | return VisitConditionVariable(LhsString, CondVarExpr: BExpr->getLHS(), BRC, R, N, |
3072 | TookTrue); |
3073 | } |
3074 | |
3075 | // For non-assignment operations, we require that we can understand |
3076 | // both the LHS and RHS. |
3077 | if (LhsString.empty() || RhsString.empty() || |
3078 | !BinaryOperator::isComparisonOp(Opc: Op) || Op == BO_Cmp) |
3079 | return nullptr; |
3080 | |
3081 | // Should we invert the strings if the LHS is not a variable name? |
3082 | SmallString<256> buf; |
3083 | llvm::raw_svector_ostream Out(buf); |
3084 | Out << (IsAssuming ? "Assuming " : "" ) |
3085 | << (shouldInvert ? RhsString : LhsString) << " is " ; |
3086 | |
3087 | // Do we need to invert the opcode? |
3088 | if (shouldInvert) |
3089 | switch (Op) { |
3090 | default: break; |
3091 | case BO_LT: Op = BO_GT; break; |
3092 | case BO_GT: Op = BO_LT; break; |
3093 | case BO_LE: Op = BO_GE; break; |
3094 | case BO_GE: Op = BO_LE; break; |
3095 | } |
3096 | |
3097 | if (!TookTrue) |
3098 | switch (Op) { |
3099 | case BO_EQ: Op = BO_NE; break; |
3100 | case BO_NE: Op = BO_EQ; break; |
3101 | case BO_LT: Op = BO_GE; break; |
3102 | case BO_GT: Op = BO_LE; break; |
3103 | case BO_LE: Op = BO_GT; break; |
3104 | case BO_GE: Op = BO_LT; break; |
3105 | default: |
3106 | return nullptr; |
3107 | } |
3108 | |
3109 | switch (Op) { |
3110 | case BO_EQ: |
3111 | Out << "equal to " ; |
3112 | break; |
3113 | case BO_NE: |
3114 | Out << "not equal to " ; |
3115 | break; |
3116 | default: |
3117 | Out << BinaryOperator::getOpcodeStr(Op) << ' '; |
3118 | break; |
3119 | } |
3120 | |
3121 | Out << (shouldInvert ? LhsString : RhsString); |
3122 | const LocationContext *LCtx = N->getLocationContext(); |
3123 | const SourceManager &SM = BRC.getSourceManager(); |
3124 | |
3125 | if (isVarAnInterestingCondition(CondVarExpr: BExpr->getLHS(), N, B: &R) || |
3126 | isVarAnInterestingCondition(CondVarExpr: BExpr->getRHS(), N, B: &R)) |
3127 | Out << WillBeUsedForACondition; |
3128 | |
3129 | // Convert 'field ...' to 'Field ...' if it is a MemberExpr. |
3130 | std::string Message = std::string(Out.str()); |
3131 | Message[0] = toupper(c: Message[0]); |
3132 | |
3133 | // If we know the value create a pop-up note to the value part of 'BExpr'. |
3134 | if (!IsAssuming) { |
3135 | PathDiagnosticLocation Loc; |
3136 | if (!shouldInvert) { |
3137 | if (LhsME && LhsME->getMemberLoc().isValid()) |
3138 | Loc = PathDiagnosticLocation(LhsME->getMemberLoc(), SM); |
3139 | else |
3140 | Loc = PathDiagnosticLocation(BExpr->getLHS(), SM, LCtx); |
3141 | } else { |
3142 | if (RhsME && RhsME->getMemberLoc().isValid()) |
3143 | Loc = PathDiagnosticLocation(RhsME->getMemberLoc(), SM); |
3144 | else |
3145 | Loc = PathDiagnosticLocation(BExpr->getRHS(), SM, LCtx); |
3146 | } |
3147 | |
3148 | return std::make_shared<PathDiagnosticPopUpPiece>(args&: Loc, args&: Message); |
3149 | } |
3150 | |
3151 | PathDiagnosticLocation Loc(Cond, SM, LCtx); |
3152 | auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args&: Message); |
3153 | if (shouldPrune) |
3154 | event->setPrunable(*shouldPrune); |
3155 | return event; |
3156 | } |
3157 | |
3158 | PathDiagnosticPieceRef ConditionBRVisitor::VisitConditionVariable( |
3159 | StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC, |
3160 | PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue) { |
3161 | // FIXME: If there's already a constraint tracker for this variable, |
3162 | // we shouldn't emit anything here (c.f. the double note in |
3163 | // test/Analysis/inlining/path-notes.c) |
3164 | SmallString<256> buf; |
3165 | llvm::raw_svector_ostream Out(buf); |
3166 | Out << "Assuming " << LhsString << " is " ; |
3167 | |
3168 | if (!printValue(CondVarExpr, Out, N, TookTrue, /*IsAssuming=*/true)) |
3169 | return nullptr; |
3170 | |
3171 | const LocationContext *LCtx = N->getLocationContext(); |
3172 | PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); |
3173 | |
3174 | if (isVarAnInterestingCondition(CondVarExpr, N, B: &report)) |
3175 | Out << WillBeUsedForACondition; |
3176 | |
3177 | auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args: Out.str()); |
3178 | |
3179 | if (isInterestingExpr(E: CondVarExpr, N, B: &report)) |
3180 | event->setPrunable(false); |
3181 | |
3182 | return event; |
3183 | } |
3184 | |
3185 | PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( |
3186 | const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC, |
3187 | PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue, |
3188 | bool IsAssuming) { |
3189 | const auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl()); |
3190 | if (!VD) |
3191 | return nullptr; |
3192 | |
3193 | SmallString<256> Buf; |
3194 | llvm::raw_svector_ostream Out(Buf); |
3195 | |
3196 | Out << (IsAssuming ? "Assuming '" : "'" ) << VD->getDeclName() << "' is " ; |
3197 | |
3198 | if (!printValue(DRE, Out, N, TookTrue, IsAssuming)) |
3199 | return nullptr; |
3200 | |
3201 | const LocationContext *LCtx = N->getLocationContext(); |
3202 | |
3203 | if (isVarAnInterestingCondition(DRE, N, &report)) |
3204 | Out << WillBeUsedForACondition; |
3205 | |
3206 | // If we know the value create a pop-up note to the 'DRE'. |
3207 | if (!IsAssuming) { |
3208 | PathDiagnosticLocation Loc(DRE, BRC.getSourceManager(), LCtx); |
3209 | return std::make_shared<PathDiagnosticPopUpPiece>(args&: Loc, args: Out.str()); |
3210 | } |
3211 | |
3212 | PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); |
3213 | auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args: Out.str()); |
3214 | |
3215 | if (isInterestingExpr(DRE, N, &report)) |
3216 | event->setPrunable(false); |
3217 | |
3218 | return std::move(event); |
3219 | } |
3220 | |
3221 | PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( |
3222 | const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, |
3223 | PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue, |
3224 | bool IsAssuming) { |
3225 | SmallString<256> Buf; |
3226 | llvm::raw_svector_ostream Out(Buf); |
3227 | |
3228 | Out << (IsAssuming ? "Assuming field '" : "Field '" ) |
3229 | << ME->getMemberDecl()->getName() << "' is " ; |
3230 | |
3231 | if (!printValue(ME, Out, N, TookTrue, IsAssuming)) |
3232 | return nullptr; |
3233 | |
3234 | const LocationContext *LCtx = N->getLocationContext(); |
3235 | PathDiagnosticLocation Loc; |
3236 | |
3237 | // If we know the value create a pop-up note to the member of the MemberExpr. |
3238 | if (!IsAssuming && ME->getMemberLoc().isValid()) |
3239 | Loc = PathDiagnosticLocation(ME->getMemberLoc(), BRC.getSourceManager()); |
3240 | else |
3241 | Loc = PathDiagnosticLocation(Cond, BRC.getSourceManager(), LCtx); |
3242 | |
3243 | if (!Loc.isValid() || !Loc.asLocation().isValid()) |
3244 | return nullptr; |
3245 | |
3246 | if (isVarAnInterestingCondition(ME, N, &report)) |
3247 | Out << WillBeUsedForACondition; |
3248 | |
3249 | // If we know the value create a pop-up note. |
3250 | if (!IsAssuming) |
3251 | return std::make_shared<PathDiagnosticPopUpPiece>(args&: Loc, args: Out.str()); |
3252 | |
3253 | auto event = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args: Out.str()); |
3254 | if (isInterestingExpr(ME, N, &report)) |
3255 | event->setPrunable(isPrunable: false); |
3256 | return event; |
3257 | } |
3258 | |
3259 | bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, |
3260 | const ExplodedNode *N, bool TookTrue, |
3261 | bool IsAssuming) { |
3262 | QualType Ty = CondVarExpr->getType(); |
3263 | |
3264 | if (Ty->isPointerType()) { |
3265 | Out << (TookTrue ? "non-null" : "null" ); |
3266 | return true; |
3267 | } |
3268 | |
3269 | if (Ty->isObjCObjectPointerType()) { |
3270 | Out << (TookTrue ? "non-nil" : "nil" ); |
3271 | return true; |
3272 | } |
3273 | |
3274 | if (!Ty->isIntegralOrEnumerationType()) |
3275 | return false; |
3276 | |
3277 | std::optional<const llvm::APSInt *> IntValue; |
3278 | if (!IsAssuming) |
3279 | IntValue = getConcreteIntegerValue(CondVarExpr, N); |
3280 | |
3281 | if (IsAssuming || !IntValue) { |
3282 | if (Ty->isBooleanType()) |
3283 | Out << (TookTrue ? "true" : "false" ); |
3284 | else |
3285 | Out << (TookTrue ? "not equal to 0" : "0" ); |
3286 | } else { |
3287 | if (Ty->isBooleanType()) |
3288 | Out << ((*IntValue)->getBoolValue() ? "true" : "false" ); |
3289 | else |
3290 | Out << **IntValue; |
3291 | } |
3292 | |
3293 | return true; |
3294 | } |
3295 | |
3296 | constexpr llvm::StringLiteral ConditionBRVisitor::GenericTrueMessage; |
3297 | constexpr llvm::StringLiteral ConditionBRVisitor::GenericFalseMessage; |
3298 | |
3299 | bool ConditionBRVisitor::isPieceMessageGeneric( |
3300 | const PathDiagnosticPiece *Piece) { |
3301 | return Piece->getString() == GenericTrueMessage || |
3302 | Piece->getString() == GenericFalseMessage; |
3303 | } |
3304 | |
3305 | //===----------------------------------------------------------------------===// |
3306 | // Implementation of LikelyFalsePositiveSuppressionBRVisitor. |
3307 | //===----------------------------------------------------------------------===// |
3308 | |
3309 | void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( |
3310 | BugReporterContext &BRC, const ExplodedNode *N, |
3311 | PathSensitiveBugReport &BR) { |
3312 | // Here we suppress false positives coming from system headers. This list is |
3313 | // based on known issues. |
3314 | const AnalyzerOptions &Options = BRC.getAnalyzerOptions(); |
3315 | const Decl *D = N->getLocationContext()->getDecl(); |
3316 | |
3317 | if (AnalysisDeclContext::isInStdNamespace(D)) { |
3318 | // Skip reports within the 'std' namespace. Although these can sometimes be |
3319 | // the user's fault, we currently don't report them very well, and |
3320 | // Note that this will not help for any other data structure libraries, like |
3321 | // TR1, Boost, or llvm/ADT. |
3322 | if (Options.ShouldSuppressFromCXXStandardLibrary) { |
3323 | BR.markInvalid(Tag: getTag(), Data: nullptr); |
3324 | return; |
3325 | } else { |
3326 | // If the complete 'std' suppression is not enabled, suppress reports |
3327 | // from the 'std' namespace that are known to produce false positives. |
3328 | |
3329 | // The analyzer issues a false use-after-free when std::list::pop_front |
3330 | // or std::list::pop_back are called multiple times because we cannot |
3331 | // reason about the internal invariants of the data structure. |
3332 | if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: D)) { |
3333 | const CXXRecordDecl *CD = MD->getParent(); |
3334 | if (CD->getName() == "list" ) { |
3335 | BR.markInvalid(Tag: getTag(), Data: nullptr); |
3336 | return; |
3337 | } |
3338 | } |
3339 | |
3340 | // The analyzer issues a false positive when the constructor of |
3341 | // std::__independent_bits_engine from algorithms is used. |
3342 | if (const auto *MD = dyn_cast<CXXConstructorDecl>(Val: D)) { |
3343 | const CXXRecordDecl *CD = MD->getParent(); |
3344 | if (CD->getName() == "__independent_bits_engine" ) { |
3345 | BR.markInvalid(Tag: getTag(), Data: nullptr); |
3346 | return; |
3347 | } |
3348 | } |
3349 | |
3350 | for (const LocationContext *LCtx = N->getLocationContext(); LCtx; |
3351 | LCtx = LCtx->getParent()) { |
3352 | const auto *MD = dyn_cast<CXXMethodDecl>(Val: LCtx->getDecl()); |
3353 | if (!MD) |
3354 | continue; |
3355 | |
3356 | const CXXRecordDecl *CD = MD->getParent(); |
3357 | // The analyzer issues a false positive on |
3358 | // std::basic_string<uint8_t> v; v.push_back(1); |
3359 | // and |
3360 | // std::u16string s; s += u'a'; |
3361 | // because we cannot reason about the internal invariants of the |
3362 | // data structure. |
3363 | if (CD->getName() == "basic_string" ) { |
3364 | BR.markInvalid(Tag: getTag(), Data: nullptr); |
3365 | return; |
3366 | } |
3367 | |
3368 | // The analyzer issues a false positive on |
3369 | // std::shared_ptr<int> p(new int(1)); p = nullptr; |
3370 | // because it does not reason properly about temporary destructors. |
3371 | if (CD->getName() == "shared_ptr" ) { |
3372 | BR.markInvalid(Tag: getTag(), Data: nullptr); |
3373 | return; |
3374 | } |
3375 | } |
3376 | } |
3377 | } |
3378 | |
3379 | // Skip reports within the sys/queue.h macros as we do not have the ability to |
3380 | // reason about data structure shapes. |
3381 | const SourceManager &SM = BRC.getSourceManager(); |
3382 | FullSourceLoc Loc = BR.getLocation().asLocation(); |
3383 | while (Loc.isMacroID()) { |
3384 | Loc = Loc.getSpellingLoc(); |
3385 | if (SM.getFilename(SpellingLoc: Loc).ends_with(Suffix: "sys/queue.h" )) { |
3386 | BR.markInvalid(Tag: getTag(), Data: nullptr); |
3387 | return; |
3388 | } |
3389 | } |
3390 | } |
3391 | |
3392 | //===----------------------------------------------------------------------===// |
3393 | // Implementation of UndefOrNullArgVisitor. |
3394 | //===----------------------------------------------------------------------===// |
3395 | |
3396 | PathDiagnosticPieceRef |
3397 | UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, |
3398 | PathSensitiveBugReport &BR) { |
3399 | ProgramStateRef State = N->getState(); |
3400 | ProgramPoint ProgLoc = N->getLocation(); |
3401 | |
3402 | // We are only interested in visiting CallEnter nodes. |
3403 | std::optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>(); |
3404 | if (!CEnter) |
3405 | return nullptr; |
3406 | |
3407 | // Check if one of the arguments is the region the visitor is tracking. |
3408 | CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); |
3409 | CallEventRef<> Call = CEMgr.getCaller(CalleeCtx: CEnter->getCalleeContext(), State); |
3410 | unsigned Idx = 0; |
3411 | ArrayRef<ParmVarDecl *> parms = Call->parameters(); |
3412 | |
3413 | for (const auto ParamDecl : parms) { |
3414 | const MemRegion *ArgReg = Call->getArgSVal(Index: Idx).getAsRegion(); |
3415 | ++Idx; |
3416 | |
3417 | // Are we tracking the argument or its subregion? |
3418 | if ( !ArgReg || !R->isSubRegionOf(R: ArgReg->StripCasts())) |
3419 | continue; |
3420 | |
3421 | // Check the function parameter type. |
3422 | assert(ParamDecl && "Formal parameter has no decl?" ); |
3423 | QualType T = ParamDecl->getType(); |
3424 | |
3425 | if (!(T->isAnyPointerType() || T->isReferenceType())) { |
3426 | // Function can only change the value passed in by address. |
3427 | continue; |
3428 | } |
3429 | |
3430 | // If it is a const pointer value, the function does not intend to |
3431 | // change the value. |
3432 | if (T->getPointeeType().isConstQualified()) |
3433 | continue; |
3434 | |
3435 | // Mark the call site (LocationContext) as interesting if the value of the |
3436 | // argument is undefined or '0'/'NULL'. |
3437 | SVal BoundVal = State->getSVal(R); |
3438 | if (BoundVal.isUndef() || BoundVal.isZeroConstant()) { |
3439 | BR.markInteresting(LC: CEnter->getCalleeContext()); |
3440 | return nullptr; |
3441 | } |
3442 | } |
3443 | return nullptr; |
3444 | } |
3445 | |
3446 | //===----------------------------------------------------------------------===// |
3447 | // Implementation of FalsePositiveRefutationBRVisitor. |
3448 | //===----------------------------------------------------------------------===// |
3449 | |
3450 | FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() |
3451 | : Constraints(ConstraintMap::Factory().getEmptyMap()) {} |
3452 | |
3453 | void FalsePositiveRefutationBRVisitor::finalizeVisitor( |
3454 | BugReporterContext &BRC, const ExplodedNode *EndPathNode, |
3455 | PathSensitiveBugReport &BR) { |
3456 | // Collect new constraints |
3457 | addConstraints(N: EndPathNode, /*OverwriteConstraintsOnExistingSyms=*/true); |
3458 | |
3459 | // Create a refutation manager |
3460 | llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver(); |
3461 | ASTContext &Ctx = BRC.getASTContext(); |
3462 | |
3463 | // Add constraints to the solver |
3464 | for (const auto &I : Constraints) { |
3465 | const SymbolRef Sym = I.first; |
3466 | auto RangeIt = I.second.begin(); |
3467 | |
3468 | llvm::SMTExprRef SMTConstraints = SMTConv::getRangeExpr( |
3469 | Solver&: RefutationSolver, Ctx, Sym, From: RangeIt->From(), To: RangeIt->To(), |
3470 | /*InRange=*/true); |
3471 | while ((++RangeIt) != I.second.end()) { |
3472 | SMTConstraints = RefutationSolver->mkOr( |
3473 | LHS: SMTConstraints, RHS: SMTConv::getRangeExpr(Solver&: RefutationSolver, Ctx, Sym, |
3474 | From: RangeIt->From(), To: RangeIt->To(), |
3475 | /*InRange=*/true)); |
3476 | } |
3477 | |
3478 | RefutationSolver->addConstraint(Exp: SMTConstraints); |
3479 | } |
3480 | |
3481 | // And check for satisfiability |
3482 | std::optional<bool> IsSAT = RefutationSolver->check(); |
3483 | if (!IsSAT) |
3484 | return; |
3485 | |
3486 | if (!*IsSAT) |
3487 | BR.markInvalid(Tag: "Infeasible constraints" , Data: EndPathNode->getLocationContext()); |
3488 | } |
3489 | |
3490 | void FalsePositiveRefutationBRVisitor::addConstraints( |
3491 | const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) { |
3492 | // Collect new constraints |
3493 | ConstraintMap NewCs = getConstraintMap(State: N->getState()); |
3494 | ConstraintMap::Factory &CF = N->getState()->get_context<ConstraintMap>(); |
3495 | |
3496 | // Add constraints if we don't have them yet |
3497 | for (auto const &C : NewCs) { |
3498 | const SymbolRef &Sym = C.first; |
3499 | if (!Constraints.contains(K: Sym)) { |
3500 | // This symbol is new, just add the constraint. |
3501 | Constraints = CF.add(Old: Constraints, K: Sym, D: C.second); |
3502 | } else if (OverwriteConstraintsOnExistingSyms) { |
3503 | // Overwrite the associated constraint of the Symbol. |
3504 | Constraints = CF.remove(Old: Constraints, K: Sym); |
3505 | Constraints = CF.add(Old: Constraints, K: Sym, D: C.second); |
3506 | } |
3507 | } |
3508 | } |
3509 | |
3510 | PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode( |
3511 | const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) { |
3512 | addConstraints(N, /*OverwriteConstraintsOnExistingSyms=*/false); |
3513 | return nullptr; |
3514 | } |
3515 | |
3516 | void FalsePositiveRefutationBRVisitor::Profile( |
3517 | llvm::FoldingSetNodeID &ID) const { |
3518 | static int Tag = 0; |
3519 | ID.AddPointer(Ptr: &Tag); |
3520 | } |
3521 | |
3522 | //===----------------------------------------------------------------------===// |
3523 | // Implementation of TagVisitor. |
3524 | //===----------------------------------------------------------------------===// |
3525 | |
3526 | int NoteTag::Kind = 0; |
3527 | |
3528 | void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const { |
3529 | static int Tag = 0; |
3530 | ID.AddPointer(Ptr: &Tag); |
3531 | } |
3532 | |
3533 | PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N, |
3534 | BugReporterContext &BRC, |
3535 | PathSensitiveBugReport &R) { |
3536 | ProgramPoint PP = N->getLocation(); |
3537 | const NoteTag *T = dyn_cast_or_null<NoteTag>(Val: PP.getTag()); |
3538 | if (!T) |
3539 | return nullptr; |
3540 | |
3541 | if (std::optional<std::string> Msg = T->generateMessage(BRC, R)) { |
3542 | PathDiagnosticLocation Loc = |
3543 | PathDiagnosticLocation::create(P: PP, SMng: BRC.getSourceManager()); |
3544 | auto Piece = std::make_shared<PathDiagnosticEventPiece>(args&: Loc, args&: *Msg); |
3545 | Piece->setPrunable(isPrunable: T->isPrunable()); |
3546 | return Piece; |
3547 | } |
3548 | |
3549 | return nullptr; |
3550 | } |
3551 | |