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:
Vlad Tsyrklevich 2019-01-18 08:43:22 +00:00
parent e84c729aca
commit d5dd6a5fdd
19 changed files with 263 additions and 460 deletions

View File

@ -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.
///

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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.

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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 {

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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>()) {

View File

@ -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) {

View File

@ -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 */

View File

@ -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 */

View File

@ -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}}
}

View File

@ -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);