* [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses()
@ 2023-10-30 20:19 Paulo Alcantara
2023-10-30 20:19 ` [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() Paulo Alcantara
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Paulo Alcantara @ 2023-10-30 20:19 UTC (permalink / raw)
To: smfrench; +Cc: linux-cifs, Paulo Alcantara
If @ses->chan_count <= 1, then for-loop body will not be executed so
no need to check it twice.
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
---
fs/smb/client/connect.c | 23 +++++++++--------------
1 file changed, 9 insertions(+), 14 deletions(-)
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 7b923e36501b..a017ee552256 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1969,9 +1969,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
void __cifs_put_smb_ses(struct cifs_ses *ses)
{
- unsigned int rc, xid;
- unsigned int chan_count;
struct TCP_Server_Info *server = ses->server;
+ unsigned int xid;
+ size_t i;
+ int rc;
spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_EXITING) {
@@ -2017,20 +2018,14 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
list_del_init(&ses->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
- chan_count = ses->chan_count;
-
/* close any extra channels */
- if (chan_count > 1) {
- int i;
-
- for (i = 1; i < chan_count; i++) {
- if (ses->chans[i].iface) {
- kref_put(&ses->chans[i].iface->refcount, release_iface);
- ses->chans[i].iface = NULL;
- }
- cifs_put_tcp_session(ses->chans[i].server, 0);
- ses->chans[i].server = NULL;
+ for (i = 1; i < ses->chan_count; i++) {
+ if (ses->chans[i].iface) {
+ kref_put(&ses->chans[i].iface->refcount, release_iface);
+ ses->chans[i].iface = NULL;
}
+ cifs_put_tcp_session(ses->chans[i].server, 0);
+ ses->chans[i].server = NULL;
}
sesInfoFree(ses);
--
2.42.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() 2023-10-30 20:19 [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Paulo Alcantara @ 2023-10-30 20:19 ` Paulo Alcantara 2023-10-31 3:17 ` Steve French 2024-06-01 11:50 ` Wang Zhaolong 2023-10-30 20:19 ` [PATCH 3/4] smb: client: fix potential deadlock when releasing mids Paulo Alcantara ` (2 subsequent siblings) 3 siblings, 2 replies; 11+ messages in thread From: Paulo Alcantara @ 2023-10-30 20:19 UTC (permalink / raw) To: smfrench; +Cc: linux-cifs, Paulo Alcantara, Frank Sorenson Skip SMB sessions that are being teared down (e.g. @ses->ses_status == SES_EXITING) in cifs_debug_data_proc_show() to avoid use-after-free in @ses. This fixes the following GPF when reading from /proc/fs/cifs/DebugData while mounting and umounting [ 816.251274] general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6d81: 0000 [#1] PREEMPT SMP NOPTI ... [ 816.260138] Call Trace: [ 816.260329] <TASK> [ 816.260499] ? die_addr+0x36/0x90 [ 816.260762] ? exc_general_protection+0x1b3/0x410 [ 816.261126] ? asm_exc_general_protection+0x26/0x30 [ 816.261502] ? cifs_debug_tcon+0xbd/0x240 [cifs] [ 816.261878] ? cifs_debug_tcon+0xab/0x240 [cifs] [ 816.262249] cifs_debug_data_proc_show+0x516/0xdb0 [cifs] [ 816.262689] ? seq_read_iter+0x379/0x470 [ 816.262995] seq_read_iter+0x118/0x470 [ 816.263291] proc_reg_read_iter+0x53/0x90 [ 816.263596] ? srso_alias_return_thunk+0x5/0x7f [ 816.263945] vfs_read+0x201/0x350 [ 816.264211] ksys_read+0x75/0x100 [ 816.264472] do_syscall_64+0x3f/0x90 [ 816.264750] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 [ 816.265135] RIP: 0033:0x7fd5e669d381 Cc: Frank Sorenson <sorenson@redhat.com> Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> --- fs/smb/client/cifs_debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c index 76922fcc4bc6..9a0ccd87468e 100644 --- a/fs/smb/client/cifs_debug.c +++ b/fs/smb/client/cifs_debug.c @@ -452,6 +452,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_printf(m, "\n\n\tSessions: "); i = 0; list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + continue; + } i++; if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || @@ -472,6 +477,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ses->ses_count, ses->serverOS, ses->serverNOS, ses->capabilities, ses->ses_status); } + spin_unlock(&ses->ses_lock); seq_printf(m, "\n\tSecurity type: %s ", get_security_type_str(server->ops->select_sectype(server, ses->sectype))); -- 2.42.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() 2023-10-30 20:19 ` [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() Paulo Alcantara @ 2023-10-31 3:17 ` Steve French 2024-06-01 11:50 ` Wang Zhaolong 1 sibling, 0 replies; 11+ messages in thread From: Steve French @ 2023-10-31 3:17 UTC (permalink / raw) To: Paulo Alcantara; +Cc: linux-cifs, Frank Sorenson fix was already in cifs-2.6.git for-next Added Cc: stable to it though Let me know if you see something missing (or if patch changed from the previous one eg) On Mon, Oct 30, 2023 at 3:20 PM Paulo Alcantara <pc@manguebit.com> wrote: > > Skip SMB sessions that are being teared down > (e.g. @ses->ses_status == SES_EXITING) in cifs_debug_data_proc_show() > to avoid use-after-free in @ses. > > This fixes the following GPF when reading from /proc/fs/cifs/DebugData > while mounting and umounting > > [ 816.251274] general protection fault, probably for non-canonical > address 0x6b6b6b6b6b6b6d81: 0000 [#1] PREEMPT SMP NOPTI > ... > [ 816.260138] Call Trace: > [ 816.260329] <TASK> > [ 816.260499] ? die_addr+0x36/0x90 > [ 816.260762] ? exc_general_protection+0x1b3/0x410 > [ 816.261126] ? asm_exc_general_protection+0x26/0x30 > [ 816.261502] ? cifs_debug_tcon+0xbd/0x240 [cifs] > [ 816.261878] ? cifs_debug_tcon+0xab/0x240 [cifs] > [ 816.262249] cifs_debug_data_proc_show+0x516/0xdb0 [cifs] > [ 816.262689] ? seq_read_iter+0x379/0x470 > [ 816.262995] seq_read_iter+0x118/0x470 > [ 816.263291] proc_reg_read_iter+0x53/0x90 > [ 816.263596] ? srso_alias_return_thunk+0x5/0x7f > [ 816.263945] vfs_read+0x201/0x350 > [ 816.264211] ksys_read+0x75/0x100 > [ 816.264472] do_syscall_64+0x3f/0x90 > [ 816.264750] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 > [ 816.265135] RIP: 0033:0x7fd5e669d381 > > Cc: Frank Sorenson <sorenson@redhat.com> > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> > --- > fs/smb/client/cifs_debug.c | 6 ++++++ > 1 file changed, 6 insertions(+) > > diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c > index 76922fcc4bc6..9a0ccd87468e 100644 > --- a/fs/smb/client/cifs_debug.c > +++ b/fs/smb/client/cifs_debug.c > @@ -452,6 +452,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) > seq_printf(m, "\n\n\tSessions: "); > i = 0; > list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { > + spin_lock(&ses->ses_lock); > + if (ses->ses_status == SES_EXITING) { > + spin_unlock(&ses->ses_lock); > + continue; > + } > i++; > if ((ses->serverDomain == NULL) || > (ses->serverOS == NULL) || > @@ -472,6 +477,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) > ses->ses_count, ses->serverOS, ses->serverNOS, > ses->capabilities, ses->ses_status); > } > + spin_unlock(&ses->ses_lock); > > seq_printf(m, "\n\tSecurity type: %s ", > get_security_type_str(server->ops->select_sectype(server, ses->sectype))); > -- > 2.42.0 > -- Thanks, Steve ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() 2023-10-30 20:19 ` [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() Paulo Alcantara 2023-10-31 3:17 ` Steve French @ 2024-06-01 11:50 ` Wang Zhaolong 1 sibling, 0 replies; 11+ messages in thread From: Wang Zhaolong @ 2024-06-01 11:50 UTC (permalink / raw) To: Paulo Alcantara, smfrench; +Cc: linux-cifs, Frank Sorenson Hello, I encountered some confusion while reviewing the source code related to CVE-2023-52752. I was able to reproduce the issue, and the original problem seems to be: --- process 1 process 2(read /proc/fs/cifs/DebugData) cifs_umount cifs_put_tlink cifs_put_tcon cifs_put_smb_ses cifs_debug_data_proc_show spin_unlock(&cifs_tcp_ses_lock) spin_lock(&cifs_tcp_ses_lock); list_for_each...(ses,server->smb_ses_list,...) cifs_free_ipc tconInfoFree(tcon) if (ses->tcon_ipc) cifs_debug_tcon(m,ses->tcon_ipc) // UAF ses->tcon_ipc = NULLl spin_unlock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock) list_del_init(&ses->smb_ses_list) spin_unlock(&cifs_tcp_ses_lock) --- In commit ff7d80a9f271 ("cifs: fix session state transition to avoid use-after-free issue"), setting ses_status to SES_EXITING was moved under the protection of cifs_tcp_ses_lock. In cifs_debug_data_proc_show(), the logic that checks ses->ses_status == SES_EXITING already seems sufficient to avoid this issue. Therefore, it appears that ses->ses_lock might not be necessary. Additionally, I am curious why ses->ses_lock needs to cover such a large scope. > diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c > index 76922fcc4bc6..9a0ccd87468e 100644 > --- a/fs/smb/client/cifs_debug.c > +++ b/fs/smb/client/cifs_debug.c > @@ -452,6 +452,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) > seq_printf(m, "\n\n\tSessions: "); > i = 0; > list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { > + spin_lock(&ses->ses_lock); > + if (ses->ses_status == SES_EXITING) { > + spin_unlock(&ses->ses_lock); > + continue; > + } > i++; > if ((ses->serverDomain == NULL) || > (ses->serverOS == NULL) || > @@ -472,6 +477,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) > ses->ses_count, ses->serverOS, ses->serverNOS, > ses->capabilities, ses->ses_status); > } > + spin_unlock(&ses->ses_lock); > > seq_printf(m, "\n\tSecurity type: %s ", > get_security_type_str(server->ops->select_sectype(server, ses->sectype))); I believe in the latest mainline, this could potentially be modified to: ``` diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c index c71ae5c04306..2d9e83b71643 100644 --- a/fs/smb/client/cifs_debug.c +++ b/fs/smb/client/cifs_debug.c @@ -485,11 +485,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_printf(m, "\n\n\tSessions: "); i = 0; list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); + if (cifs_ses_exiting(ses)) continue; - } i++; if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || @@ -512,7 +509,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) } if (ses->expired_pwd) seq_puts(m, "password no longer valid "); - spin_unlock(&ses->ses_lock); seq_printf(m, "\n\tSecurity type: %s ", get_security_type_str(server->ops->select_sectype(server, ses->sectype))); ``` Best regards, Wang Zhaolong ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/4] smb: client: fix potential deadlock when releasing mids 2023-10-30 20:19 [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Paulo Alcantara 2023-10-30 20:19 ` [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() Paulo Alcantara @ 2023-10-30 20:19 ` Paulo Alcantara 2023-10-31 3:23 ` Steve French 2023-10-30 20:19 ` [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() Paulo Alcantara 2023-10-31 3:24 ` [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Steve French 3 siblings, 1 reply; 11+ messages in thread From: Paulo Alcantara @ 2023-10-30 20:19 UTC (permalink / raw) To: smfrench; +Cc: linux-cifs, Paulo Alcantara, Frank Sorenson All release_mid() callers seem to hold a reference of @mid so there is no need to call kref_put(&mid->refcount, __release_mid) under @server->mid_lock spinlock. If they don't, then an use-after-free bug would have occurred anyway. By getting rid of such spinlock also fixes a potential deadlock as shown below CPU 0 CPU 1 ------------------------------------------------------------------ cifs_demultiplex_thread() cifs_debug_data_proc_show() release_mid() spin_lock(&server->mid_lock); spin_lock(&cifs_tcp_ses_lock) spin_lock(&server->mid_lock) __release_mid() smb2_find_smb_tcon() spin_lock(&cifs_tcp_ses_lock) *deadlock* Cc: Frank Sorenson <sorenson@redhat.com> Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> --- fs/smb/client/cifsproto.h | 7 ++++++- fs/smb/client/smb2misc.c | 2 +- fs/smb/client/transport.c | 11 +---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index 0c37eefa18a5..890ceddae07e 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -81,7 +81,7 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, extern char *build_wildcard_path_from_dentry(struct dentry *direntry); char *cifs_build_devname(char *nodename, const char *prepath); extern void delete_mid(struct mid_q_entry *mid); -extern void release_mid(struct mid_q_entry *mid); +void __release_mid(struct kref *refcount); extern void cifs_wake_up_task(struct mid_q_entry *mid); extern int cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid); @@ -740,4 +740,9 @@ static inline bool dfs_src_pathname_equal(const char *s1, const char *s2) return true; } +static inline void release_mid(struct mid_q_entry *mid) +{ + kref_put(&mid->refcount, __release_mid); +} + #endif /* _CIFSPROTO_H */ diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 25f7cd6f23d6..32dfa0f7a78c 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -787,7 +787,7 @@ __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, { struct close_cancelled_open *cancelled; - cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); + cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL); if (!cancelled) return -ENOMEM; diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 14710afdc2a3..d553b7a54621 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -76,7 +76,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) return temp; } -static void __release_mid(struct kref *refcount) +void __release_mid(struct kref *refcount) { struct mid_q_entry *midEntry = container_of(refcount, struct mid_q_entry, refcount); @@ -156,15 +156,6 @@ static void __release_mid(struct kref *refcount) mempool_free(midEntry, cifs_mid_poolp); } -void release_mid(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->server; - - spin_lock(&server->mid_lock); - kref_put(&mid->refcount, __release_mid); - spin_unlock(&server->mid_lock); -} - void delete_mid(struct mid_q_entry *mid) { -- 2.42.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 3/4] smb: client: fix potential deadlock when releasing mids 2023-10-30 20:19 ` [PATCH 3/4] smb: client: fix potential deadlock when releasing mids Paulo Alcantara @ 2023-10-31 3:23 ` Steve French 0 siblings, 0 replies; 11+ messages in thread From: Steve French @ 2023-10-31 3:23 UTC (permalink / raw) To: Paulo Alcantara; +Cc: linux-cifs, Frank Sorenson fix was already in cifs-2.6.git for-next, but added Cc: stable Let me know if I missed anything On Mon, Oct 30, 2023 at 3:20 PM Paulo Alcantara <pc@manguebit.com> wrote: > > All release_mid() callers seem to hold a reference of @mid so there is > no need to call kref_put(&mid->refcount, __release_mid) under > @server->mid_lock spinlock. If they don't, then an use-after-free bug > would have occurred anyway. > > By getting rid of such spinlock also fixes a potential deadlock as > shown below > > CPU 0 CPU 1 > ------------------------------------------------------------------ > cifs_demultiplex_thread() cifs_debug_data_proc_show() > release_mid() > spin_lock(&server->mid_lock); > spin_lock(&cifs_tcp_ses_lock) > spin_lock(&server->mid_lock) > __release_mid() > smb2_find_smb_tcon() > spin_lock(&cifs_tcp_ses_lock) *deadlock* > > Cc: Frank Sorenson <sorenson@redhat.com> > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> > --- > fs/smb/client/cifsproto.h | 7 ++++++- > fs/smb/client/smb2misc.c | 2 +- > fs/smb/client/transport.c | 11 +---------- > 3 files changed, 8 insertions(+), 12 deletions(-) > > diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h > index 0c37eefa18a5..890ceddae07e 100644 > --- a/fs/smb/client/cifsproto.h > +++ b/fs/smb/client/cifsproto.h > @@ -81,7 +81,7 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, > extern char *build_wildcard_path_from_dentry(struct dentry *direntry); > char *cifs_build_devname(char *nodename, const char *prepath); > extern void delete_mid(struct mid_q_entry *mid); > -extern void release_mid(struct mid_q_entry *mid); > +void __release_mid(struct kref *refcount); > extern void cifs_wake_up_task(struct mid_q_entry *mid); > extern int cifs_handle_standard(struct TCP_Server_Info *server, > struct mid_q_entry *mid); > @@ -740,4 +740,9 @@ static inline bool dfs_src_pathname_equal(const char *s1, const char *s2) > return true; > } > > +static inline void release_mid(struct mid_q_entry *mid) > +{ > + kref_put(&mid->refcount, __release_mid); > +} > + > #endif /* _CIFSPROTO_H */ > diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c > index 25f7cd6f23d6..32dfa0f7a78c 100644 > --- a/fs/smb/client/smb2misc.c > +++ b/fs/smb/client/smb2misc.c > @@ -787,7 +787,7 @@ __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, > { > struct close_cancelled_open *cancelled; > > - cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); > + cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL); > if (!cancelled) > return -ENOMEM; > > diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c > index 14710afdc2a3..d553b7a54621 100644 > --- a/fs/smb/client/transport.c > +++ b/fs/smb/client/transport.c > @@ -76,7 +76,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) > return temp; > } > > -static void __release_mid(struct kref *refcount) > +void __release_mid(struct kref *refcount) > { > struct mid_q_entry *midEntry = > container_of(refcount, struct mid_q_entry, refcount); > @@ -156,15 +156,6 @@ static void __release_mid(struct kref *refcount) > mempool_free(midEntry, cifs_mid_poolp); > } > > -void release_mid(struct mid_q_entry *mid) > -{ > - struct TCP_Server_Info *server = mid->server; > - > - spin_lock(&server->mid_lock); > - kref_put(&mid->refcount, __release_mid); > - spin_unlock(&server->mid_lock); > -} > - > void > delete_mid(struct mid_q_entry *mid) > { > -- > 2.42.0 > -- Thanks, Steve ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() 2023-10-30 20:19 [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Paulo Alcantara 2023-10-30 20:19 ` [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() Paulo Alcantara 2023-10-30 20:19 ` [PATCH 3/4] smb: client: fix potential deadlock when releasing mids Paulo Alcantara @ 2023-10-30 20:19 ` Paulo Alcantara 2023-10-31 3:09 ` Steve French 2023-10-31 3:24 ` [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Steve French 3 siblings, 1 reply; 11+ messages in thread From: Paulo Alcantara @ 2023-10-30 20:19 UTC (permalink / raw) To: smfrench; +Cc: linux-cifs, Paulo Alcantara The following UAF was triggered when running fstests generic/072 with KASAN enabled against Windows Server 2022 and mount options 'multichannel,max_channels=2,vers=3.1.1,mfsymlinks,noperm' BUG: KASAN: slab-use-after-free in smb2_query_info_compound+0x423/0x6d0 [cifs] Read of size 8 at addr ffff888014941048 by task xfs_io/27534 CPU: 0 PID: 27534 Comm: xfs_io Not tainted 6.6.0-rc7 #1 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014 Call Trace: dump_stack_lvl+0x4a/0x80 print_report+0xcf/0x650 ? srso_alias_return_thunk+0x5/0x7f ? srso_alias_return_thunk+0x5/0x7f ? __phys_addr+0x46/0x90 kasan_report+0xda/0x110 ? smb2_query_info_compound+0x423/0x6d0 [cifs] ? smb2_query_info_compound+0x423/0x6d0 [cifs] smb2_query_info_compound+0x423/0x6d0 [cifs] ? __pfx_smb2_query_info_compound+0x10/0x10 [cifs] ? srso_alias_return_thunk+0x5/0x7f ? __stack_depot_save+0x39/0x480 ? kasan_save_stack+0x33/0x60 ? kasan_set_track+0x25/0x30 ? ____kasan_slab_free+0x126/0x170 smb2_queryfs+0xc2/0x2c0 [cifs] ? __pfx_smb2_queryfs+0x10/0x10 [cifs] ? __pfx___lock_acquire+0x10/0x10 smb311_queryfs+0x210/0x220 [cifs] ? __pfx_smb311_queryfs+0x10/0x10 [cifs] ? srso_alias_return_thunk+0x5/0x7f ? __lock_acquire+0x480/0x26c0 ? lock_release+0x1ed/0x640 ? srso_alias_return_thunk+0x5/0x7f ? do_raw_spin_unlock+0x9b/0x100 cifs_statfs+0x18c/0x4b0 [cifs] statfs_by_dentry+0x9b/0xf0 fd_statfs+0x4e/0xb0 __do_sys_fstatfs+0x7f/0xe0 ? __pfx___do_sys_fstatfs+0x10/0x10 ? srso_alias_return_thunk+0x5/0x7f ? lockdep_hardirqs_on_prepare+0x136/0x200 ? srso_alias_return_thunk+0x5/0x7f do_syscall_64+0x3f/0x90 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Allocated by task 27534: kasan_save_stack+0x33/0x60 kasan_set_track+0x25/0x30 __kasan_kmalloc+0x8f/0xa0 open_cached_dir+0x71b/0x1240 [cifs] smb2_query_info_compound+0x5c3/0x6d0 [cifs] smb2_queryfs+0xc2/0x2c0 [cifs] smb311_queryfs+0x210/0x220 [cifs] cifs_statfs+0x18c/0x4b0 [cifs] statfs_by_dentry+0x9b/0xf0 fd_statfs+0x4e/0xb0 __do_sys_fstatfs+0x7f/0xe0 do_syscall_64+0x3f/0x90 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Freed by task 27534: kasan_save_stack+0x33/0x60 kasan_set_track+0x25/0x30 kasan_save_free_info+0x2b/0x50 ____kasan_slab_free+0x126/0x170 slab_free_freelist_hook+0xd0/0x1e0 __kmem_cache_free+0x9d/0x1b0 open_cached_dir+0xff5/0x1240 [cifs] smb2_query_info_compound+0x5c3/0x6d0 [cifs] smb2_queryfs+0xc2/0x2c0 [cifs] This is a race between open_cached_dir() and cached_dir_lease_break() where the cache entry for the open directory handle receives a lease break while creating it. And before returning from open_cached_dir(), we put the last reference of the new @cfid because of !@cfid->has_lease. Besides the UAF, while running xfstests a lot of missed lease breaks have been noticed in tests that run several concurrent statfs(2) calls on those cached fids CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame... CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1... CIFS: VFS: \\w22-root1.gandalf.test smb buf 00000000715bfe83 len 108 CIFS: VFS: Dump pending requests: CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame... CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1... CIFS: VFS: \\w22-root1.gandalf.test smb buf 000000005aa7316e len 108 ... To fix both, in open_cached_dir() ensure that @cfid->has_lease is set right before sending out compounded request so that any potential lease break will be get processed by demultiplex thread while we're still caching @cfid. And, if open failed for some reason, re-check @cfid->has_lease to decide whether or not put lease reference. Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> --- fs/smb/client/cached_dir.c | 84 ++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index fe1bf5b6e0cb..59f6b8e32cc9 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -32,7 +32,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, * fully cached or it may be in the process of * being deleted due to a lease break. */ - if (!cfid->has_lease) { + if (!cfid->time || !cfid->has_lease) { spin_unlock(&cfids->cfid_list_lock); return NULL; } @@ -193,10 +193,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, npath = path_no_prefix(cifs_sb, path); if (IS_ERR(npath)) { rc = PTR_ERR(npath); - kfree(utf16_path); - return rc; + goto out; } + if (!npath[0]) { + dentry = dget(cifs_sb->root); + } else { + dentry = path_to_dentry(cifs_sb, npath); + if (IS_ERR(dentry)) { + rc = -ENOENT; + goto out; + } + } + cfid->dentry = dentry; + /* * We do not hold the lock for the open because in case * SMB2_open needs to reconnect. @@ -249,6 +259,15 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, smb2_set_related(&rqst[1]); + /* + * Set @cfid->has_lease to true before sending out compounded request so + * its lease reference can be put in cached_dir_lease_break() due to a + * potential lease break right after the request is sent or while @cfid + * is still being cached. Concurrent processes won't be to use it yet + * due to @cfid->time being zero. + */ + cfid->has_lease = true; + rc = compound_send_recv(xid, ses, server, flags, 2, rqst, resp_buftype, rsp_iov); @@ -263,6 +282,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, cfid->tcon = tcon; cfid->is_open = true; + spin_lock(&cfids->cfid_list_lock); + o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; oparms.fid->persistent_fid = o_rsp->PersistentFileId; oparms.fid->volatile_fid = o_rsp->VolatileFileId; @@ -270,18 +291,25 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); #endif /* CIFS_DEBUG2 */ - if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) + rc = -EINVAL; + if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) { + spin_unlock(&cfids->cfid_list_lock); goto oshr_free; + } smb2_parse_contexts(server, o_rsp, &oparms.fid->epoch, oparms.fid->lease_key, &oplock, NULL, NULL); - if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) + if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) { + spin_unlock(&cfids->cfid_list_lock); goto oshr_free; + } qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) + if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) { + spin_unlock(&cfids->cfid_list_lock); goto oshr_free; + } if (!smb2_validate_and_copy_iov( le16_to_cpu(qi_rsp->OutputBufferOffset), sizeof(struct smb2_file_all_info), @@ -289,37 +317,24 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, (char *)&cfid->file_all_info)) cfid->file_all_info_is_valid = true; - if (!npath[0]) - dentry = dget(cifs_sb->root); - else { - dentry = path_to_dentry(cifs_sb, npath); - if (IS_ERR(dentry)) { - rc = -ENOENT; - goto oshr_free; - } - } - spin_lock(&cfids->cfid_list_lock); - cfid->dentry = dentry; cfid->time = jiffies; - cfid->has_lease = true; spin_unlock(&cfids->cfid_list_lock); + /* At this point the directory handle is fully cached */ + rc = 0; oshr_free: - kfree(utf16_path); SMB2_open_free(&rqst[0]); SMB2_query_info_free(&rqst[1]); free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - spin_lock(&cfids->cfid_list_lock); - if (!cfid->has_lease) { - if (rc) { - if (cfid->on_list) { - list_del(&cfid->entry); - cfid->on_list = false; - cfids->num_entries--; - } - rc = -ENOENT; - } else { + if (rc) { + spin_lock(&cfids->cfid_list_lock); + if (cfid->on_list) { + list_del(&cfid->entry); + cfid->on_list = false; + cfids->num_entries--; + } + if (cfid->has_lease) { /* * We are guaranteed to have two references at this * point. One for the caller and one for a potential @@ -327,25 +342,24 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, * will be closed when the caller closes the cached * handle. */ + cfid->has_lease = false; spin_unlock(&cfids->cfid_list_lock); kref_put(&cfid->refcount, smb2_close_cached_fid); goto out; } + spin_unlock(&cfids->cfid_list_lock); } - spin_unlock(&cfids->cfid_list_lock); +out: if (rc) { if (cfid->is_open) SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, cfid->fid.volatile_fid); free_cached_dir(cfid); - cfid = NULL; - } -out: - if (rc == 0) { + } else { *ret_cfid = cfid; atomic_inc(&tcon->num_remote_opens); } - + kfree(utf16_path); return rc; } -- 2.42.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() 2023-10-30 20:19 ` [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() Paulo Alcantara @ 2023-10-31 3:09 ` Steve French 2023-11-04 12:23 ` Shyam Prasad N 0 siblings, 1 reply; 11+ messages in thread From: Steve French @ 2023-10-31 3:09 UTC (permalink / raw) To: Paulo Alcantara; +Cc: linux-cifs added Cc: stable and tentatively merged this to for-next On Mon, Oct 30, 2023 at 3:20 PM Paulo Alcantara <pc@manguebit.com> wrote: > > The following UAF was triggered when running fstests generic/072 with > KASAN enabled against Windows Server 2022 and mount options > 'multichannel,max_channels=2,vers=3.1.1,mfsymlinks,noperm' > > BUG: KASAN: slab-use-after-free in smb2_query_info_compound+0x423/0x6d0 [cifs] > Read of size 8 at addr ffff888014941048 by task xfs_io/27534 > > CPU: 0 PID: 27534 Comm: xfs_io Not tainted 6.6.0-rc7 #1 > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS > rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014 > Call Trace: > dump_stack_lvl+0x4a/0x80 > print_report+0xcf/0x650 > ? srso_alias_return_thunk+0x5/0x7f > ? srso_alias_return_thunk+0x5/0x7f > ? __phys_addr+0x46/0x90 > kasan_report+0xda/0x110 > ? smb2_query_info_compound+0x423/0x6d0 [cifs] > ? smb2_query_info_compound+0x423/0x6d0 [cifs] > smb2_query_info_compound+0x423/0x6d0 [cifs] > ? __pfx_smb2_query_info_compound+0x10/0x10 [cifs] > ? srso_alias_return_thunk+0x5/0x7f > ? __stack_depot_save+0x39/0x480 > ? kasan_save_stack+0x33/0x60 > ? kasan_set_track+0x25/0x30 > ? ____kasan_slab_free+0x126/0x170 > smb2_queryfs+0xc2/0x2c0 [cifs] > ? __pfx_smb2_queryfs+0x10/0x10 [cifs] > ? __pfx___lock_acquire+0x10/0x10 > smb311_queryfs+0x210/0x220 [cifs] > ? __pfx_smb311_queryfs+0x10/0x10 [cifs] > ? srso_alias_return_thunk+0x5/0x7f > ? __lock_acquire+0x480/0x26c0 > ? lock_release+0x1ed/0x640 > ? srso_alias_return_thunk+0x5/0x7f > ? do_raw_spin_unlock+0x9b/0x100 > cifs_statfs+0x18c/0x4b0 [cifs] > statfs_by_dentry+0x9b/0xf0 > fd_statfs+0x4e/0xb0 > __do_sys_fstatfs+0x7f/0xe0 > ? __pfx___do_sys_fstatfs+0x10/0x10 > ? srso_alias_return_thunk+0x5/0x7f > ? lockdep_hardirqs_on_prepare+0x136/0x200 > ? srso_alias_return_thunk+0x5/0x7f > do_syscall_64+0x3f/0x90 > entry_SYSCALL_64_after_hwframe+0x6e/0xd8 > > Allocated by task 27534: > kasan_save_stack+0x33/0x60 > kasan_set_track+0x25/0x30 > __kasan_kmalloc+0x8f/0xa0 > open_cached_dir+0x71b/0x1240 [cifs] > smb2_query_info_compound+0x5c3/0x6d0 [cifs] > smb2_queryfs+0xc2/0x2c0 [cifs] > smb311_queryfs+0x210/0x220 [cifs] > cifs_statfs+0x18c/0x4b0 [cifs] > statfs_by_dentry+0x9b/0xf0 > fd_statfs+0x4e/0xb0 > __do_sys_fstatfs+0x7f/0xe0 > do_syscall_64+0x3f/0x90 > entry_SYSCALL_64_after_hwframe+0x6e/0xd8 > > Freed by task 27534: > kasan_save_stack+0x33/0x60 > kasan_set_track+0x25/0x30 > kasan_save_free_info+0x2b/0x50 > ____kasan_slab_free+0x126/0x170 > slab_free_freelist_hook+0xd0/0x1e0 > __kmem_cache_free+0x9d/0x1b0 > open_cached_dir+0xff5/0x1240 [cifs] > smb2_query_info_compound+0x5c3/0x6d0 [cifs] > smb2_queryfs+0xc2/0x2c0 [cifs] > > This is a race between open_cached_dir() and cached_dir_lease_break() > where the cache entry for the open directory handle receives a lease > break while creating it. And before returning from open_cached_dir(), > we put the last reference of the new @cfid because of > !@cfid->has_lease. > > Besides the UAF, while running xfstests a lot of missed lease breaks > have been noticed in tests that run several concurrent statfs(2) calls > on those cached fids > > CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame... > CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1... > CIFS: VFS: \\w22-root1.gandalf.test smb buf 00000000715bfe83 len 108 > CIFS: VFS: Dump pending requests: > CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame... > CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1... > CIFS: VFS: \\w22-root1.gandalf.test smb buf 000000005aa7316e len 108 > ... > > To fix both, in open_cached_dir() ensure that @cfid->has_lease is set > right before sending out compounded request so that any potential > lease break will be get processed by demultiplex thread while we're > still caching @cfid. And, if open failed for some reason, re-check > @cfid->has_lease to decide whether or not put lease reference. > > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> > --- > fs/smb/client/cached_dir.c | 84 ++++++++++++++++++++++---------------- > 1 file changed, 49 insertions(+), 35 deletions(-) > > diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c > index fe1bf5b6e0cb..59f6b8e32cc9 100644 > --- a/fs/smb/client/cached_dir.c > +++ b/fs/smb/client/cached_dir.c > @@ -32,7 +32,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, > * fully cached or it may be in the process of > * being deleted due to a lease break. > */ > - if (!cfid->has_lease) { > + if (!cfid->time || !cfid->has_lease) { > spin_unlock(&cfids->cfid_list_lock); > return NULL; > } > @@ -193,10 +193,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > npath = path_no_prefix(cifs_sb, path); > if (IS_ERR(npath)) { > rc = PTR_ERR(npath); > - kfree(utf16_path); > - return rc; > + goto out; > } > > + if (!npath[0]) { > + dentry = dget(cifs_sb->root); > + } else { > + dentry = path_to_dentry(cifs_sb, npath); > + if (IS_ERR(dentry)) { > + rc = -ENOENT; > + goto out; > + } > + } > + cfid->dentry = dentry; > + > /* > * We do not hold the lock for the open because in case > * SMB2_open needs to reconnect. > @@ -249,6 +259,15 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > smb2_set_related(&rqst[1]); > > + /* > + * Set @cfid->has_lease to true before sending out compounded request so > + * its lease reference can be put in cached_dir_lease_break() due to a > + * potential lease break right after the request is sent or while @cfid > + * is still being cached. Concurrent processes won't be to use it yet > + * due to @cfid->time being zero. > + */ > + cfid->has_lease = true; > + > rc = compound_send_recv(xid, ses, server, > flags, 2, rqst, > resp_buftype, rsp_iov); > @@ -263,6 +282,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > cfid->tcon = tcon; > cfid->is_open = true; > > + spin_lock(&cfids->cfid_list_lock); > + > o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; > oparms.fid->persistent_fid = o_rsp->PersistentFileId; > oparms.fid->volatile_fid = o_rsp->VolatileFileId; > @@ -270,18 +291,25 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); > #endif /* CIFS_DEBUG2 */ > > - if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) > + rc = -EINVAL; > + if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) { > + spin_unlock(&cfids->cfid_list_lock); > goto oshr_free; > + } > > smb2_parse_contexts(server, o_rsp, > &oparms.fid->epoch, > oparms.fid->lease_key, &oplock, > NULL, NULL); > - if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) > + if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) { > + spin_unlock(&cfids->cfid_list_lock); > goto oshr_free; > + } > qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; > - if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) > + if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) { > + spin_unlock(&cfids->cfid_list_lock); > goto oshr_free; > + } > if (!smb2_validate_and_copy_iov( > le16_to_cpu(qi_rsp->OutputBufferOffset), > sizeof(struct smb2_file_all_info), > @@ -289,37 +317,24 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > (char *)&cfid->file_all_info)) > cfid->file_all_info_is_valid = true; > > - if (!npath[0]) > - dentry = dget(cifs_sb->root); > - else { > - dentry = path_to_dentry(cifs_sb, npath); > - if (IS_ERR(dentry)) { > - rc = -ENOENT; > - goto oshr_free; > - } > - } > - spin_lock(&cfids->cfid_list_lock); > - cfid->dentry = dentry; > cfid->time = jiffies; > - cfid->has_lease = true; > spin_unlock(&cfids->cfid_list_lock); > + /* At this point the directory handle is fully cached */ > + rc = 0; > > oshr_free: > - kfree(utf16_path); > SMB2_open_free(&rqst[0]); > SMB2_query_info_free(&rqst[1]); > free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); > free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); > - spin_lock(&cfids->cfid_list_lock); > - if (!cfid->has_lease) { > - if (rc) { > - if (cfid->on_list) { > - list_del(&cfid->entry); > - cfid->on_list = false; > - cfids->num_entries--; > - } > - rc = -ENOENT; > - } else { > + if (rc) { > + spin_lock(&cfids->cfid_list_lock); > + if (cfid->on_list) { > + list_del(&cfid->entry); > + cfid->on_list = false; > + cfids->num_entries--; > + } > + if (cfid->has_lease) { > /* > * We are guaranteed to have two references at this > * point. One for the caller and one for a potential > @@ -327,25 +342,24 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > * will be closed when the caller closes the cached > * handle. > */ > + cfid->has_lease = false; > spin_unlock(&cfids->cfid_list_lock); > kref_put(&cfid->refcount, smb2_close_cached_fid); > goto out; > } > + spin_unlock(&cfids->cfid_list_lock); > } > - spin_unlock(&cfids->cfid_list_lock); > +out: > if (rc) { > if (cfid->is_open) > SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, > cfid->fid.volatile_fid); > free_cached_dir(cfid); > - cfid = NULL; > - } > -out: > - if (rc == 0) { > + } else { > *ret_cfid = cfid; > atomic_inc(&tcon->num_remote_opens); > } > - > + kfree(utf16_path); > return rc; > } > > -- > 2.42.0 > -- Thanks, Steve ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() 2023-10-31 3:09 ` Steve French @ 2023-11-04 12:23 ` Shyam Prasad N 0 siblings, 0 replies; 11+ messages in thread From: Shyam Prasad N @ 2023-11-04 12:23 UTC (permalink / raw) To: Steve French; +Cc: Paulo Alcantara, linux-cifs On Tue, Oct 31, 2023 at 8:39 AM Steve French <smfrench@gmail.com> wrote: > > added Cc: stable and tentatively merged this to for-next > > On Mon, Oct 30, 2023 at 3:20 PM Paulo Alcantara <pc@manguebit.com> wrote: > > > > The following UAF was triggered when running fstests generic/072 with > > KASAN enabled against Windows Server 2022 and mount options > > 'multichannel,max_channels=2,vers=3.1.1,mfsymlinks,noperm' > > > > BUG: KASAN: slab-use-after-free in smb2_query_info_compound+0x423/0x6d0 [cifs] > > Read of size 8 at addr ffff888014941048 by task xfs_io/27534 > > > > CPU: 0 PID: 27534 Comm: xfs_io Not tainted 6.6.0-rc7 #1 > > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS > > rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014 > > Call Trace: > > dump_stack_lvl+0x4a/0x80 > > print_report+0xcf/0x650 > > ? srso_alias_return_thunk+0x5/0x7f > > ? srso_alias_return_thunk+0x5/0x7f > > ? __phys_addr+0x46/0x90 > > kasan_report+0xda/0x110 > > ? smb2_query_info_compound+0x423/0x6d0 [cifs] > > ? smb2_query_info_compound+0x423/0x6d0 [cifs] > > smb2_query_info_compound+0x423/0x6d0 [cifs] > > ? __pfx_smb2_query_info_compound+0x10/0x10 [cifs] > > ? srso_alias_return_thunk+0x5/0x7f > > ? __stack_depot_save+0x39/0x480 > > ? kasan_save_stack+0x33/0x60 > > ? kasan_set_track+0x25/0x30 > > ? ____kasan_slab_free+0x126/0x170 > > smb2_queryfs+0xc2/0x2c0 [cifs] > > ? __pfx_smb2_queryfs+0x10/0x10 [cifs] > > ? __pfx___lock_acquire+0x10/0x10 > > smb311_queryfs+0x210/0x220 [cifs] > > ? __pfx_smb311_queryfs+0x10/0x10 [cifs] > > ? srso_alias_return_thunk+0x5/0x7f > > ? __lock_acquire+0x480/0x26c0 > > ? lock_release+0x1ed/0x640 > > ? srso_alias_return_thunk+0x5/0x7f > > ? do_raw_spin_unlock+0x9b/0x100 > > cifs_statfs+0x18c/0x4b0 [cifs] > > statfs_by_dentry+0x9b/0xf0 > > fd_statfs+0x4e/0xb0 > > __do_sys_fstatfs+0x7f/0xe0 > > ? __pfx___do_sys_fstatfs+0x10/0x10 > > ? srso_alias_return_thunk+0x5/0x7f > > ? lockdep_hardirqs_on_prepare+0x136/0x200 > > ? srso_alias_return_thunk+0x5/0x7f > > do_syscall_64+0x3f/0x90 > > entry_SYSCALL_64_after_hwframe+0x6e/0xd8 > > > > Allocated by task 27534: > > kasan_save_stack+0x33/0x60 > > kasan_set_track+0x25/0x30 > > __kasan_kmalloc+0x8f/0xa0 > > open_cached_dir+0x71b/0x1240 [cifs] > > smb2_query_info_compound+0x5c3/0x6d0 [cifs] > > smb2_queryfs+0xc2/0x2c0 [cifs] > > smb311_queryfs+0x210/0x220 [cifs] > > cifs_statfs+0x18c/0x4b0 [cifs] > > statfs_by_dentry+0x9b/0xf0 > > fd_statfs+0x4e/0xb0 > > __do_sys_fstatfs+0x7f/0xe0 > > do_syscall_64+0x3f/0x90 > > entry_SYSCALL_64_after_hwframe+0x6e/0xd8 > > > > Freed by task 27534: > > kasan_save_stack+0x33/0x60 > > kasan_set_track+0x25/0x30 > > kasan_save_free_info+0x2b/0x50 > > ____kasan_slab_free+0x126/0x170 > > slab_free_freelist_hook+0xd0/0x1e0 > > __kmem_cache_free+0x9d/0x1b0 > > open_cached_dir+0xff5/0x1240 [cifs] > > smb2_query_info_compound+0x5c3/0x6d0 [cifs] > > smb2_queryfs+0xc2/0x2c0 [cifs] > > > > This is a race between open_cached_dir() and cached_dir_lease_break() > > where the cache entry for the open directory handle receives a lease > > break while creating it. And before returning from open_cached_dir(), > > we put the last reference of the new @cfid because of > > !@cfid->has_lease. > > > > Besides the UAF, while running xfstests a lot of missed lease breaks > > have been noticed in tests that run several concurrent statfs(2) calls > > on those cached fids > > > > CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame... > > CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1... > > CIFS: VFS: \\w22-root1.gandalf.test smb buf 00000000715bfe83 len 108 > > CIFS: VFS: Dump pending requests: > > CIFS: VFS: \\w22-root1.gandalf.test No task to wake, unknown frame... > > CIFS: VFS: \\w22-root1.gandalf.test Cmd: 18 Err: 0x0 Flags: 0x1... > > CIFS: VFS: \\w22-root1.gandalf.test smb buf 000000005aa7316e len 108 > > ... > > > > To fix both, in open_cached_dir() ensure that @cfid->has_lease is set > > right before sending out compounded request so that any potential > > lease break will be get processed by demultiplex thread while we're > > still caching @cfid. And, if open failed for some reason, re-check > > @cfid->has_lease to decide whether or not put lease reference. > > > > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> > > --- > > fs/smb/client/cached_dir.c | 84 ++++++++++++++++++++++---------------- > > 1 file changed, 49 insertions(+), 35 deletions(-) > > > > diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c > > index fe1bf5b6e0cb..59f6b8e32cc9 100644 > > --- a/fs/smb/client/cached_dir.c > > +++ b/fs/smb/client/cached_dir.c > > @@ -32,7 +32,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, > > * fully cached or it may be in the process of > > * being deleted due to a lease break. > > */ > > - if (!cfid->has_lease) { > > + if (!cfid->time || !cfid->has_lease) { > > spin_unlock(&cfids->cfid_list_lock); > > return NULL; > > } > > @@ -193,10 +193,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > npath = path_no_prefix(cifs_sb, path); > > if (IS_ERR(npath)) { > > rc = PTR_ERR(npath); > > - kfree(utf16_path); > > - return rc; > > + goto out; > > } > > > > + if (!npath[0]) { > > + dentry = dget(cifs_sb->root); > > + } else { > > + dentry = path_to_dentry(cifs_sb, npath); > > + if (IS_ERR(dentry)) { > > + rc = -ENOENT; > > + goto out; > > + } > > + } > > + cfid->dentry = dentry; > > + > > /* > > * We do not hold the lock for the open because in case > > * SMB2_open needs to reconnect. > > @@ -249,6 +259,15 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > > > smb2_set_related(&rqst[1]); > > > > + /* > > + * Set @cfid->has_lease to true before sending out compounded request so > > + * its lease reference can be put in cached_dir_lease_break() due to a > > + * potential lease break right after the request is sent or while @cfid > > + * is still being cached. Concurrent processes won't be to use it yet > > + * due to @cfid->time being zero. > > + */ > > + cfid->has_lease = true; > > + > > rc = compound_send_recv(xid, ses, server, > > flags, 2, rqst, > > resp_buftype, rsp_iov); > > @@ -263,6 +282,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > cfid->tcon = tcon; > > cfid->is_open = true; > > > > + spin_lock(&cfids->cfid_list_lock); > > + > > o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; > > oparms.fid->persistent_fid = o_rsp->PersistentFileId; > > oparms.fid->volatile_fid = o_rsp->VolatileFileId; > > @@ -270,18 +291,25 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); > > #endif /* CIFS_DEBUG2 */ > > > > - if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) > > + rc = -EINVAL; > > + if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) { > > + spin_unlock(&cfids->cfid_list_lock); > > goto oshr_free; > > + } > > > > smb2_parse_contexts(server, o_rsp, > > &oparms.fid->epoch, > > oparms.fid->lease_key, &oplock, > > NULL, NULL); > > - if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) > > + if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) { > > + spin_unlock(&cfids->cfid_list_lock); > > goto oshr_free; > > + } > > qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; > > - if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) > > + if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) { > > + spin_unlock(&cfids->cfid_list_lock); > > goto oshr_free; > > + } > > if (!smb2_validate_and_copy_iov( > > le16_to_cpu(qi_rsp->OutputBufferOffset), > > sizeof(struct smb2_file_all_info), > > @@ -289,37 +317,24 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > (char *)&cfid->file_all_info)) > > cfid->file_all_info_is_valid = true; > > > > - if (!npath[0]) > > - dentry = dget(cifs_sb->root); > > - else { > > - dentry = path_to_dentry(cifs_sb, npath); > > - if (IS_ERR(dentry)) { > > - rc = -ENOENT; > > - goto oshr_free; > > - } > > - } > > - spin_lock(&cfids->cfid_list_lock); > > - cfid->dentry = dentry; > > cfid->time = jiffies; > > - cfid->has_lease = true; > > spin_unlock(&cfids->cfid_list_lock); > > + /* At this point the directory handle is fully cached */ > > + rc = 0; > > > > oshr_free: > > - kfree(utf16_path); > > SMB2_open_free(&rqst[0]); > > SMB2_query_info_free(&rqst[1]); > > free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); > > free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); > > - spin_lock(&cfids->cfid_list_lock); > > - if (!cfid->has_lease) { > > - if (rc) { > > - if (cfid->on_list) { > > - list_del(&cfid->entry); > > - cfid->on_list = false; > > - cfids->num_entries--; > > - } > > - rc = -ENOENT; > > - } else { > > + if (rc) { > > + spin_lock(&cfids->cfid_list_lock); > > + if (cfid->on_list) { > > + list_del(&cfid->entry); > > + cfid->on_list = false; > > + cfids->num_entries--; > > + } > > + if (cfid->has_lease) { > > /* > > * We are guaranteed to have two references at this > > * point. One for the caller and one for a potential > > @@ -327,25 +342,24 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, > > * will be closed when the caller closes the cached > > * handle. > > */ > > + cfid->has_lease = false; > > spin_unlock(&cfids->cfid_list_lock); > > kref_put(&cfid->refcount, smb2_close_cached_fid); > > goto out; > > } > > + spin_unlock(&cfids->cfid_list_lock); > > } > > - spin_unlock(&cfids->cfid_list_lock); > > +out: > > if (rc) { > > if (cfid->is_open) > > SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, > > cfid->fid.volatile_fid); > > free_cached_dir(cfid); > > - cfid = NULL; > > - } > > -out: > > - if (rc == 0) { > > + } else { > > *ret_cfid = cfid; > > atomic_inc(&tcon->num_remote_opens); > > } > > - > > + kfree(utf16_path); > > return rc; > > } > > > > -- > > 2.42.0 > > > > > -- > Thanks, > > Steve Don't know if I'm too late for the review. If not, you can add my RB. -- Regards, Shyam ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() 2023-10-30 20:19 [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Paulo Alcantara ` (2 preceding siblings ...) 2023-10-30 20:19 ` [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() Paulo Alcantara @ 2023-10-31 3:24 ` Steve French 2023-11-02 12:30 ` Shyam Prasad N 3 siblings, 1 reply; 11+ messages in thread From: Steve French @ 2023-10-31 3:24 UTC (permalink / raw) To: Paulo Alcantara; +Cc: linux-cifs tentatively merged into cifs-2.6.git for-next pending review/testing On Mon, Oct 30, 2023 at 3:20 PM Paulo Alcantara <pc@manguebit.com> wrote: > > If @ses->chan_count <= 1, then for-loop body will not be executed so > no need to check it twice. > > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> > --- > fs/smb/client/connect.c | 23 +++++++++-------------- > 1 file changed, 9 insertions(+), 14 deletions(-) > > diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c > index 7b923e36501b..a017ee552256 100644 > --- a/fs/smb/client/connect.c > +++ b/fs/smb/client/connect.c > @@ -1969,9 +1969,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) > > void __cifs_put_smb_ses(struct cifs_ses *ses) > { > - unsigned int rc, xid; > - unsigned int chan_count; > struct TCP_Server_Info *server = ses->server; > + unsigned int xid; > + size_t i; > + int rc; > > spin_lock(&ses->ses_lock); > if (ses->ses_status == SES_EXITING) { > @@ -2017,20 +2018,14 @@ void __cifs_put_smb_ses(struct cifs_ses *ses) > list_del_init(&ses->smb_ses_list); > spin_unlock(&cifs_tcp_ses_lock); > > - chan_count = ses->chan_count; > - > /* close any extra channels */ > - if (chan_count > 1) { > - int i; > - > - for (i = 1; i < chan_count; i++) { > - if (ses->chans[i].iface) { > - kref_put(&ses->chans[i].iface->refcount, release_iface); > - ses->chans[i].iface = NULL; > - } > - cifs_put_tcp_session(ses->chans[i].server, 0); > - ses->chans[i].server = NULL; > + for (i = 1; i < ses->chan_count; i++) { > + if (ses->chans[i].iface) { > + kref_put(&ses->chans[i].iface->refcount, release_iface); > + ses->chans[i].iface = NULL; > } > + cifs_put_tcp_session(ses->chans[i].server, 0); > + ses->chans[i].server = NULL; > } > > sesInfoFree(ses); > -- > 2.42.0 > -- Thanks, Steve ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() 2023-10-31 3:24 ` [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Steve French @ 2023-11-02 12:30 ` Shyam Prasad N 0 siblings, 0 replies; 11+ messages in thread From: Shyam Prasad N @ 2023-11-02 12:30 UTC (permalink / raw) To: Steve French; +Cc: Paulo Alcantara, linux-cifs On Tue, Oct 31, 2023 at 8:54 AM Steve French <smfrench@gmail.com> wrote: > > tentatively merged into cifs-2.6.git for-next pending review/testing > > On Mon, Oct 30, 2023 at 3:20 PM Paulo Alcantara <pc@manguebit.com> wrote: > > > > If @ses->chan_count <= 1, then for-loop body will not be executed so > > no need to check it twice. > > > > Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> > > --- > > fs/smb/client/connect.c | 23 +++++++++-------------- > > 1 file changed, 9 insertions(+), 14 deletions(-) > > > > diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c > > index 7b923e36501b..a017ee552256 100644 > > --- a/fs/smb/client/connect.c > > +++ b/fs/smb/client/connect.c > > @@ -1969,9 +1969,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) > > > > void __cifs_put_smb_ses(struct cifs_ses *ses) > > { > > - unsigned int rc, xid; > > - unsigned int chan_count; > > struct TCP_Server_Info *server = ses->server; > > + unsigned int xid; > > + size_t i; > > + int rc; > > > > spin_lock(&ses->ses_lock); > > if (ses->ses_status == SES_EXITING) { > > @@ -2017,20 +2018,14 @@ void __cifs_put_smb_ses(struct cifs_ses *ses) > > list_del_init(&ses->smb_ses_list); > > spin_unlock(&cifs_tcp_ses_lock); > > > > - chan_count = ses->chan_count; > > - > > /* close any extra channels */ > > - if (chan_count > 1) { > > - int i; > > - > > - for (i = 1; i < chan_count; i++) { > > - if (ses->chans[i].iface) { > > - kref_put(&ses->chans[i].iface->refcount, release_iface); > > - ses->chans[i].iface = NULL; > > - } > > - cifs_put_tcp_session(ses->chans[i].server, 0); > > - ses->chans[i].server = NULL; > > + for (i = 1; i < ses->chan_count; i++) { > > + if (ses->chans[i].iface) { > > + kref_put(&ses->chans[i].iface->refcount, release_iface); > > + ses->chans[i].iface = NULL; > > } > > + cifs_put_tcp_session(ses->chans[i].server, 0); > > + ses->chans[i].server = NULL; > > } > > > > sesInfoFree(ses); > > -- > > 2.42.0 > > > > > -- > Thanks, > > Steve Looks good to me. You can add my RB. -- Regards, Shyam ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2024-06-01 11:50 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-10-30 20:19 [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Paulo Alcantara 2023-10-30 20:19 ` [PATCH 2/4] smb: client: fix use-after-free bug in cifs_debug_data_proc_show() Paulo Alcantara 2023-10-31 3:17 ` Steve French 2024-06-01 11:50 ` Wang Zhaolong 2023-10-30 20:19 ` [PATCH 3/4] smb: client: fix potential deadlock when releasing mids Paulo Alcantara 2023-10-31 3:23 ` Steve French 2023-10-30 20:19 ` [PATCH 4/4] smb: client: fix use-after-free in smb2_query_info_compound() Paulo Alcantara 2023-10-31 3:09 ` Steve French 2023-11-04 12:23 ` Shyam Prasad N 2023-10-31 3:24 ` [PATCH 1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() Steve French 2023-11-02 12:30 ` Shyam Prasad N
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox