diff mbox series

[3/4] smb: client: fix potential deadlock when releasing mids

Message ID 20231030201956.2660-3-pc@manguebit.com
State New
Headers show
Series [1/4] smb: client: remove extra @chan_count check in __cifs_put_smb_ses() | expand

Commit Message

Paulo Alcantara Oct. 30, 2023, 8:19 p.m. UTC
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(-)

Comments

Steve French Oct. 31, 2023, 3:23 a.m. UTC | #1
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
>
diff mbox series

Patch

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)
 {