]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
common/async: fix stack-use-after-scope in co_waiter 67568/head
authorKefu Chai <k.chai@proxmox.com>
Fri, 27 Feb 2026 04:57:34 +0000 (12:57 +0800)
committerKefu Chai <k.chai@proxmox.com>
Fri, 27 Feb 2026 11:25:51 +0000 (19:25 +0800)
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 <k.chai@proxmox.com>
src/common/async/co_waiter.h

index 3e035f86368d3cf2dbd7731a913ee5e1b97f87a2..ca587396ef1661ba434dd875785b0c325125e7b0 100644 (file)
@@ -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<void, Executor> {
  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;