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/AST/Attr.h"
14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 <optional>
21
22using namespace clang;
23using namespace ento;
24
25namespace {
26class UndefCapturedBlockVarChecker
27 : public Checker< check::PostStmt<BlockExpr> > {
28 const BugType BT{this, "uninitialized variable captured by block"};
29
30public:
31 void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
32};
33} // end anonymous namespace
34
35static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
36 const VarDecl *VD) {
37 if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(Val: S))
38 if (BR->getDecl() == VD)
39 return BR;
40
41 for (const Stmt *Child : S->children())
42 if (Child)
43 if (const DeclRefExpr *BR = FindBlockDeclRefExpr(S: Child, VD))
44 return BR;
45
46 return nullptr;
47}
48
49void
50UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
51 CheckerContext &C) const {
52 if (!BE->getBlockDecl()->hasCaptures())
53 return;
54
55 ProgramStateRef state = C.getState();
56 auto *R = cast<BlockDataRegion>(Val: C.getSVal(E: BE).getAsRegion());
57
58 for (auto Var : R->referenced_vars()) {
59 // This VarRegion is the region associated with the block; we need
60 // the one associated with the encompassing context.
61 const VarRegion *VR = Var.getCapturedRegion();
62 const VarDecl *VD = VR->getDecl();
63
64 if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
65 continue;
66
67 // Get the VarRegion associated with VD in the local stack frame.
68 if (std::optional<UndefinedVal> V =
69 state->getSVal(R: Var.getOriginalRegion()).getAs<UndefinedVal>()) {
70 if (ExplodedNode *N = C.generateErrorNode()) {
71 auto R = std::make_unique<PathSensitiveBugReport>(
72 args: BT,
73 args: "Variable '" + Twine(VD->getName()) +
74 "' is uninitialized when captured by block",
75 args&: N);
76 if (const Expr *Ex = FindBlockDeclRefExpr(S: BE->getBody(), VD))
77 R->addRange(R: Ex->getSourceRange());
78 bugreporter::trackStoredValue(V: *V, R: VR, Report&: *R,
79 Opts: {.Kind: bugreporter::TrackingKind::Thorough,
80 /*EnableNullFPSuppression*/ false});
81 R->disablePathPruning();
82 // need location of block
83 C.emitReport(R: std::move(R));
84 }
85 }
86 }
87}
88
89void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
90 mgr.registerChecker<UndefCapturedBlockVarChecker>();
91}
92
93bool ento::shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager &mgr) {
94 return true;
95}
96