/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- 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 | | // Reports inconsistencies between the casted type of the return value of a |
10 | | // malloc/calloc/realloc call and the operand of any sizeof expressions |
11 | | // contained within its argument(s). |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "clang/AST/StmtVisitor.h" |
16 | | #include "clang/AST/TypeLoc.h" |
17 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
18 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
19 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
22 | | #include "llvm/ADT/SmallString.h" |
23 | | #include "llvm/ADT/iterator_range.h" |
24 | | #include "llvm/Support/raw_ostream.h" |
25 | | |
26 | | using namespace clang; |
27 | | using namespace ento; |
28 | | |
29 | | namespace { |
30 | | |
31 | | typedef std::pair<const TypeSourceInfo *, const CallExpr *> TypeCallPair; |
32 | | typedef llvm::PointerUnion<const Stmt *, const VarDecl *> ExprParent; |
33 | | |
34 | | class CastedAllocFinder |
35 | | : public ConstStmtVisitor<CastedAllocFinder, TypeCallPair> { |
36 | | IdentifierInfo *II_malloc, *II_calloc, *II_realloc; |
37 | | |
38 | | public: |
39 | | struct CallRecord { |
40 | | ExprParent CastedExprParent; |
41 | | const Expr *CastedExpr; |
42 | | const TypeSourceInfo *ExplicitCastType; |
43 | | const CallExpr *AllocCall; |
44 | | |
45 | | CallRecord(ExprParent CastedExprParent, const Expr *CastedExpr, |
46 | | const TypeSourceInfo *ExplicitCastType, |
47 | | const CallExpr *AllocCall) |
48 | 312 | : CastedExprParent(CastedExprParent), CastedExpr(CastedExpr), |
49 | 312 | ExplicitCastType(ExplicitCastType), AllocCall(AllocCall) {} |
50 | | }; |
51 | | |
52 | | typedef std::vector<CallRecord> CallVec; |
53 | | CallVec Calls; |
54 | | |
55 | | CastedAllocFinder(ASTContext *Ctx) : |
56 | 520 | II_malloc(&Ctx->Idents.get("malloc")), |
57 | 520 | II_calloc(&Ctx->Idents.get("calloc")), |
58 | 520 | II_realloc(&Ctx->Idents.get("realloc")) {} |
59 | | |
60 | 7.79k | void VisitChild(ExprParent Parent, const Stmt *S) { |
61 | 7.79k | TypeCallPair AllocCall = Visit(S); |
62 | 7.79k | if (AllocCall.second && AllocCall.second != S351 ) |
63 | 312 | Calls.push_back(CallRecord(Parent, cast<Expr>(S), AllocCall.first, |
64 | 312 | AllocCall.second)); |
65 | 7.79k | } |
66 | | |
67 | 7.59k | void VisitChildren(const Stmt *S) { |
68 | 7.59k | for (const Stmt *Child : S->children()) |
69 | 7.23k | if (Child) |
70 | 7.22k | VisitChild(S, Child); |
71 | 7.59k | } |
72 | | |
73 | 4.29k | TypeCallPair VisitCastExpr(const CastExpr *E) { |
74 | 4.29k | return Visit(E->getSubExpr()); |
75 | 4.29k | } |
76 | | |
77 | 188 | TypeCallPair VisitExplicitCastExpr(const ExplicitCastExpr *E) { |
78 | 188 | return TypeCallPair(E->getTypeInfoAsWritten(), |
79 | 188 | Visit(E->getSubExpr()).second); |
80 | 188 | } |
81 | | |
82 | 35 | TypeCallPair VisitParenExpr(const ParenExpr *E) { |
83 | 35 | return Visit(E->getSubExpr()); |
84 | 35 | } |
85 | | |
86 | 6.13k | TypeCallPair VisitStmt(const Stmt *S) { |
87 | 6.13k | VisitChildren(S); |
88 | 6.13k | return TypeCallPair(); |
89 | 6.13k | } |
90 | | |
91 | 1.46k | TypeCallPair VisitCallExpr(const CallExpr *E) { |
92 | 1.46k | VisitChildren(E); |
93 | 1.46k | const FunctionDecl *FD = E->getDirectCallee(); |
94 | 1.46k | if (FD) { |
95 | 1.45k | IdentifierInfo *II = FD->getIdentifier(); |
96 | 1.45k | if (II == II_malloc || II == II_calloc1.16k || II == II_realloc1.14k ) |
97 | 351 | return TypeCallPair((const TypeSourceInfo *)nullptr, E); |
98 | 1.45k | } |
99 | 1.11k | return TypeCallPair(); |
100 | 1.46k | } |
101 | | |
102 | 716 | TypeCallPair VisitDeclStmt(const DeclStmt *S) { |
103 | 716 | for (const auto *I : S->decls()) |
104 | 776 | if (const VarDecl *VD = dyn_cast<VarDecl>(I)) |
105 | 636 | if (const Expr *Init = VD->getInit()) |
106 | 563 | VisitChild(VD, Init); |
107 | 716 | return TypeCallPair(); |
108 | 716 | } |
109 | | }; |
110 | | |
111 | | class SizeofFinder : public ConstStmtVisitor<SizeofFinder> { |
112 | | public: |
113 | | std::vector<const UnaryExprOrTypeTraitExpr *> Sizeofs; |
114 | | |
115 | 22 | void VisitBinMul(const BinaryOperator *E) { |
116 | 22 | Visit(E->getLHS()); |
117 | 22 | Visit(E->getRHS()); |
118 | 22 | } |
119 | | |
120 | 223 | void VisitImplicitCastExpr(const ImplicitCastExpr *E) { |
121 | 223 | return Visit(E->getSubExpr()); |
122 | 223 | } |
123 | | |
124 | 0 | void VisitParenExpr(const ParenExpr *E) { |
125 | 0 | return Visit(E->getSubExpr()); |
126 | 0 | } |
127 | | |
128 | 103 | void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) { |
129 | 103 | if (E->getKind() != UETT_SizeOf) |
130 | 0 | return; |
131 | | |
132 | 103 | Sizeofs.push_back(E); |
133 | 103 | } |
134 | | }; |
135 | | |
136 | | // Determine if the pointee and sizeof types are compatible. Here |
137 | | // we ignore constness of pointer types. |
138 | 108 | static bool typesCompatible(ASTContext &C, QualType A, QualType B) { |
139 | | // sizeof(void*) is compatible with any other pointer. |
140 | 108 | if (B->isVoidPointerType() && A->getAs<PointerType>()2 ) |
141 | 1 | return true; |
142 | | |
143 | | // sizeof(pointer type) is compatible with void* |
144 | 107 | if (A->isVoidPointerType() && B->getAs<PointerType>()1 ) |
145 | 1 | return true; |
146 | | |
147 | 108 | while (106 true) { |
148 | 108 | A = A.getCanonicalType(); |
149 | 108 | B = B.getCanonicalType(); |
150 | | |
151 | 108 | if (A.getTypePtr() == B.getTypePtr()) |
152 | 91 | return true; |
153 | | |
154 | 17 | if (const PointerType *ptrA = A->getAs<PointerType>()) |
155 | 3 | if (const PointerType *ptrB = B->getAs<PointerType>()) { |
156 | 2 | A = ptrA->getPointeeType(); |
157 | 2 | B = ptrB->getPointeeType(); |
158 | 2 | continue; |
159 | 2 | } |
160 | | |
161 | 15 | break; |
162 | 17 | } |
163 | | |
164 | 15 | return false; |
165 | 106 | } |
166 | | |
167 | 13 | static bool compatibleWithArrayType(ASTContext &C, QualType PT, QualType T) { |
168 | | // Ex: 'int a[10][2]' is compatible with 'int', 'int[2]', 'int[10][2]'. |
169 | 15 | while (const ArrayType *AT = T->getAsArrayTypeUnsafe()) { |
170 | 5 | QualType ElemType = AT->getElementType(); |
171 | 5 | if (typesCompatible(C, PT, AT->getElementType())) |
172 | 3 | return true; |
173 | 2 | T = ElemType; |
174 | 2 | } |
175 | | |
176 | 10 | return false; |
177 | 13 | } |
178 | | |
179 | | class MallocSizeofChecker : public Checker<check::ASTCodeBody> { |
180 | | public: |
181 | | void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, |
182 | 520 | BugReporter &BR) const { |
183 | 520 | AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D); |
184 | 520 | CastedAllocFinder Finder(&BR.getContext()); |
185 | 520 | Finder.Visit(D->getBody()); |
186 | 520 | for (const auto &CallRec : Finder.Calls) { |
187 | 312 | QualType CastedType = CallRec.CastedExpr->getType(); |
188 | 312 | if (!CastedType->isPointerType()) |
189 | 1 | continue; |
190 | 311 | QualType PointeeType = CastedType->getPointeeType(); |
191 | 311 | if (PointeeType->isVoidType()) |
192 | 0 | continue; |
193 | | |
194 | 360 | for (const Expr *Arg : CallRec.AllocCall->arguments())311 { |
195 | 360 | if (!Arg->getType()->isIntegralOrUnscopedEnumerationType()) |
196 | 34 | continue; |
197 | | |
198 | 326 | SizeofFinder SFinder; |
199 | 326 | SFinder.Visit(Arg); |
200 | 326 | if (SFinder.Sizeofs.size() != 1) |
201 | 223 | continue; |
202 | | |
203 | 103 | QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument(); |
204 | | |
205 | 103 | if (typesCompatible(BR.getContext(), PointeeType, SizeofType)) |
206 | 90 | continue; |
207 | | |
208 | | // If the argument to sizeof is an array, the result could be a |
209 | | // pointer to any array element. |
210 | 13 | if (compatibleWithArrayType(BR.getContext(), PointeeType, SizeofType)) |
211 | 3 | continue; |
212 | | |
213 | 10 | const TypeSourceInfo *TSI = nullptr; |
214 | 10 | if (CallRec.CastedExprParent.is<const VarDecl *>()) { |
215 | 10 | TSI = CallRec.CastedExprParent.get<const VarDecl *>() |
216 | 10 | ->getTypeSourceInfo(); |
217 | 10 | } else { |
218 | 0 | TSI = CallRec.ExplicitCastType; |
219 | 0 | } |
220 | | |
221 | 10 | SmallString<64> buf; |
222 | 10 | llvm::raw_svector_ostream OS(buf); |
223 | | |
224 | 10 | OS << "Result of "; |
225 | 10 | const FunctionDecl *Callee = CallRec.AllocCall->getDirectCallee(); |
226 | 10 | if (Callee && Callee->getIdentifier()) |
227 | 10 | OS << '\'' << Callee->getIdentifier()->getName() << '\''; |
228 | 0 | else |
229 | 0 | OS << "call"; |
230 | 10 | OS << " is converted to a pointer of type '" << PointeeType |
231 | 10 | << "', which is incompatible with " |
232 | 10 | << "sizeof operand type '" << SizeofType << "'"; |
233 | 10 | SmallVector<SourceRange, 4> Ranges; |
234 | 10 | Ranges.push_back(CallRec.AllocCall->getCallee()->getSourceRange()); |
235 | 10 | Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange()); |
236 | 10 | if (TSI) |
237 | 10 | Ranges.push_back(TSI->getTypeLoc().getSourceRange()); |
238 | | |
239 | 10 | PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( |
240 | 10 | CallRec.AllocCall->getCallee(), BR.getSourceManager(), ADC); |
241 | | |
242 | 10 | BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch", |
243 | 10 | categories::UnixAPI, OS.str(), L, Ranges); |
244 | 10 | } |
245 | 311 | } |
246 | 520 | } |
247 | | }; |
248 | | |
249 | | } |
250 | | |
251 | 68 | void ento::registerMallocSizeofChecker(CheckerManager &mgr) { |
252 | 68 | mgr.registerChecker<MallocSizeofChecker>(); |
253 | 68 | } |
254 | | |
255 | 140 | bool ento::shouldRegisterMallocSizeofChecker(const CheckerManager &mgr) { |
256 | 140 | return true; |
257 | 140 | } |