1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
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 | |
31 | |
32 | |
33 | |
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 | : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {} |
43 | |
44 | bool operator()(const llvm::APSInt &EnumDeclInitValue) { |
45 | DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue); |
46 | DefinedOrUnknownSVal ElemEqualsValueToCast = |
47 | SVB.evalEQ(PS, EnumDeclValue, CompareValue); |
48 | |
49 | return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true)); |
50 | } |
51 | }; |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
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 | |
70 | EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) { |
71 | EnumValueVector DeclValues( |
72 | std::distance(ED->enumerator_begin(), ED->enumerator_end())); |
73 | llvm::transform(ED->enumerators(), DeclValues.begin(), |
74 | [](const EnumConstantDecl *D) { return D->getInitVal(); }); |
75 | return DeclValues; |
76 | } |
77 | } |
78 | |
79 | void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const { |
80 | if (const ExplodedNode *N = C.generateNonFatalErrorNode()) { |
81 | if (!EnumValueCastOutOfRange) |
82 | EnumValueCastOutOfRange.reset( |
83 | new BuiltinBug(this, "Enum cast out of range", |
84 | "The value provided to the cast expression is not in " |
85 | "the valid range of values for the enum")); |
86 | C.emitReport(llvm::make_unique<BugReport>( |
87 | *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(), |
88 | N)); |
89 | } |
90 | } |
91 | |
92 | void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, |
93 | CheckerContext &C) const { |
94 | |
95 | const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = |
96 | C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); |
97 | |
98 | |
99 | |
100 | if (!ValueToCast) |
101 | return; |
102 | |
103 | const QualType T = CE->getType(); |
104 | |
105 | if (!T->isEnumeralType()) |
106 | return; |
107 | |
108 | |
109 | |
110 | |
111 | |
112 | const EnumDecl *ED = T->castAs<EnumType>()->getDecl(); |
113 | |
114 | EnumValueVector DeclValues = getDeclValuesForEnum(ED); |
115 | |
116 | bool PossibleValueMatch = llvm::any_of( |
117 | DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast)); |
118 | |
119 | |
120 | |
121 | if (!PossibleValueMatch) |
122 | reportWarning(C); |
123 | } |
124 | |
125 | void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) { |
126 | mgr.registerChecker<EnumCastOutOfRangeChecker>(); |
127 | } |
128 | |
129 | bool ento::shouldRegisterEnumCastOutOfRangeChecker(const LangOptions &LO) { |
130 | return true; |
131 | } |
132 | |