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