Files
clang-p2996/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Artem Dergachev 3ef5deb3a7 [analyzer] In getSVal() API, disable auto-detection of void type as char type.
This is a follow-up from r314910. When a checker developer attempts to
dereference a location in memory through ProgramState::getSVal(Loc) or
ProgramState::getSVal(const MemRegion *), without specifying the second
optional QualType parameter for the type of the value he tries to find at this
location, the type is auto-detected from location type. If the location
represents a value beyond a void pointer, we thought that auto-detecting the
type as 'char' is a good idea. However, in most practical cases, the correct
behavior would be to specify the type explicitly, as it is available from other
sources, and the few cases where we actually need to take a 'char' are
workarounds rather than an intended behavior. Therefore, try to fail with an
easy-to-understand assertion when asked to read from a void pointer location.

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

llvm-svn: 320451
2017-12-12 02:27:55 +00:00

618 lines
22 KiB
C++

//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines CallAndMessageChecker, a builtin checker that checks for various
// errors of call and objc message expressions.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
struct ChecksFilter {
DefaultBool Check_CallAndMessageUnInitRefArg;
DefaultBool Check_CallAndMessageChecker;
CheckName CheckName_CallAndMessageUnInitRefArg;
CheckName CheckName_CallAndMessageChecker;
};
class CallAndMessageChecker
: public Checker< check::PreStmt<CallExpr>,
check::PreStmt<CXXDeleteExpr>,
check::PreObjCMessage,
check::ObjCMessageNil,
check::PreCall > {
mutable std::unique_ptr<BugType> BT_call_null;
mutable std::unique_ptr<BugType> BT_call_undef;
mutable std::unique_ptr<BugType> BT_cxx_call_null;
mutable std::unique_ptr<BugType> BT_cxx_call_undef;
mutable std::unique_ptr<BugType> BT_call_arg;
mutable std::unique_ptr<BugType> BT_cxx_delete_undef;
mutable std::unique_ptr<BugType> BT_msg_undef;
mutable std::unique_ptr<BugType> BT_objc_prop_undef;
mutable std::unique_ptr<BugType> BT_objc_subscript_undef;
mutable std::unique_ptr<BugType> BT_msg_arg;
mutable std::unique_ptr<BugType> BT_msg_ret;
mutable std::unique_ptr<BugType> BT_call_few_args;
public:
ChecksFilter Filter;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
/// Fill in the return value that results from messaging nil based on the
/// return type and architecture and diagnose if the return value will be
/// garbage.
void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
const Expr *ArgEx, int ArgumentNumber,
bool CheckUninitFields, const CallEvent &Call,
std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl) const;
static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
ExplodedNode *N) const;
void HandleNilReceiver(CheckerContext &C,
ProgramStateRef state,
const ObjCMethodCall &msg) const;
void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
if (!BT)
BT.reset(new BuiltinBug(this, desc));
}
bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
SourceRange ArgRange, const Expr *ArgEx,
std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl, const char *BD,
int ArgumentNumber) const;
};
} // end anonymous namespace
void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
const Expr *BadE) {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
if (BadE) {
R->addRange(BadE->getSourceRange());
if (BadE->isGLValue())
BadE = bugreporter::getDerefExpr(BadE);
bugreporter::trackNullOrUndefValue(N, BadE, *R);
}
C.emitReport(std::move(R));
}
static void describeUninitializedArgumentInCall(const CallEvent &Call,
int ArgumentNumber,
llvm::raw_svector_ostream &Os) {
switch (Call.getKind()) {
case CE_ObjCMessage: {
const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
switch (Msg.getMessageKind()) {
case OCM_Message:
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " argument in message expression is an uninitialized value";
return;
case OCM_PropertyAccess:
assert(Msg.isSetter() && "Getters have no args");
Os << "Argument for property setter is an uninitialized value";
return;
case OCM_Subscript:
if (Msg.isSetter() && (ArgumentNumber == 0))
Os << "Argument for subscript setter is an uninitialized value";
else
Os << "Subscript index is an uninitialized value";
return;
}
llvm_unreachable("Unknown message kind.");
}
case CE_Block:
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " block call argument is an uninitialized value";
return;
default:
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is an uninitialized value";
return;
}
}
bool CallAndMessageChecker::uninitRefOrPointer(
CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,
std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,
int ArgumentNumber) const {
if (!Filter.Check_CallAndMessageUnInitRefArg)
return false;
// No parameter declaration available, i.e. variadic function argument.
if(!ParamDecl)
return false;
// If parameter is declared as pointer to const in function declaration,
// then check if corresponding argument in function call is
// pointing to undefined symbol value (uninitialized memory).
SmallString<200> Buf;
llvm::raw_svector_ostream Os(Buf);
if (ParamDecl->getType()->isPointerType()) {
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is a pointer to uninitialized value";
} else if (ParamDecl->getType()->isReferenceType()) {
Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
<< " function call argument is an uninitialized value";
} else
return false;
if(!ParamDecl->getType()->getPointeeType().isConstQualified())
return false;
if (const MemRegion *SValMemRegion = V.getAsRegion()) {
const ProgramStateRef State = C.getState();
const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);
if (PSV.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
R->addRange(ArgRange);
if (ArgEx) {
bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
}
C.emitReport(std::move(R));
}
return true;
}
}
return false;
}
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SVal V,
SourceRange ArgRange,
const Expr *ArgEx,
int ArgumentNumber,
bool CheckUninitFields,
const CallEvent &Call,
std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl
) const {
const char *BD = "Uninitialized argument value";
if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD,
ArgumentNumber))
return true;
if (V.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
// Generate a report for this bug.
SmallString<200> Buf;
llvm::raw_svector_ostream Os(Buf);
describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
C.emitReport(std::move(R));
}
return true;
}
if (!CheckUninitFields)
return false;
if (Optional<nonloc::LazyCompoundVal> LV =
V.getAs<nonloc::LazyCompoundVal>()) {
class FindUninitializedField {
public:
SmallVector<const FieldDecl *, 10> FieldChain;
private:
StoreManager &StoreMgr;
MemRegionManager &MrMgr;
Store store;
public:
FindUninitializedField(StoreManager &storeMgr,
MemRegionManager &mrMgr, Store s)
: StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
bool Find(const TypedValueRegion *R) {
QualType T = R->getValueType();
if (const RecordType *RT = T->getAsStructureType()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
for (const auto *I : RD->fields()) {
const FieldRegion *FR = MrMgr.getFieldRegion(I, R);
FieldChain.push_back(I);
T = I->getType();
if (T->getAsStructureType()) {
if (Find(FR))
return true;
}
else {
const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
if (V.isUndef())
return true;
}
FieldChain.pop_back();
}
}
return false;
}
};
const LazyCompoundValData *D = LV->getCVData();
FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
C.getSValBuilder().getRegionManager(),
D->getStore());
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";
if (F.FieldChain.size() == 1)
os << " (e.g., field: '" << *F.FieldChain[0] << "')";
else {
os << " (e.g., via the field chain: '";
bool first = true;
for (SmallVectorImpl<const FieldDecl *>::iterator
DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
if (first)
first = false;
else
os << '.';
os << **DI;
}
os << "')";
}
// Generate a report for this bug.
auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
R->addRange(ArgRange);
// FIXME: enhance track back for uninitialized value for arbitrary
// memregions
C.emitReport(std::move(R));
}
return true;
}
}
return false;
}
void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const{
const Expr *Callee = CE->getCallee()->IgnoreParens();
ProgramStateRef State = C.getState();
const LocationContext *LCtx = C.getLocationContext();
SVal L = State->getSVal(Callee, LCtx);
if (L.isUndef()) {
if (!BT_call_undef)
BT_call_undef.reset(new BuiltinBug(
this, "Called function pointer is an uninitialized pointer value"));
emitBadCall(BT_call_undef.get(), C, Callee);
return;
}
ProgramStateRef StNonNull, StNull;
std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
if (!BT_call_null)
BT_call_null.reset(new BuiltinBug(
this, "Called function pointer is null (null dereference)"));
emitBadCall(BT_call_null.get(), C, Callee);
return;
}
C.addTransition(StNonNull);
}
void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
CheckerContext &C) const {
SVal Arg = C.getSVal(DE->getArgument());
if (Arg.isUndef()) {
StringRef Desc;
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
if (!BT_cxx_delete_undef)
BT_cxx_delete_undef.reset(
new BuiltinBug(this, "Uninitialized argument value"));
if (DE->isArrayFormAsWritten())
Desc = "Argument to 'delete[]' is uninitialized";
else
Desc = "Argument to 'delete' is uninitialized";
BugType *BT = BT_cxx_delete_undef.get();
auto R = llvm::make_unique<BugReport>(*BT, Desc, N);
bugreporter::trackNullOrUndefValue(N, DE, *R);
C.emitReport(std::move(R));
return;
}
}
void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
// If this is a call to a C++ method, check if the callee is null or
// undefined.
if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
SVal V = CC->getCXXThisVal();
if (V.isUndef()) {
if (!BT_cxx_call_undef)
BT_cxx_call_undef.reset(
new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
return;
}
ProgramStateRef StNonNull, StNull;
std::tie(StNonNull, StNull) =
State->assume(V.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
if (!BT_cxx_call_null)
BT_cxx_call_null.reset(
new BuiltinBug(this, "Called C++ object pointer is null"));
emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
return;
}
State = StNonNull;
}
const Decl *D = Call.getDecl();
if (D && (isa<FunctionDecl>(D) || isa<BlockDecl>(D))) {
// If we have a function or block declaration, we can make sure we pass
// enough parameters.
unsigned Params = Call.parameters().size();
if (Call.getNumArgs() < Params) {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
LazyInit_BT("Function call with too few arguments", BT_call_few_args);
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
if (isa<FunctionDecl>(D)) {
os << "Function ";
} else {
assert(isa<BlockDecl>(D));
os << "Block ";
}
os << "taking " << Params << " argument"
<< (Params == 1 ? "" : "s") << " is called with fewer ("
<< Call.getNumArgs() << ")";
C.emitReport(
llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N));
}
}
// Don't check for uninitialized field values in arguments if the
// caller has a body that is available and we have the chance to inline it.
// This is a hack, but is a reasonable compromise betweens sometimes warning
// and sometimes not depending on if we decide to inline a function.
const bool checkUninitFields =
!(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
std::unique_ptr<BugType> *BT;
if (isa<ObjCMethodCall>(Call))
BT = &BT_msg_arg;
else
BT = &BT_call_arg;
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
const ParmVarDecl *ParamDecl = nullptr;
if(FD && i < FD->getNumParams())
ParamDecl = FD->getParamDecl(i);
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
Call.getArgExpr(i), i,
checkUninitFields, Call, *BT, ParamDecl))
return;
}
// If we make it here, record our assumptions about the callee.
C.addTransition(State);
}
void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
SVal recVal = msg.getReceiverSVal();
if (recVal.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
BugType *BT = nullptr;
switch (msg.getMessageKind()) {
case OCM_Message:
if (!BT_msg_undef)
BT_msg_undef.reset(new BuiltinBug(this,
"Receiver in message expression "
"is an uninitialized value"));
BT = BT_msg_undef.get();
break;
case OCM_PropertyAccess:
if (!BT_objc_prop_undef)
BT_objc_prop_undef.reset(new BuiltinBug(
this, "Property access on an uninitialized object pointer"));
BT = BT_objc_prop_undef.get();
break;
case OCM_Subscript:
if (!BT_objc_subscript_undef)
BT_objc_subscript_undef.reset(new BuiltinBug(
this, "Subscript access on an uninitialized object pointer"));
BT = BT_objc_subscript_undef.get();
break;
}
assert(BT && "Unknown message kind.");
auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
const ObjCMessageExpr *ME = msg.getOriginExpr();
R->addRange(ME->getReceiverRange());
// FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
if (const Expr *ReceiverE = ME->getInstanceReceiver())
bugreporter::trackNullOrUndefValue(N, ReceiverE, *R);
C.emitReport(std::move(R));
}
return;
}
}
void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
CheckerContext &C) const {
HandleNilReceiver(C, C.getState(), msg);
}
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
const ObjCMethodCall &msg,
ExplodedNode *N) const {
if (!BT_msg_ret)
BT_msg_ret.reset(
new BuiltinBug(this, "Receiver in message expression is 'nil'"));
const ObjCMessageExpr *ME = msg.getOriginExpr();
QualType ResTy = msg.getResultType();
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
os << "The receiver of message '";
ME->getSelector().print(os);
os << "' is nil";
if (ResTy->isReferenceType()) {
os << ", which results in forming a null reference";
} else {
os << " and returns a value of type '";
msg.getResultType().print(os, C.getLangOpts());
os << "' that will be garbage";
}
auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N);
report->addRange(ME->getReceiverRange());
// FIXME: This won't track "self" in messages to super.
if (const Expr *receiver = ME->getInstanceReceiver()) {
bugreporter::trackNullOrUndefValue(N, receiver, *report);
}
C.emitReport(std::move(report));
}
static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
return (triple.getVendor() == llvm::Triple::Apple &&
(triple.isiOS() || triple.isWatchOS() ||
!triple.isMacOSXVersionLT(10,5)));
}
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
ProgramStateRef state,
const ObjCMethodCall &Msg) const {
ASTContext &Ctx = C.getASTContext();
static CheckerProgramPointTag Tag(this, "NilReceiver");
// Check the return type of the message expression. A message to nil will
// return different values depending on the return type and the architecture.
QualType RetTy = Msg.getResultType();
CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
const LocationContext *LCtx = C.getLocationContext();
if (CanRetTy->isStructureOrClassType()) {
// Structure returns are safe since the compiler zeroes them out.
SVal V = C.getSValBuilder().makeZeroVal(RetTy);
C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag);
return;
}
// Other cases: check if sizeof(return type) > sizeof(void*)
if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
.isConsumedExpr(Msg.getOriginExpr())) {
// Compute: sizeof(void *) and sizeof(return type)
const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
if (CanRetTy.getTypePtr()->isReferenceType()||
(voidPtrSize < returnTypeSize &&
!(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
(Ctx.FloatTy == CanRetTy ||
Ctx.DoubleTy == CanRetTy ||
Ctx.LongDoubleTy == CanRetTy ||
Ctx.LongLongTy == CanRetTy ||
Ctx.UnsignedLongLongTy == CanRetTy)))) {
if (ExplodedNode *N = C.generateErrorNode(state, &Tag))
emitNilReceiverBug(C, Msg, N);
return;
}
// Handle the safe cases where the return value is 0 if the
// receiver is nil.
//
// FIXME: For now take the conservative approach that we only
// return null values if we *know* that the receiver is nil.
// This is because we can have surprises like:
//
// ... = [[NSScreens screens] objectAtIndex:0];
//
// What can happen is that [... screens] could return nil, but
// it most likely isn't nil. We should assume the semantics
// of this case unless we have *a lot* more knowledge.
//
SVal V = C.getSValBuilder().makeZeroVal(RetTy);
C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag);
return;
}
C.addTransition(state);
}
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
CallAndMessageChecker *Checker = \
mgr.registerChecker<CallAndMessageChecker>(); \
Checker->Filter.Check_##name = true; \
Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \
}
REGISTER_CHECKER(CallAndMessageUnInitRefArg)
REGISTER_CHECKER(CallAndMessageChecker)