FAILED: patch "[PATCH] ntfs3: free the sbi after a grace period" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x c01b091b06412d3b1b53d8c2925f6de586c8a2fc
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026102158-quantum-annex-72cd@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c01b091b06412d3b1b53d8c2925f6de586c8a2fc Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Fri, 9 Oct 2026 23:51:57 +0200
Subject: [PATCH] ntfs3: free the sbi after a grace period
If the "nocase" mount option ntfs3 uses custom hash and dentry compare functions. Both access ntfs3 superblock private information in rcu pathwalk. ntfs_put_super() and ntfs3_kill_sb() drop the info without any rcu delay. A lookup that's still inside the filesystem when the last reference to the mount is dropped causes a UAF. With a pathwalk held up in ntfs_d_hash() while the last reference to the mount goes away:
BUG: KASAN: slab-use-after-free in ntfs_nls_to_utf16+0x752/0x940 Read of size 8 at addr ff1100010453c850 by task walker/99 Call Trace: <TASK> ntfs_nls_to_utf16+0x752/0x940 ntfs_d_hash+0x22b/0x500 link_path_walk+0x11ad/0x1d30 path_lookupat+0x63/0xb00 filename_lookup+0x1f1/0x5b0 vfs_statx+0xd7/0x3b0 [...] Allocated by task 95: __kmalloc_cache_noprof+0x249/0x560 ntfs_init_fs_context+0x2db/0x5a0 alloc_fs_context+0x52e/0xc70 [...] Freed by task 98: kfree+0x1db/0x5b0 deactivate_locked_super+0x9c/0xf0 cleanup_mnt+0x9f/0x130 task_work_run+0x131/0x250
The bug is currently masked by the grace period in namespace_unlock() for umount(2). Keep the necessary information until the filesystem private info is freed and free it with an rcu delay.
ntfs3_free_sbi() now runs from an rcu callback and drops the upcase table via ntfs_put_shared(). So switch s_shared_lock over to spin_lock_bh(). Otherwise the callback can deadlock against a task holding the lock on the same cpu.
fs/ntfs3/ntfs_fs.h +1 -0
@@ -211,6 +211,7 @@ struct ntfs_index {/* Ntfs file system in-core superblock data. */ struct ntfs_sb_info {struct super_block *sb; +struct rcu_head rcu;u32 discard_granularity;u64 discard_granularity_mask_inv;// ~(discard_granularity_mask_inv-1)
fs/ntfs3/super.c +21 -12
@@ -196,7 +196,7 @@ void *ntfs_set_shared(void *ptr, u32 bytes)void *ret =NULL ;int i, j =-1 ; -spin_lock (&s_shared_lock); +spin_lock_bh (&s_shared_lock);for (i =0 ; i <ARRAY_SIZE (s_shared); i++) {if (!s_shared[i].cnt) { j = i;
@@ -214,7 +214,7 @@ void *ntfs_set_shared(void *ptr, u32 bytes) s_shared[j].cnt =1 ; ret = ptr; } -spin_unlock (&s_shared_lock); +spin_unlock_bh (&s_shared_lock);return ret; }
@@ -231,7 +231,7 @@ void *ntfs_put_shared(void *ptr)void *ret = ptr;int i; -spin_lock (&s_shared_lock); +spin_lock_bh (&s_shared_lock);for (i =0 ; i <ARRAY_SIZE (s_shared); i++) {if (s_shared[i].cnt && s_shared[i].ptr == ptr) {if (--s_shared[i].cnt)
@@ -239,7 +239,7 @@ void *ntfs_put_shared(void *ptr)break ; } } -spin_unlock (&s_shared_lock); +spin_unlock_bh (&s_shared_lock);return ret; }
@@ -708,6 +708,8 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)static void ntfs3_free_sbi (struct ntfs_sb_info *sbi) { +if (sbi->options) +put_mount_options (sbi->options);kfree (sbi->new_rec);kvfree (ntfs_put_shared (sbi->upcase));kvfree (sbi->def_table);
@@ -719,6 +721,11 @@ static void ntfs3_free_sbi(struct ntfs_sb_info *sbi)kfree (sbi); } +static void ntfs3_free_sbi_rcu (struct rcu_head *head) +{ +ntfs3_free_sbi (container_of(head ,struct ntfs_sb_info ,rcu )); +} +static void ntfs_put_super (struct super_block *sb) {struct ntfs_sb_info *sbi = sb->s_fs_info;
@@ -728,11 +735,11 @@ static void ntfs_put_super(struct super_block *sb)/* Mark rw ntfs as clear, if possible. */ ntfs_set_state (sbi,NTFS_DIRTY_CLEAR ); -if (sbi->options) { -put_mount_options (sbi->options); - sbi->options =NULL ; - } - +/* +* The mount options stay until the sbi is freed: ->d_hash() and +* ->d_compare() of a pathwalk in rcu mode read the nls table through +* them. +*/ ntfs3_put_sbi (sbi); }
@@ -1939,9 +1946,11 @@ static void ntfs3_kill_sb(struct super_block *sb)kill_block_super (sb); -if (sbi->options) -put_mount_options (sbi->options); -ntfs3_free_sbi (sbi); +/* +* A pathwalk in rcu mode may still be in ->d_hash() or ->d_compare() +* and read the upcase table and the nls table through the sbi. +*/ +call_rcu (&sbi->rcu, ntfs3_free_sbi_rcu); }// clang-format off
Sasha Levin
maintainerPATCH 1/1 ntfs3: free the sbi after a grace period
From: Christian Brauner <[email protected]>
[ Upstream commit c01b091b06412d3b1b53d8c2925f6de586c8a2fc ]
If the "nocase" mount option ntfs3 uses custom hash and dentry compare functions. Both access ntfs3 superblock private information in rcu pathwalk. ntfs_put_super() and ntfs3_kill_sb() drop the info without any rcu delay. A lookup that's still inside the filesystem when the last reference to the mount is dropped causes a UAF. With a pathwalk held up in ntfs_d_hash() while the last reference to the mount goes away:
BUG: KASAN: slab-use-after-free in ntfs_nls_to_utf16+0x752/0x940 Read of size 8 at addr ff1100010453c850 by task walker/99 Call Trace: <TASK> ntfs_nls_to_utf16+0x752/0x940 ntfs_d_hash+0x22b/0x500 link_path_walk+0x11ad/0x1d30 path_lookupat+0x63/0xb00 filename_lookup+0x1f1/0x5b0 vfs_statx+0xd7/0x3b0 [...] Allocated by task 95: __kmalloc_cache_noprof+0x249/0x560 ntfs_init_fs_context+0x2db/0x5a0 alloc_fs_context+0x52e/0xc70 [...] Freed by task 98: kfree+0x1db/0x5b0 deactivate_locked_super+0x9c/0xf0 cleanup_mnt+0x9f/0x130 task_work_run+0x131/0x250
The bug is currently masked by the grace period in namespace_unlock() for umount(2). Keep the necessary information until the filesystem private info is freed and free it with an rcu delay.
ntfs3_free_sbi() now runs from an rcu callback and drops the upcase table via ntfs_put_shared(). So switch s_shared_lock over to spin_lock_bh(). Otherwise the callback can deadlock against a task holding the lock on the same cpu.
--- fs/ntfs3/ntfs_fs.h | 1 + fs/ntfs3/super.c | 29 ++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-)
fs/ntfs3/ntfs_fs.h +1 -0
@@ -209,6 +209,7 @@ struct ntfs_index {/* Ntfs file system in-core superblock data. */ struct ntfs_sb_info {struct super_block *sb; +struct rcu_head rcu;u32 discard_granularity;u64 discard_granularity_mask_inv;// ~(discard_granularity_mask_inv-1)
fs/ntfs3/super.c +22 -7
@@ -187,7 +187,7 @@ void *ntfs_set_shared(void *ptr, u32 bytes)void *ret =NULL ;int i, j =-1 ; -spin_lock (&s_shared_lock); +spin_lock_bh (&s_shared_lock);for (i =0 ; i <ARRAY_SIZE (s_shared); i++) {if (!s_shared[i].cnt) { j = i;
@@ -205,7 +205,7 @@ void *ntfs_set_shared(void *ptr, u32 bytes) s_shared[j].cnt =1 ; ret = ptr; } -spin_unlock (&s_shared_lock); +spin_unlock_bh (&s_shared_lock);return ret; }
@@ -222,7 +222,7 @@ void *ntfs_put_shared(void *ptr)void *ret = ptr;int i; -spin_lock (&s_shared_lock); +spin_lock_bh (&s_shared_lock);for (i =0 ; i <ARRAY_SIZE (s_shared); i++) {if (s_shared[i].cnt && s_shared[i].ptr == ptr) {if (--s_shared[i].cnt)
@@ -230,7 +230,7 @@ void *ntfs_put_shared(void *ptr)break ; } } -spin_unlock (&s_shared_lock); +spin_unlock_bh (&s_shared_lock);return ret; }
@@ -630,6 +630,8 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)static void ntfs3_free_sbi (struct ntfs_sb_info *sbi) { +if (sbi->options) +put_mount_options (sbi->options);kfree (sbi->new_rec);kvfree (ntfs_put_shared (sbi->upcase));kvfree (sbi->def_table);
@@ -641,6 +643,11 @@ static void ntfs3_free_sbi(struct ntfs_sb_info *sbi)kfree (sbi); } +static void ntfs3_free_sbi_rcu (struct rcu_head *head) +{ +ntfs3_free_sbi (container_of(head ,struct ntfs_sb_info ,rcu )); +} +static void ntfs_put_super (struct super_block *sb) {struct ntfs_sb_info *sbi = sb->s_fs_info;
@@ -657,6 +664,12 @@ static void ntfs_put_super(struct super_block *sb)/* Mark rw ntfs as clear, if possible. */ ntfs_set_state (sbi,NTFS_DIRTY_CLEAR ); + +/* +* The mount options stay until the sbi is freed: ->d_hash() and +* ->d_compare() of a pathwalk in rcu mode read the nls table through +* them. +*/ ntfs3_put_sbi (sbi); }
@@ -1806,9 +1819,11 @@ static void ntfs3_kill_sb(struct super_block *sb)kill_block_super (sb); -if (sbi->options) -put_mount_options (sbi->options); -ntfs3_free_sbi (sbi); +/* +* A pathwalk in rcu mode may still be in ->d_hash() or ->d_compare() +* and read the upcase table and the nls table through the sbi. +*/ +call_rcu (&sbi->rcu, ntfs3_free_sbi_rcu); }// clang-format off
Signature · 1 line
2.53.0