Linux: Fix zfs_prune panics v2 (#17121)

It turns out that approach taken in the original version of the patch
was wrong. So now, we're taking approach in-line with how kernel
actually does it - when sb is being torn down, access to it
is serialized via sb->s_umount rwsem, only when that lock is taken
is it okay to work with s_flags - and the other mistake I was doing
was trying to make SB_ACTIVE work, but apparently the kernel checks
the negative variant - not SB_DYING and not SB_BORN.

Kernels pre-6.6 don't have SB_DYING, but check if sb is hashed
instead.

Signed-off-by: Pavel Snajdr <snajpa@snajpa.net>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
Pavel Snajdr
2025-03-25 23:20:16 +01:00
committed by GitHub
parent 9611dfdc70
commit a0e62718cf
3 changed files with 38 additions and 9 deletions
+17 -9
View File
@@ -377,17 +377,25 @@ zpl_prune_sb(uint64_t nr_to_scan, void *arg)
int objects = 0;
/*
* deactivate_locked_super calls shrinker_free and only then
* sops->kill_sb cb, resulting in UAF on umount when trying to reach
* for the shrinker functions in zpl_prune_sb of in-umount dataset.
* Increment if s_active is not zero, but don't prune if it is -
* umount could be underway.
* Ensure the superblock is not in the process of being torn down.
*/
if (atomic_inc_not_zero(&sb->s_active)) {
(void) -zfs_prune(sb, nr_to_scan, &objects);
atomic_dec(&sb->s_active);
#ifdef HAVE_SB_DYING
if (down_read_trylock(&sb->s_umount)) {
if (!(sb->s_flags & SB_DYING) && sb->s_root &&
(sb->s_flags & SB_BORN)) {
(void) zfs_prune(sb, nr_to_scan, &objects);
}
up_read(&sb->s_umount);
}
#else
if (down_read_trylock(&sb->s_umount)) {
if (!hlist_unhashed(&sb->s_instances) &&
sb->s_root && (sb->s_flags & SB_BORN)) {
(void) zfs_prune(sb, nr_to_scan, &objects);
}
up_read(&sb->s_umount);
}
#endif
}
const struct super_operations zpl_super_operations = {