Skip to content

Commit 279b83c

Browse files
committed
Merge tag 'fs.fixes.v5.18-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux
Pull mount_setattr fix from Christian Brauner: "The recent cleanup in e257039 ("mount_setattr(): clean the control flow and calling conventions") switched the mount attribute codepaths from do-while to for loops as they are more idiomatic when walking mounts. However, we did originally choose do-while constructs because if we request a mount or mount tree to be made read-only we need to hold writers in the following way: The mount attribute code will grab lock_mount_hash() and then call mnt_hold_writers() which will _unconditionally_ set MNT_WRITE_HOLD on the mount. Any callers that need write access have to call mnt_want_write(). They will immediately see that MNT_WRITE_HOLD is set on the mount and the caller will then either spin (on non-preempt-rt) or wait on lock_mount_hash() (on preempt-rt). The fact that MNT_WRITE_HOLD is set unconditionally means that once mnt_hold_writers() returns we need to _always_ pair it with mnt_unhold_writers() in both the failure and success paths. The do-while constructs did take care of this. But Al's change to a for loop in the failure path stops on the first mount we failed to change mount attributes _without_ going into the loop to call mnt_unhold_writers(). This in turn means that once we failed to make a mount read-only via mount_setattr() - i.e. there are already writers on that mount - we will block any writers indefinitely. Fix this by ensuring that the for loop always unsets MNT_WRITE_HOLD including the first mount we failed to change to read-only. Also sprinkle a few comments into the cleanup code to remind people about what is happening including myself. After all, I didn't catch it during review. This is only relevant on mainline and was reported by syzbot. Details about the syzbot reports are all in the commit message" * tag 'fs.fixes.v5.18-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux: fs: unset MNT_WRITE_HOLD on failure
2 parents 2d23096 + 0014eda commit 279b83c

File tree

1 file changed

+13
-1
lines changed

1 file changed

+13
-1
lines changed

fs/namespace.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4058,10 +4058,22 @@ static int mount_setattr_prepare(struct mount_kattr *kattr, struct mount *mnt)
40584058
if (err) {
40594059
struct mount *p;
40604060

4061-
for (p = mnt; p != m; p = next_mnt(p, mnt)) {
4061+
/*
4062+
* If we had to call mnt_hold_writers() MNT_WRITE_HOLD will
4063+
* be set in @mnt_flags. The loop unsets MNT_WRITE_HOLD for all
4064+
* mounts and needs to take care to include the first mount.
4065+
*/
4066+
for (p = mnt; p; p = next_mnt(p, mnt)) {
40624067
/* If we had to hold writers unblock them. */
40634068
if (p->mnt.mnt_flags & MNT_WRITE_HOLD)
40644069
mnt_unhold_writers(p);
4070+
4071+
/*
4072+
* We're done once the first mount we changed got
4073+
* MNT_WRITE_HOLD unset.
4074+
*/
4075+
if (p == m)
4076+
break;
40654077
}
40664078
}
40674079
return err;

0 commit comments

Comments
 (0)