* FAILED: patch "[PATCH] ntfs3: free the sbi after a grace period" failed to apply to 6.6-stable tree
@ 2026-10-21 14:38 gregkh
2026-10-21 21:37 ` [PATCH 6.6.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.6-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.6.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-strife-renewable-5171@gregkh' --subject-prefix 'PATCH 6.6.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.6.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.6-stable tree gregkh
@ 2026-10-21 21:37 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-10-21 21:37 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: Omitted removal of mount-option cleanup already absent from ntfs_put_super(). ]
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 5e7536ad0b94c..8850ee2b75b11 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 15b1f2027b4ed..3b7731b188302 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -183,7 +183,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;
@@ -201,7 +201,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;
}
@@ -218,7 +218,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)
@@ -226,7 +226,7 @@ void *ntfs_put_shared(void *ptr)
break;
}
}
- spin_unlock(&s_shared_lock);
+ spin_unlock_bh(&s_shared_lock);
return ret;
}
@@ -624,6 +624,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);
@@ -635,6 +637,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;
@@ -651,6 +658,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);
}
@@ -1807,9 +1820,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 21:37 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.6-stable tree gregkh
2026-10-21 21:37 ` [PATCH 6.6.y] ntfs3: free the sbi after a grace period Sasha Levin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.