forked from OSchip/llvm-project
Add basic checking for passing NULL to CFRetain/CFRelease, since those functions
are not explicitly marked as not accepting NULL pointers. This check illustrates how we need more refactoring in the custom-check logic. llvm-svn: 75570
This commit is contained in:
parent
2571eb6453
commit
c057f417d8
|
@ -457,8 +457,85 @@ clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
|
|||
return new AuditCFNumberCreate(Ctx, BR);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CFRetain/CFRelease auditing for null arguments.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck {
|
||||
APIMisuse *BT;
|
||||
|
||||
// FIXME: Either this should be refactored into GRSimpleAPICheck, or
|
||||
// it should always be passed with a call to Audit. The latter
|
||||
// approach makes this class more stateless.
|
||||
ASTContext& Ctx;
|
||||
IdentifierInfo *Retain, *Release;
|
||||
BugReporter& BR;
|
||||
|
||||
public:
|
||||
AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
|
||||
: BT(0), Ctx(ctx),
|
||||
Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
|
||||
BR(br){}
|
||||
|
||||
~AuditCFRetainRelease() {}
|
||||
|
||||
bool Audit(ExplodedNode<GRState>* N, GRStateManager&);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
bool AuditCFRetainRelease::Audit(ExplodedNode<GRState>* N, GRStateManager&) {
|
||||
CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
|
||||
|
||||
// If the CallExpr doesn't have exactly 1 argument just give up checking.
|
||||
if (CE->getNumArgs() != 1)
|
||||
return false;
|
||||
|
||||
// Check if we called CFRetain/CFRelease.
|
||||
const GRState* state = N->getState();
|
||||
SVal X = state->getSVal(CE->getCallee());
|
||||
const FunctionDecl* FD = X.getAsFunctionDecl();
|
||||
|
||||
if (!FD)
|
||||
return false;
|
||||
|
||||
const IdentifierInfo *FuncII = FD->getIdentifier();
|
||||
if (!(FuncII == Retain || FuncII == Release))
|
||||
return false;
|
||||
|
||||
// Finally, check if the argument is NULL.
|
||||
// FIXME: We should be able to bifurcate the state here, as a successful
|
||||
// check will result in the value not being NULL afterwards.
|
||||
// FIXME: Need a way to register vistors for the BugReporter. Would like
|
||||
// to benefit from the same diagnostics that regular null dereference
|
||||
// reporting has.
|
||||
if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
|
||||
if (!BT)
|
||||
BT = new APIMisuse("null passed to CFRetain/CFRelease");
|
||||
|
||||
const char *description = (FuncII == Retain)
|
||||
? "Null pointer argument in call to CFRetain"
|
||||
: "Null pointer argument in call to CFRelease";
|
||||
|
||||
RangedBugReport *report = new RangedBugReport(*BT, description, N);
|
||||
report->addRange(CE->getArg(0)->getSourceRange());
|
||||
BR.EmitReport(report);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
GRSimpleAPICheck*
|
||||
clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
|
||||
return new AuditCFRetainRelease(Ctx, BR);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Check registration.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void clang::RegisterAppleChecks(GRExprEngine& Eng) {
|
||||
ASTContext& Ctx = Eng.getContext();
|
||||
|
@ -466,9 +543,8 @@ void clang::RegisterAppleChecks(GRExprEngine& Eng) {
|
|||
|
||||
Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
|
||||
Stmt::ObjCMessageExprClass);
|
||||
|
||||
Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR),
|
||||
Stmt::CallExprClass);
|
||||
Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
|
||||
Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
|
||||
|
||||
RegisterNSErrorChecks(BR, Eng);
|
||||
}
|
||||
|
|
|
@ -32,12 +32,15 @@ class GRStateManager;
|
|||
class BugReporter;
|
||||
class GRExprEngine;
|
||||
|
||||
GRSimpleAPICheck* CreateBasicObjCFoundationChecks(ASTContext& Ctx,
|
||||
GRSimpleAPICheck *CreateBasicObjCFoundationChecks(ASTContext& Ctx,
|
||||
BugReporter& BR);
|
||||
|
||||
GRSimpleAPICheck* CreateAuditCFNumberCreate(ASTContext& Ctx,
|
||||
GRSimpleAPICheck *CreateAuditCFNumberCreate(ASTContext& Ctx,
|
||||
BugReporter& BR);
|
||||
|
||||
GRSimpleAPICheck *CreateAuditCFRetainRelease(ASTContext& Ctx,
|
||||
BugReporter& BR);
|
||||
|
||||
void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng);
|
||||
|
||||
} // end clang namespace
|
||||
|
|
|
@ -390,6 +390,18 @@ void f15() {
|
|||
CFRelease(*B); // no-warning
|
||||
}
|
||||
|
||||
// Test when we pass NULL to CFRetain/CFRelease.
|
||||
void f16(int x, CFTypeRef p) {
|
||||
if (p)
|
||||
return;
|
||||
|
||||
if (x) {
|
||||
CFRelease(p); // expected-warning{{Null pointer argument in call to CFRelease}}
|
||||
}
|
||||
else {
|
||||
CFRetain(p); // expected-warning{{Null pointer argument in call to CFRetain}}
|
||||
}
|
||||
}
|
||||
|
||||
// Test basic tracking of ivars associated with 'self'. For the retain/release
|
||||
// checker we currently do not want to flag leaks associated with stores
|
||||
|
|
Loading…
Reference in New Issue