]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commit
journal/ObjectPlayer: don't acquire locks in destructor 68613/head
authorKefu Chai <k.chai@proxmox.com>
Sun, 14 Jun 2026 06:52:00 +0000 (14:52 +0800)
committerKefu Chai <k.chai@proxmox.com>
Thu, 18 Jun 2026 11:42:49 +0000 (19:42 +0800)
commita35a92639f5dc8d6a98415711242f2491fb45f46
treea0e8be10b1041c01ec8bbea9c937fb392c136e2b
parente4cf2f0c8485d04183d8f1c5cf31dee60042e078
journal/ObjectPlayer: don't acquire locks in destructor

~ObjectPlayer took m_timer_lock only to assert two invariants. but that
lock is borrowed by reference from the caller's SafeTimer, and an
ObjectPlayer can outlive it: a C_Fetch/C_WatchFetch completion on the
librados finisher may hold the last reference and run ~ObjectPlayer after
the timer and its lock are already gone. re-taking the freed lock is a
heap-use-after-free, which unittest_journal hits on arm64 under ASan:

  ==ERROR: AddressSanitizer: heap-use-after-free
    journal::ObjectPlayer::~ObjectPlayer  ObjectPlayer.cc:63
    journal::ObjectPlayer::C_WatchFetch::~C_WatchFetch
    journal::ObjectPlayer::C_Fetch::finish

the lock isn't needed though: at refcount 0 the watch has been cancelled
(m_watch_ctx == nullptr, asserted below) so no timer task references us, and
no fetch is in flight since a pending fetch holds a reference. nothing else
can touch our state. Furthermore, we can also skip acquiring `m_lock` as
well, because, in the destructor, it shouldn't really matter -- if one
of these asserts fails because the execution of the destructor races
with some `ObjectPlayer` mthod, we would get what the `assert()` was
added for. They are here to catch bugs and such a race just being
possible is a bug in itself.

Fixes: https://tracker.ceph.com/issues/77496
Signed-off-by: Kefu Chai <k.chai@proxmox.com>
src/journal/ObjectPlayer.cc