smb: client: fix potential deadlock when releasing mids
commit e6322fd177c6885a21dd4609dc5e5c973d1a2eb7 upstream.
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 anyways.
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: stable@vger.kernel.org
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
558817597d
commit
9eb44db68c
3 changed files with 8 additions and 12 deletions
|
|
@ -79,7 +79,7 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||||
const char *fullpath, const struct dfs_info3_param *ref,
|
const char *fullpath, const struct dfs_info3_param *ref,
|
||||||
char **devname);
|
char **devname);
|
||||||
extern void delete_mid(struct mid_q_entry *mid);
|
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 void cifs_wake_up_task(struct mid_q_entry *mid);
|
||||||
extern int cifs_handle_standard(struct TCP_Server_Info *server,
|
extern int cifs_handle_standard(struct TCP_Server_Info *server,
|
||||||
struct mid_q_entry *mid);
|
struct mid_q_entry *mid);
|
||||||
|
|
@ -694,4 +694,9 @@ struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
|
||||||
void cifs_put_tcon_super(struct super_block *sb);
|
void cifs_put_tcon_super(struct super_block *sb);
|
||||||
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
|
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
|
||||||
|
|
||||||
|
static inline void release_mid(struct mid_q_entry *mid)
|
||||||
|
{
|
||||||
|
kref_put(&mid->refcount, __release_mid);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _CIFSPROTO_H */
|
#endif /* _CIFSPROTO_H */
|
||||||
|
|
|
||||||
|
|
@ -787,7 +787,7 @@ __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid,
|
||||||
{
|
{
|
||||||
struct close_cancelled_open *cancelled;
|
struct close_cancelled_open *cancelled;
|
||||||
|
|
||||||
cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC);
|
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
|
||||||
if (!cancelled)
|
if (!cancelled)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __release_mid(struct kref *refcount)
|
void __release_mid(struct kref *refcount)
|
||||||
{
|
{
|
||||||
struct mid_q_entry *midEntry =
|
struct mid_q_entry *midEntry =
|
||||||
container_of(refcount, struct mid_q_entry, refcount);
|
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);
|
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
|
void
|
||||||
delete_mid(struct mid_q_entry *mid)
|
delete_mid(struct mid_q_entry *mid)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue