forked from OSchip/llvm-project
Fix failing MSan bots
Revert r351508-351514, this block of changes introduced a consistent MSan failure on the sanitizer bots. llvm-svn: 351528
This commit is contained in:
parent
e84c729aca
commit
d5dd6a5fdd
|
@ -95,7 +95,7 @@ protected:
|
|||
friend class BugReportEquivClass;
|
||||
friend class BugReporter;
|
||||
|
||||
const BugType& BT;
|
||||
BugType& BT;
|
||||
const Decl *DeclWithIssue = nullptr;
|
||||
std::string ShortDescription;
|
||||
std::string Description;
|
||||
|
@ -164,15 +164,15 @@ private:
|
|||
void popInterestingSymbolsAndRegions();
|
||||
|
||||
public:
|
||||
BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode)
|
||||
BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode)
|
||||
: BT(bt), Description(desc), ErrorNode(errornode) {}
|
||||
|
||||
BugReport(const BugType& bt, StringRef shortDesc, StringRef desc,
|
||||
BugReport(BugType& bt, StringRef shortDesc, StringRef desc,
|
||||
const ExplodedNode *errornode)
|
||||
: BT(bt), ShortDescription(shortDesc), Description(desc),
|
||||
ErrorNode(errornode) {}
|
||||
|
||||
BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l)
|
||||
BugReport(BugType &bt, StringRef desc, PathDiagnosticLocation l)
|
||||
: BT(bt), Description(desc), Location(l) {}
|
||||
|
||||
/// Create a BugReport with a custom uniqueing location.
|
||||
|
@ -190,7 +190,7 @@ public:
|
|||
virtual ~BugReport();
|
||||
|
||||
const BugType& getBugType() const { return BT; }
|
||||
//BugType& getBugType() { return BT; }
|
||||
BugType& getBugType() { return BT; }
|
||||
|
||||
/// True when the report has an execution path associated with it.
|
||||
///
|
||||
|
@ -481,7 +481,7 @@ public:
|
|||
return {};
|
||||
}
|
||||
|
||||
void Register(const BugType *BT);
|
||||
void Register(BugType *BT);
|
||||
|
||||
/// Add the given report to the set of reports tracked by BugReporter.
|
||||
///
|
||||
|
|
|
@ -38,14 +38,12 @@ private:
|
|||
virtual void anchor();
|
||||
|
||||
public:
|
||||
BugType(CheckName Check, StringRef Name, StringRef Cat,
|
||||
bool SuppressOnSink=false)
|
||||
BugType(CheckName Check, StringRef Name, StringRef Cat)
|
||||
: Check(Check), Name(Name), Category(Cat), Checker(nullptr),
|
||||
SuppressOnSink(SuppressOnSink) {}
|
||||
BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat,
|
||||
bool SuppressOnSink=false)
|
||||
SuppressOnSink(false) {}
|
||||
BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat)
|
||||
: Check(Checker->getCheckName()), Name(Name), Category(Cat),
|
||||
Checker(Checker), SuppressOnSink(SuppressOnSink) {}
|
||||
Checker(Checker), SuppressOnSink(false) {}
|
||||
virtual ~BugType() = default;
|
||||
|
||||
StringRef getName() const { return Name; }
|
||||
|
@ -66,6 +64,7 @@ public:
|
|||
/// type should be suppressed if the end node of the report is post-dominated
|
||||
/// by a sink node.
|
||||
bool isSuppressOnSink() const { return SuppressOnSink; }
|
||||
void setSuppressOnSink(bool x) { SuppressOnSink = x; }
|
||||
};
|
||||
|
||||
class BuiltinBug : public BugType {
|
||||
|
|
|
@ -685,10 +685,6 @@ public:
|
|||
Optional<BehaviorSummary> canEval(const CallExpr *CE, const FunctionDecl *FD,
|
||||
bool &hasTrustedImplementationAnnotation);
|
||||
|
||||
/// \return Whether the type corresponds to a known smart pointer
|
||||
/// implementation (that is, everything about it is inlineable).
|
||||
static bool isKnownSmartPointer(QualType QT);
|
||||
|
||||
bool isTrustedReferenceCountImplementation(const FunctionDecl *FD);
|
||||
|
||||
const RetainSummary *getSummary(const CallEvent &Call,
|
||||
|
|
|
@ -399,14 +399,14 @@ bool isZero(ProgramStateRef State, const NonLoc &Val);
|
|||
|
||||
IteratorChecker::IteratorChecker() {
|
||||
OutOfRangeBugType.reset(
|
||||
new BugType(this, "Iterator out of range", "Misuse of STL APIs",
|
||||
/*SuppressOnSink=*/true));
|
||||
new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
|
||||
OutOfRangeBugType->setSuppressOnSink(true);
|
||||
MismatchedBugType.reset(
|
||||
new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
|
||||
/*SuppressOnSink=*/true));
|
||||
new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs"));
|
||||
MismatchedBugType->setSuppressOnSink(true);
|
||||
InvalidatedBugType.reset(
|
||||
new BugType(this, "Iterator invalidated", "Misuse of STL APIs",
|
||||
/*SuppressOnSink=*/true));
|
||||
new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
|
||||
InvalidatedBugType->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
void IteratorChecker::checkPreCall(const CallEvent &Call,
|
||||
|
|
|
@ -2301,14 +2301,14 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
|
|||
|
||||
assert(N);
|
||||
if (!BT_Leak[*CheckKind]) {
|
||||
BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak",
|
||||
categories::MemoryError));
|
||||
// Leaks should not be reported if they are post-dominated by a sink:
|
||||
// (1) Sinks are higher importance bugs.
|
||||
// (2) NoReturnFunctionChecker uses sink nodes to represent paths ending
|
||||
// with __noreturn functions such as assert() or exit(). We choose not
|
||||
// to report leaks on such paths.
|
||||
BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak",
|
||||
categories::MemoryError,
|
||||
/*SuppressOnSink=*/true));
|
||||
BT_Leak[*CheckKind]->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
// Most bug reports are cached at the location where they occurred.
|
||||
|
|
|
@ -29,10 +29,6 @@ const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) {
|
|||
return State->get<RefBindings>(Sym);
|
||||
}
|
||||
|
||||
} // end namespace retaincountchecker
|
||||
} // end namespace ento
|
||||
} // end namespace clang
|
||||
|
||||
static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
|
||||
RefVal Val) {
|
||||
assert(Sym != nullptr);
|
||||
|
@ -43,6 +39,73 @@ static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
|
|||
return State->remove<RefBindings>(Sym);
|
||||
}
|
||||
|
||||
class UseAfterRelease : public RefCountBug {
|
||||
public:
|
||||
UseAfterRelease(const CheckerBase *checker)
|
||||
: RefCountBug(checker, "Use-after-release") {}
|
||||
|
||||
const char *getDescription() const override {
|
||||
return "Reference-counted object is used after it is released";
|
||||
}
|
||||
};
|
||||
|
||||
class BadRelease : public RefCountBug {
|
||||
public:
|
||||
BadRelease(const CheckerBase *checker) : RefCountBug(checker, "Bad release") {}
|
||||
|
||||
const char *getDescription() const override {
|
||||
return "Incorrect decrement of the reference count of an object that is "
|
||||
"not owned at this point by the caller";
|
||||
}
|
||||
};
|
||||
|
||||
class DeallocNotOwned : public RefCountBug {
|
||||
public:
|
||||
DeallocNotOwned(const CheckerBase *checker)
|
||||
: RefCountBug(checker, "-dealloc sent to non-exclusively owned object") {}
|
||||
|
||||
const char *getDescription() const override {
|
||||
return "-dealloc sent to object that may be referenced elsewhere";
|
||||
}
|
||||
};
|
||||
|
||||
class OverAutorelease : public RefCountBug {
|
||||
public:
|
||||
OverAutorelease(const CheckerBase *checker)
|
||||
: RefCountBug(checker, "Object autoreleased too many times") {}
|
||||
|
||||
const char *getDescription() const override {
|
||||
return "Object autoreleased too many times";
|
||||
}
|
||||
};
|
||||
|
||||
class ReturnedNotOwnedForOwned : public RefCountBug {
|
||||
public:
|
||||
ReturnedNotOwnedForOwned(const CheckerBase *checker)
|
||||
: RefCountBug(checker, "Method should return an owned object") {}
|
||||
|
||||
const char *getDescription() const override {
|
||||
return "Object with a +0 retain count returned to caller where a +1 "
|
||||
"(owning) retain count is expected";
|
||||
}
|
||||
};
|
||||
|
||||
class Leak : public RefCountBug {
|
||||
public:
|
||||
Leak(const CheckerBase *checker, StringRef name) : RefCountBug(checker, name) {
|
||||
// Leaks should not be reported if they are post-dominated by a sink.
|
||||
setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
const char *getDescription() const override { return ""; }
|
||||
|
||||
bool isLeak() const override { return true; }
|
||||
};
|
||||
|
||||
} // end namespace retaincountchecker
|
||||
} // end namespace ento
|
||||
} // end namespace clang
|
||||
|
||||
void RefVal::print(raw_ostream &Out) const {
|
||||
if (!T.isNull())
|
||||
Out << "Tracked " << T.getAsString() << " | ";
|
||||
|
@ -351,6 +414,20 @@ void RetainCountChecker::checkPostCall(const CallEvent &Call,
|
|||
checkSummary(*Summ, Call, C);
|
||||
}
|
||||
|
||||
RefCountBug *
|
||||
RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const {
|
||||
if (!leakWithinFunction)
|
||||
leakWithinFunction.reset(new Leak(this, "Leak"));
|
||||
return leakWithinFunction.get();
|
||||
}
|
||||
|
||||
RefCountBug *
|
||||
RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const {
|
||||
if (!leakAtReturn)
|
||||
leakAtReturn.reset(new Leak(this, "Leak of returned object"));
|
||||
return leakAtReturn.get();
|
||||
}
|
||||
|
||||
/// GetReturnType - Used to get the return type of a message expression or
|
||||
/// function call with the intention of affixing that type to a tracked symbol.
|
||||
/// While the return type can be queried directly from RetEx, when
|
||||
|
@ -452,13 +529,6 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
|
|||
C.addTransition(state);
|
||||
}
|
||||
|
||||
static bool isSmartPtrField(const MemRegion *MR) {
|
||||
const auto *TR = dyn_cast<TypedValueRegion>(
|
||||
cast<SubRegion>(MR)->getSuperRegion());
|
||||
return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType());
|
||||
}
|
||||
|
||||
|
||||
/// A value escapes in these possible cases:
|
||||
///
|
||||
/// - binding to something that is not a memory region.
|
||||
|
@ -466,15 +536,10 @@ static bool isSmartPtrField(const MemRegion *MR) {
|
|||
/// - binding to a variable that has a destructor attached using CleanupAttr
|
||||
///
|
||||
/// We do not currently model what happens when a symbol is
|
||||
/// assigned to a struct field, unless it is a known smart pointer
|
||||
/// implementation, about which we know that it is inlined.
|
||||
/// assigned to a struct field, so be conservative here and let the symbol go.
|
||||
/// FIXME: This could definitely be improved upon.
|
||||
static bool shouldEscapeRegion(const MemRegion *R) {
|
||||
if (isSmartPtrField(R))
|
||||
return false;
|
||||
|
||||
const auto *VR = dyn_cast<VarRegion>(R);
|
||||
|
||||
if (!R->hasStackStorage() || !VR)
|
||||
return true;
|
||||
|
||||
|
@ -802,23 +867,6 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
|
|||
return setRefBinding(state, sym, V);
|
||||
}
|
||||
|
||||
const RefCountBug &
|
||||
RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind,
|
||||
SymbolRef Sym) const {
|
||||
switch (ErrorKind) {
|
||||
case RefVal::ErrorUseAfterRelease:
|
||||
return useAfterRelease;
|
||||
case RefVal::ErrorReleaseNotOwned:
|
||||
return releaseNotOwned;
|
||||
case RefVal::ErrorDeallocNotOwned:
|
||||
if (Sym->getType()->getPointeeCXXRecordDecl())
|
||||
return freeNotOwned;
|
||||
return deallocNotOwned;
|
||||
default:
|
||||
llvm_unreachable("Unhandled error.");
|
||||
}
|
||||
}
|
||||
|
||||
void RetainCountChecker::processNonLeakError(ProgramStateRef St,
|
||||
SourceRange ErrorRange,
|
||||
RefVal::Kind ErrorKind,
|
||||
|
@ -838,9 +886,30 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
|
|||
if (!N)
|
||||
return;
|
||||
|
||||
RefCountBug *BT;
|
||||
switch (ErrorKind) {
|
||||
default:
|
||||
llvm_unreachable("Unhandled error.");
|
||||
case RefVal::ErrorUseAfterRelease:
|
||||
if (!useAfterRelease)
|
||||
useAfterRelease.reset(new UseAfterRelease(this));
|
||||
BT = useAfterRelease.get();
|
||||
break;
|
||||
case RefVal::ErrorReleaseNotOwned:
|
||||
if (!releaseNotOwned)
|
||||
releaseNotOwned.reset(new BadRelease(this));
|
||||
BT = releaseNotOwned.get();
|
||||
break;
|
||||
case RefVal::ErrorDeallocNotOwned:
|
||||
if (!deallocNotOwned)
|
||||
deallocNotOwned.reset(new DeallocNotOwned(this));
|
||||
BT = deallocNotOwned.get();
|
||||
break;
|
||||
}
|
||||
|
||||
assert(BT);
|
||||
auto report = llvm::make_unique<RefCountReport>(
|
||||
errorKindToBugKind(ErrorKind, Sym),
|
||||
C.getASTContext().getLangOpts(), N, Sym);
|
||||
*BT, C.getASTContext().getLangOpts(), N, Sym);
|
||||
report->addRange(ErrorRange);
|
||||
C.emitReport(std::move(report));
|
||||
}
|
||||
|
@ -1043,8 +1112,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
|
|||
ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
|
||||
if (N) {
|
||||
const LangOptions &LOpts = C.getASTContext().getLangOpts();
|
||||
auto R =
|
||||
llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C);
|
||||
auto R = llvm::make_unique<RefLeakReport>(
|
||||
*getLeakAtReturnBug(LOpts), LOpts, N, Sym, C);
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
return N;
|
||||
|
@ -1068,8 +1137,11 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
|
|||
|
||||
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
|
||||
if (N) {
|
||||
if (!returnNotOwnedForOwned)
|
||||
returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
|
||||
|
||||
auto R = llvm::make_unique<RefCountReport>(
|
||||
returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
|
||||
*returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
return N;
|
||||
|
@ -1221,9 +1293,12 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
|
|||
os << "but ";
|
||||
os << "has a +" << V.getCount() << " retain count";
|
||||
|
||||
if (!overAutorelease)
|
||||
overAutorelease.reset(new OverAutorelease(this));
|
||||
|
||||
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
|
||||
auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym,
|
||||
os.str());
|
||||
auto R = llvm::make_unique<RefCountReport>(*overAutorelease, LOpts, N, Sym,
|
||||
os.str());
|
||||
Ctx.emitReport(std::move(R));
|
||||
}
|
||||
|
||||
|
@ -1269,8 +1344,11 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
|
|||
|
||||
if (N) {
|
||||
for (SymbolRef L : Leaked) {
|
||||
const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn;
|
||||
Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx));
|
||||
RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts)
|
||||
: getLeakAtReturnBug(LOpts);
|
||||
assert(BT && "BugType not initialized.");
|
||||
|
||||
Ctx.emitReport(llvm::make_unique<RefLeakReport>(*BT, LOpts, N, L, Ctx));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -251,15 +251,10 @@ class RetainCountChecker
|
|||
check::RegionChanges,
|
||||
eval::Assume,
|
||||
eval::Call > {
|
||||
|
||||
RefCountBug useAfterRelease{this, RefCountBug::UseAfterRelease};
|
||||
RefCountBug releaseNotOwned{this, RefCountBug::ReleaseNotOwned};
|
||||
RefCountBug deallocNotOwned{this, RefCountBug::DeallocNotOwned};
|
||||
RefCountBug freeNotOwned{this, RefCountBug::FreeNotOwned};
|
||||
RefCountBug overAutorelease{this, RefCountBug::OverAutorelease};
|
||||
RefCountBug returnNotOwnedForOwned{this, RefCountBug::ReturnNotOwnedForOwned};
|
||||
RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction};
|
||||
RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn};
|
||||
mutable std::unique_ptr<RefCountBug> useAfterRelease, releaseNotOwned;
|
||||
mutable std::unique_ptr<RefCountBug> deallocNotOwned;
|
||||
mutable std::unique_ptr<RefCountBug> overAutorelease, returnNotOwnedForOwned;
|
||||
mutable std::unique_ptr<RefCountBug> leakWithinFunction, leakAtReturn;
|
||||
|
||||
mutable std::unique_ptr<RetainSummaryManager> Summaries;
|
||||
public:
|
||||
|
@ -271,7 +266,11 @@ public:
|
|||
/// Track sublcasses of OSObject.
|
||||
bool TrackOSObjects = false;
|
||||
|
||||
RetainCountChecker() {};
|
||||
RetainCountChecker() {}
|
||||
|
||||
RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const;
|
||||
|
||||
RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const;
|
||||
|
||||
RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const {
|
||||
// FIXME: We don't support ARC being turned on and off during one analysis.
|
||||
|
@ -337,9 +336,6 @@ public:
|
|||
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
|
||||
CheckerContext &C) const;
|
||||
|
||||
const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind,
|
||||
SymbolRef Sym) const;
|
||||
|
||||
void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
|
||||
RefVal::Kind ErrorKind, SymbolRef Sym,
|
||||
CheckerContext &C) const;
|
||||
|
|
|
@ -19,54 +19,6 @@ using namespace clang;
|
|||
using namespace ento;
|
||||
using namespace retaincountchecker;
|
||||
|
||||
StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
|
||||
switch (BT) {
|
||||
case UseAfterRelease:
|
||||
return "Use-after-release";
|
||||
case ReleaseNotOwned:
|
||||
return "Bad release";
|
||||
case DeallocNotOwned:
|
||||
return "-dealloc sent to non-exclusively owned object";
|
||||
case FreeNotOwned:
|
||||
return "freeing non-exclusively owned object";
|
||||
case OverAutorelease:
|
||||
return "Object autoreleased too many times";
|
||||
case ReturnNotOwnedForOwned:
|
||||
return "Method should return an owned object";
|
||||
case LeakWithinFunction:
|
||||
return "Leak";
|
||||
case LeakAtReturn:
|
||||
return "Leak of returned object";
|
||||
}
|
||||
}
|
||||
|
||||
StringRef RefCountBug::getDescription() const {
|
||||
switch (BT) {
|
||||
case UseAfterRelease:
|
||||
return "Reference-counted object is used after it is released";
|
||||
case ReleaseNotOwned:
|
||||
return "Incorrect decrement of the reference count of an object that is "
|
||||
"not owned at this point by the caller";
|
||||
case DeallocNotOwned:
|
||||
return "-dealloc sent to object that may be referenced elsewhere";
|
||||
case FreeNotOwned:
|
||||
return "'free' called on an object that may be referenced elsewhere";
|
||||
case OverAutorelease:
|
||||
return "Object autoreleased too many times";
|
||||
case ReturnNotOwnedForOwned:
|
||||
return "Object with a +0 retain count returned to caller where a +1 "
|
||||
"(owning) retain count is expected";
|
||||
case LeakWithinFunction:
|
||||
case LeakAtReturn:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
|
||||
: BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
|
||||
/*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
|
||||
BT(BT) {}
|
||||
|
||||
static bool isNumericLiteralExpression(const Expr *E) {
|
||||
// FIXME: This set of cases was copied from SemaExprObjC.
|
||||
return isa<IntegerLiteral>(E) ||
|
||||
|
@ -90,8 +42,7 @@ static std::string getPrettyTypeName(QualType QT) {
|
|||
/// Write information about the type state change to {@code os},
|
||||
/// return whether the note should be generated.
|
||||
static bool shouldGenerateNote(llvm::raw_string_ostream &os,
|
||||
const RefVal *PrevT,
|
||||
const RefVal &CurrV,
|
||||
const RefVal *PrevT, const RefVal &CurrV,
|
||||
bool DeallocSent) {
|
||||
// Get the previous type state.
|
||||
RefVal PrevV = *PrevT;
|
||||
|
@ -181,32 +132,6 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
|
|||
return None;
|
||||
}
|
||||
|
||||
Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
|
||||
if (ME->getMemberDecl()->getNameAsString() != "alloc")
|
||||
return None;
|
||||
const Expr *This = ME->getBase()->IgnoreParenImpCasts();
|
||||
if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
|
||||
const ValueDecl *VD = DRE->getDecl();
|
||||
if (VD->getNameAsString() != "metaClass")
|
||||
return None;
|
||||
|
||||
if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
|
||||
return RD->getNameAsString();
|
||||
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
std::string findAllocatedObjectName(const Stmt *S,
|
||||
QualType QT) {
|
||||
if (const auto *CE = dyn_cast<CallExpr>(S))
|
||||
if (auto Out = findMetaClassAlloc(CE->getCallee()))
|
||||
return *Out;
|
||||
return getPrettyTypeName(QT);
|
||||
}
|
||||
|
||||
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
|
||||
const LocationContext *LCtx,
|
||||
const RefVal &CurrV, SymbolRef &Sym,
|
||||
|
@ -264,7 +189,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
|
|||
os << "a Core Foundation object of type '"
|
||||
<< Sym->getType().getAsString() << "' with a ";
|
||||
} else if (CurrV.getObjKind() == ObjKind::OS) {
|
||||
os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
|
||||
os << "an OSObject of type '" << getPrettyTypeName(Sym->getType())
|
||||
<< "' with a ";
|
||||
} else if (CurrV.getObjKind() == ObjKind::Generalized) {
|
||||
os << "an object of type '" << Sym->getType().getAsString()
|
||||
|
@ -413,7 +338,15 @@ annotateConsumedSummaryMismatch(const ExplodedNode *N,
|
|||
if (os.str().empty())
|
||||
return nullptr;
|
||||
|
||||
PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
|
||||
// FIXME: remove the code duplication with NoStoreFuncVisitor.
|
||||
PathDiagnosticLocation L;
|
||||
if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
|
||||
L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext());
|
||||
} else {
|
||||
L = PathDiagnosticLocation(
|
||||
Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
|
||||
}
|
||||
|
||||
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
|
||||
}
|
||||
|
||||
|
@ -421,11 +354,6 @@ std::shared_ptr<PathDiagnosticPiece>
|
|||
RefCountReportVisitor::VisitNode(const ExplodedNode *N,
|
||||
BugReporterContext &BRC, BugReport &BR) {
|
||||
|
||||
const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
|
||||
|
||||
bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
|
||||
BT.getBugType() == RefCountBug::DeallocNotOwned;
|
||||
|
||||
const SourceManager &SM = BRC.getSourceManager();
|
||||
CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
|
||||
if (auto CE = N->getLocationAs<CallExitBegin>())
|
||||
|
@ -444,8 +372,7 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
|
|||
const LocationContext *LCtx = N->getLocationContext();
|
||||
|
||||
const RefVal* CurrT = getRefBinding(CurrSt, Sym);
|
||||
if (!CurrT)
|
||||
return nullptr;
|
||||
if (!CurrT) return nullptr;
|
||||
|
||||
const RefVal &CurrV = *CurrT;
|
||||
const RefVal *PrevT = getRefBinding(PrevSt, Sym);
|
||||
|
@ -455,12 +382,6 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
|
|||
std::string sbuf;
|
||||
llvm::raw_string_ostream os(sbuf);
|
||||
|
||||
if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
|
||||
os << "Object is now not exclusively owned";
|
||||
auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
|
||||
return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
|
||||
}
|
||||
|
||||
// This is the allocation site since the previous node had no bindings
|
||||
// for this symbol.
|
||||
if (!PrevT) {
|
||||
|
@ -507,9 +428,9 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
|
|||
// program point
|
||||
bool DeallocSent = false;
|
||||
|
||||
const ProgramPointTag *Tag = N->getLocation().getTag();
|
||||
if (Tag && Tag->getTagDescription().contains(
|
||||
RetainCountChecker::DeallocTagDescription)) {
|
||||
if (N->getLocation().getTag() &&
|
||||
N->getLocation().getTag()->getTagDescription().contains(
|
||||
RetainCountChecker::DeallocTagDescription)) {
|
||||
// We only have summaries attached to nodes after evaluating CallExpr and
|
||||
// ObjCMessageExprs.
|
||||
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
|
||||
|
@ -764,15 +685,15 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
|
|||
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
|
||||
}
|
||||
|
||||
RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
|
||||
RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
|
||||
ExplodedNode *n, SymbolRef sym,
|
||||
bool isLeak)
|
||||
: BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
|
||||
if (!isLeak)
|
||||
bool registerVisitor)
|
||||
: BugReport(D, D.getDescription(), n), Sym(sym) {
|
||||
if (registerVisitor)
|
||||
addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
|
||||
}
|
||||
|
||||
RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
|
||||
RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
|
||||
ExplodedNode *n, SymbolRef sym,
|
||||
StringRef endText)
|
||||
: BugReport(D, D.getDescription(), endText, n) {
|
||||
|
@ -858,10 +779,10 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
|
||||
RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts,
|
||||
ExplodedNode *n, SymbolRef sym,
|
||||
CheckerContext &Ctx)
|
||||
: RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
|
||||
: RefCountReport(D, LOpts, n, sym, false) {
|
||||
|
||||
deriveAllocLocation(Ctx, sym);
|
||||
if (!AllocBinding)
|
||||
|
|
|
@ -25,44 +25,32 @@ namespace ento {
|
|||
namespace retaincountchecker {
|
||||
|
||||
class RefCountBug : public BugType {
|
||||
public:
|
||||
enum RefCountBugType {
|
||||
UseAfterRelease,
|
||||
ReleaseNotOwned,
|
||||
DeallocNotOwned,
|
||||
FreeNotOwned,
|
||||
OverAutorelease,
|
||||
ReturnNotOwnedForOwned,
|
||||
LeakWithinFunction,
|
||||
LeakAtReturn,
|
||||
};
|
||||
RefCountBug(const CheckerBase *checker, RefCountBugType BT);
|
||||
StringRef getDescription() const;
|
||||
RefCountBugType getBugType() const {
|
||||
return BT;
|
||||
}
|
||||
protected:
|
||||
RefCountBug(const CheckerBase *checker, StringRef name)
|
||||
: BugType(checker, name, categories::MemoryRefCount) {}
|
||||
|
||||
private:
|
||||
RefCountBugType BT;
|
||||
static StringRef bugTypeToName(RefCountBugType BT);
|
||||
public:
|
||||
virtual const char *getDescription() const = 0;
|
||||
|
||||
virtual bool isLeak() const { return false; }
|
||||
};
|
||||
|
||||
class RefCountReport : public BugReport {
|
||||
protected:
|
||||
SymbolRef Sym;
|
||||
bool isLeak;
|
||||
|
||||
public:
|
||||
RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
|
||||
RefCountReport(RefCountBug &D, const LangOptions &LOpts,
|
||||
ExplodedNode *n, SymbolRef sym,
|
||||
bool isLeak=false);
|
||||
bool registerVisitor = true);
|
||||
|
||||
RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
|
||||
RefCountReport(RefCountBug &D, const LangOptions &LOpts,
|
||||
ExplodedNode *n, SymbolRef sym,
|
||||
StringRef endText);
|
||||
|
||||
llvm::iterator_range<ranges_iterator> getRanges() override {
|
||||
if (!isLeak)
|
||||
const RefCountBug& BugTy = static_cast<RefCountBug&>(getBugType());
|
||||
if (!BugTy.isLeak())
|
||||
return BugReport::getRanges();
|
||||
return llvm::make_range(ranges_iterator(), ranges_iterator());
|
||||
}
|
||||
|
@ -81,7 +69,7 @@ class RefLeakReport : public RefCountReport {
|
|||
void createDescription(CheckerContext &Ctx);
|
||||
|
||||
public:
|
||||
RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
|
||||
RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
|
||||
SymbolRef sym, CheckerContext &Ctx);
|
||||
|
||||
PathDiagnosticLocation getLocation(const SourceManager &SM) const override {
|
||||
|
|
|
@ -109,10 +109,10 @@ SimpleStreamChecker::SimpleStreamChecker()
|
|||
DoubleCloseBugType.reset(
|
||||
new BugType(this, "Double fclose", "Unix Stream API Error"));
|
||||
|
||||
// Sinks are higher importance bugs as well as calls to assert() or exit(0).
|
||||
LeakBugType.reset(
|
||||
new BugType(this, "Resource Leak", "Unix Stream API Error",
|
||||
/*SuppressOnSink=*/true));
|
||||
new BugType(this, "Resource Leak", "Unix Stream API Error"));
|
||||
// Sinks are higher importance bugs as well as calls to assert() or exit(0).
|
||||
LeakBugType->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
|
||||
|
|
|
@ -276,8 +276,8 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
|
|||
new BugType(CheckNames[CK_Unterminated].getName().empty()
|
||||
? CheckNames[CK_Uninitialized]
|
||||
: CheckNames[CK_Unterminated],
|
||||
"Leaked va_list", categories::MemoryError,
|
||||
/*SuppressOnSink=*/true));
|
||||
"Leaked va_list", categories::MemoryError));
|
||||
BT_leakedvalist->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
const ExplodedNode *StartNode = getStartCallSite(N, Reg);
|
||||
|
|
|
@ -1247,7 +1247,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
|
|||
|
||||
static std::unique_ptr<PathDiagnostic>
|
||||
generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) {
|
||||
const BugType &BT = R->getBugType();
|
||||
BugType &BT = R->getBugType();
|
||||
return llvm::make_unique<PathDiagnostic>(
|
||||
R->getBugType().getCheckName(), R->getDeclWithIssue(),
|
||||
R->getBugType().getName(), R->getDescription(),
|
||||
|
@ -2684,7 +2684,7 @@ GRBugReporter::generatePathDiagnostics(
|
|||
return Out;
|
||||
}
|
||||
|
||||
void BugReporter::Register(const BugType *BT) {
|
||||
void BugReporter::Register(BugType *BT) {
|
||||
BugTypes = F.add(BugTypes, BT);
|
||||
}
|
||||
|
||||
|
@ -2718,7 +2718,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
|
|||
R->Profile(ID);
|
||||
|
||||
// Lookup the equivance class. If there isn't one, create it.
|
||||
const BugType& BT = R->getBugType();
|
||||
BugType& BT = R->getBugType();
|
||||
Register(&BT);
|
||||
void *InsertPos;
|
||||
BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos);
|
||||
|
@ -2836,7 +2836,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
|
|||
SmallVectorImpl<BugReport*> &bugReports) {
|
||||
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
|
||||
assert(I != E);
|
||||
const BugType& BT = I->getBugType();
|
||||
BugType& BT = I->getBugType();
|
||||
|
||||
// If we don't need to suppress any of the nodes because they are
|
||||
// post-dominated by a sink, simply add all the nodes in the equivalence class
|
||||
|
|
|
@ -308,8 +308,9 @@ public:
|
|||
if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
|
||||
potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
|
||||
IvarR->getDecl()))
|
||||
return notModifiedDiagnostics(N, {}, SelfRegion, "self",
|
||||
/*FirstIsReferenceType=*/false, 1);
|
||||
return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion,
|
||||
"self", /*FirstIsReferenceType=*/false,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +318,8 @@ public:
|
|||
const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
|
||||
if (RegionOfInterest->isSubRegionOf(ThisR)
|
||||
&& !CCall->getDecl()->isImplicit())
|
||||
return notModifiedDiagnostics(N, {}, ThisR, "this",
|
||||
return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR,
|
||||
"this",
|
||||
/*FirstIsReferenceType=*/false, 1);
|
||||
|
||||
// Do not generate diagnostics for not modified parameters in
|
||||
|
@ -336,17 +338,18 @@ public:
|
|||
QualType T = PVD->getType();
|
||||
while (const MemRegion *R = S.getAsRegion()) {
|
||||
if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T))
|
||||
return notModifiedDiagnostics(N, {}, R, ParamName,
|
||||
ParamIsReferenceType, IndirectionLevel);
|
||||
return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R,
|
||||
ParamName, ParamIsReferenceType,
|
||||
IndirectionLevel);
|
||||
|
||||
QualType PT = T->getPointeeType();
|
||||
if (PT.isNull() || PT->isVoidType()) break;
|
||||
|
||||
if (const RecordDecl *RD = PT->getAsRecordDecl())
|
||||
if (auto P = findRegionOfInterestInRecord(RD, State, R))
|
||||
return notModifiedDiagnostics(N, *P, RegionOfInterest, ParamName,
|
||||
ParamIsReferenceType,
|
||||
IndirectionLevel);
|
||||
return notModifiedDiagnostics(
|
||||
Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName,
|
||||
ParamIsReferenceType, IndirectionLevel);
|
||||
|
||||
S = State->getSVal(R, PT);
|
||||
T = PT;
|
||||
|
@ -520,12 +523,19 @@ private:
|
|||
|
||||
/// \return Diagnostics piece for region not modified in the current function.
|
||||
std::shared_ptr<PathDiagnosticPiece>
|
||||
notModifiedDiagnostics(const ExplodedNode *N, const RegionVector &FieldChain,
|
||||
notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc,
|
||||
CallEventRef<> Call, const RegionVector &FieldChain,
|
||||
const MemRegion *MatchedRegion, StringRef FirstElement,
|
||||
bool FirstIsReferenceType, unsigned IndirectionLevel) {
|
||||
|
||||
PathDiagnosticLocation L =
|
||||
PathDiagnosticLocation::create(N->getLocation(), SM);
|
||||
PathDiagnosticLocation L;
|
||||
if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
|
||||
L = PathDiagnosticLocation::createBegin(RS, SM, Ctx);
|
||||
} else {
|
||||
L = PathDiagnosticLocation(
|
||||
Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(),
|
||||
SM);
|
||||
}
|
||||
|
||||
SmallString<256> sbuf;
|
||||
llvm::raw_svector_ostream os(sbuf);
|
||||
|
|
|
@ -735,12 +735,6 @@ PathDiagnosticLocation::create(const ProgramPoint& P,
|
|||
return getLocationForCaller(CEE->getCalleeContext(),
|
||||
CEE->getLocationContext(),
|
||||
SMng);
|
||||
} else if (auto CEB = P.getAs<CallExitBegin>()) {
|
||||
if (const ReturnStmt *RS = CEB->getReturnStmt())
|
||||
return PathDiagnosticLocation::createBegin(RS, SMng,
|
||||
CEB->getLocationContext());
|
||||
return PathDiagnosticLocation(
|
||||
CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng);
|
||||
} else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
|
||||
CFGElement BlockFront = BE->getBlock()->front();
|
||||
if (auto StmtElt = BlockFront.getAs<CFGStmt>()) {
|
||||
|
|
|
@ -146,7 +146,7 @@ static bool isSubclass(const Decl *D,
|
|||
}
|
||||
|
||||
static bool isOSObjectSubclass(const Decl *D) {
|
||||
return isSubclass(D, "OSMetaClassBase");
|
||||
return isSubclass(D, "OSObject");
|
||||
}
|
||||
|
||||
static bool isOSObjectDynamicCast(StringRef S) {
|
||||
|
@ -199,20 +199,6 @@ static bool isOSObjectRelated(const CXXMethodDecl *MD) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RetainSummaryManager::isKnownSmartPointer(QualType QT) {
|
||||
QT = QT.getCanonicalType();
|
||||
const auto *RD = QT->getAsCXXRecordDecl();
|
||||
if (!RD)
|
||||
return false;
|
||||
const IdentifierInfo *II = RD->getIdentifier();
|
||||
if (II && II->getName() == "smart_ptr")
|
||||
if (const auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext()))
|
||||
if (ND->getNameAsString() == "os")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const RetainSummary *
|
||||
RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
|
||||
StringRef FName, QualType RetTy) {
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#ifndef _OS_BASE_H
|
||||
#define _OS_BASE_H
|
||||
|
||||
#define OS_CONSUME __attribute__((os_consumed))
|
||||
#define OS_RETURNS_RETAINED __attribute__((os_returns_retained))
|
||||
#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero))
|
||||
#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero))
|
||||
#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained))
|
||||
#define OS_CONSUMES_THIS __attribute__((os_consumes_this))
|
||||
|
||||
#define OSTypeID(type) (type::metaClass)
|
||||
|
||||
#define OSDynamicCast(type, inst) \
|
||||
((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
|
||||
|
||||
#define OSTypeAlloc(type) ((type *) ((type::metaClass)->alloc()))
|
||||
|
||||
using size_t = decltype(sizeof(int));
|
||||
|
||||
struct OSMetaClass;
|
||||
|
||||
struct OSMetaClassBase {
|
||||
static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
|
||||
const OSMetaClass *meta);
|
||||
|
||||
virtual void retain() const;
|
||||
virtual void release() const;
|
||||
virtual void free();
|
||||
virtual ~OSMetaClassBase(){};
|
||||
};
|
||||
|
||||
struct OSObject : public OSMetaClassBase {
|
||||
virtual ~OSObject(){}
|
||||
|
||||
unsigned int foo() { return 42; }
|
||||
|
||||
virtual OS_RETURNS_NOT_RETAINED OSObject *identity();
|
||||
|
||||
static OSObject *generateObject(int);
|
||||
|
||||
static OSObject *getObject();
|
||||
static OSObject *GetObject();
|
||||
|
||||
static void * operator new(size_t size);
|
||||
|
||||
static const OSMetaClass * const metaClass;
|
||||
};
|
||||
|
||||
struct OSMetaClass : public OSMetaClassBase {
|
||||
virtual OSObject * alloc() const;
|
||||
virtual ~OSMetaClass(){}
|
||||
};
|
||||
|
||||
#endif /* _OS_BASE_H */
|
|
@ -1,89 +0,0 @@
|
|||
#ifndef _OS_SMART_POINTER_H
|
||||
#define _OS_SMART_POINTER_H
|
||||
|
||||
#include "os_object_base.h"
|
||||
|
||||
namespace os {
|
||||
|
||||
template<class T>
|
||||
struct smart_ptr {
|
||||
smart_ptr() : pointer(nullptr) {}
|
||||
|
||||
explicit smart_ptr(T *&p) : pointer(p) {
|
||||
if (pointer) {
|
||||
_retain(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
smart_ptr(smart_ptr const &rhs) : pointer(rhs.pointer) {
|
||||
if (pointer) {
|
||||
_retain(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
smart_ptr & operator=(T *&rhs) {
|
||||
smart_ptr(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
smart_ptr & operator=(smart_ptr &rhs) {
|
||||
smart_ptr(rhs).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~smart_ptr() {
|
||||
if (pointer) {
|
||||
_release(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
smart_ptr().swap(*this);
|
||||
}
|
||||
|
||||
T *get() const {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
T ** get_for_out_param() {
|
||||
reset();
|
||||
return &pointer;
|
||||
}
|
||||
|
||||
T * operator->() const {
|
||||
OSPTR_LOG("Dereference smart_ptr with %p\n", pointer);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
explicit
|
||||
operator bool() const {
|
||||
return pointer != nullptr;
|
||||
}
|
||||
|
||||
inline void
|
||||
swap(smart_ptr &p) {
|
||||
T *temp = pointer;
|
||||
pointer = p.pointer;
|
||||
p.pointer = temp;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_retain(T *obj) {
|
||||
obj->retain();
|
||||
}
|
||||
|
||||
static inline void
|
||||
_release(T *obj) {
|
||||
obj->release();
|
||||
}
|
||||
|
||||
static inline T *
|
||||
_alloc() {
|
||||
return new T;
|
||||
}
|
||||
|
||||
T *pointer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _OS_SMART_POINTER_H */
|
|
@ -1,10 +1,44 @@
|
|||
// RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
|
||||
// RUN: -analyzer-checker=core,osx -verify %s
|
||||
|
||||
#include "os_object_base.h"
|
||||
#include "os_smart_ptr.h"
|
||||
struct OSMetaClass;
|
||||
|
||||
#define OS_CONSUME __attribute__((os_consumed))
|
||||
#define OS_RETURNS_RETAINED __attribute__((os_returns_retained))
|
||||
#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero))
|
||||
#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero))
|
||||
#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained))
|
||||
#define OS_CONSUMES_THIS __attribute__((os_consumes_this))
|
||||
|
||||
#define OSTypeID(type) (type::metaClass)
|
||||
|
||||
#define OSDynamicCast(type, inst) \
|
||||
((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
|
||||
|
||||
using size_t = decltype(sizeof(int));
|
||||
|
||||
struct OSObject {
|
||||
virtual void retain();
|
||||
virtual void release() {};
|
||||
virtual void free();
|
||||
virtual ~OSObject(){}
|
||||
|
||||
unsigned int foo() { return 42; }
|
||||
|
||||
virtual OS_RETURNS_NOT_RETAINED OSObject *identity();
|
||||
|
||||
static OSObject *generateObject(int);
|
||||
|
||||
static OSObject *getObject();
|
||||
static OSObject *GetObject();
|
||||
|
||||
static void * operator new(size_t size);
|
||||
|
||||
static const OSMetaClass * const metaClass;
|
||||
};
|
||||
|
||||
struct OSIterator : public OSObject {
|
||||
|
||||
static const OSMetaClass * const metaClass;
|
||||
};
|
||||
|
||||
|
@ -54,6 +88,9 @@ struct OtherStruct {
|
|||
OtherStruct(OSArray *arr);
|
||||
};
|
||||
|
||||
struct OSMetaClassBase {
|
||||
static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta);
|
||||
};
|
||||
|
||||
void escape(void *);
|
||||
void escape_with_source(void *p) {}
|
||||
|
@ -579,68 +616,3 @@ typedef bool (^Blk)(OSObject *);
|
|||
void test_escape_to_unknown_block(Blk blk) {
|
||||
blk(getObject()); // no-crash
|
||||
}
|
||||
|
||||
using OSObjectPtr = os::smart_ptr<OSObject>;
|
||||
|
||||
void test_smart_ptr_uaf() {
|
||||
OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
|
||||
{
|
||||
OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
|
||||
// expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
|
||||
// expected-note@os_smart_ptr.h:13{{Taking true branch}}
|
||||
// expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
|
||||
// expected-note@os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}}
|
||||
// expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
|
||||
} // expected-note{{Calling '~smart_ptr'}}
|
||||
// expected-note@os_smart_ptr.h:35{{Taking true branch}}
|
||||
// expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
|
||||
// expected-note@os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}}
|
||||
// expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
|
||||
// expected-note@-5{{Returning from '~smart_ptr'}}
|
||||
obj->release(); // expected-note{{Object released}}
|
||||
obj->release(); // expected-warning{{Reference-counted object is used after it is released}}
|
||||
// expected-note@-1{{Reference-counted object is used after it is released}}
|
||||
}
|
||||
|
||||
void test_smart_ptr_leak() {
|
||||
OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
|
||||
{
|
||||
OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
|
||||
// expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
|
||||
// expected-note@os_smart_ptr.h:13{{Taking true branch}}
|
||||
// expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
|
||||
// expected-note@os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}}
|
||||
// expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
|
||||
} // expected-note{{Calling '~smart_ptr'}}
|
||||
// expected-note@os_smart_ptr.h:35{{Taking true branch}}
|
||||
// expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
|
||||
// expected-note@os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}}
|
||||
// expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
|
||||
// expected-note@-5{{Returning from '~smart_ptr'}}
|
||||
} // expected-warning{{Potential leak of an object stored into 'obj'}}
|
||||
// expected-note@-1{{Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1}}
|
||||
|
||||
void test_smart_ptr_no_leak() {
|
||||
OSObject *obj = new OSObject;
|
||||
{
|
||||
OSObjectPtr p(obj);
|
||||
}
|
||||
obj->release();
|
||||
}
|
||||
|
||||
void test_ostypealloc_correct_diagnostic_name() {
|
||||
OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}}
|
||||
arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
|
||||
arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
|
||||
} // expected-note{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}}
|
||||
// expected-warning@-1{{Potential leak of an object stored into 'arr'}}
|
||||
|
||||
void escape_elsewhere(OSObject *obj);
|
||||
|
||||
void test_free_on_escaped_object_diagnostics() {
|
||||
OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
|
||||
escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}}
|
||||
obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}}
|
||||
// expected-warning@-1{{'free' called on an object that may be referenced elsewhere}}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.OSObjectRetainCount -DNO_OS_OBJECT -verify %s
|
||||
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-config "osx.cocoa.RetainCount:CheckOSObject=false" -DNO_OS_OBJECT -verify %s
|
||||
|
||||
#include "os_object_base.h"
|
||||
|
||||
typedef const void * CFTypeRef;
|
||||
extern CFTypeRef CFRetain(CFTypeRef cf);
|
||||
extern void CFRelease(CFTypeRef cf);
|
||||
|
@ -13,6 +11,14 @@ extern CFTypeRef CFCreate() CF_RETURNS_RETAINED;
|
|||
|
||||
using size_t = decltype(sizeof(int));
|
||||
|
||||
struct OSObject {
|
||||
virtual void retain();
|
||||
virtual void release();
|
||||
|
||||
static void * operator new(size_t size);
|
||||
virtual ~OSObject(){}
|
||||
};
|
||||
|
||||
void cf_overrelease() {
|
||||
CFTypeRef cf = CFCreate();
|
||||
CFRelease(cf);
|
||||
|
|
Loading…
Reference in New Issue