|
@@ -29,6 +29,10 @@ 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);
|
|
@@ -39,73 +43,6 @@ 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() << " | ";
|
|
@@ -414,20 +351,6 @@ 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
|
|
@@ -529,6 +452,13 @@ 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.
|
|
@@ -536,10 +466,15 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
|
|
|
/// - 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, so be conservative here and let the symbol go.
|
|
|
+/// assigned to a struct field, unless it is a known smart pointer
|
|
|
+/// implementation, about which we know that it is inlined.
|
|
|
/// 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;
|
|
|
|
|
@@ -867,6 +802,23 @@ 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,
|
|
@@ -886,30 +838,9 @@ 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>(
|
|
|
- *BT, C.getASTContext().getLangOpts(), N, Sym);
|
|
|
+ errorKindToBugKind(ErrorKind, Sym),
|
|
|
+ C.getASTContext().getLangOpts(), N, Sym);
|
|
|
report->addRange(ErrorRange);
|
|
|
C.emitReport(std::move(report));
|
|
|
}
|
|
@@ -1112,8 +1043,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>(
|
|
|
- *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C);
|
|
|
+ auto R =
|
|
|
+ llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C);
|
|
|
C.emitReport(std::move(R));
|
|
|
}
|
|
|
return N;
|
|
@@ -1137,11 +1068,8 @@ 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;
|
|
@@ -1293,12 +1221,9 @@ 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));
|
|
|
}
|
|
|
|
|
@@ -1344,11 +1269,8 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
|
|
|
|
|
|
if (N) {
|
|
|
for (SymbolRef L : Leaked) {
|
|
|
- RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts)
|
|
|
- : getLeakAtReturnBug(LOpts);
|
|
|
- assert(BT && "BugType not initialized.");
|
|
|
-
|
|
|
- Ctx.emitReport(llvm::make_unique<RefLeakReport>(*BT, LOpts, N, L, Ctx));
|
|
|
+ const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn;
|
|
|
+ Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx));
|
|
|
}
|
|
|
}
|
|
|
|