clang  14.0.0git
MallocSizeofChecker.cpp
Go to the documentation of this file.
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 
16 #include "clang/AST/StmtVisitor.h"
17 #include "clang/AST/TypeLoc.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 
30 typedef std::pair<const TypeSourceInfo *, const CallExpr *> TypeCallPair;
31 typedef llvm::PointerUnion<const Stmt *, const VarDecl *> ExprParent;
32 
33 class CastedAllocFinder
34  : public ConstStmtVisitor<CastedAllocFinder, TypeCallPair> {
35  IdentifierInfo *II_malloc, *II_calloc, *II_realloc;
36 
37 public:
38  struct CallRecord {
39  ExprParent CastedExprParent;
40  const Expr *CastedExpr;
41  const TypeSourceInfo *ExplicitCastType;
42  const CallExpr *AllocCall;
43 
44  CallRecord(ExprParent CastedExprParent, const Expr *CastedExpr,
45  const TypeSourceInfo *ExplicitCastType,
46  const CallExpr *AllocCall)
47  : CastedExprParent(CastedExprParent), CastedExpr(CastedExpr),
48  ExplicitCastType(ExplicitCastType), AllocCall(AllocCall) {}
49  };
50 
51  typedef std::vector<CallRecord> CallVec;
52  CallVec Calls;
53 
54  CastedAllocFinder(ASTContext *Ctx) :
55  II_malloc(&Ctx->Idents.get("malloc")),
56  II_calloc(&Ctx->Idents.get("calloc")),
57  II_realloc(&Ctx->Idents.get("realloc")) {}
58 
59  void VisitChild(ExprParent Parent, const Stmt *S) {
60  TypeCallPair AllocCall = Visit(S);
61  if (AllocCall.second && AllocCall.second != S)
62  Calls.push_back(CallRecord(Parent, cast<Expr>(S), AllocCall.first,
63  AllocCall.second));
64  }
65 
66  void VisitChildren(const Stmt *S) {
67  for (const Stmt *Child : S->children())
68  if (Child)
69  VisitChild(S, Child);
70  }
71 
72  TypeCallPair VisitCastExpr(const CastExpr *E) {
73  return Visit(E->getSubExpr());
74  }
75 
76  TypeCallPair VisitExplicitCastExpr(const ExplicitCastExpr *E) {
77  return TypeCallPair(E->getTypeInfoAsWritten(),
78  Visit(E->getSubExpr()).second);
79  }
80 
81  TypeCallPair VisitParenExpr(const ParenExpr *E) {
82  return Visit(E->getSubExpr());
83  }
84 
85  TypeCallPair VisitStmt(const Stmt *S) {
86  VisitChildren(S);
87  return TypeCallPair();
88  }
89 
90  TypeCallPair VisitCallExpr(const CallExpr *E) {
91  VisitChildren(E);
92  const FunctionDecl *FD = E->getDirectCallee();
93  if (FD) {
94  IdentifierInfo *II = FD->getIdentifier();
95  if (II == II_malloc || II == II_calloc || II == II_realloc)
96  return TypeCallPair((const TypeSourceInfo *)nullptr, E);
97  }
98  return TypeCallPair();
99  }
100 
101  TypeCallPair VisitDeclStmt(const DeclStmt *S) {
102  for (const auto *I : S->decls())
103  if (const VarDecl *VD = dyn_cast<VarDecl>(I))
104  if (const Expr *Init = VD->getInit())
105  VisitChild(VD, Init);
106  return TypeCallPair();
107  }
108 };
109 
110 class SizeofFinder : public ConstStmtVisitor<SizeofFinder> {
111 public:
112  std::vector<const UnaryExprOrTypeTraitExpr *> Sizeofs;
113 
114  void VisitBinMul(const BinaryOperator *E) {
115  Visit(E->getLHS());
116  Visit(E->getRHS());
117  }
118 
119  void VisitImplicitCastExpr(const ImplicitCastExpr *E) {
120  return Visit(E->getSubExpr());
121  }
122 
123  void VisitParenExpr(const ParenExpr *E) {
124  return Visit(E->getSubExpr());
125  }
126 
127  void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
128  if (E->getKind() != UETT_SizeOf)
129  return;
130 
131  Sizeofs.push_back(E);
132  }
133 };
134 
135 // Determine if the pointee and sizeof types are compatible. Here
136 // we ignore constness of pointer types.
137 static bool typesCompatible(ASTContext &C, QualType A, QualType B) {
138  // sizeof(void*) is compatible with any other pointer.
139  if (B->isVoidPointerType() && A->getAs<PointerType>())
140  return true;
141 
142  // sizeof(pointer type) is compatible with void*
143  if (A->isVoidPointerType() && B->getAs<PointerType>())
144  return true;
145 
146  while (true) {
147  A = A.getCanonicalType();
148  B = B.getCanonicalType();
149 
150  if (A.getTypePtr() == B.getTypePtr())
151  return true;
152 
153  if (const PointerType *ptrA = A->getAs<PointerType>())
154  if (const PointerType *ptrB = B->getAs<PointerType>()) {
155  A = ptrA->getPointeeType();
156  B = ptrB->getPointeeType();
157  continue;
158  }
159 
160  break;
161  }
162 
163  return false;
164 }
165 
166 static bool compatibleWithArrayType(ASTContext &C, QualType PT, QualType T) {
167  // Ex: 'int a[10][2]' is compatible with 'int', 'int[2]', 'int[10][2]'.
168  while (const ArrayType *AT = T->getAsArrayTypeUnsafe()) {
169  QualType ElemType = AT->getElementType();
170  if (typesCompatible(C, PT, AT->getElementType()))
171  return true;
172  T = ElemType;
173  }
174 
175  return false;
176 }
177 
178 class MallocSizeofChecker : public Checker<check::ASTCodeBody> {
179 public:
180  void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
181  BugReporter &BR) const {
182  AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D);
183  CastedAllocFinder Finder(&BR.getContext());
184  Finder.Visit(D->getBody());
185  for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(),
186  e = Finder.Calls.end(); i != e; ++i) {
187  QualType CastedType = i->CastedExpr->getType();
188  if (!CastedType->isPointerType())
189  continue;
190  QualType PointeeType = CastedType->getPointeeType();
191  if (PointeeType->isVoidType())
192  continue;
193 
194  for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
195  ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
196  if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
197  continue;
198 
199  SizeofFinder SFinder;
200  SFinder.Visit(*ai);
201  if (SFinder.Sizeofs.size() != 1)
202  continue;
203 
204  QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument();
205 
206  if (typesCompatible(BR.getContext(), PointeeType, SizeofType))
207  continue;
208 
209  // If the argument to sizeof is an array, the result could be a
210  // pointer to any array element.
211  if (compatibleWithArrayType(BR.getContext(), PointeeType, SizeofType))
212  continue;
213 
214  const TypeSourceInfo *TSI = nullptr;
215  if (i->CastedExprParent.is<const VarDecl *>()) {
216  TSI =
217  i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo();
218  } else {
219  TSI = i->ExplicitCastType;
220  }
221 
222  SmallString<64> buf;
223  llvm::raw_svector_ostream OS(buf);
224 
225  OS << "Result of ";
226  const FunctionDecl *Callee = i->AllocCall->getDirectCallee();
227  if (Callee && Callee->getIdentifier())
228  OS << '\'' << Callee->getIdentifier()->getName() << '\'';
229  else
230  OS << "call";
231  OS << " is converted to a pointer of type '"
232  << PointeeType.getAsString() << "', which is incompatible with "
233  << "sizeof operand type '" << SizeofType.getAsString() << "'";
235  Ranges.push_back(i->AllocCall->getCallee()->getSourceRange());
236  Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange());
237  if (TSI)
238  Ranges.push_back(TSI->getTypeLoc().getSourceRange());
239 
240  PathDiagnosticLocation L =
241  PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
242  BR.getSourceManager(), ADC);
243 
244  BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
245  categories::UnixAPI, OS.str(), L, Ranges);
246  }
247  }
248  }
249 };
250 
251 }
252 
253 void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
254  mgr.registerChecker<MallocSizeofChecker>();
255 }
256 
257 bool ento::shouldRegisterMallocSizeofChecker(const CheckerManager &mgr) {
258  return true;
259 }
clang::Type::getAsArrayTypeUnsafe
const ArrayType * getAsArrayTypeUnsafe() const
A variant of getAs<> for array types which silently discards qualifiers from the outermost type.
Definition: Type.h:7212
clang::Type::isVoidPointerType
bool isVoidPointerType() const
Definition: Type.cpp:589
llvm::SmallVector
Definition: LLVM.h:38
clang::CastExpr::getSubExpr
Expr * getSubExpr()
Definition: Expr.h:3524
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:673
clang::AnalysisDeclContext
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Definition: AnalysisDeclContext.h:72
clang::QualType::getCanonicalType
QualType getCanonicalType() const
Definition: Type.h:6463
clang::QualType::getAsString
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition: Type.h:1015
clang::Type::isVoidType
bool isVoidType() const
Definition: Type.h:6955
clang::UnaryExprOrTypeTraitExpr
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition: Expr.h:2543
clang::CallExpr::getDirectCallee
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:2965
BuiltinCheckerRegistration.h
clang::ParenExpr::getSubExpr
const Expr * getSubExpr() const
Definition: Expr.h:2121
clang::BinaryOperator
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3803
clang::ExplicitCastExpr::getTypeInfoAsWritten
TypeSourceInfo * getTypeInfoAsWritten() const
getTypeInfoAsWritten - Returns the type source info for the type that this expression is casting to.
Definition: Expr.h:3715
clang::syntax::NodeRole::Callee
@ Callee
CheckerManager.h
BugReporter.h
clang::ASTContext
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:212
clang::Stmt::CastIterator
Iterator for iterating over Stmt * arrays that contain only T *.
Definition: Stmt.h:1119
clang::ArrayType
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition: Type.h:2883
clang::Type::getAs
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:7161
llvm::SmallString
Definition: LLVM.h:37
clang::VarDecl
Represents a variable declaration or definition.
Definition: Decl.h:876
clang::Type::getPointeeType
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:625
clang::Type::isPointerType
bool isPointerType() const
Definition: Type.h:6672
clang::NamedDecl::getIdentifier
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:270
clang::BinaryOperator::getLHS
Expr * getLHS() const
Definition: Expr.h:3852
clang::ParenExpr
ParenExpr - This represents a parethesized expression, e.g.
Definition: Expr.h:2106
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:89
StmtVisitor.h
clang::ConstStmtVisitor
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:193
clang::IdentifierInfo
One of these records is kept for each identifier that is lexed.
Definition: IdentifierTable.h:84
clang::DeclStmt
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1292
clang::Decl::getBody
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition: DeclBase.h:1010
Checker.h
clang::PointerType
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition: Type.h:2640
clang::UnaryExprOrTypeTraitExpr::getKind
UnaryExprOrTypeTrait getKind() const
Definition: Expr.h:2575
clang
Definition: CalledOnceCheck.h:17
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:69
clang::BinaryOperator::getRHS
Expr * getRHS() const
Definition: Expr.h:3854
clang::ento::PathDiagnosticLocation::createBegin
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Definition: PathDiagnostic.cpp:580
clang::TypeSourceInfo
A container of type source information.
Definition: Type.h:6395
clang::ImplicitCastExpr
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3618
clang::TypeLoc::getSourceRange
SourceRange getSourceRange() const LLVM_READONLY
Get the full source range.
Definition: TypeLoc.h:152
clang::QualType::getTypePtr
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:6424
Parent
NodeId Parent
Definition: ASTDiff.cpp:192
clang::Expr
This represents one expression.
Definition: Expr.h:109
AnalysisManager.h
clang::CastExpr
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3473
clang::ento::categories::UnixAPI
const char *const UnixAPI
Definition: CommonBugCategories.cpp:21
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1856
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2795
clang::ExplicitCastExpr
ExplicitCastExpr - An explicit cast written in the source code.
Definition: Expr.h:3695
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
TypeLoc.h
clang::TypeSourceInfo::getTypeLoc
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
Definition: TypeLoc.h:244