cifs: sanity check length of data to send before sending

We had a bug discovered recently where an upper layer function
(cifs_iovec_write) could pass down a smb_rqst with an invalid amount of
data in it. The length of the SMB frame would be correct, but the rqst
struct would cause smb_send_rqst to send nearly 4GB of data.

This should never be the case. Add some sanity checking to the beginning
of smb_send_rqst that ensures that the amount of data we're going to
send agrees with the length in the RFC1002 header. If it doesn't, WARN()
and return -EIO to the upper layers.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Acked-by: Sachin Prabhu <sprabhu@redhat.com>
Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
Jeff Layton 2014-02-14 07:21:00 -05:00 committed by Steve French
parent 6b1168e161
commit a26054d184

View File

@ -270,6 +270,26 @@ cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
iov->iov_len = rqst->rq_pagesz;
}
static unsigned long
rqst_len(struct smb_rqst *rqst)
{
unsigned int i;
struct kvec *iov = rqst->rq_iov;
unsigned long buflen = 0;
/* total up iov array first */
for (i = 0; i < rqst->rq_nvec; i++)
buflen += iov[i].iov_len;
/* add in the page array if there is one */
if (rqst->rq_npages) {
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1);
buflen += rqst->rq_tailsz;
}
return buflen;
}
static int
smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
@ -277,6 +297,7 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
unsigned long send_length;
unsigned int i;
size_t total_len = 0, sent;
struct socket *ssocket = server->ssocket;
@ -285,6 +306,14 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
if (ssocket == NULL)
return -ENOTSOCK;
/* sanity check send length */
send_length = rqst_len(rqst);
if (send_length != smb_buf_length + 4) {
WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n",
send_length, smb_buf_length);
return -EIO;
}
cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length);
dump_smb(iov[0].iov_base, iov[0].iov_len);