From: Ronnie Sahlberg lsahlber@redhat.com
mainline inclusion from mainline-5.2-rc6 commit 0ff2b018b02f category: bugfix bugzilla: 16811 CVE: NA
https://gitee.com/src-openeuler/cifs-utils/issues/I1C1QY?from=project-issue -------------------------------------------------
RH Bugzilla: 1702264
We need to protect so that the call to smb2_reconnect() in smb2_reconnect_server() does not end up freeing the session because it can lead to a use after free and crash.
Reviewed-by: Aurelien Aptel aaptel@suse.com Cc: stable@vger.kernel.org Signed-off-by: Ronnie Sahlberg lsahlber@redhat.com Signed-off-by: Steve French stfrench@microsoft.com Reviewed-by: Pavel Shilovsky pshilov@microsoft.com Conflicts: fs/cifs/cifsproto.h fs/cifs/connect.c [yyl: make cifs_put_smb_ses() extern] --- fs/cifs/cifsproto.h | 2 ++ fs/cifs/connect.c | 3 +-- fs/cifs/smb2pdu.c | 10 +++++++++- 3 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index d7ac75e..bd97f2a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -526,6 +526,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24);
+extern void cifs_put_smb_ses(struct cifs_ses *ses); + void cifs_readdata_release(struct kref *refcount); int cifs_async_readv(struct cifs_readdata *rdata); int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 975f800..f031420 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2733,8 +2733,7 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) return NULL; }
-static void -cifs_put_smb_ses(struct cifs_ses *ses) +void cifs_put_smb_ses(struct cifs_ses *ses) { unsigned int rc, xid; struct TCP_Server_Info *server = ses->server; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 99efb2b..76dfb4f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2920,9 +2920,14 @@ void smb2_reconnect_server(struct work_struct *work) tcon_exist = true; } } + /* + * IPC has the same lifetime as its session and uses its + * refcount. + */ if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); tcon_exist = true; + ses->ses_count++; } } /* @@ -2941,7 +2946,10 @@ void smb2_reconnect_server(struct work_struct *work) else resched = true; list_del_init(&tcon->rlist); - cifs_put_tcon(tcon); + if (tcon->ipc) + cifs_put_smb_ses(tcon->ses); + else + cifs_put_tcon(tcon); }
cifs_dbg(FYI, "Reconnecting tcons finished\n");