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