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