llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
Kristof Umann 76a21502fd [analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.

One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.

This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:

class CheckerRegistry;

void registerBuiltinCheckers(CheckerRegistry &registry);

it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.

Differential Revision: https://reviews.llvm.org/D54436

llvm-svn: 349275
2018-12-15 16:23:51 +00:00

253 lines
7.9 KiB
C++

// MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- C++ -*-=//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Reports inconsistencies between the casted type of the return value of a
// malloc/calloc/realloc call and the operand of any sizeof expressions
// contained within its argument(s).
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
typedef std::pair<const TypeSourceInfo *, const CallExpr *> TypeCallPair;
typedef llvm::PointerUnion<const Stmt *, const VarDecl *> ExprParent;
class CastedAllocFinder
: public ConstStmtVisitor<CastedAllocFinder, TypeCallPair> {
IdentifierInfo *II_malloc, *II_calloc, *II_realloc;
public:
struct CallRecord {
ExprParent CastedExprParent;
const Expr *CastedExpr;
const TypeSourceInfo *ExplicitCastType;
const CallExpr *AllocCall;
CallRecord(ExprParent CastedExprParent, const Expr *CastedExpr,
const TypeSourceInfo *ExplicitCastType,
const CallExpr *AllocCall)
: CastedExprParent(CastedExprParent), CastedExpr(CastedExpr),
ExplicitCastType(ExplicitCastType), AllocCall(AllocCall) {}
};
typedef std::vector<CallRecord> CallVec;
CallVec Calls;
CastedAllocFinder(ASTContext *Ctx) :
II_malloc(&Ctx->Idents.get("malloc")),
II_calloc(&Ctx->Idents.get("calloc")),
II_realloc(&Ctx->Idents.get("realloc")) {}
void VisitChild(ExprParent Parent, const Stmt *S) {
TypeCallPair AllocCall = Visit(S);
if (AllocCall.second && AllocCall.second != S)
Calls.push_back(CallRecord(Parent, cast<Expr>(S), AllocCall.first,
AllocCall.second));
}
void VisitChildren(const Stmt *S) {
for (const Stmt *Child : S->children())
if (Child)
VisitChild(S, Child);
}
TypeCallPair VisitCastExpr(const CastExpr *E) {
return Visit(E->getSubExpr());
}
TypeCallPair VisitExplicitCastExpr(const ExplicitCastExpr *E) {
return TypeCallPair(E->getTypeInfoAsWritten(),
Visit(E->getSubExpr()).second);
}
TypeCallPair VisitParenExpr(const ParenExpr *E) {
return Visit(E->getSubExpr());
}
TypeCallPair VisitStmt(const Stmt *S) {
VisitChildren(S);
return TypeCallPair();
}
TypeCallPair VisitCallExpr(const CallExpr *E) {
VisitChildren(E);
const FunctionDecl *FD = E->getDirectCallee();
if (FD) {
IdentifierInfo *II = FD->getIdentifier();
if (II == II_malloc || II == II_calloc || II == II_realloc)
return TypeCallPair((const TypeSourceInfo *)nullptr, E);
}
return TypeCallPair();
}
TypeCallPair VisitDeclStmt(const DeclStmt *S) {
for (const auto *I : S->decls())
if (const VarDecl *VD = dyn_cast<VarDecl>(I))
if (const Expr *Init = VD->getInit())
VisitChild(VD, Init);
return TypeCallPair();
}
};
class SizeofFinder : public ConstStmtVisitor<SizeofFinder> {
public:
std::vector<const UnaryExprOrTypeTraitExpr *> Sizeofs;
void VisitBinMul(const BinaryOperator *E) {
Visit(E->getLHS());
Visit(E->getRHS());
}
void VisitImplicitCastExpr(const ImplicitCastExpr *E) {
return Visit(E->getSubExpr());
}
void VisitParenExpr(const ParenExpr *E) {
return Visit(E->getSubExpr());
}
void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
if (E->getKind() != UETT_SizeOf)
return;
Sizeofs.push_back(E);
}
};
// Determine if the pointee and sizeof types are compatible. Here
// we ignore constness of pointer types.
static bool typesCompatible(ASTContext &C, QualType A, QualType B) {
// sizeof(void*) is compatible with any other pointer.
if (B->isVoidPointerType() && A->getAs<PointerType>())
return true;
while (true) {
A = A.getCanonicalType();
B = B.getCanonicalType();
if (A.getTypePtr() == B.getTypePtr())
return true;
if (const PointerType *ptrA = A->getAs<PointerType>())
if (const PointerType *ptrB = B->getAs<PointerType>()) {
A = ptrA->getPointeeType();
B = ptrB->getPointeeType();
continue;
}
break;
}
return false;
}
static bool compatibleWithArrayType(ASTContext &C, QualType PT, QualType T) {
// Ex: 'int a[10][2]' is compatible with 'int', 'int[2]', 'int[10][2]'.
while (const ArrayType *AT = T->getAsArrayTypeUnsafe()) {
QualType ElemType = AT->getElementType();
if (typesCompatible(C, PT, AT->getElementType()))
return true;
T = ElemType;
}
return false;
}
class MallocSizeofChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D);
CastedAllocFinder Finder(&BR.getContext());
Finder.Visit(D->getBody());
for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(),
e = Finder.Calls.end(); i != e; ++i) {
QualType CastedType = i->CastedExpr->getType();
if (!CastedType->isPointerType())
continue;
QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType();
if (PointeeType->isVoidType())
continue;
for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
continue;
SizeofFinder SFinder;
SFinder.Visit(*ai);
if (SFinder.Sizeofs.size() != 1)
continue;
QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument();
if (typesCompatible(BR.getContext(), PointeeType, SizeofType))
continue;
// If the argument to sizeof is an array, the result could be a
// pointer to any array element.
if (compatibleWithArrayType(BR.getContext(), PointeeType, SizeofType))
continue;
const TypeSourceInfo *TSI = nullptr;
if (i->CastedExprParent.is<const VarDecl *>()) {
TSI =
i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo();
} else {
TSI = i->ExplicitCastType;
}
SmallString<64> buf;
llvm::raw_svector_ostream OS(buf);
OS << "Result of ";
const FunctionDecl *Callee = i->AllocCall->getDirectCallee();
if (Callee && Callee->getIdentifier())
OS << '\'' << Callee->getIdentifier()->getName() << '\'';
else
OS << "call";
OS << " is converted to a pointer of type '"
<< PointeeType.getAsString() << "', which is incompatible with "
<< "sizeof operand type '" << SizeofType.getAsString() << "'";
SmallVector<SourceRange, 4> Ranges;
Ranges.push_back(i->AllocCall->getCallee()->getSourceRange());
Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange());
if (TSI)
Ranges.push_back(TSI->getTypeLoc().getSourceRange());
PathDiagnosticLocation L =
PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
BR.getSourceManager(), ADC);
BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
categories::UnixAPI, OS.str(), L, Ranges);
}
}
}
};
}
void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
mgr.registerChecker<MallocSizeofChecker>();
}