* FAILED: patch "[PATCH] ntfs3: free the sbi after a grace period" failed to apply to 6.12-stable tree
@ 2026-10-21 14:38 gregkh
2026-10-21 15:58 ` [PATCH 6.12.y] ntfs3: free the sbi after a grace period Sasha Levin
0 siblings, 1 reply; 2+ messages in thread
From: gregkh @ 2026-10-21 14:38 UTC (permalink / raw)
To: brauner; +Cc: stable
The 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 <stable@vger.kernel.org>.
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 '<stable@vger.kernel.org>' --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 <brauner@kernel.org>
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.
Fixes: a3a956c78efa ("fs/ntfs3: Add option "nocase"")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261009-work-fixes-rcu-v1-7-7446d2d335e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 5811d89d67b3..bbcd2e02bf63 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -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)
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index f4a42a0c73a4..841f9068d82f 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 6.12.y] ntfs3: free the sbi after a grace period
2026-10-21 14:38 FAILED: patch "[PATCH] ntfs3: free the sbi after a grace period" failed to apply to 6.12-stable tree gregkh
@ 2026-10-21 15:58 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-10-21 15:58 UTC (permalink / raw)
To: stable; +Cc: Christian Brauner, Sasha Levin
From: Christian Brauner <brauner@kernel.org>
[ 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.
Fixes: a3a956c78efa ("fs/ntfs3: Add option "nocase"")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261009-work-fixes-rcu-v1-7-7446d2d335e6@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
[ sashal: Adjusted ntfs_put_super() context because the early mount-options cleanup block was already absent. ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/ntfs3/ntfs_fs.h | 1 +
fs/ntfs3/super.c | 29 ++++++++++++++++++++++-------
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 11be9a6f041cf..934dcb136d6da 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -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)
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 4b1941e3f3a67..c7c1896acc2c5 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -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
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-21 15:58 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-21 14:38 FAILED: patch "[PATCH] ntfs3: free the sbi after a grace period" failed to apply to 6.12-stable tree gregkh
2026-10-21 15:58 ` [PATCH 6.12.y] ntfs3: free the sbi after a grace period Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox