clang  10.0.0svn
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 
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 class UndefResultChecker
28  : public Checker< check::PostStmt<BinaryOperator> > {
29 
30  mutable std::unique_ptr<BugType> BT;
31 
32 public:
33  void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
34 };
35 } // end anonymous namespace
36 
37 static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
38  ProgramStateRef state = C.getState();
39 
40  if (!isa<ArraySubscriptExpr>(Ex))
41  return false;
42 
43  SVal Loc = C.getSVal(Ex);
44  if (!Loc.isValid())
45  return false;
46 
47  const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion();
48  const ElementRegion *ER = dyn_cast<ElementRegion>(MR);
49  if (!ER)
50  return false;
51 
52  DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
53  DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(
54  state, ER->getSuperRegion(), ER->getValueType());
55  ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
56  ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
57  return StOutBound && !StInBound;
58 }
59 
60 static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
61  return C.isGreaterOrEqual(
62  B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
63 }
64 
66  CheckerContext &C) {
67  SValBuilder &SB = C.getSValBuilder();
68  ProgramStateRef State = C.getState();
69  const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
70  const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
71  assert(LHS && RHS && "Values unknown, inconsistent state");
72  return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
73 }
74 
75 void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
76  CheckerContext &C) const {
77  if (C.getSVal(B).isUndef()) {
78 
79  // Do not report assignments of uninitialized values inside swap functions.
80  // This should allow to swap partially uninitialized structs
81  // (radar://14129997)
82  if (const FunctionDecl *EnclosingFunctionDecl =
83  dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
84  if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
85  return;
86 
87  // Generate an error node.
88  ExplodedNode *N = C.generateErrorNode();
89  if (!N)
90  return;
91 
92  if (!BT)
93  BT.reset(
94  new BuiltinBug(this, "Result of operation is garbage or undefined"));
95 
96  SmallString<256> sbuf;
97  llvm::raw_svector_ostream OS(sbuf);
98  const Expr *Ex = nullptr;
99  bool isLeft = true;
100 
101  if (C.getSVal(B->getLHS()).isUndef()) {
102  Ex = B->getLHS()->IgnoreParenCasts();
103  isLeft = true;
104  }
105  else if (C.getSVal(B->getRHS()).isUndef()) {
106  Ex = B->getRHS()->IgnoreParenCasts();
107  isLeft = false;
108  }
109 
110  if (Ex) {
111  OS << "The " << (isLeft ? "left" : "right") << " operand of '"
113  << "' is a garbage value";
114  if (isArrayIndexOutOfBounds(C, Ex))
115  OS << " due to array index out of bounds";
116  } else {
117  // Neither operand was undefined, but the result is undefined.
118  if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
119  B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
120  C.isNegative(B->getRHS())) {
121  OS << "The result of the "
122  << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
123  : "right")
124  << " shift is undefined because the right operand is negative";
125  Ex = B->getRHS();
126  } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
127  B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
128  isShiftOverflow(B, C)) {
129 
130  OS << "The result of the "
131  << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
132  : "right")
133  << " shift is undefined due to shifting by ";
134  Ex = B->getRHS();
135 
136  SValBuilder &SB = C.getSValBuilder();
137  const llvm::APSInt *I =
138  SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
139  if (!I)
140  OS << "a value that is";
141  else if (I->isUnsigned())
142  OS << '\'' << I->getZExtValue() << "\', which is";
143  else
144  OS << '\'' << I->getSExtValue() << "\', which is";
145 
146  OS << " greater or equal to the width of type '"
147  << B->getLHS()->getType().getAsString() << "'.";
148  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
149  C.isNegative(B->getLHS())) {
150  OS << "The result of the left shift is undefined because the left "
151  "operand is negative";
152  Ex = B->getLHS();
153  } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
155  ProgramStateRef State = C.getState();
156  SValBuilder &SB = C.getSValBuilder();
157  const llvm::APSInt *LHS =
158  SB.getKnownValue(State, C.getSVal(B->getLHS()));
159  const llvm::APSInt *RHS =
160  SB.getKnownValue(State, C.getSVal(B->getRHS()));
161  OS << "The result of the left shift is undefined due to shifting \'"
162  << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
163  << "\', which is unrepresentable in the unsigned version of "
164  << "the return type \'" << B->getLHS()->getType().getAsString()
165  << "\'";
166  Ex = B->getLHS();
167  } else {
168  OS << "The result of the '"
170  << "' expression is undefined";
171  }
172  }
173  auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
174  if (Ex) {
175  report->addRange(Ex->getSourceRange());
176  bugreporter::trackExpressionValue(N, Ex, *report);
177  }
178  else
179  bugreporter::trackExpressionValue(N, B, *report);
180 
181  C.emitReport(std::move(report));
182  }
183 }
184 
185 void ento::registerUndefResultChecker(CheckerManager &mgr) {
186  mgr.registerChecker<UndefResultChecker>();
187 }
188 
189 bool ento::shouldRegisterUndefResultChecker(const LangOptions &LO) {
190  return true;
191 }
Represents a function declaration or definition.
Definition: Decl.h:1784
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Opcode getOpcode() const
Definition: Expr.h:3444
LineState State
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
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:3465
static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex)
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3409
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2971
static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B, CheckerContext &C)
This represents one expression.
Definition: Expr.h:108
QualType getType() const
Definition: Expr.h:137
llvm::APSInt APSInt
Expr * getLHS() const
Definition: Expr.h:3449
Dataflow Directional Tag Classes.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition: Type.h:979
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:262
Expr * getRHS() const
Definition: Expr.h:3451