clang  14.0.0git
UndefResultChecker.cpp
Go to the documentation of this file.
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 
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  mutable std::unique_ptr<BugType> BT;
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>(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>(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 = state->assumeInBound(Idx, ElementCount, true);
57  ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
58  return StOutBound && !StInBound;
59 }
60 
61 static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
62  return C.isGreaterOrEqual(
63  B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
64 }
65 
67  CheckerContext &C) {
68  SValBuilder &SB = C.getSValBuilder();
69  ProgramStateRef State = C.getState();
70  const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
71  const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
72  assert(LHS && RHS && "Values unknown, inconsistent state");
73  return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
74 }
75 
76 void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
77  CheckerContext &C) const {
78  if (C.getSVal(B).isUndef()) {
79 
80  // Do not report assignments of uninitialized values inside swap functions.
81  // This should allow to swap partially uninitialized structs
82  // (radar://14129997)
83  if (const FunctionDecl *EnclosingFunctionDecl =
84  dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
85  if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
86  return;
87 
88  // Generate an error node.
89  ExplodedNode *N = C.generateErrorNode();
90  if (!N)
91  return;
92 
93  if (!BT)
94  BT.reset(
95  new BuiltinBug(this, "Result of operation is garbage or undefined"));
96 
97  SmallString<256> sbuf;
98  llvm::raw_svector_ostream OS(sbuf);
99  const Expr *Ex = nullptr;
100  bool isLeft = true;
101 
102  if (C.getSVal(B->getLHS()).isUndef()) {
103  Ex = B->getLHS()->IgnoreParenCasts();
104  isLeft = true;
105  }
106  else if (C.getSVal(B->getRHS()).isUndef()) {
107  Ex = B->getRHS()->IgnoreParenCasts();
108  isLeft = false;
109  }
110 
111  if (Ex) {
112  OS << "The " << (isLeft ? "left" : "right") << " operand of '"
114  << "' is a garbage value";
115  if (isArrayIndexOutOfBounds(C, Ex))
116  OS << " due to array index out of bounds";
117  } else {
118  // Neither operand was undefined, but the result is undefined.
119  if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
120  B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
121  C.isNegative(B->getRHS())) {
122  OS << "The result of the "
123  << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
124  : "right")
125  << " shift is undefined because the right operand is negative";
126  Ex = B->getRHS();
127  } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
128  B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
129  isShiftOverflow(B, C)) {
130 
131  OS << "The result of the "
132  << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
133  : "right")
134  << " shift is undefined due to shifting by ";
135  Ex = B->getRHS();
136 
137  SValBuilder &SB = C.getSValBuilder();
138  const llvm::APSInt *I =
139  SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
140  if (!I)
141  OS << "a value that is";
142  else if (I->isUnsigned())
143  OS << '\'' << I->getZExtValue() << "\', which is";
144  else
145  OS << '\'' << I->getSExtValue() << "\', which is";
146 
147  OS << " greater or equal to the width of type '"
148  << B->getLHS()->getType().getAsString() << "'.";
149  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
150  C.isNegative(B->getLHS())) {
151  OS << "The result of the left shift is undefined because the left "
152  "operand is negative";
153  Ex = B->getLHS();
154  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
156  ProgramStateRef State = C.getState();
157  SValBuilder &SB = C.getSValBuilder();
158  const llvm::APSInt *LHS =
159  SB.getKnownValue(State, C.getSVal(B->getLHS()));
160  const llvm::APSInt *RHS =
161  SB.getKnownValue(State, C.getSVal(B->getRHS()));
162  OS << "The result of the left shift is undefined due to shifting \'"
163  << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
164  << "\', which is unrepresentable in the unsigned version of "
165  << "the return type \'" << B->getLHS()->getType().getAsString()
166  << "\'";
167  Ex = B->getLHS();
168  } else {
169  OS << "The result of the '"
171  << "' expression is undefined";
172  }
173  }
174  auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
175  if (Ex) {
176  report->addRange(Ex->getSourceRange());
177  bugreporter::trackExpressionValue(N, Ex, *report);
178  }
179  else
180  bugreporter::trackExpressionValue(N, B, *report);
181 
182  C.emitReport(std::move(report));
183  }
184 }
185 
186 void ento::registerUndefResultChecker(CheckerManager &mgr) {
187  mgr.registerChecker<UndefResultChecker>();
188 }
189 
190 bool ento::shouldRegisterUndefResultChecker(const CheckerManager &mgr) {
191  return true;
192 }
DynamicExtent.h
clang::Stmt::getSourceRange
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:324
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:54
clang::BinaryOperator::getOpcodeStr
StringRef getOpcodeStr() const
Definition: Expr.h:3868
clang::QualType::getAsString
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition: Type.h:1015
clang::BinaryOperator::getOpcode
Opcode getOpcode() const
Definition: Expr.h:3847
APSInt
llvm::APSInt APSInt
Definition: ByteCodeEmitter.cpp:19
BuiltinCheckerRegistration.h
clang::BinaryOperator
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3803
CheckerManager.h
llvm::SmallString
Definition: LLVM.h:37
clang::ento::getDynamicElementCount
DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, QualType Ty)
state
and static some checkers Checker The latter are built on top of the former via the Checker and CheckerVisitor and attempts to isolate them from much of the gore of the internal analysis the analyzer is basically a source code simulator that traces out possible paths of execution The state of the and the combination of state and program point is a node in an exploded which has the entry program point and initial state
Definition: README.txt:30
clang::ento::bugreporter::trackExpressionValue
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
clang::Expr::IgnoreParenCasts
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2924
BugType.h
clang::BinaryOperator::getLHS
Expr * getLHS() const
Definition: Expr.h:3852
isLeftShiftResultUnrepresentable
static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B, CheckerContext &C)
Definition: UndefResultChecker.cpp:66
State
LineState State
Definition: UnwrappedLineFormatter.cpp:986
isArrayIndexOutOfBounds
static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex)
Definition: UndefResultChecker.cpp:38
CheckerContext.h
Checker.h
ExprEngine.h
clang
Definition: CalledOnceCheck.h:17
clang::BinaryOperator::getRHS
Expr * getRHS() const
Definition: Expr.h:3854
clang::Expr::getType
QualType getType() const
Definition: Expr.h:141
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1856
isShiftOverflow
static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C)
Definition: UndefResultChecker.cpp:61
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...