diff options
author | Jeff Layton <jlayton@redhat.com> | 2014-02-14 07:21:00 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2014-02-23 21:55:07 -0500 |
commit | a26054d184763969a411e3939fe243516715ff59 (patch) | |
tree | 949134388a332f66f28a80d329263031e7136d43 /fs | |
parent | 6b1168e1617d9d4db73ef5276490627abf5adec4 (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/transport.c | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index b37570952846..18cd5650a5fc 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -270,6 +270,26 @@ cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx, | |||
270 | iov->iov_len = rqst->rq_pagesz; | 270 | iov->iov_len = rqst->rq_pagesz; |
271 | } | 271 | } |
272 | 272 | ||
273 | static unsigned long | ||
274 | rqst_len(struct smb_rqst *rqst) | ||
275 | { | ||
276 | unsigned int i; | ||
277 | struct kvec *iov = rqst->rq_iov; | ||
278 | unsigned long buflen = 0; | ||
279 | |||
280 | /* total up iov array first */ | ||
281 | for (i = 0; i < rqst->rq_nvec; i++) | ||
282 | buflen += iov[i].iov_len; | ||
283 | |||
284 | /* add in the page array if there is one */ | ||
285 | if (rqst->rq_npages) { | ||
286 | buflen += rqst->rq_pagesz * (rqst->rq_npages - 1); | ||
287 | buflen += rqst->rq_tailsz; | ||
288 | } | ||
289 | |||
290 | return buflen; | ||
291 | } | ||
292 | |||
273 | static int | 293 | static int |
274 | smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) | 294 | smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) |
275 | { | 295 | { |
@@ -277,6 +297,7 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) | |||
277 | struct kvec *iov = rqst->rq_iov; | 297 | struct kvec *iov = rqst->rq_iov; |
278 | int n_vec = rqst->rq_nvec; | 298 | int n_vec = rqst->rq_nvec; |
279 | unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); | 299 | unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); |
300 | unsigned long send_length; | ||
280 | unsigned int i; | 301 | unsigned int i; |
281 | size_t total_len = 0, sent; | 302 | size_t total_len = 0, sent; |
282 | struct socket *ssocket = server->ssocket; | 303 | struct socket *ssocket = server->ssocket; |
@@ -285,6 +306,14 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) | |||
285 | if (ssocket == NULL) | 306 | if (ssocket == NULL) |
286 | return -ENOTSOCK; | 307 | return -ENOTSOCK; |
287 | 308 | ||
309 | /* sanity check send length */ | ||
310 | send_length = rqst_len(rqst); | ||
311 | if (send_length != smb_buf_length + 4) { | ||
312 | WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n", | ||
313 | send_length, smb_buf_length); | ||
314 | return -EIO; | ||
315 | } | ||
316 | |||
288 | cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length); | 317 | cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length); |
289 | dump_smb(iov[0].iov_base, iov[0].iov_len); | 318 | dump_smb(iov[0].iov_base, iov[0].iov_len); |
290 | 319 | ||