clang  8.0.0svn
UndefResultChecker.cpp
Go to the documentation of this file.
1 //=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This defines UndefResultChecker, a builtin check in ExprEngine that
11 // performs checks for undefined results of non-assignment binary operators.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ClangSACheckers.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  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 NumElements = C.getStoreManager().getSizeInElements(
55  state, ER->getSuperRegion(), ER->getValueType());
56  ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
57  ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, 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 = llvm::make_unique<BugReport>(*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 }
Represents a function declaration or definition.
Definition: Decl.h:1732
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Opcode getOpcode() const
Definition: Expr.h:3234
LineState State
i32 captured_struct **param SharedsTy A type which contains references the shared variables *param Shareds Context with the list of shared variables from the p *TaskFunction *param Data Additional data for task generation like final * state
static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C)
StringRef getOpcodeStr() const
Definition: Expr.h:3253
static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex)
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3193
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
Definition: Expr.cpp:2571
static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B, CheckerContext &C)
This represents one expression.
Definition: Expr.h:106
QualType getType() const
Definition: Expr.h:128
Expr * getLHS() const
Definition: Expr.h:3237
Dataflow Directional Tag Classes.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition: Type.h:975
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:268
Expr * getRHS() const
Definition: Expr.h:3239