r340502 - [analyzer] Preliminary version of retain count checking for OSObjects
George Karpenkov via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 22 17:26:59 PDT 2018
Author: george.karpenkov
Date: Wed Aug 22 17:26:59 2018
New Revision: 340502
URL: http://llvm.org/viewvc/llvm-project?rev=340502&view=rev
Log:
[analyzer] Preliminary version of retain count checking for OSObjects
Has quite a lot of false positives, disabled behind the flag.
Differential Revision: https://reviews.llvm.org/D50880
Added:
cfe/trunk/test/Analysis/osobject-retain-release.cpp
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h?rev=340502&r1=340501&r2=340502&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h Wed Aug 22 17:26:59 2018
@@ -154,7 +154,10 @@ public:
/// Indicates that the tracked object could be a CF or Objective-C object.
AnyObj,
/// Indicates that the tracked object is a generalized object.
- Generalized
+ Generalized,
+
+ /// A descendant of OSObject.
+ OS
};
private:
@@ -318,14 +321,21 @@ class RetainSummary {
/// this is the effect applied to the state of the receiver.
ArgEffect Receiver;
+ /// Effect on "this" pointer - applicable only to C++ method calls.
+ ArgEffect This;
+
/// Ret - The effect on the return value. Used to indicate if the
/// function/method call returns a new tracked symbol.
RetEffect Ret;
public:
- RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff,
- ArgEffect ReceiverEff)
- : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {}
+ RetainSummary(ArgEffects A,
+ RetEffect R,
+ ArgEffect defaultEff,
+ ArgEffect ReceiverEff,
+ ArgEffect ThisEff)
+ : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff),
+ This(ThisEff), Ret(R) {}
/// getArg - Return the argument effect on the argument specified by
/// idx (starting from 0).
@@ -359,12 +369,20 @@ public:
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
ArgEffect getReceiverEffect() const { return Receiver; }
+ ArgEffect getThisEffect() const { return This; }
+
+ bool isNoop() const {
+ return Ret == RetEffect::MakeNoRet() && Receiver == DoNothing
+ && DefaultArgEffect == MayEscape && This == DoNothing
+ && Args.isEmpty();
+ }
+
/// Test if two retain summaries are identical. Note that merely equivalent
/// summaries are not necessarily identical (for example, if an explicit
/// argument effect matches the default effect).
bool operator==(const RetainSummary &Other) const {
return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect &&
- Receiver == Other.Receiver && Ret == Other.Ret;
+ Receiver == Other.Receiver && This == Other.This && Ret == Other.Ret;
}
/// Profile this summary for inclusion in a FoldingSet.
@@ -372,6 +390,7 @@ public:
ID.Add(Args);
ID.Add(DefaultArgEffect);
ID.Add(Receiver);
+ ID.Add(This);
ID.Add(Ret);
}
@@ -460,6 +479,9 @@ class RetainSummaryManager {
/// Records whether or not the analyzed code runs in ARC mode.
const bool ARCEnabled;
+ /// Track sublcasses of OSObject
+ const bool TrackOSObjects;
+
/// FuncSummaries - A map from FunctionDecls to summaries.
FuncSummariesTy FuncSummaries;
@@ -496,6 +518,19 @@ class RetainSummaryManager {
/// data in ScratchArgs.
ArgEffects getArgEffects();
+ /// Create an OS object at +1.
+ const RetainSummary *getOSSummaryCreateRule(const FunctionDecl *FD);
+
+ /// Get an OS object at +0.
+ const RetainSummary *getOSSummaryGetRule(const FunctionDecl *FD);
+
+ /// Increment the reference count on OS object.
+ const RetainSummary *getOSSummaryRetainRule(const FunctionDecl *FD);
+
+ /// Decrement the reference count on OS object.
+ const RetainSummary *getOSSummaryReleaseRule(const FunctionDecl *FD);
+
+
enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable };
const RetainSummary *getUnarySummary(const FunctionType* FT,
@@ -509,8 +544,10 @@ class RetainSummaryManager {
const RetainSummary *getPersistentSummary(RetEffect RetEff,
ArgEffect ReceiverEff = DoNothing,
- ArgEffect DefaultEff = MayEscape) {
- RetainSummary Summ(getArgEffects(), RetEff, DefaultEff, ReceiverEff);
+ ArgEffect DefaultEff = MayEscape,
+ ArgEffect ThisEff = DoNothing) {
+ RetainSummary Summ(getArgEffects(), RetEff, DefaultEff, ReceiverEff,
+ ThisEff);
return getPersistentSummary(Summ);
}
@@ -584,9 +621,12 @@ class RetainSummaryManager {
bool &AllowAnnotations);
public:
- RetainSummaryManager(ASTContext &ctx, bool usesARC)
+ RetainSummaryManager(ASTContext &ctx,
+ bool usesARC,
+ bool trackOSObject)
: Ctx(ctx),
ARCEnabled(usesARC),
+ TrackOSObjects(trackOSObject),
AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
ObjCAllocRetE(usesARC ? RetEffect::MakeNotOwned(RetEffect::ObjC)
: RetEffect::MakeOwned(RetEffect::ObjC)),
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp?rev=340502&r1=340501&r2=340502&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp Wed Aug 22 17:26:59 2018
@@ -418,13 +418,18 @@ void RetainCountChecker::processSummaryO
}
// Consult the summary for the return value.
+ SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol();
RetEffect RE = Summ.getRetEffect();
- if (RE.getKind() == RetEffect::NoRetHard) {
- SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol();
- if (Sym)
- state = removeRefBinding(state, Sym);
+ if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) {
+ if (Optional<RefVal> updatedRefVal =
+ refValFromRetEffect(RE, MCall->getResultType())) {
+ state = setRefBinding(state, Sym, *updatedRefVal);
+ }
}
+ if (RE.getKind() == RetEffect::NoRetHard && Sym)
+ state = removeRefBinding(state, Sym);
+
C.addTransition(state);
}
@@ -490,11 +495,10 @@ void RetainCountChecker::checkSummary(co
}
}
- // Evaluate the effect on the message receiver.
+ // Evaluate the effect on the message receiver / `this` argument.
bool ReceiverIsTracked = false;
if (!hasErr) {
- const ObjCMethodCall *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg);
- if (MsgInvocation) {
+ if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
if (const RefVal *T = getRefBinding(state, Sym)) {
ReceiverIsTracked = true;
@@ -505,6 +509,17 @@ void RetainCountChecker::checkSummary(co
ErrorSym = Sym;
}
}
+ }
+ } else if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) {
+ if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
+ state = updateSymbol(state, Sym, *T, Summ.getThisEffect(),
+ hasErr, C);
+ if (hasErr) {
+ ErrorRange = MCall->getOriginExpr()->getSourceRange();
+ ErrorSym = Sym;
+ }
+ }
}
}
}
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h?rev=340502&r1=340501&r2=340502&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h Wed Aug 22 17:26:59 2018
@@ -98,7 +98,7 @@ private:
/// The kind of object being tracked (CF or ObjC), if known.
///
/// See the RetEffect::ObjKind enum for possible values.
- unsigned RawObjectKind : 2;
+ unsigned RawObjectKind : 3;
/// True if the current state and/or retain count may turn out to not be the
/// best possible approximation of the reference counting state.
@@ -268,6 +268,8 @@ class RetainCountChecker
mutable std::unique_ptr<RetainSummaryManager> Summaries;
mutable SummaryLogTy SummaryLog;
+
+ AnalyzerOptions &Options;
mutable bool ShouldResetSummaryLog;
/// Optional setting to indicate if leak reports should include
@@ -275,12 +277,17 @@ class RetainCountChecker
mutable bool IncludeAllocationLine;
public:
- RetainCountChecker(AnalyzerOptions &AO)
- : ShouldResetSummaryLog(false),
- IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {}
+ RetainCountChecker(AnalyzerOptions &Options)
+ : Options(Options), ShouldResetSummaryLog(false),
+ IncludeAllocationLine(
+ shouldIncludeAllocationSiteInLeakDiagnostics(Options)) {}
~RetainCountChecker() override { DeleteContainerSeconds(DeadSymbolTags); }
+ bool shouldCheckOSObjectRetainCount() const {
+ return Options.getBooleanOption("CheckOSObject", false, this);
+ }
+
void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
ExprEngine &Eng) const {
// FIXME: This is a hack to make sure the summary log gets cleared between
@@ -333,10 +340,12 @@ public:
// FIXME: We don't support ARC being turned on and off during one analysis.
// (nor, for that matter, do we support changing ASTContexts)
bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount;
- if (!Summaries)
- Summaries.reset(new RetainSummaryManager(Ctx, ARCEnabled));
- else
+ if (!Summaries) {
+ Summaries.reset(new RetainSummaryManager(
+ Ctx, ARCEnabled, shouldCheckOSObjectRetainCount()));
+ } else {
assert(Summaries->isARCEnabled() == ARCEnabled);
+ }
return *Summaries;
}
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp?rev=340502&r1=340501&r2=340502&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp Wed Aug 22 17:26:59 2018
@@ -120,6 +120,9 @@ CFRefReportVisitor::VisitNode(const Expl
if (CurrV.getObjKind() == RetEffect::CF) {
os << " returns a Core Foundation object of type "
<< Sym->getType().getAsString() << " with a ";
+ } else if (CurrV.getObjKind() == RetEffect::OS) {
+ os << " returns an OSObject of type "
+ << Sym->getType().getAsString() << " with a ";
} else if (CurrV.getObjKind() == RetEffect::Generalized) {
os << " returns an object of type " << Sym->getType().getAsString()
<< " with a ";
Modified: cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp?rev=340502&r1=340501&r2=340502&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp Wed Aug 22 17:26:59 2018
@@ -53,6 +53,31 @@ RetainSummaryManager::getPersistentSumma
return Summ;
}
+static bool isOSObjectSubclass(QualType T);
+
+static bool isOSObjectSubclass(const CXXRecordDecl *RD) {
+ if (RD->getDeclName().getAsString() == "OSObject")
+ return true;
+
+ const CXXRecordDecl *RDD = RD->getDefinition();
+ if (!RDD)
+ return false;
+
+ for (const CXXBaseSpecifier Spec : RDD->bases()) {
+ if (isOSObjectSubclass(Spec.getType()))
+ return true;
+ }
+ return false;
+}
+
+/// \return Whether type represents an OSObject successor.
+static bool isOSObjectSubclass(QualType T) {
+ if (const auto *RD = T->getAsCXXRecordDecl()) {
+ return isOSObjectSubclass(RD);
+ }
+ return false;
+}
+
static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) {
for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
if (Ann->getAnnotation() == rcAnnotation)
@@ -196,6 +221,17 @@ RetainSummaryManager::generateSummary(co
}
if (RetTy->isPointerType()) {
+ if (TrackOSObjects && isOSObjectSubclass(RetTy->getPointeeType())) {
+ if (const IdentifierInfo *II = FD->getIdentifier()) {
+ StringRef FuncName = II->getName();
+ if (FuncName.contains_lower("with")
+ || FuncName.contains_lower("create")
+ || FuncName.contains_lower("copy"))
+ return getOSSummaryCreateRule(FD);
+ }
+ return getOSSummaryGetRule(FD);
+ }
+
// For CoreFoundation ('CF') types.
if (cocoa::isRefType(RetTy, "CF", FName)) {
if (isRetain(FD, FName)) {
@@ -241,6 +277,17 @@ RetainSummaryManager::generateSummary(co
}
}
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const CXXRecordDecl *Parent = MD->getParent();
+ if (TrackOSObjects && isOSObjectSubclass(Parent)) {
+ if (isRelease(FD, FName))
+ return getOSSummaryReleaseRule(FD);
+
+ if (isRetain(FD, FName))
+ return getOSSummaryRetainRule(FD);
+ }
+ }
+
// Check for release functions, the only kind of functions that we care
// about that don't return a pointer type.
if (FName.size() >= 2 && FName[0] == 'C' &&
@@ -279,6 +326,14 @@ RetainSummaryManager::generateSummary(co
}
}
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+
+ // Stop tracking arguments passed to C++ methods, as those might be
+ // wrapping smart pointers.
+ return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking,
+ DoNothing);
+ }
+
return getDefaultSummary();
}
@@ -411,6 +466,8 @@ RetainSummaryManager::getSummary(const C
Summ = getFunctionSummary(cast<SimpleFunctionCall>(Call).getDecl());
break;
case CE_CXXMember:
+ Summ = getFunctionSummary(cast<CXXMemberCall>(Call).getDecl());
+ break;
case CE_CXXMemberOperator:
case CE_Block:
case CE_CXXConstructor:
@@ -514,6 +571,32 @@ RetainSummaryManager::getUnarySummary(co
}
const RetainSummary *
+RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ /*ReceiverEff=*/DoNothing,
+ /*DefaultEff=*/DoNothing,
+ /*ThisEff=*/IncRef);
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ /*ReceiverEff=*/DoNothing,
+ /*DefaultEff=*/DoNothing,
+ /*ThisEff=*/DecRef);
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeOwned(RetEffect::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::OS));
+}
+
+const RetainSummary *
RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
assert (ScratchArgs.isEmpty());
@@ -877,7 +960,7 @@ void RetainSummaryManager::InitializeMet
CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) {
ASTContext &Ctx = MD->getASTContext();
LangOptions L = Ctx.getLangOpts();
- RetainSummaryManager M(Ctx, L.ObjCAutoRefCount);
+ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false);
const RetainSummary *S = M.getMethodSummary(MD);
CallEffects CE(S->getRetEffect());
CE.Receiver = S->getReceiverEffect();
@@ -891,7 +974,7 @@ CallEffects CallEffects::getEffect(const
CallEffects CallEffects::getEffect(const FunctionDecl *FD) {
ASTContext &Ctx = FD->getASTContext();
LangOptions L = Ctx.getLangOpts();
- RetainSummaryManager M(Ctx, L.ObjCAutoRefCount);
+ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false);
const RetainSummary *S = M.getFunctionSummary(FD);
CallEffects CE(S->getRetEffect());
unsigned N = FD->param_size();
Added: cfe/trunk/test/Analysis/osobject-retain-release.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/osobject-retain-release.cpp?rev=340502&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/osobject-retain-release.cpp (added)
+++ cfe/trunk/test/Analysis/osobject-retain-release.cpp Wed Aug 22 17:26:59 2018
@@ -0,0 +1,105 @@
+// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-config osx.cocoa.RetainCount:CheckOSObject=true -analyzer-output=text -verify %s
+
+struct OSObject {
+ virtual void retain();
+ virtual void release();
+
+ virtual ~OSObject(){}
+};
+
+struct OSArray : public OSObject {
+ unsigned int getCount();
+
+ static OSArray *withCapacity(unsigned int capacity);
+};
+
+void use_after_release() {
+ OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
+ arr->release(); // expected-note{{Object released}}
+ arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
+ // expected-note at -1{{Reference-counted object is used after it is released}}
+}
+
+void potential_leak() {
+ OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct 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}}
+ arr->getCount();
+} // expected-warning{{Potential leak of an object stored into 'arr'}}
+ // expected-note at -1{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}}
+
+void proper_cleanup() {
+ OSArray *arr = OSArray::withCapacity(10); // +1
+ arr->retain(); // +2
+ arr->release(); // +1
+ arr->getCount();
+ arr->release(); // 0
+}
+
+struct ArrayOwner {
+ OSArray *arr;
+
+ OSArray *getArray() {
+ return arr;
+ }
+
+ OSArray *createArray() {
+ return OSArray::withCapacity(10);
+ }
+
+ OSArray *createArraySourceUnknown();
+
+ OSArray *getArraySourceUnknown();
+};
+
+//unsigned int leak_on_create_no_release(ArrayOwner *owner) {
+ //OSArray *myArray =
+
+//}
+
+unsigned int no_warning_on_getter(ArrayOwner *owner) {
+ OSArray *arr = owner->getArray();
+ return arr->getCount();
+}
+
+unsigned int warn_on_overrelease(ArrayOwner *owner) {
+ OSArray *arr = owner->getArray(); // expected-note{{function call returns an OSObject of type struct OSArray * with a +0 retain count}}
+ arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+ // expected-note at -1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+ return arr->getCount();
+}
+
+unsigned int nowarn_on_release_of_created(ArrayOwner *owner) {
+ OSArray *arr = owner->createArray();
+ unsigned int out = arr->getCount();
+ arr->release();
+ return out;
+}
+
+unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) {
+ OSArray *arr = owner->createArraySourceUnknown();
+ unsigned int out = arr->getCount();
+ arr->release();
+ return out;
+}
+
+unsigned int no_warn_ok_release(ArrayOwner *owner) {
+ OSArray *arr = owner->getArray(); // +0
+ arr->retain(); // +1
+ arr->release(); // +0
+ return arr->getCount(); // no-warning
+}
+
+unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
+ OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{function call returns an OSObject of type struct OSArray * with a +0 retain count}}
+ arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+ // expected-note at -1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+ return arr->getCount();
+}
+
+unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
+ OSArray *arr = owner->getArraySourceUnknown(); // +0
+ arr->retain(); // +1
+ arr->release(); // +0
+ return arr->getCount();
+}
More information about the cfe-commits
mailing list