1//=== UndefBranchChecker.cpp -----------------------------------*- 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 file defines UndefBranchChecker, which checks for undefined branch
10// condition.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/StmtObjC.h"
15#include "clang/AST/Type.h"
16#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18#include "clang/StaticAnalyzer/Core/Checker.h"
19#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21#include <optional>
22#include <utility>
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29class UndefBranchChecker : public Checker<check::BranchCondition> {
30 const BugType BT{this, "Branch condition evaluates to a garbage value"};
31
32 struct FindUndefExpr {
33 ProgramStateRef St;
34 const LocationContext *LCtx;
35
36 FindUndefExpr(ProgramStateRef S, const LocationContext *L)
37 : St(std::move(S)), LCtx(L) {}
38
39 const Expr *FindExpr(const Expr *Ex) {
40 if (!MatchesCriteria(Ex))
41 return nullptr;
42
43 for (const Stmt *SubStmt : Ex->children())
44 if (const Expr *ExI = dyn_cast_or_null<Expr>(Val: SubStmt))
45 if (const Expr *E2 = FindExpr(Ex: ExI))
46 return E2;
47
48 return Ex;
49 }
50
51 bool MatchesCriteria(const Expr *Ex) {
52 return St->getSVal(Ex, LCtx).isUndef();
53 }
54 };
55
56public:
57 void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
58};
59
60} // namespace
61
62void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
63 CheckerContext &Ctx) const {
64 // ObjCForCollection is a loop, but has no actual condition.
65 if (isa<ObjCForCollectionStmt>(Val: Condition))
66 return;
67 if (!Ctx.getSVal(S: Condition).isUndef())
68 return;
69
70 // Generate a sink node, which implicitly marks both outgoing branches as
71 // infeasible.
72 ExplodedNode *N = Ctx.generateErrorNode();
73 if (!N)
74 return;
75 // What's going on here: we want to highlight the subexpression of the
76 // condition that is the most likely source of the "uninitialized
77 // branch condition." We do a recursive walk of the condition's
78 // subexpressions and roughly look for the most nested subexpression
79 // that binds to Undefined. We then highlight that expression's range.
80
81 // Get the predecessor node and check if is a PostStmt with the Stmt
82 // being the terminator condition. We want to inspect the state
83 // of that node instead because it will contain main information about
84 // the subexpressions.
85
86 // Note: any predecessor will do. They should have identical state,
87 // since all the BlockEdge did was act as an error sink since the value
88 // had to already be undefined.
89 assert(!N->pred_empty());
90 const Expr *Ex = cast<Expr>(Val: Condition);
91 ExplodedNode *PrevN = *N->pred_begin();
92 ProgramPoint P = PrevN->getLocation();
93 ProgramStateRef St = N->getState();
94
95 if (std::optional<PostStmt> PS = P.getAs<PostStmt>())
96 if (PS->getStmt() == Ex)
97 St = PrevN->getState();
98
99 FindUndefExpr FindIt(St, Ctx.getLocationContext());
100 Ex = FindIt.FindExpr(Ex);
101
102 // Emit the bug report.
103 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args: BT.getDescription(), args&: N);
104 bugreporter::trackExpressionValue(N, E: Ex, R&: *R);
105 R->addRange(R: Ex->getSourceRange());
106
107 Ctx.emitReport(R: std::move(R));
108}
109
110void ento::registerUndefBranchChecker(CheckerManager &mgr) {
111 mgr.registerChecker<UndefBranchChecker>();
112}
113
114bool ento::shouldRegisterUndefBranchChecker(const CheckerManager &mgr) {
115 return true;
116}
117