librbd: fix use-after-free releasing object map locks in deep copy
DeepCopyRequest::send_copy_object_map() holds the destination image's owner_lock and image_lock (shared), issues an asynchronous object_map->rollback(), and then releases the two locks through m_dst_image_ctx. The rollback callback drives the rest of the request to completion (handle_copy_object_map() ... finish() -> put()) on other threads. That path needs image_lock, so it only proceeds once this method releases it, and can then run to completion and free 'this' before the following owner_lock.unlock_shared() executes, dereferencing the freed DeepCopyRequest. ASan reported this as a heap-use-after-free in TestImageReplayer.StartReplayAndWrite: the object was freed on the finisher thread (handle_copy_metadata() -> finish()) while the io_context thread was still in send_copy_object_map(). The destination image ctx outlives the request, so operate through a local reference to it and release the locks through that reference rather than through 'this', as operation/SnapshotRollbackRequest::send_rollback_object_map() already does. Keep the explicit lock_shared()/unlock_shared() calls rather than scoped std::shared_lock guards: unlike operation/SnapshotRollbackRequest::send_rollback_object_map(), send_copy_object_map() has four exit paths (send_copy_metadata(), send_refresh_object_map(), finish(), and the object map rollback), each of which must release the locks before running a different continuation, so RAII guards would push that continuation out of the locked scope and read less clearly than unlocking at each exit. Fixes: https://tracker.ceph.com/issues/77551 Signed-off-by: Kefu Chai <k.chai@proxmox.com>
K
Kefu Chai committed
ecd883a1b3924b981042df3903cb1dc6b55d5920
Parent: c5693b3