ImageUpdateWatchers::flush() requests aren't tracked with
m_in_flight-like mechanism the way ImageUpdateWatchers::send_notify()
requests are, but in both cases callbacks that represent delayed work
that is very likely to (indirectly) reference ImageCtx are involved.
When the image is getting closed, ImageUpdateWatchers::shut_down() is
called before anything that belongs to ImageCtx is destroyed. However,
the shutdown can complete prematurely in the face of a pending flush if
one gets sent shortly before CloseRequest is invoked. The callback for
that flush will then race with CloseRequest and may execute after parts
of or even the entire ImageCtx is destroyed, leading to use-after-free
and various segfaults.
Fixes: https://tracker.ceph.com/issues/75161
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
(cherry picked from commit
3ea6ee62aa339d1ad9976fdcc6e207a505f9bf44)
void shut_down(Context *on_finish) {
ldout(m_cct, 20) << "ImageUpdateWatchers::" << __func__ << dendl;
+ if (m_work_queue != nullptr) {
+ // ensure all pending flush() callbacks execute before completing
+ on_finish = create_async_context_callback(m_work_queue, on_finish);
+ }
{
std::lock_guard locker{m_lock};
ceph_assert(m_on_shut_down_finish == nullptr);
return;
}
}
- ldout(m_cct, 20) << "ImageUpdateWatchers::" << __func__
- << ": completing shut down" << dendl;
on_finish->complete(0);
}
}
if (on_shut_down_finish != nullptr) {
- ldout(m_cct, 20) << "ImageUpdateWatchers::" << __func__
- << ": completing shut down" << dendl;
on_shut_down_finish->complete(0);
}
}