TSHttpSsnReenable() can resume a session on the wrong network thread. Its fast path checks that the caller is an ET_NET thread, but does not check that it is the session's assigned thread.
Trigger
- A session-start hook pauses a session on its owner thread A.
- A plugin later calls
TSHttpSsnReenable() from another network thread B.
- B acquires the session mutex. ATS immediately calls the session handler on B, without returning to A.
- The next session hook runs on B.
A callback from TS_THREAD_POOL_TASK does not trigger this branch: that path already schedules back to the session's owner. The problem is a call from a different network thread.
Reproduction
Use at least two network threads and register two TS_HTTP_SSN_START_HOOK callbacks:
- The first records thread A and returns without reenabling the session.
- Use
TSContScheduleOnThread() to call TSHttpSsnReenable() on B after the first hook returns.
- Record the executing thread in the second hook.
Observed on an ATS 11.0.0 debug build; addresses are replaced with A/B:
session first owner=A other=B
session resume owner=A current=B
session second owner=A current=B wrong_thread=1
Confirmed: the second session hook runs on the wrong thread. No ATS core changes were needed. The probe then returns to A before completing session startup; it does not reproduce an HTTP/2 crash.
The same API branch exists in 10.1.2, but that version was not run.
Agreed fix direction
After discussing this with community members, we agreed that work protected by a NetHandler mutex should run on that NetHandler's owning network thread. Acquiring a mutex on another thread does not establish connection ownership.
- Caller is on another thread: compare against the session's exact affinity thread and schedule the callback there with the required locks. This applies even when the caller is another ET_NET thread.
- Caller is already on the owner: preserve synchronous execution with the required locks and the existing lock-failure fallback.
- Task-thread callers remain supported: retain the existing return to the session's owner from
TS_THREAD_POOL_TASK.
TSHttpTxnReenable() already follows the exact-affinity approach. The preferred fix is to enforce it at the session reenable entry point, before session processing can run on the wrong thread. The related NetHandler mutex audit is described in #13743.
Impact
After the hooks finish, session startup runs synchronously. For HTTP/2, startup can immediately process buffered input. This provides a potential route into connection processing on the wrong thread; that consequence was traced in source, not reproduced as a crash. We have not confirmed that this path caused #13358.
Source references and related issues
Found while investigating #13358 and #13362. We have not identified a plugin in that deployment that triggers this path. The shipped callers examined reenable synchronously. Certifier uses TSVConnReenable(), covered separately in #13743.
TSHttpSsnReenable()can resume a session on the wrong network thread. Its fast path checks that the caller is anET_NETthread, but does not check that it is the session's assigned thread.Trigger
TSHttpSsnReenable()from another network thread B.A callback from
TS_THREAD_POOL_TASKdoes not trigger this branch: that path already schedules back to the session's owner. The problem is a call from a different network thread.Reproduction
Use at least two network threads and register two
TS_HTTP_SSN_START_HOOKcallbacks:TSContScheduleOnThread()to callTSHttpSsnReenable()on B after the first hook returns.Observed on an ATS 11.0.0 debug build; addresses are replaced with A/B:
Confirmed: the second session hook runs on the wrong thread. No ATS core changes were needed. The probe then returns to A before completing session startup; it does not reproduce an HTTP/2 crash.
The same API branch exists in 10.1.2, but that version was not run.
Agreed fix direction
After discussing this with community members, we agreed that work protected by a NetHandler mutex should run on that NetHandler's owning network thread. Acquiring a mutex on another thread does not establish connection ownership.
TS_THREAD_POOL_TASK.TSHttpTxnReenable()already follows the exact-affinity approach. The preferred fix is to enforce it at the session reenable entry point, before session processing can run on the wrong thread. The related NetHandler mutex audit is described in #13743.Impact
After the hooks finish, session startup runs synchronously. For HTTP/2, startup can immediately process buffered input. This provides a potential route into connection processing on the wrong thread; that consequence was traced in source, not reproduced as a crash. We have not confirmed that this path caused #13358.
Source references and related issues
TSHttpSsnReenable()skips the affinity check when an ET_NET caller acquires the session mutex.TSHttpTxnReenable()already checks the exact owner thread.ProxySession::handle_api_return()callsstart()directly.Http2ClientSession::start()can process buffered input immediately.Found while investigating #13358 and #13362. We have not identified a plugin in that deployment that triggers this path. The shipped callers examined reenable synchronously. Certifier uses
TSVConnReenable(), covered separately in #13743.