From: Ilya Dryomov Date: Wed, 11 Mar 2026 11:04:24 +0000 (+0100) Subject: librbd/migration/QCOWFormat: avoid use-after-free in execute_request() X-Git-Tag: v21.0.0~16^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F67919%2Fhead;p=ceph.git 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 --- 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,