1 //=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This defines UndefResultChecker, a builtin check in ExprEngine that
10 // performs checks for undefined results of non-assignment binary operators.
12 //===----------------------------------------------------------------------===//
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"
24 using namespace clang
;
28 class UndefResultChecker
29 : public Checker
< check::PostStmt
<BinaryOperator
> > {
31 const BugType BT
{this, "Result of operation is garbage or undefined"};
34 void checkPostStmt(const BinaryOperator
*B
, CheckerContext
&C
) const;
36 } // end anonymous namespace
38 static bool isArrayIndexOutOfBounds(CheckerContext
&C
, const Expr
*Ex
) {
39 ProgramStateRef state
= C
.getState();
41 if (!isa
<ArraySubscriptExpr
>(Ex
))
44 SVal Loc
= C
.getSVal(Ex
);
48 const MemRegion
*MR
= Loc
.castAs
<loc::MemRegionVal
>().getRegion();
49 const ElementRegion
*ER
= dyn_cast
<ElementRegion
>(MR
);
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(StInBound
, StOutBound
) = state
->assumeInBoundDual(Idx
, ElementCount
);
58 return StOutBound
&& !StInBound
;
61 void UndefResultChecker::checkPostStmt(const BinaryOperator
*B
,
62 CheckerContext
&C
) const {
63 if (C
.getSVal(B
).isUndef()) {
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
>(C
.getStackFrame()->getDecl()))
69 if (C
.getCalleeName(EnclosingFunctionDecl
) == "swap")
72 // Generate an error node.
73 ExplodedNode
*N
= C
.generateErrorNode();
77 SmallString
<256> sbuf
;
78 llvm::raw_svector_ostream
OS(sbuf
);
79 const Expr
*Ex
= nullptr;
82 if (C
.getSVal(B
->getLHS()).isUndef()) {
83 Ex
= B
->getLHS()->IgnoreParenCasts();
86 else if (C
.getSVal(B
->getRHS()).isUndef()) {
87 Ex
= B
->getRHS()->IgnoreParenCasts();
92 OS
<< "The " << (isLeft
? "left" : "right") << " operand of '"
93 << BinaryOperator::getOpcodeStr(B
->getOpcode())
94 << "' is a garbage value";
95 if (isArrayIndexOutOfBounds(C
, Ex
))
96 OS
<< " due to array index out of bounds";
98 // Neither operand was undefined, but the result is undefined.
99 OS
<< "The result of the '"
100 << BinaryOperator::getOpcodeStr(B
->getOpcode())
101 << "' expression is undefined";
103 auto report
= std::make_unique
<PathSensitiveBugReport
>(BT
, OS
.str(), N
);
105 report
->addRange(Ex
->getSourceRange());
106 bugreporter::trackExpressionValue(N
, Ex
, *report
);
109 bugreporter::trackExpressionValue(N
, B
, *report
);
111 C
.emitReport(std::move(report
));
115 void ento::registerUndefResultChecker(CheckerManager
&mgr
) {
116 mgr
.registerChecker
<UndefResultChecker
>();
119 bool ento::shouldRegisterUndefResultChecker(const CheckerManager
&mgr
) {