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