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