clang  8.0.0
EnumCastOutOfRangeChecker.cpp
Go to the documentation of this file.
1 //===- EnumCastOutOfRangeChecker.cpp ---------------------------*- 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 // The EnumCastOutOfRangeChecker is responsible for checking integer to
11 // enumeration casts that could result in undefined values. This could happen
12 // if the value that we cast from is out of the value range of the enumeration.
13 // Reference:
14 // [ISO/IEC 14882-2014] ISO/IEC 14882-2014.
15 // Programming Languages — C++, Fourth Edition. 2014.
16 // C++ Standard, [dcl.enum], in paragraph 8, which defines the range of an enum
17 // C++ Standard, [expr.static.cast], paragraph 10, which defines the behaviour
18 // of casting an integer value that is out of range
19 // SEI CERT C++ Coding Standard, INT50-CPP. Do not cast to an out-of-range
20 // enumeration value
21 //===----------------------------------------------------------------------===//
22 
26 
27 using namespace clang;
28 using namespace ento;
29 
30 namespace {
31 // This evaluator checks two SVals for equality. The first SVal is provided via
32 // the constructor, the second is the parameter of the overloaded () operator.
33 // It uses the in-built ConstraintManager to resolve the equlity to possible or
34 // not possible ProgramStates.
35 class ConstraintBasedEQEvaluator {
36  const DefinedOrUnknownSVal CompareValue;
37  const ProgramStateRef PS;
38  SValBuilder &SVB;
39 
40 public:
41  ConstraintBasedEQEvaluator(CheckerContext &C,
42  const DefinedOrUnknownSVal CompareValue)
43  : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {}
44 
45  bool operator()(const llvm::APSInt &EnumDeclInitValue) {
46  DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue);
47  DefinedOrUnknownSVal ElemEqualsValueToCast =
48  SVB.evalEQ(PS, EnumDeclValue, CompareValue);
49 
50  return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true));
51  }
52 };
53 
54 // This checker checks CastExpr statements.
55 // If the value provided to the cast is one of the values the enumeration can
56 // represent, the said value matches the enumeration. If the checker can
57 // establish the impossibility of matching it gives a warning.
58 // Being conservative, it does not warn if there is slight possibility the
59 // value can be matching.
60 class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
61  mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange;
62  void reportWarning(CheckerContext &C) const;
63 
64 public:
65  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
66 };
67 
68 using EnumValueVector = llvm::SmallVector<llvm::APSInt, 6>;
69 
70 // Collects all of the values an enum can represent (as SVals).
71 EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
72  EnumValueVector DeclValues(
74  llvm::transform(ED->enumerators(), DeclValues.begin(),
75  [](const EnumConstantDecl *D) { return D->getInitVal(); });
76  return DeclValues;
77 }
78 } // namespace
79 
80 void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
81  if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
82  if (!EnumValueCastOutOfRange)
83  EnumValueCastOutOfRange.reset(
84  new BuiltinBug(this, "Enum cast out of range",
85  "The value provided to the cast expression is not in "
86  "the valid range of values for the enum"));
87  C.emitReport(llvm::make_unique<BugReport>(
88  *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
89  N));
90  }
91 }
92 
93 void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
94  CheckerContext &C) const {
95  // Get the value of the expression to cast.
96  const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
97  C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
98 
99  // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
100  // don't analyze further.
101  if (!ValueToCast)
102  return;
103 
104  const QualType T = CE->getType();
105  // Check whether the cast type is an enum.
106  if (!T->isEnumeralType())
107  return;
108 
109  // If the cast is an enum, get its declaration.
110  // If the isEnumeralType() returned true, then the declaration must exist
111  // even if it is a stub declaration. It is up to the getDeclValuesForEnum()
112  // function to handle this.
113  const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
114 
115  EnumValueVector DeclValues = getDeclValuesForEnum(ED);
116  // Check if any of the enum values possibly match.
117  bool PossibleValueMatch = llvm::any_of(
118  DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
119 
120  // If there is no value that can possibly match any of the enum values, then
121  // warn.
122  if (!PossibleValueMatch)
123  reportWarning(C);
124 }
125 
126 void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
127  mgr.registerChecker<EnumCastOutOfRangeChecker>();
128 }
enumerator_iterator enumerator_end() const
Definition: Decl.h:3464
A (possibly-)qualified type.
Definition: Type.h:638
An instance of this object exists for each enum constant that is defined.
Definition: Decl.h:2786
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
float __ovld __cnfn distance(float p0, float p1)
Returns the distance between p0 and p1.
enumerator_range enumerators() const
Definition: Decl.h:3453
bool isEnumeralType() const
Definition: Type.h:6373
Expr * getSubExpr()
Definition: Expr.h:3050
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:2998
const T * castAs() const
Member-template castAs<specific type>.
Definition: Type.h:6811
QualType getType() const
Definition: Expr.h:128
enumerator_iterator enumerator_begin() const
Definition: Decl.h:3457
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of enums...
Definition: Type.h:4396
Dataflow Directional Tag Classes.
Represents an enum.
Definition: Decl.h:3326