1 // MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- C++ -*-=//
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
7 //===----------------------------------------------------------------------===//
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).
13 //===----------------------------------------------------------------------===//
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"
26 using namespace clang
;
31 typedef std::pair
<const TypeSourceInfo
*, const CallExpr
*> TypeCallPair
;
32 typedef llvm::PointerUnion
<const Stmt
*, const VarDecl
*> ExprParent
;
34 class CastedAllocFinder
35 : public ConstStmtVisitor
<CastedAllocFinder
, TypeCallPair
> {
36 IdentifierInfo
*II_malloc
, *II_calloc
, *II_realloc
;
40 ExprParent CastedExprParent
;
41 const Expr
*CastedExpr
;
42 const TypeSourceInfo
*ExplicitCastType
;
43 const CallExpr
*AllocCall
;
45 CallRecord(ExprParent CastedExprParent
, const Expr
*CastedExpr
,
46 const TypeSourceInfo
*ExplicitCastType
,
47 const CallExpr
*AllocCall
)
48 : CastedExprParent(CastedExprParent
), CastedExpr(CastedExpr
),
49 ExplicitCastType(ExplicitCastType
), AllocCall(AllocCall
) {}
52 typedef std::vector
<CallRecord
> CallVec
;
55 CastedAllocFinder(ASTContext
*Ctx
) :
56 II_malloc(&Ctx
->Idents
.get("malloc")),
57 II_calloc(&Ctx
->Idents
.get("calloc")),
58 II_realloc(&Ctx
->Idents
.get("realloc")) {}
60 void VisitChild(ExprParent Parent
, const Stmt
*S
) {
61 TypeCallPair AllocCall
= Visit(S
);
62 if (AllocCall
.second
&& AllocCall
.second
!= S
)
63 Calls
.push_back(CallRecord(Parent
, cast
<Expr
>(S
), AllocCall
.first
,
67 void VisitChildren(const Stmt
*S
) {
68 for (const Stmt
*Child
: S
->children())
73 TypeCallPair
VisitCastExpr(const CastExpr
*E
) {
74 return Visit(E
->getSubExpr());
77 TypeCallPair
VisitExplicitCastExpr(const ExplicitCastExpr
*E
) {
78 return TypeCallPair(E
->getTypeInfoAsWritten(),
79 Visit(E
->getSubExpr()).second
);
82 TypeCallPair
VisitParenExpr(const ParenExpr
*E
) {
83 return Visit(E
->getSubExpr());
86 TypeCallPair
VisitStmt(const Stmt
*S
) {
88 return TypeCallPair();
91 TypeCallPair
VisitCallExpr(const CallExpr
*E
) {
93 const FunctionDecl
*FD
= E
->getDirectCallee();
95 IdentifierInfo
*II
= FD
->getIdentifier();
96 if (II
== II_malloc
|| II
== II_calloc
|| II
== II_realloc
)
97 return TypeCallPair((const TypeSourceInfo
*)nullptr, E
);
99 return TypeCallPair();
102 TypeCallPair
VisitDeclStmt(const DeclStmt
*S
) {
103 for (const auto *I
: S
->decls())
104 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(I
))
105 if (const Expr
*Init
= VD
->getInit())
106 VisitChild(VD
, Init
);
107 return TypeCallPair();
111 class SizeofFinder
: public ConstStmtVisitor
<SizeofFinder
> {
113 std::vector
<const UnaryExprOrTypeTraitExpr
*> Sizeofs
;
115 void VisitBinMul(const BinaryOperator
*E
) {
120 void VisitImplicitCastExpr(const ImplicitCastExpr
*E
) {
121 return Visit(E
->getSubExpr());
124 void VisitParenExpr(const ParenExpr
*E
) {
125 return Visit(E
->getSubExpr());
128 void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr
*E
) {
129 if (E
->getKind() != UETT_SizeOf
)
132 Sizeofs
.push_back(E
);
136 // Determine if the pointee and sizeof types are compatible. Here
137 // we ignore constness of pointer types.
138 static bool typesCompatible(ASTContext
&C
, QualType A
, QualType B
) {
139 // sizeof(void*) is compatible with any other pointer.
140 if (B
->isVoidPointerType() && A
->getAs
<PointerType
>())
143 // sizeof(pointer type) is compatible with void*
144 if (A
->isVoidPointerType() && B
->getAs
<PointerType
>())
148 A
= A
.getCanonicalType();
149 B
= B
.getCanonicalType();
151 if (A
.getTypePtr() == B
.getTypePtr())
154 if (const PointerType
*ptrA
= A
->getAs
<PointerType
>())
155 if (const PointerType
*ptrB
= B
->getAs
<PointerType
>()) {
156 A
= ptrA
->getPointeeType();
157 B
= ptrB
->getPointeeType();
167 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 while (const ArrayType
*AT
= T
->getAsArrayTypeUnsafe()) {
170 QualType ElemType
= AT
->getElementType();
171 if (typesCompatible(C
, PT
, AT
->getElementType()))
179 class MallocSizeofChecker
: public Checker
<check::ASTCodeBody
> {
181 void checkASTCodeBody(const Decl
*D
, AnalysisManager
& mgr
,
182 BugReporter
&BR
) const {
183 AnalysisDeclContext
*ADC
= mgr
.getAnalysisDeclContext(D
);
184 CastedAllocFinder
Finder(&BR
.getContext());
185 Finder
.Visit(D
->getBody());
186 for (const auto &CallRec
: Finder
.Calls
) {
187 QualType CastedType
= CallRec
.CastedExpr
->getType();
188 if (!CastedType
->isPointerType())
190 QualType PointeeType
= CastedType
->getPointeeType();
191 if (PointeeType
->isVoidType())
194 for (const Expr
*Arg
: CallRec
.AllocCall
->arguments()) {
195 if (!Arg
->getType()->isIntegralOrUnscopedEnumerationType())
198 SizeofFinder SFinder
;
200 if (SFinder
.Sizeofs
.size() != 1)
203 QualType SizeofType
= SFinder
.Sizeofs
[0]->getTypeOfArgument();
205 if (typesCompatible(BR
.getContext(), PointeeType
, SizeofType
))
208 // If the argument to sizeof is an array, the result could be a
209 // pointer to any array element.
210 if (compatibleWithArrayType(BR
.getContext(), PointeeType
, SizeofType
))
213 const TypeSourceInfo
*TSI
= nullptr;
214 if (CallRec
.CastedExprParent
.is
<const VarDecl
*>()) {
215 TSI
= CallRec
.CastedExprParent
.get
<const VarDecl
*>()
216 ->getTypeSourceInfo();
218 TSI
= CallRec
.ExplicitCastType
;
222 llvm::raw_svector_ostream
OS(buf
);
225 const FunctionDecl
*Callee
= CallRec
.AllocCall
->getDirectCallee();
226 if (Callee
&& Callee
->getIdentifier())
227 OS
<< '\'' << Callee
->getIdentifier()->getName() << '\'';
230 OS
<< " is converted to a pointer of type '" << PointeeType
231 << "', which is incompatible with "
232 << "sizeof operand type '" << SizeofType
<< "'";
233 SmallVector
<SourceRange
, 4> Ranges
;
234 Ranges
.push_back(CallRec
.AllocCall
->getCallee()->getSourceRange());
235 Ranges
.push_back(SFinder
.Sizeofs
[0]->getSourceRange());
237 Ranges
.push_back(TSI
->getTypeLoc().getSourceRange());
239 PathDiagnosticLocation L
= PathDiagnosticLocation::createBegin(
240 CallRec
.AllocCall
->getCallee(), BR
.getSourceManager(), ADC
);
242 BR
.EmitBasicReport(D
, this, "Allocator sizeof operand mismatch",
243 categories::UnixAPI
, OS
.str(), L
, Ranges
);
251 void ento::registerMallocSizeofChecker(CheckerManager
&mgr
) {
252 mgr
.registerChecker
<MallocSizeofChecker
>();
255 bool ento::shouldRegisterMallocSizeofChecker(const CheckerManager
&mgr
) {