f4a2713ac8
Change-Id: Ia40e9ffdf29b5dab2f122f673ff6802a58bc690f
157 lines
5.4 KiB
C++
157 lines
5.4 KiB
C++
//== BoolAssignmentChecker.cpp - Boolean assignment checker -----*- C++ -*--==//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This defines BoolAssignmentChecker, a builtin check in ExprEngine that
|
|
// performs checks for assignment of non-Boolean values to Boolean variables.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ClangSACheckers.h"
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
|
|
namespace {
|
|
class BoolAssignmentChecker : public Checker< check::Bind > {
|
|
mutable OwningPtr<BuiltinBug> BT;
|
|
void emitReport(ProgramStateRef state, CheckerContext &C) const;
|
|
public:
|
|
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
void BoolAssignmentChecker::emitReport(ProgramStateRef state,
|
|
CheckerContext &C) const {
|
|
if (ExplodedNode *N = C.addTransition(state)) {
|
|
if (!BT)
|
|
BT.reset(new BuiltinBug("Assignment of a non-Boolean value"));
|
|
C.emitReport(new BugReport(*BT, BT->getDescription(), N));
|
|
}
|
|
}
|
|
|
|
static bool isBooleanType(QualType Ty) {
|
|
if (Ty->isBooleanType()) // C++ or C99
|
|
return true;
|
|
|
|
if (const TypedefType *TT = Ty->getAs<TypedefType>())
|
|
return TT->getDecl()->getName() == "BOOL" || // Objective-C
|
|
TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99
|
|
TT->getDecl()->getName() == "Boolean"; // MacTypes.h
|
|
|
|
return false;
|
|
}
|
|
|
|
void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
|
|
CheckerContext &C) const {
|
|
|
|
// We are only interested in stores into Booleans.
|
|
const TypedValueRegion *TR =
|
|
dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion());
|
|
|
|
if (!TR)
|
|
return;
|
|
|
|
QualType valTy = TR->getValueType();
|
|
|
|
if (!isBooleanType(valTy))
|
|
return;
|
|
|
|
// Get the value of the right-hand side. We only care about values
|
|
// that are defined (UnknownVals and UndefinedVals are handled by other
|
|
// checkers).
|
|
Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
|
|
if (!DV)
|
|
return;
|
|
|
|
// Check if the assigned value meets our criteria for correctness. It must
|
|
// be a value that is either 0 or 1. One way to check this is to see if
|
|
// the value is possibly < 0 (for a negative value) or greater than 1.
|
|
ProgramStateRef state = C.getState();
|
|
SValBuilder &svalBuilder = C.getSValBuilder();
|
|
ConstraintManager &CM = C.getConstraintManager();
|
|
|
|
// First, ensure that the value is >= 0.
|
|
DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
|
|
SVal greaterThanOrEqualToZeroVal =
|
|
svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
|
|
svalBuilder.getConditionType());
|
|
|
|
Optional<DefinedSVal> greaterThanEqualToZero =
|
|
greaterThanOrEqualToZeroVal.getAs<DefinedSVal>();
|
|
|
|
if (!greaterThanEqualToZero) {
|
|
// The SValBuilder cannot construct a valid SVal for this condition.
|
|
// This means we cannot properly reason about it.
|
|
return;
|
|
}
|
|
|
|
ProgramStateRef stateLT, stateGE;
|
|
llvm::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
|
|
|
|
// Is it possible for the value to be less than zero?
|
|
if (stateLT) {
|
|
// It is possible for the value to be less than zero. We only
|
|
// want to emit a warning, however, if that value is fully constrained.
|
|
// If it it possible for the value to be >= 0, then essentially the
|
|
// value is underconstrained and there is nothing left to be done.
|
|
if (!stateGE)
|
|
emitReport(stateLT, C);
|
|
|
|
// In either case, we are done.
|
|
return;
|
|
}
|
|
|
|
// If we reach here, it must be the case that the value is constrained
|
|
// to only be >= 0.
|
|
assert(stateGE == state);
|
|
|
|
// At this point we know that the value is >= 0.
|
|
// Now check to ensure that the value is <= 1.
|
|
DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
|
|
SVal lessThanEqToOneVal =
|
|
svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal,
|
|
svalBuilder.getConditionType());
|
|
|
|
Optional<DefinedSVal> lessThanEqToOne =
|
|
lessThanEqToOneVal.getAs<DefinedSVal>();
|
|
|
|
if (!lessThanEqToOne) {
|
|
// The SValBuilder cannot construct a valid SVal for this condition.
|
|
// This means we cannot properly reason about it.
|
|
return;
|
|
}
|
|
|
|
ProgramStateRef stateGT, stateLE;
|
|
llvm::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
|
|
|
|
// Is it possible for the value to be greater than one?
|
|
if (stateGT) {
|
|
// It is possible for the value to be greater than one. We only
|
|
// want to emit a warning, however, if that value is fully constrained.
|
|
// If it is possible for the value to be <= 1, then essentially the
|
|
// value is underconstrained and there is nothing left to be done.
|
|
if (!stateLE)
|
|
emitReport(stateGT, C);
|
|
|
|
// In either case, we are done.
|
|
return;
|
|
}
|
|
|
|
// If we reach here, it must be the case that the value is constrained
|
|
// to only be <= 1.
|
|
assert(stateLE == state);
|
|
}
|
|
|
|
void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
|
|
mgr.registerChecker<BoolAssignmentChecker>();
|
|
}
|