| 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>(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 | |
| 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>(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 | |
| 110 | void ento::registerUndefBranchChecker(CheckerManager &mgr) { |
| 111 | mgr.registerChecker<UndefBranchChecker>(); |
| 112 | } |
| 113 | |
| 114 | bool ento::shouldRegisterUndefBranchChecker(const CheckerManager &mgr) { |
| 115 | return true; |
| 116 | } |
| 117 | |