1*b5893f02SDimitry Andric //===- EnumCastOutOfRangeChecker.cpp ---------------------------*- C++ -*--===//
2*b5893f02SDimitry Andric //
3*b5893f02SDimitry Andric //                     The LLVM Compiler Infrastructure
4*b5893f02SDimitry Andric //
5*b5893f02SDimitry Andric // This file is distributed under the University of Illinois Open Source
6*b5893f02SDimitry Andric // License. See LICENSE.TXT for details.
7*b5893f02SDimitry Andric //
8*b5893f02SDimitry Andric //===----------------------------------------------------------------------===//
9*b5893f02SDimitry Andric //
10*b5893f02SDimitry Andric // The EnumCastOutOfRangeChecker is responsible for checking integer to
11*b5893f02SDimitry Andric // enumeration casts that could result in undefined values. This could happen
12*b5893f02SDimitry Andric // if the value that we cast from is out of the value range of the enumeration.
13*b5893f02SDimitry Andric // Reference:
14*b5893f02SDimitry Andric // [ISO/IEC 14882-2014] ISO/IEC 14882-2014.
15*b5893f02SDimitry Andric //   Programming Languages — C++, Fourth Edition. 2014.
16*b5893f02SDimitry Andric // C++ Standard, [dcl.enum], in paragraph 8, which defines the range of an enum
17*b5893f02SDimitry Andric // C++ Standard, [expr.static.cast], paragraph 10, which defines the behaviour
18*b5893f02SDimitry Andric //   of casting an integer value that is out of range
19*b5893f02SDimitry Andric // SEI CERT C++ Coding Standard, INT50-CPP. Do not cast to an out-of-range
20*b5893f02SDimitry Andric //   enumeration value
21*b5893f02SDimitry Andric //===----------------------------------------------------------------------===//
22*b5893f02SDimitry Andric 
23*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
24*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25*b5893f02SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26*b5893f02SDimitry Andric 
27*b5893f02SDimitry Andric using namespace clang;
28*b5893f02SDimitry Andric using namespace ento;
29*b5893f02SDimitry Andric 
30*b5893f02SDimitry Andric namespace {
31*b5893f02SDimitry Andric // This evaluator checks two SVals for equality. The first SVal is provided via
32*b5893f02SDimitry Andric // the constructor, the second is the parameter of the overloaded () operator.
33*b5893f02SDimitry Andric // It uses the in-built ConstraintManager to resolve the equlity to possible or
34*b5893f02SDimitry Andric // not possible ProgramStates.
35*b5893f02SDimitry Andric class ConstraintBasedEQEvaluator {
36*b5893f02SDimitry Andric   const DefinedOrUnknownSVal CompareValue;
37*b5893f02SDimitry Andric   const ProgramStateRef PS;
38*b5893f02SDimitry Andric   SValBuilder &SVB;
39*b5893f02SDimitry Andric 
40*b5893f02SDimitry Andric public:
ConstraintBasedEQEvaluator(CheckerContext & C,const DefinedOrUnknownSVal CompareValue)41*b5893f02SDimitry Andric   ConstraintBasedEQEvaluator(CheckerContext &C,
42*b5893f02SDimitry Andric                              const DefinedOrUnknownSVal CompareValue)
43*b5893f02SDimitry Andric       : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {}
44*b5893f02SDimitry Andric 
operator ()(const llvm::APSInt & EnumDeclInitValue)45*b5893f02SDimitry Andric   bool operator()(const llvm::APSInt &EnumDeclInitValue) {
46*b5893f02SDimitry Andric     DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue);
47*b5893f02SDimitry Andric     DefinedOrUnknownSVal ElemEqualsValueToCast =
48*b5893f02SDimitry Andric         SVB.evalEQ(PS, EnumDeclValue, CompareValue);
49*b5893f02SDimitry Andric 
50*b5893f02SDimitry Andric     return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true));
51*b5893f02SDimitry Andric   }
52*b5893f02SDimitry Andric };
53*b5893f02SDimitry Andric 
54*b5893f02SDimitry Andric // This checker checks CastExpr statements.
55*b5893f02SDimitry Andric // If the value provided to the cast is one of the values the enumeration can
56*b5893f02SDimitry Andric // represent, the said value matches the enumeration. If the checker can
57*b5893f02SDimitry Andric // establish the impossibility of matching it gives a warning.
58*b5893f02SDimitry Andric // Being conservative, it does not warn if there is slight possibility the
59*b5893f02SDimitry Andric // value can be matching.
60*b5893f02SDimitry Andric class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
61*b5893f02SDimitry Andric   mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange;
62*b5893f02SDimitry Andric   void reportWarning(CheckerContext &C) const;
63*b5893f02SDimitry Andric 
64*b5893f02SDimitry Andric public:
65*b5893f02SDimitry Andric   void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
66*b5893f02SDimitry Andric };
67*b5893f02SDimitry Andric 
68*b5893f02SDimitry Andric using EnumValueVector = llvm::SmallVector<llvm::APSInt, 6>;
69*b5893f02SDimitry Andric 
70*b5893f02SDimitry Andric // Collects all of the values an enum can represent (as SVals).
getDeclValuesForEnum(const EnumDecl * ED)71*b5893f02SDimitry Andric EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
72*b5893f02SDimitry Andric   EnumValueVector DeclValues(
73*b5893f02SDimitry Andric       std::distance(ED->enumerator_begin(), ED->enumerator_end()));
74*b5893f02SDimitry Andric   llvm::transform(ED->enumerators(), DeclValues.begin(),
75*b5893f02SDimitry Andric                  [](const EnumConstantDecl *D) { return D->getInitVal(); });
76*b5893f02SDimitry Andric   return DeclValues;
77*b5893f02SDimitry Andric }
78*b5893f02SDimitry Andric } // namespace
79*b5893f02SDimitry Andric 
reportWarning(CheckerContext & C) const80*b5893f02SDimitry Andric void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
81*b5893f02SDimitry Andric   if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
82*b5893f02SDimitry Andric     if (!EnumValueCastOutOfRange)
83*b5893f02SDimitry Andric       EnumValueCastOutOfRange.reset(
84*b5893f02SDimitry Andric           new BuiltinBug(this, "Enum cast out of range",
85*b5893f02SDimitry Andric                          "The value provided to the cast expression is not in "
86*b5893f02SDimitry Andric                          "the valid range of values for the enum"));
87*b5893f02SDimitry Andric     C.emitReport(llvm::make_unique<BugReport>(
88*b5893f02SDimitry Andric         *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
89*b5893f02SDimitry Andric         N));
90*b5893f02SDimitry Andric   }
91*b5893f02SDimitry Andric }
92*b5893f02SDimitry Andric 
checkPreStmt(const CastExpr * CE,CheckerContext & C) const93*b5893f02SDimitry Andric void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
94*b5893f02SDimitry Andric                                              CheckerContext &C) const {
95*b5893f02SDimitry Andric   // Get the value of the expression to cast.
96*b5893f02SDimitry Andric   const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
97*b5893f02SDimitry Andric       C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
98*b5893f02SDimitry Andric 
99*b5893f02SDimitry Andric   // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
100*b5893f02SDimitry Andric   // don't analyze further.
101*b5893f02SDimitry Andric   if (!ValueToCast)
102*b5893f02SDimitry Andric     return;
103*b5893f02SDimitry Andric 
104*b5893f02SDimitry Andric   const QualType T = CE->getType();
105*b5893f02SDimitry Andric   // Check whether the cast type is an enum.
106*b5893f02SDimitry Andric   if (!T->isEnumeralType())
107*b5893f02SDimitry Andric     return;
108*b5893f02SDimitry Andric 
109*b5893f02SDimitry Andric   // If the cast is an enum, get its declaration.
110*b5893f02SDimitry Andric   // If the isEnumeralType() returned true, then the declaration must exist
111*b5893f02SDimitry Andric   // even if it is a stub declaration. It is up to the getDeclValuesForEnum()
112*b5893f02SDimitry Andric   // function to handle this.
113*b5893f02SDimitry Andric   const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
114*b5893f02SDimitry Andric 
115*b5893f02SDimitry Andric   EnumValueVector DeclValues = getDeclValuesForEnum(ED);
116*b5893f02SDimitry Andric   // Check if any of the enum values possibly match.
117*b5893f02SDimitry Andric   bool PossibleValueMatch = llvm::any_of(
118*b5893f02SDimitry Andric       DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
119*b5893f02SDimitry Andric 
120*b5893f02SDimitry Andric   // If there is no value that can possibly match any of the enum values, then
121*b5893f02SDimitry Andric   // warn.
122*b5893f02SDimitry Andric   if (!PossibleValueMatch)
123*b5893f02SDimitry Andric     reportWarning(C);
124*b5893f02SDimitry Andric }
125*b5893f02SDimitry Andric 
registerEnumCastOutOfRangeChecker(CheckerManager & mgr)126*b5893f02SDimitry Andric void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
127*b5893f02SDimitry Andric   mgr.registerChecker<EnumCastOutOfRangeChecker>();
128*b5893f02SDimitry Andric }
129