Skip to content

Commit 4540680

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Fix memory leak in UnauthenticatedSessionTable. (#30025)
UnauthenticatedSessionTable essentially assumed that non-heap pools were used and was: 1) Never releasing its entries back to the pool. 2) Assuming that the pool would fill up and then its "reuse already allocated entry with zero refcount" code would kick in. Since heap pools never fill up, this meant that every single UnauthenticatedSession allocated was leaked. And we had a helpful "release them all on destruction" to cover up the leak at shutdown and prevent leak tools from finding it. This fix: * Preserves existing behavior for non-heap pools. * Switches to releasing UnauthenticatedSessions back to the pool in the heap case.
1 parent f387381 commit 4540680

File tree

1 file changed

+91
-14
lines changed

1 file changed

+91
-14
lines changed

src/transport/UnauthenticatedSessionTable.h

+91-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <lib/support/CodeUtils.h>
2323
#include <lib/support/Pool.h>
2424
#include <messaging/ReliableMessageProtocolConfig.h>
25+
#include <system/SystemConfig.h>
2526
#include <system/TimeSource.h>
2627
#include <transport/PeerMessageCounter.h>
2728
#include <transport/Session.h>
@@ -34,8 +35,7 @@ namespace Transport {
3435
* @brief
3536
* An UnauthenticatedSession stores the binding of TransportAddress, and message counters.
3637
*/
37-
class UnauthenticatedSession : public Session,
38-
public ReferenceCounted<UnauthenticatedSession, NoopDeletor<UnauthenticatedSession>, 0>
38+
class UnauthenticatedSession : public Session, public ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>
3939
{
4040
public:
4141
enum class SessionRole
@@ -44,6 +44,7 @@ class UnauthenticatedSession : public Session,
4444
kResponder,
4545
};
4646

47+
protected:
4748
UnauthenticatedSession(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID, const ReliableMessageProtocolConfig & config) :
4849
mEphemeralInitiatorNodeId(ephemeralInitiatorNodeID), mSessionRole(sessionRole),
4950
mLastActivityTime(System::SystemClock().GetMonotonicTimestamp()),
@@ -52,6 +53,7 @@ class UnauthenticatedSession : public Session,
5253
{}
5354
~UnauthenticatedSession() override { VerifyOrDie(GetReferenceCount() == 0); }
5455

56+
public:
5557
UnauthenticatedSession(const UnauthenticatedSession &) = delete;
5658
UnauthenticatedSession & operator=(const UnauthenticatedSession &) = delete;
5759
UnauthenticatedSession(UnauthenticatedSession &&) = delete;
@@ -68,8 +70,8 @@ class UnauthenticatedSession : public Session,
6870

6971
Session::SessionType GetSessionType() const override { return Session::SessionType::kUnauthenticated; }
7072

71-
void Retain() override { ReferenceCounted<UnauthenticatedSession, NoopDeletor<UnauthenticatedSession>, 0>::Retain(); }
72-
void Release() override { ReferenceCounted<UnauthenticatedSession, NoopDeletor<UnauthenticatedSession>, 0>::Release(); }
73+
void Retain() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Retain(); }
74+
void Release() override { ReferenceCounted<UnauthenticatedSession, UnauthenticatedSession, 0>::Release(); }
7375

7476
bool IsActiveSession() const override { return true; }
7577

@@ -132,6 +134,23 @@ class UnauthenticatedSession : public Session,
132134

133135
PeerMessageCounter & GetPeerMessageCounter() { return mPeerMessageCounter; }
134136

137+
static void Release(UnauthenticatedSession * obj)
138+
{
139+
// When using heap pools, we need to make sure to release ourselves back to
140+
// the pool. When not using heap pools, we don't want the extra cost of the
141+
// table pointer here, and the table itself handles entry reuse and cleanup
142+
// as needed.
143+
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
144+
obj->ReleaseSelfToPool();
145+
#else
146+
// Just do nothing.
147+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
148+
}
149+
150+
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
151+
virtual void ReleaseSelfToPool() = 0;
152+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
153+
135154
private:
136155
const NodeId mEphemeralInitiatorNodeId;
137156
const SessionRole mSessionRole;
@@ -142,6 +161,35 @@ class UnauthenticatedSession : public Session,
142161
PeerMessageCounter mPeerMessageCounter;
143162
};
144163

164+
template <size_t kMaxSessionCount>
165+
class UnauthenticatedSessionTable;
166+
167+
namespace detail {
168+
169+
template <size_t kMaxSessionCount>
170+
class UnauthenticatedSessionPoolEntry : public UnauthenticatedSession
171+
{
172+
public:
173+
UnauthenticatedSessionPoolEntry(SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
174+
const ReliableMessageProtocolConfig & config,
175+
UnauthenticatedSessionTable<kMaxSessionCount> & sessionTable) :
176+
UnauthenticatedSession(sessionRole, ephemeralInitiatorNodeID, config)
177+
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
178+
,
179+
mSessionTable(sessionTable)
180+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
181+
{}
182+
183+
private:
184+
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
185+
virtual void ReleaseSelfToPool();
186+
187+
UnauthenticatedSessionTable<kMaxSessionCount> & mSessionTable;
188+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
189+
};
190+
191+
} // namespace detail
192+
145193
/*
146194
* @brief
147195
* An table which manages UnauthenticatedSessions
@@ -153,7 +201,17 @@ template <size_t kMaxSessionCount>
153201
class UnauthenticatedSessionTable
154202
{
155203
public:
156-
~UnauthenticatedSessionTable() { mEntries.ReleaseAll(); }
204+
~UnauthenticatedSessionTable()
205+
{
206+
#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
207+
// When not using heap pools, our entries never actually get released
208+
// back to the pool (which lets us make the entries 4 bytes smaller by
209+
// not storing a reference to the table in them) and we LRU reuse ones
210+
// that have 0 refcount. But we should release them all here, to ensure
211+
// that we don't hit fatal asserts in our pool destructor.
212+
mEntries.ReleaseAll();
213+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
214+
}
157215

158216
/**
159217
* Get a responder session with the given ephemeralInitiatorNodeID. If the session doesn't exist in the cache, allocate a new
@@ -203,6 +261,9 @@ class UnauthenticatedSessionTable
203261
}
204262

205263
private:
264+
using EntryType = detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>;
265+
friend EntryType;
266+
206267
/**
207268
* Allocates a new session out of the internal resource pool.
208269
*
@@ -213,17 +274,23 @@ class UnauthenticatedSessionTable
213274
CHIP_ERROR AllocEntry(UnauthenticatedSession::SessionRole sessionRole, NodeId ephemeralInitiatorNodeID,
214275
const ReliableMessageProtocolConfig & config, UnauthenticatedSession *& entry)
215276
{
216-
entry = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, config);
217-
if (entry != nullptr)
277+
auto entryToUse = mEntries.CreateObject(sessionRole, ephemeralInitiatorNodeID, config, *this);
278+
if (entryToUse != nullptr)
279+
{
280+
entry = entryToUse;
218281
return CHIP_NO_ERROR;
282+
}
219283

220-
entry = FindLeastRecentUsedEntry();
221-
if (entry == nullptr)
284+
#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
285+
entryToUse = FindLeastRecentUsedEntry();
286+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
287+
if (entryToUse == nullptr)
222288
{
223289
return CHIP_ERROR_NO_MEMORY;
224290
}
225291

226-
mEntries.ResetObject(entry, sessionRole, ephemeralInitiatorNodeID, config);
292+
mEntries.ResetObject(entryToUse, sessionRole, ephemeralInitiatorNodeID, config, *this);
293+
entry = entryToUse;
227294
return CHIP_NO_ERROR;
228295
}
229296

@@ -242,12 +309,12 @@ class UnauthenticatedSessionTable
242309
return result;
243310
}
244311

245-
UnauthenticatedSession * FindLeastRecentUsedEntry()
312+
EntryType * FindLeastRecentUsedEntry()
246313
{
247-
UnauthenticatedSession * result = nullptr;
314+
EntryType * result = nullptr;
248315
System::Clock::Timestamp oldestTime = System::Clock::Timestamp(std::numeric_limits<System::Clock::Timestamp::rep>::max());
249316

250-
mEntries.ForEachActiveObject([&](UnauthenticatedSession * entry) {
317+
mEntries.ForEachActiveObject([&](EntryType * entry) {
251318
if (entry->GetReferenceCount() == 0 && entry->GetLastActivityTime() < oldestTime)
252319
{
253320
result = entry;
@@ -259,8 +326,18 @@ class UnauthenticatedSessionTable
259326
return result;
260327
}
261328

262-
ObjectPool<UnauthenticatedSession, kMaxSessionCount> mEntries;
329+
void ReleaseEntry(EntryType * entry) { mEntries.ReleaseObject(entry); }
330+
331+
ObjectPool<EntryType, kMaxSessionCount> mEntries;
263332
};
264333

334+
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
335+
template <size_t kMaxSessionCount>
336+
void detail::UnauthenticatedSessionPoolEntry<kMaxSessionCount>::ReleaseSelfToPool()
337+
{
338+
mSessionTable.ReleaseEntry(this);
339+
}
340+
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
341+
265342
} // namespace Transport
266343
} // namespace chip

0 commit comments

Comments
 (0)