From: Kefu Chai Date: Fri, 27 Feb 2026 04:57:34 +0000 (+0800) Subject: common/async: fix stack-use-after-scope in co_waiter X-Git-Tag: v21.0.0~31^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=b760a940d275fcf9e9a2a9e38f1253f4937ecd76;p=ceph.git common/async: fix stack-use-after-scope in co_waiter When co_waiter is destroyed, the cancellation slot may still hold a reference to the op_cancellation callback which captures 'this'. If the cancellation signal is emitted after co_waiter is destroyed (e.g., during co_throttle shutdown), it results in a stack-use-after-scope error. Fix by adding a destructor that retrieves the cancellation slot from the handler (if still active) and clears it before destruction. This ensures the cancellation callback is removed before the co_waiter object goes out of scope, preventing use-after-scope errors. The cancellation slot cannot be stored as a member variable because it becomes invalid after the handler is moved out in complete(). Instead, we retrieve it on demand from the handler in the destructor, which is the only place we need it. Signed-off-by: Kefu Chai --- diff --git a/src/common/async/co_waiter.h b/src/common/async/co_waiter.h index 3e035f86368..ca587396ef1 100644 --- a/src/common/async/co_waiter.h +++ b/src/common/async/co_waiter.h @@ -46,6 +46,16 @@ class co_waiter { public: co_waiter() = default; + ~co_waiter() { + // Clear the cancellation slot to prevent use-after-scope + if (handler) { + auto slot = boost::asio::get_associated_cancellation_slot(*handler); + if (slot.is_connected()) { + slot.clear(); + } + } + } + // copy and move are disabled because the cancellation handler captures 'this' co_waiter(const co_waiter&) = delete; co_waiter& operator=(const co_waiter&) = delete; @@ -116,6 +126,16 @@ class co_waiter { public: co_waiter() = default; + ~co_waiter() { + // Clear the cancellation slot to prevent use-after-scope + if (handler) { + auto slot = boost::asio::get_associated_cancellation_slot(*handler); + if (slot.is_connected()) { + slot.clear(); + } + } + } + // copy and move are disabled because the cancellation handler captures 'this' co_waiter(const co_waiter&) = delete; co_waiter& operator=(const co_waiter&) = delete;