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 21 using namespace clang; 22 using namespace ento; 23 24 namespace { 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 33 void 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 42 static 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 54 void 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 155 void ento::registerBoolAssignmentChecker(CheckerManager &mgr) { 156 mgr.registerChecker<BoolAssignmentChecker>(); 157 } 158