1 | //=== UndefResultChecker.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 defines UndefResultChecker, a builtin check in ExprEngine that |
10 | // performs checks for undefined results of non-assignment binary operators. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
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/DynamicExtent.h" |
20 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
21 | #include "llvm/ADT/SmallString.h" |
22 | #include "llvm/Support/raw_ostream.h" |
23 | |
24 | using namespace clang; |
25 | using namespace ento; |
26 | |
27 | namespace { |
28 | class UndefResultChecker |
29 | : public Checker< check::PostStmt<BinaryOperator> > { |
30 | |
31 | const BugType BT{this, "Result of operation is garbage or undefined" }; |
32 | |
33 | public: |
34 | void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; |
35 | }; |
36 | } // end anonymous namespace |
37 | |
38 | static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) { |
39 | ProgramStateRef state = C.getState(); |
40 | |
41 | if (!isa<ArraySubscriptExpr>(Val: Ex)) |
42 | return false; |
43 | |
44 | SVal Loc = C.getSVal(Ex); |
45 | if (!Loc.isValid()) |
46 | return false; |
47 | |
48 | const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion(); |
49 | const ElementRegion *ER = dyn_cast<ElementRegion>(Val: MR); |
50 | if (!ER) |
51 | return false; |
52 | |
53 | DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); |
54 | DefinedOrUnknownSVal ElementCount = getDynamicElementCount( |
55 | state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType()); |
56 | ProgramStateRef StInBound, StOutBound; |
57 | std::tie(args&: StInBound, args&: StOutBound) = state->assumeInBoundDual(idx: Idx, upperBound: ElementCount); |
58 | return StOutBound && !StInBound; |
59 | } |
60 | |
61 | void UndefResultChecker::checkPostStmt(const BinaryOperator *B, |
62 | CheckerContext &C) const { |
63 | if (C.getSVal(B).isUndef()) { |
64 | |
65 | // Do not report assignments of uninitialized values inside swap functions. |
66 | // This should allow to swap partially uninitialized structs |
67 | if (const FunctionDecl *EnclosingFunctionDecl = |
68 | dyn_cast<FunctionDecl>(Val: C.getStackFrame()->getDecl())) |
69 | if (C.getCalleeName(FunDecl: EnclosingFunctionDecl) == "swap" ) |
70 | return; |
71 | |
72 | // Generate an error node. |
73 | ExplodedNode *N = C.generateErrorNode(); |
74 | if (!N) |
75 | return; |
76 | |
77 | SmallString<256> sbuf; |
78 | llvm::raw_svector_ostream OS(sbuf); |
79 | const Expr *Ex = nullptr; |
80 | bool isLeft = true; |
81 | |
82 | if (C.getSVal(B->getLHS()).isUndef()) { |
83 | Ex = B->getLHS()->IgnoreParenCasts(); |
84 | isLeft = true; |
85 | } |
86 | else if (C.getSVal(B->getRHS()).isUndef()) { |
87 | Ex = B->getRHS()->IgnoreParenCasts(); |
88 | isLeft = false; |
89 | } |
90 | |
91 | if (Ex) { |
92 | OS << "The " << (isLeft ? "left" : "right" ) << " operand of '" |
93 | << BinaryOperator::getOpcodeStr(Op: B->getOpcode()) |
94 | << "' is a garbage value" ; |
95 | if (isArrayIndexOutOfBounds(C, Ex)) |
96 | OS << " due to array index out of bounds" ; |
97 | } else { |
98 | // Neither operand was undefined, but the result is undefined. |
99 | OS << "The result of the '" |
100 | << BinaryOperator::getOpcodeStr(Op: B->getOpcode()) |
101 | << "' expression is undefined" ; |
102 | } |
103 | auto report = std::make_unique<PathSensitiveBugReport>(args: BT, args: OS.str(), args&: N); |
104 | if (Ex) { |
105 | report->addRange(R: Ex->getSourceRange()); |
106 | bugreporter::trackExpressionValue(N, E: Ex, R&: *report); |
107 | } |
108 | else |
109 | bugreporter::trackExpressionValue(N, B, *report); |
110 | |
111 | C.emitReport(R: std::move(report)); |
112 | } |
113 | } |
114 | |
115 | void ento::registerUndefResultChecker(CheckerManager &mgr) { |
116 | mgr.registerChecker<UndefResultChecker>(); |
117 | } |
118 | |
119 | bool ento::shouldRegisterUndefResultChecker(const CheckerManager &mgr) { |
120 | return true; |
121 | } |
122 | |