Previously, the analyzer used isIntegerType() everywhere, which uses the C definition of "integer". The C++ predicate with the same behavior is isIntegerOrUnscopedEnumerationType(). However, the analyzer is /really/ using this to ask if it's some sort of "integrally representable" type, i.e. it should include C++11 scoped enumerations as well. hasIntegerRepresentation() sounds like the right predicate, but that includes vectors, which the analyzer represents by its elements. This commit audits all uses of isIntegerType() and replaces them with the general isIntegerOrEnumerationType(), except in some specific cases where it makes sense to exclude scoped enumerations, or any enumerations. These cases now use isIntegerOrUnscopedEnumerationType() and getAs<BuiltinType>() plus BuiltinType::isInteger(). isIntegerType() is hereby banned in the analyzer - lib/StaticAnalysis and include/clang/StaticAnalysis. :-) Fixes real assertion failures. PR15703 / <rdar://problem/12350701> llvm-svn: 179081
253 lines
7.9 KiB
C++
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 "ClangSACheckers.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 (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end();
|
|
I!=E; ++I)
|
|
if (const Stmt *child = *I)
|
|
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 *)0, E);
|
|
}
|
|
return TypeCallPair();
|
|
}
|
|
|
|
TypeCallPair VisitDeclStmt(const DeclStmt *S) {
|
|
for (DeclStmt::const_decl_iterator I = S->decl_begin(), E = S->decl_end();
|
|
I!=E; ++I)
|
|
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) {
|
|
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 = 0;
|
|
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, "Allocator sizeof operand mismatch",
|
|
categories::UnixAPI,
|
|
OS.str(),
|
|
L, Ranges.data(), Ranges.size());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
|
|
mgr.registerChecker<MallocSizeofChecker>();
|
|
}
|