clang  9.0.0svn
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  while (true) {
143  A = A.getCanonicalType();
144  B = B.getCanonicalType();
145 
146  if (A.getTypePtr() == B.getTypePtr())
147  return true;
148 
149  if (const PointerType *ptrA = A->getAs<PointerType>())
150  if (const PointerType *ptrB = B->getAs<PointerType>()) {
151  A = ptrA->getPointeeType();
152  B = ptrB->getPointeeType();
153  continue;
154  }
155 
156  break;
157  }
158 
159  return false;
160 }
161 
162 static bool compatibleWithArrayType(ASTContext &C, QualType PT, QualType T) {
163  // Ex: 'int a[10][2]' is compatible with 'int', 'int[2]', 'int[10][2]'.
164  while (const ArrayType *AT = T->getAsArrayTypeUnsafe()) {
165  QualType ElemType = AT->getElementType();
166  if (typesCompatible(C, PT, AT->getElementType()))
167  return true;
168  T = ElemType;
169  }
170 
171  return false;
172 }
173 
174 class MallocSizeofChecker : public Checker<check::ASTCodeBody> {
175 public:
176  void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
177  BugReporter &BR) const {
178  AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D);
179  CastedAllocFinder Finder(&BR.getContext());
180  Finder.Visit(D->getBody());
181  for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(),
182  e = Finder.Calls.end(); i != e; ++i) {
183  QualType CastedType = i->CastedExpr->getType();
184  if (!CastedType->isPointerType())
185  continue;
186  QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType();
187  if (PointeeType->isVoidType())
188  continue;
189 
190  for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
191  ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
192  if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
193  continue;
194 
195  SizeofFinder SFinder;
196  SFinder.Visit(*ai);
197  if (SFinder.Sizeofs.size() != 1)
198  continue;
199 
200  QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument();
201 
202  if (typesCompatible(BR.getContext(), PointeeType, SizeofType))
203  continue;
204 
205  // If the argument to sizeof is an array, the result could be a
206  // pointer to any array element.
207  if (compatibleWithArrayType(BR.getContext(), PointeeType, SizeofType))
208  continue;
209 
210  const TypeSourceInfo *TSI = nullptr;
211  if (i->CastedExprParent.is<const VarDecl *>()) {
212  TSI =
213  i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo();
214  } else {
215  TSI = i->ExplicitCastType;
216  }
217 
218  SmallString<64> buf;
219  llvm::raw_svector_ostream OS(buf);
220 
221  OS << "Result of ";
222  const FunctionDecl *Callee = i->AllocCall->getDirectCallee();
223  if (Callee && Callee->getIdentifier())
224  OS << '\'' << Callee->getIdentifier()->getName() << '\'';
225  else
226  OS << "call";
227  OS << " is converted to a pointer of type '"
228  << PointeeType.getAsString() << "', which is incompatible with "
229  << "sizeof operand type '" << SizeofType.getAsString() << "'";
231  Ranges.push_back(i->AllocCall->getCallee()->getSourceRange());
232  Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange());
233  if (TSI)
234  Ranges.push_back(TSI->getTypeLoc().getSourceRange());
235 
236  PathDiagnosticLocation L =
237  PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
238  BR.getSourceManager(), ADC);
239 
240  BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
241  categories::UnixAPI, OS.str(), L, Ranges);
242  }
243  }
244  }
245 };
246 
247 }
248 
249 void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
250  mgr.registerChecker<MallocSizeofChecker>();
251 }
252 
253 bool ento::shouldRegisterMallocSizeofChecker(const LangOptions &LO) {
254  return true;
255 }
Represents a function declaration or definition.
Definition: Decl.h:1737
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition: Type.h:2547
A (possibly-)qualified type.
Definition: Type.h:639
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:192
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:981
Stmt - This represents one statement.
Definition: Stmt.h:65
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
Definition: Type.cpp:505
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
ParenExpr - This represents a parethesized expression, e.g.
Definition: Expr.h:1877
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition: Type.h:2822
TypeSourceInfo * getTypeInfoAsWritten() const
getTypeInfoAsWritten - Returns the type source info for the type that this expression is casting to...
Definition: Expr.h:3234
A container of type source information.
Definition: Decl.h:86
Represents a variable declaration or definition.
Definition: Decl.h:812
const T * getAs() const
Member-template getAs<specific type>&#39;.
Definition: Type.h:6766
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:269
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:154
AnalysisDeclContext contains the context data for the function or method under analysis.
Expr * getSubExpr()
Definition: Expr.h:3088
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
const ArrayType * getAsArrayTypeUnsafe() const
A variant of getAs<> for array types which silently discards qualifiers from the outermost type...
Definition: Type.h:6815
child_range children()
Definition: Stmt.cpp:212
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3313
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:6082
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3036
NodeId Parent
Definition: ASTDiff.cpp:191
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand...
Definition: Expr.h:2255
This represents one expression.
Definition: Expr.h:108
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:2565
Defines the clang::TypeLoc interface and its subclasses.
const Expr * getSubExpr() const
Definition: Expr.h:1893
bool isVoidPointerType() const
Definition: Type.cpp:469
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
QualType getCanonicalType() const
Definition: Type.h:6121
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
Definition: Stmt.h:1153
UnaryExprOrTypeTrait getKind() const
Definition: Expr.h:2286
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3153
Expr * getLHS() const
Definition: Expr.h:3353
StringRef getName() const
Return the actual identifier string.
Dataflow Directional Tag Classes.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition: Type.h:972
ExplicitCastExpr - An explicit cast written in the source code.
Definition: Expr.h:3216
SourceRange getSourceRange() const LLVM_READONLY
Get the full source range.
Definition: TypeLoc.h:150
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
Iterator for iterating over Stmt * arrays that contain only T *.
Definition: Stmt.h:1002
decl_range decls()
Definition: Stmt.h:1196
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
Definition: TypeLoc.h:237
bool isVoidType() const
Definition: Type.h:6558
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2429
Expr * getRHS() const
Definition: Expr.h:3355
bool isPointerType() const
Definition: Type.h:6306