From ef93632ae44fb2c458f3f59244ca01300b9190d0 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Wed, 11 Mar 2026 12:04:24 +0100 Subject: [PATCH] librbd/migration/QCOWFormat: avoid use-after-free in execute_request() Both L2TableCache and QCOWFormat can be destroyed after the completion for the last L2 cache request is posted, particularly so in unit tests. The strand destructor doesn't drain the handler queue in any way but merely ensures that previously posted handlers would get dispatched in a non-concurrent fashion. As a result, use-after-free can ensue when execute_request() unnecessarily dispatches itself for the last time. Fixes: https://tracker.ceph.com/issues/75378 Signed-off-by: Ilya Dryomov --- src/librbd/migration/QCOWFormat.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librbd/migration/QCOWFormat.cc b/src/librbd/migration/QCOWFormat.cc index b011d33a03e6..fc13beed0242 100644 --- a/src/librbd/migration/QCOWFormat.cc +++ b/src/librbd/migration/QCOWFormat.cc @@ -411,13 +411,19 @@ private: ceph_assert(false); } + requests.pop_front(); + bool more = !requests.empty(); + // complete the L2 cache request + // expect to be destroyed after posting completion if there are no + // more requests boost::asio::post(*qcow_format->m_image_ctx->asio_engine, [r, ctx=request.on_finish]() { ctx->complete(r); }); - requests.pop_front(); // process next request (if any) - dispatch_request(); + if (more) { + dispatch_request(); + } } int l2_table_lookup(uint64_t l2_offset, -- 2.47.3