statmount: read the parent of an unmounted mount under mount_lock

statmount_mnt_basic() reads the ids of the parent through m->mnt_parent
with namespace_sem held for reading. That's fine for a mount that is
in a namespace. Everything above it is pinned by that namespace.

STATMOUNT_BY_FD also accepts a file descriptor on a mount that has
been unmounted on purpose and that only the file descriptor keeps alive.
If that mount stayed attached to its parent when it was unmounted the
parent is unmounted as well and nothing pins it for the child.

The final mntput() unmounts the children under mount_lock alone and
frees the parent afterwards. Concurrently, statmount_mnt_basic() reads
m->mnt_parent twice while that can happen and can easily dereference a
mount that's already gone.

Read the parent of an unmounted mount under mount_lock. The parent
can't be put while it is held and both ids come from the same mount. A
mounted mount's parent is pinned by the namespace and a mount can't
leave its namespace while namespace_sem is held, so the common case
takes no global lock.

Fixes: 0e5032237e ("statmount: accept fd as a parameter")
Cc: stable@vger.kernel.org # v7.0+
Link: https://patch.msgid.link/20260926-work-mount-fixes-2-v1-2-f357abf3d17b@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-09-26 15:44:25 +02:00
parent 476ef286a7
commit bb46094057
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -5335,15 +5335,25 @@ static void statmount_sb_basic(struct kstatmount *s)
s->sm.sb_flags = sb->s_flags & (SB_RDONLY|SB_SYNCHRONOUS|SB_DIRSYNC|SB_LAZYTIME); s->sm.sb_flags = sb->s_flags & (SB_RDONLY|SB_SYNCHRONOUS|SB_DIRSYNC|SB_LAZYTIME);
} }
static void statmount_mnt_parent(struct kstatmount *s, const struct mount *m)
{
s->sm.mnt_parent_id = m->mnt_parent->mnt_id_unique;
s->sm.mnt_parent_id_old = m->mnt_parent->mnt_id;
}
static void statmount_mnt_basic(struct kstatmount *s) static void statmount_mnt_basic(struct kstatmount *s)
{ {
struct mount *m = real_mount(s->mnt); struct mount *m = real_mount(s->mnt);
s->sm.mask |= STATMOUNT_MNT_BASIC; s->sm.mask |= STATMOUNT_MNT_BASIC;
s->sm.mnt_id = m->mnt_id_unique; s->sm.mnt_id = m->mnt_id_unique;
s->sm.mnt_parent_id = m->mnt_parent->mnt_id_unique;
s->sm.mnt_id_old = m->mnt_id; s->sm.mnt_id_old = m->mnt_id;
s->sm.mnt_parent_id_old = m->mnt_parent->mnt_id; /* An unmounted mount is cut loose from its parent under mount_lock alone. */
if (likely(is_mounted(s->mnt)))
statmount_mnt_parent(s, m);
else
scoped_guard(mount_locked_reader)
statmount_mnt_parent(s, m);
s->sm.mnt_attr = mnt_to_attr_flags(&m->mnt); s->sm.mnt_attr = mnt_to_attr_flags(&m->mnt);
s->sm.mnt_propagation = mnt_to_propagation_flags(m); s->sm.mnt_propagation = mnt_to_propagation_flags(m);
s->sm.mnt_peer_group = m->mnt_group_id; s->sm.mnt_peer_group = m->mnt_group_id;