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