Four smb3 fixes for stable

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAl+9eV4ACgkQiiy9cAdy
 T1EXOAv+JF6QZiwB6TELiusDLw+6UWpT7CcT1guL+eSrFVYEPIhqF4V4QXY0oA0Y
 F8jTpHxEx8wdECAPPNGHh/a4E+Y1vV/W8Nv5DkglAwjeXAD2Y84VAp8hH890jnn0
 M8I9qdnbfSodRueshpKScRPHbfp4Smlz1BR9R0syk7T7TmCy8aKNwYN1lBy5Nf9f
 ICMn1F5e9z4nX43NJIwzO+NSPehtLm8ULFZER/pQ+tGDhwXTdFc9HPzfu0ZoYbEO
 zADjmY4PItVYRINnWBntEBLYcAFeAB0finPTP2kCfXfRDF5cPgElp84F3Uro7se5
 bioboePO+bUS0jigIiP3qZ7zTHEdJoICsiJzVGmDZYsawK3MAwamp2EH3axAr4B/
 h4LULgN7nCatPW5lMo3/3EPZXVbXVTOYIB2REtqJugK8USQ9+v9SMLNT/qWn0GE5
 bzZoZ22wkHEOn4EIxYSCX4tgj9cJ2v9B/0NMpTQLTECKBQi3iV32GxLglvMwZru6
 eWKL5tZj
 =lxdD
 -----END PGP SIGNATURE-----

Merge tag '5.10-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "Four smb3 fixes for stable: one fixes a memleak, the other three
  address a problem found with decryption offload that can cause a use
  after free"

* tag '5.10-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb3: Handle error case during offload read path
  smb3: Avoid Mid pending list corruption
  smb3: Call cifs reconnect from demultiplex thread
  cifs: fix a memleak with modefromsid
This commit is contained in:
Linus Torvalds 2020-11-24 15:33:18 -08:00
commit 127c501a03
2 changed files with 74 additions and 15 deletions

View File

@ -1266,6 +1266,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
} else if (mode_from_special_sid) { } else if (mode_from_special_sid) {
rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true);
kfree(pntsd);
} else { } else {
/* get approximated mode from ACL */ /* get approximated mode from ACL */
rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false);

View File

@ -264,7 +264,7 @@ smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
} }
static struct mid_q_entry * static struct mid_q_entry *
smb2_find_mid(struct TCP_Server_Info *server, char *buf) __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
{ {
struct mid_q_entry *mid; struct mid_q_entry *mid;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
@ -281,6 +281,10 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
(mid->mid_state == MID_REQUEST_SUBMITTED) && (mid->mid_state == MID_REQUEST_SUBMITTED) &&
(mid->command == shdr->Command)) { (mid->command == shdr->Command)) {
kref_get(&mid->refcount); kref_get(&mid->refcount);
if (dequeue) {
list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
}
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
return mid; return mid;
} }
@ -289,6 +293,18 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
return NULL; return NULL;
} }
static struct mid_q_entry *
smb2_find_mid(struct TCP_Server_Info *server, char *buf)
{
return __smb2_find_mid(server, buf, false);
}
static struct mid_q_entry *
smb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf)
{
return __smb2_find_mid(server, buf, true);
}
static void static void
smb2_dump_detail(void *buf, struct TCP_Server_Info *server) smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
{ {
@ -4356,7 +4372,8 @@ init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size,
static int static int
handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
char *buf, unsigned int buf_len, struct page **pages, char *buf, unsigned int buf_len, struct page **pages,
unsigned int npages, unsigned int page_data_size) unsigned int npages, unsigned int page_data_size,
bool is_offloaded)
{ {
unsigned int data_offset; unsigned int data_offset;
unsigned int data_len; unsigned int data_len;
@ -4378,7 +4395,8 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
if (server->ops->is_session_expired && if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) { server->ops->is_session_expired(buf)) {
cifs_reconnect(server); if (!is_offloaded)
cifs_reconnect(server);
return -1; return -1;
} }
@ -4402,7 +4420,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
cifs_dbg(FYI, "%s: server returned error %d\n", cifs_dbg(FYI, "%s: server returned error %d\n",
__func__, rdata->result); __func__, rdata->result);
/* normal error on read response */ /* normal error on read response */
dequeue_mid(mid, false); if (is_offloaded)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
dequeue_mid(mid, false);
return 0; return 0;
} }
@ -4426,7 +4447,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
__func__, data_offset); __func__, data_offset);
rdata->result = -EIO; rdata->result = -EIO;
dequeue_mid(mid, rdata->result); if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
dequeue_mid(mid, rdata->result);
return 0; return 0;
} }
@ -4442,21 +4466,30 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
__func__, data_offset); __func__, data_offset);
rdata->result = -EIO; rdata->result = -EIO;
dequeue_mid(mid, rdata->result); if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
dequeue_mid(mid, rdata->result);
return 0; return 0;
} }
if (data_len > page_data_size - pad_len) { if (data_len > page_data_size - pad_len) {
/* data_len is corrupt -- discard frame */ /* data_len is corrupt -- discard frame */
rdata->result = -EIO; rdata->result = -EIO;
dequeue_mid(mid, rdata->result); if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
dequeue_mid(mid, rdata->result);
return 0; return 0;
} }
rdata->result = init_read_bvec(pages, npages, page_data_size, rdata->result = init_read_bvec(pages, npages, page_data_size,
cur_off, &bvec); cur_off, &bvec);
if (rdata->result != 0) { if (rdata->result != 0) {
dequeue_mid(mid, rdata->result); if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
dequeue_mid(mid, rdata->result);
return 0; return 0;
} }
@ -4471,7 +4504,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
/* read response payload cannot be in both buf and pages */ /* read response payload cannot be in both buf and pages */
WARN_ONCE(1, "buf can not contain only a part of read data"); WARN_ONCE(1, "buf can not contain only a part of read data");
rdata->result = -EIO; rdata->result = -EIO;
dequeue_mid(mid, rdata->result); if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
dequeue_mid(mid, rdata->result);
return 0; return 0;
} }
@ -4482,7 +4518,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
if (length < 0) if (length < 0)
return length; return length;
dequeue_mid(mid, false); if (is_offloaded)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
dequeue_mid(mid, false);
return length; return length;
} }
@ -4511,15 +4550,34 @@ static void smb2_decrypt_offload(struct work_struct *work)
} }
dw->server->lstrp = jiffies; dw->server->lstrp = jiffies;
mid = smb2_find_mid(dw->server, dw->buf); mid = smb2_find_dequeue_mid(dw->server, dw->buf);
if (mid == NULL) if (mid == NULL)
cifs_dbg(FYI, "mid not found\n"); cifs_dbg(FYI, "mid not found\n");
else { else {
mid->decrypted = true; mid->decrypted = true;
rc = handle_read_data(dw->server, mid, dw->buf, rc = handle_read_data(dw->server, mid, dw->buf,
dw->server->vals->read_rsp_size, dw->server->vals->read_rsp_size,
dw->ppages, dw->npages, dw->len); dw->ppages, dw->npages, dw->len,
mid->callback(mid); true);
if (rc >= 0) {
#ifdef CONFIG_CIFS_STATS2
mid->when_received = jiffies;
#endif
mid->callback(mid);
} else {
spin_lock(&GlobalMid_Lock);
if (dw->server->tcpStatus == CifsNeedReconnect) {
mid->mid_state = MID_RETRY_NEEDED;
spin_unlock(&GlobalMid_Lock);
mid->callback(mid);
} else {
mid->mid_state = MID_REQUEST_SUBMITTED;
mid->mid_flags &= ~(MID_DELETED);
list_add_tail(&mid->qhead,
&dw->server->pending_mid_q);
spin_unlock(&GlobalMid_Lock);
}
}
cifs_mid_q_entry_release(mid); cifs_mid_q_entry_release(mid);
} }
@ -4622,7 +4680,7 @@ non_offloaded_decrypt:
(*mid)->decrypted = true; (*mid)->decrypted = true;
rc = handle_read_data(server, *mid, buf, rc = handle_read_data(server, *mid, buf,
server->vals->read_rsp_size, server->vals->read_rsp_size,
pages, npages, len); pages, npages, len, false);
} }
free_pages: free_pages:
@ -4765,7 +4823,7 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
char *buf = server->large_buf ? server->bigbuf : server->smallbuf; char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
return handle_read_data(server, mid, buf, server->pdu_size, return handle_read_data(server, mid, buf, server->pdu_size,
NULL, 0, 0); NULL, 0, 0, false);
} }
static int static int