forked from OSchip/llvm-project
[analyzer] Model another special-case kind of cast for OSObject RetainCountChecker
Differential Revision: https://reviews.llvm.org/D56951 llvm-svn: 351864
This commit is contained in:
parent
b8ecd7e49b
commit
db0c66eeb0
|
@ -677,6 +677,9 @@ public:
|
||||||
// Function returns the first argument.
|
// Function returns the first argument.
|
||||||
Identity,
|
Identity,
|
||||||
|
|
||||||
|
// Function returns "this" argument.
|
||||||
|
IdentityThis,
|
||||||
|
|
||||||
// Function either returns zero, or the input parameter.
|
// Function either returns zero, or the input parameter.
|
||||||
IdentityOrZero
|
IdentityOrZero
|
||||||
};
|
};
|
||||||
|
|
|
@ -849,7 +849,6 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
||||||
// Get the callee. We're only interested in simple C functions.
|
|
||||||
ProgramStateRef state = C.getState();
|
ProgramStateRef state = C.getState();
|
||||||
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
||||||
if (!FD)
|
if (!FD)
|
||||||
|
@ -874,18 +873,27 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
|
||||||
|
|
||||||
// Bind the return value.
|
// Bind the return value.
|
||||||
if (BSmr == BehaviorSummary::Identity ||
|
if (BSmr == BehaviorSummary::Identity ||
|
||||||
BSmr == BehaviorSummary::IdentityOrZero) {
|
BSmr == BehaviorSummary::IdentityOrZero ||
|
||||||
SVal RetVal = state->getSVal(CE->getArg(0), LCtx);
|
BSmr == BehaviorSummary::IdentityThis) {
|
||||||
|
|
||||||
|
const Expr *BindReturnTo =
|
||||||
|
(BSmr == BehaviorSummary::IdentityThis)
|
||||||
|
? cast<CXXMemberCallExpr>(CE)->getImplicitObjectArgument()
|
||||||
|
: CE->getArg(0);
|
||||||
|
SVal RetVal = state->getSVal(BindReturnTo, LCtx);
|
||||||
|
|
||||||
// If the receiver is unknown or the function has
|
// If the receiver is unknown or the function has
|
||||||
// 'rc_ownership_trusted_implementation' annotate attribute, conjure a
|
// 'rc_ownership_trusted_implementation' annotate attribute, conjure a
|
||||||
// return value.
|
// return value.
|
||||||
|
// FIXME: this branch is very strange.
|
||||||
if (RetVal.isUnknown() ||
|
if (RetVal.isUnknown() ||
|
||||||
(hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
|
(hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
|
||||||
SValBuilder &SVB = C.getSValBuilder();
|
SValBuilder &SVB = C.getSValBuilder();
|
||||||
RetVal =
|
RetVal =
|
||||||
SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
|
SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind the value.
|
||||||
state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
|
state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
|
||||||
|
|
||||||
if (BSmr == BehaviorSummary::IdentityOrZero) {
|
if (BSmr == BehaviorSummary::IdentityOrZero) {
|
||||||
|
|
|
@ -152,6 +152,10 @@ static bool isOSObjectDynamicCast(StringRef S) {
|
||||||
return S == "safeMetaCast";
|
return S == "safeMetaCast";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isOSObjectThisCast(StringRef S) {
|
||||||
|
return S == "metaCast";
|
||||||
|
}
|
||||||
|
|
||||||
static bool isOSIteratorSubclass(const Decl *D) {
|
static bool isOSIteratorSubclass(const Decl *D) {
|
||||||
return isSubclass(D, "OSIterator");
|
return isSubclass(D, "OSIterator");
|
||||||
}
|
}
|
||||||
|
@ -219,13 +223,13 @@ RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
|
||||||
const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
|
const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
|
||||||
if (PD && isOSObjectSubclass(PD)) {
|
if (PD && isOSObjectSubclass(PD)) {
|
||||||
if (const IdentifierInfo *II = FD->getIdentifier()) {
|
if (const IdentifierInfo *II = FD->getIdentifier()) {
|
||||||
if (isOSObjectDynamicCast(II->getName()))
|
StringRef FuncName = II->getName();
|
||||||
|
if (isOSObjectDynamicCast(FuncName) || isOSObjectThisCast(FuncName))
|
||||||
return getDefaultSummary();
|
return getDefaultSummary();
|
||||||
|
|
||||||
// All objects returned with functions *not* starting with
|
// All objects returned with functions *not* starting with
|
||||||
// get, or iterators, are returned at +1.
|
// get, or iterators, are returned at +1.
|
||||||
if ((!II->getName().startswith("get") &&
|
if ((!FuncName.startswith("get") && !FuncName.startswith("Get")) ||
|
||||||
!II->getName().startswith("Get")) ||
|
|
||||||
isOSIteratorSubclass(PD)) {
|
isOSIteratorSubclass(PD)) {
|
||||||
return getOSSummaryCreateRule(FD);
|
return getOSSummaryCreateRule(FD);
|
||||||
} else {
|
} else {
|
||||||
|
@ -703,8 +707,13 @@ RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
|
||||||
// the input was non-zero),
|
// the input was non-zero),
|
||||||
// or that it returns zero (when the cast failed, or the input
|
// or that it returns zero (when the cast failed, or the input
|
||||||
// was zero).
|
// was zero).
|
||||||
if (TrackOSObjects && isOSObjectDynamicCast(FName)) {
|
if (TrackOSObjects) {
|
||||||
return BehaviorSummary::IdentityOrZero;
|
if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
|
||||||
|
return BehaviorSummary::IdentityOrZero;
|
||||||
|
} else if (isOSObjectThisCast(FName) && isa<CXXMethodDecl>(FD) &&
|
||||||
|
!cast<CXXMethodDecl>(FD)->isStatic()) {
|
||||||
|
return BehaviorSummary::IdentityThis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FunctionDecl* FDD = FD->getDefinition();
|
const FunctionDecl* FDD = FD->getDefinition();
|
||||||
|
|
|
@ -23,6 +23,8 @@ struct OSMetaClassBase {
|
||||||
static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
|
static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
|
||||||
const OSMetaClass *meta);
|
const OSMetaClass *meta);
|
||||||
|
|
||||||
|
OSMetaClassBase *metaCast(const char *toMeta);
|
||||||
|
|
||||||
virtual void retain() const;
|
virtual void retain() const;
|
||||||
virtual void release() const;
|
virtual void release() const;
|
||||||
virtual void free();
|
virtual void free();
|
||||||
|
|
|
@ -54,6 +54,9 @@ struct OtherStruct {
|
||||||
OtherStruct(OSArray *arr);
|
OtherStruct(OSArray *arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool test_meta_cast_no_leak(OSMetaClassBase *arg) {
|
||||||
|
return arg && arg->metaCast("blah") != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void escape(void *);
|
void escape(void *);
|
||||||
void escape_with_source(void *p) {}
|
void escape_with_source(void *p) {}
|
||||||
|
|
Loading…
Reference in New Issue