/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
Line | Count | Source |
1 | | // UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=// |
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 checker detects blocks that capture uninitialized values. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
14 | | #include "clang/AST/Attr.h" |
15 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
16 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
17 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
18 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
20 | | #include "llvm/ADT/SmallString.h" |
21 | | #include "llvm/Support/raw_ostream.h" |
22 | | #include <optional> |
23 | | |
24 | | using namespace clang; |
25 | | using namespace ento; |
26 | | |
27 | | namespace { |
28 | | class UndefCapturedBlockVarChecker |
29 | | : public Checker< check::PostStmt<BlockExpr> > { |
30 | | mutable std::unique_ptr<BugType> BT; |
31 | | |
32 | | public: |
33 | | void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; |
34 | | }; |
35 | | } // end anonymous namespace |
36 | | |
37 | | static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, |
38 | 30 | const VarDecl *VD) { |
39 | 30 | if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S)) |
40 | 10 | if (BR->getDecl() == VD) |
41 | 6 | return BR; |
42 | | |
43 | 24 | for (const Stmt *Child : S->children()) |
44 | 24 | if (Child) |
45 | 24 | if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD)) |
46 | 20 | return BR; |
47 | | |
48 | 4 | return nullptr; |
49 | 24 | } |
50 | | |
51 | | void |
52 | | UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, |
53 | 383 | CheckerContext &C) const { |
54 | 383 | if (!BE->getBlockDecl()->hasCaptures()) |
55 | 153 | return; |
56 | | |
57 | 230 | ProgramStateRef state = C.getState(); |
58 | 230 | auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); |
59 | | |
60 | 295 | for (auto Var : R->referenced_vars()) { |
61 | | // This VarRegion is the region associated with the block; we need |
62 | | // the one associated with the encompassing context. |
63 | 295 | const VarRegion *VR = Var.getCapturedRegion(); |
64 | 295 | const VarDecl *VD = VR->getDecl(); |
65 | | |
66 | 295 | if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()222 ) |
67 | 89 | continue; |
68 | | |
69 | | // Get the VarRegion associated with VD in the local stack frame. |
70 | 206 | if (std::optional<UndefinedVal> V = |
71 | 206 | state->getSVal(Var.getOriginalRegion()).getAs<UndefinedVal>()) { |
72 | 6 | if (ExplodedNode *N = C.generateErrorNode()) { |
73 | 6 | if (!BT) |
74 | 4 | BT.reset( |
75 | 4 | new BugType(this, "uninitialized variable captured by block")); |
76 | | |
77 | | // Generate a bug report. |
78 | 6 | SmallString<128> buf; |
79 | 6 | llvm::raw_svector_ostream os(buf); |
80 | | |
81 | 6 | os << "Variable '" << VD->getName() |
82 | 6 | << "' is uninitialized when captured by block"; |
83 | | |
84 | 6 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); |
85 | 6 | if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) |
86 | 6 | R->addRange(Ex->getSourceRange()); |
87 | 6 | bugreporter::trackStoredValue(*V, VR, *R, |
88 | 6 | {bugreporter::TrackingKind::Thorough, |
89 | 6 | /*EnableNullFPSuppression*/ false}); |
90 | 6 | R->disablePathPruning(); |
91 | | // need location of block |
92 | 6 | C.emitReport(std::move(R)); |
93 | 6 | } |
94 | 6 | } |
95 | 206 | } |
96 | 230 | } |
97 | | |
98 | 1.27k | void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) { |
99 | 1.27k | mgr.registerChecker<UndefCapturedBlockVarChecker>(); |
100 | 1.27k | } |
101 | | |
102 | 2.55k | bool ento::shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager &mgr) { |
103 | 2.55k | return true; |
104 | 2.55k | } |