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