xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp (revision 1db9f3b21e39176dd5b67cf8ac378633b172463e)
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 
24 using namespace clang;
25 using namespace ento;
26 
27 namespace {
28 
29 class 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>(SubStmt))
45           if (const Expr *E2 = FindExpr(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 
56 public:
57   void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
58 };
59 
60 } // namespace
61 
62 void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
63                                               CheckerContext &Ctx) const {
64   // ObjCForCollection is a loop, but has no actual condition.
65   if (isa<ObjCForCollectionStmt>(Condition))
66     return;
67   if (!Ctx.getSVal(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>(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>(BT, BT.getDescription(), N);
104   bugreporter::trackExpressionValue(N, Ex, *R);
105   R->addRange(Ex->getSourceRange());
106 
107   Ctx.emitReport(std::move(R));
108 }
109 
110 void ento::registerUndefBranchChecker(CheckerManager &mgr) {
111   mgr.registerChecker<UndefBranchChecker>();
112 }
113 
114 bool ento::shouldRegisterUndefBranchChecker(const CheckerManager &mgr) {
115   return true;
116 }
117