/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //=== CastSizeChecker.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 | | // CastSizeChecker checks when casting a malloc'ed symbolic region to type T, |
10 | | // whether the size of the symbolic region is a multiple of the size of T. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/AST/CharUnits.h" |
15 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
16 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
17 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
18 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
20 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
21 | | |
22 | | using namespace clang; |
23 | | using namespace ento; |
24 | | |
25 | | namespace { |
26 | | class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > { |
27 | | mutable std::unique_ptr<BugType> BT; |
28 | | |
29 | | public: |
30 | | void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; |
31 | | }; |
32 | | } |
33 | | |
34 | | /// Check if we are casting to a struct with a flexible array at the end. |
35 | | /// \code |
36 | | /// struct foo { |
37 | | /// size_t len; |
38 | | /// struct bar data[]; |
39 | | /// }; |
40 | | /// \endcode |
41 | | /// or |
42 | | /// \code |
43 | | /// struct foo { |
44 | | /// size_t len; |
45 | | /// struct bar data[0]; |
46 | | /// } |
47 | | /// \endcode |
48 | | /// In these cases it is also valid to allocate size of struct foo + a multiple |
49 | | /// of struct bar. |
50 | | static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize, |
51 | 19 | CharUnits TypeSize, QualType ToPointeeTy) { |
52 | 19 | const RecordType *RT = ToPointeeTy->getAs<RecordType>(); |
53 | 19 | if (!RT) |
54 | 4 | return false; |
55 | | |
56 | 15 | const RecordDecl *RD = RT->getDecl(); |
57 | 15 | RecordDecl::field_iterator Iter(RD->field_begin()); |
58 | 15 | RecordDecl::field_iterator End(RD->field_end()); |
59 | 15 | const FieldDecl *Last = nullptr; |
60 | 45 | for (; Iter != End; ++Iter30 ) |
61 | 30 | Last = *Iter; |
62 | 15 | assert(Last && "empty structs should already be handled"); |
63 | | |
64 | 15 | const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual(); |
65 | 15 | CharUnits FlexSize; |
66 | 15 | if (const ConstantArrayType *ArrayTy = |
67 | 15 | Ctx.getAsConstantArrayType(Last->getType())) { |
68 | 11 | FlexSize = Ctx.getTypeSizeInChars(ElemType); |
69 | 11 | if (ArrayTy->getSize() == 1 && TypeSize > FlexSize5 ) |
70 | 5 | TypeSize -= FlexSize; |
71 | 6 | else if (ArrayTy->getSize() != 0) |
72 | 2 | return false; |
73 | 11 | } else if (4 RD->hasFlexibleArrayMember()4 ) { |
74 | 4 | FlexSize = Ctx.getTypeSizeInChars(ElemType); |
75 | 4 | } else { |
76 | 0 | return false; |
77 | 0 | } |
78 | | |
79 | 13 | if (FlexSize.isZero()) |
80 | 0 | return false; |
81 | | |
82 | 13 | CharUnits Left = RegionSize - TypeSize; |
83 | 13 | if (Left.isNegative()) |
84 | 0 | return false; |
85 | | |
86 | 13 | return Left % FlexSize == 0; |
87 | 13 | } |
88 | | |
89 | 7.99k | void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { |
90 | 7.99k | const Expr *E = CE->getSubExpr(); |
91 | 7.99k | ASTContext &Ctx = C.getASTContext(); |
92 | 7.99k | QualType ToTy = Ctx.getCanonicalType(CE->getType()); |
93 | 7.99k | const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); |
94 | | |
95 | 7.99k | if (!ToPTy) |
96 | 3.61k | return; |
97 | | |
98 | 4.38k | QualType ToPointeeTy = ToPTy->getPointeeType(); |
99 | | |
100 | | // Only perform the check if 'ToPointeeTy' is a complete type. |
101 | 4.38k | if (ToPointeeTy->isIncompleteType()) |
102 | 643 | return; |
103 | | |
104 | 3.73k | ProgramStateRef state = C.getState(); |
105 | 3.73k | const MemRegion *R = C.getSVal(E).getAsRegion(); |
106 | 3.73k | if (!R) |
107 | 177 | return; |
108 | | |
109 | 3.56k | const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); |
110 | 3.56k | if (!SR) |
111 | 3.23k | return; |
112 | | |
113 | 326 | SValBuilder &svalBuilder = C.getSValBuilder(); |
114 | | |
115 | 326 | DefinedOrUnknownSVal Size = getDynamicExtent(state, SR, svalBuilder); |
116 | 326 | const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); |
117 | 326 | if (!SizeInt) |
118 | 102 | return; |
119 | | |
120 | 224 | CharUnits regionSize = CharUnits::fromQuantity(SizeInt->getZExtValue()); |
121 | 224 | CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); |
122 | | |
123 | | // Ignore void, and a few other un-sizeable types. |
124 | 224 | if (typeSize.isZero()) |
125 | 3 | return; |
126 | | |
127 | 221 | if (regionSize % typeSize == 0) |
128 | 202 | return; |
129 | | |
130 | 19 | if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) |
131 | 7 | return; |
132 | | |
133 | 12 | if (ExplodedNode *errorNode = C.generateErrorNode()) { |
134 | 12 | if (!BT) |
135 | 2 | BT.reset(new BugType(this, "Cast region with wrong size.")); |
136 | 12 | constexpr llvm::StringLiteral Msg = |
137 | 12 | "Cast a region whose size is not a multiple of the destination type " |
138 | 12 | "size."; |
139 | 12 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, errorNode); |
140 | 12 | R->addRange(CE->getSourceRange()); |
141 | 12 | C.emitReport(std::move(R)); |
142 | 12 | } |
143 | 12 | } |
144 | | |
145 | 62 | void ento::registerCastSizeChecker(CheckerManager &mgr) { |
146 | 62 | mgr.registerChecker<CastSizeChecker>(); |
147 | 62 | } |
148 | | |
149 | 164 | bool ento::shouldRegisterCastSizeChecker(const CheckerManager &mgr) { |
150 | | // PR31226: C++ is more complicated than what this checker currently supports. |
151 | | // There are derived-to-base casts, there are different rules for 0-size |
152 | | // structures, no flexible arrays, etc. |
153 | | // FIXME: Disabled on C++ for now. |
154 | 164 | const LangOptions &LO = mgr.getLangOpts(); |
155 | 164 | return !LO.CPlusPlus; |
156 | 164 | } |