1//== BoolAssignmentChecker.cpp - Boolean assignment checker -----*- 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 BoolAssignmentChecker, a builtin check in ExprEngine that
11// performs checks for assignment of non-Boolean values to Boolean variables.
12//
13//===----------------------------------------------------------------------===//
14
15#include "ClangSACheckers.h"
16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
21using namespace clang;
22using namespace ento;
23
24namespace {
25  class BoolAssignmentChecker : public Checker< check::Bind > {
26    mutable std::unique_ptr<BuiltinBug> BT;
27    void emitReport(ProgramStateRef state, CheckerContext &C) const;
28  public:
29    void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
30  };
31} // end anonymous namespace
32
33void BoolAssignmentChecker::emitReport(ProgramStateRef state,
34                                       CheckerContext &C) const {
35  if (ExplodedNode *N = C.addTransition(state)) {
36    if (!BT)
37      BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
38    C.emitReport(new BugReport(*BT, BT->getDescription(), N));
39  }
40}
41
42static bool isBooleanType(QualType Ty) {
43  if (Ty->isBooleanType()) // C++ or C99
44    return true;
45
46  if (const TypedefType *TT = Ty->getAs<TypedefType>())
47    return TT->getDecl()->getName() == "BOOL"   || // Objective-C
48           TT->getDecl()->getName() == "_Bool"  || // stdbool.h < C99
49           TT->getDecl()->getName() == "Boolean";  // MacTypes.h
50
51  return false;
52}
53
54void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
55                                      CheckerContext &C) const {
56
57  // We are only interested in stores into Booleans.
58  const TypedValueRegion *TR =
59    dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion());
60
61  if (!TR)
62    return;
63
64  QualType valTy = TR->getValueType();
65
66  if (!isBooleanType(valTy))
67    return;
68
69  // Get the value of the right-hand side.  We only care about values
70  // that are defined (UnknownVals and UndefinedVals are handled by other
71  // checkers).
72  Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
73  if (!DV)
74    return;
75
76  // Check if the assigned value meets our criteria for correctness.  It must
77  // be a value that is either 0 or 1.  One way to check this is to see if
78  // the value is possibly < 0 (for a negative value) or greater than 1.
79  ProgramStateRef state = C.getState();
80  SValBuilder &svalBuilder = C.getSValBuilder();
81  ConstraintManager &CM = C.getConstraintManager();
82
83  // First, ensure that the value is >= 0.
84  DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
85  SVal greaterThanOrEqualToZeroVal =
86    svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
87                          svalBuilder.getConditionType());
88
89  Optional<DefinedSVal> greaterThanEqualToZero =
90      greaterThanOrEqualToZeroVal.getAs<DefinedSVal>();
91
92  if (!greaterThanEqualToZero) {
93    // The SValBuilder cannot construct a valid SVal for this condition.
94    // This means we cannot properly reason about it.
95    return;
96  }
97
98  ProgramStateRef stateLT, stateGE;
99  std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
100
101  // Is it possible for the value to be less than zero?
102  if (stateLT) {
103    // It is possible for the value to be less than zero.  We only
104    // want to emit a warning, however, if that value is fully constrained.
105    // If it it possible for the value to be >= 0, then essentially the
106    // value is underconstrained and there is nothing left to be done.
107    if (!stateGE)
108      emitReport(stateLT, C);
109
110    // In either case, we are done.
111    return;
112  }
113
114  // If we reach here, it must be the case that the value is constrained
115  // to only be >= 0.
116  assert(stateGE == state);
117
118  // At this point we know that the value is >= 0.
119  // Now check to ensure that the value is <= 1.
120  DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
121  SVal lessThanEqToOneVal =
122    svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal,
123                          svalBuilder.getConditionType());
124
125  Optional<DefinedSVal> lessThanEqToOne =
126      lessThanEqToOneVal.getAs<DefinedSVal>();
127
128  if (!lessThanEqToOne) {
129    // The SValBuilder cannot construct a valid SVal for this condition.
130    // This means we cannot properly reason about it.
131    return;
132  }
133
134  ProgramStateRef stateGT, stateLE;
135  std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
136
137  // Is it possible for the value to be greater than one?
138  if (stateGT) {
139    // It is possible for the value to be greater than one.  We only
140    // want to emit a warning, however, if that value is fully constrained.
141    // If it is possible for the value to be <= 1, then essentially the
142    // value is underconstrained and there is nothing left to be done.
143    if (!stateLE)
144      emitReport(stateGT, C);
145
146    // In either case, we are done.
147    return;
148  }
149
150  // If we reach here, it must be the case that the value is constrained
151  // to only be <= 1.
152  assert(stateLE == state);
153}
154
155void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
156    mgr.registerChecker<BoolAssignmentChecker>();
157}
158