summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-04-27 16:39:19 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-04-27 16:39:19 -0400
commit8b5d11e4b095450e2f259d5f60ea18c13d2fe0a2 (patch)
tree8f33faf583528a703b3bf5bbf69ad0e7dc3ad94b /fs
parent19ac4474203863a8141663d73d5976fe25464bfd (diff)
parent13bf9fbff0e5e099e2b6f003a0ab8ae145436309 (diff)
Merge tag 'nfsd-4.11-3' of git://linux-nfs.org/~bfields/linux
Pull nfsd fixes from Bruce Fields: "Thanks to Ari Kauppi and Tuomas Haanpää at Synopsis for spotting bugs in our NFSv2/v3 xdr code that could crash the server or leak memory" * tag 'nfsd-4.11-3' of git://linux-nfs.org/~bfields/linux: nfsd: stricter decoding of write-like NFSv2/v3 ops nfsd4: minor NFSv2/v3 write decoding cleanup nfsd: check for oversized NFSv2/v3 arguments
Diffstat (limited to 'fs')
-rw-r--r--fs/nfsd/nfs3xdr.c13
-rw-r--r--fs/nfsd/nfssvc.c36
-rw-r--r--fs/nfsd/nfsxdr.c10
3 files changed, 51 insertions, 8 deletions
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index dba2ff8eaa68..452334694a5d 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -358,6 +358,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
358{ 358{
359 unsigned int len, v, hdr, dlen; 359 unsigned int len, v, hdr, dlen;
360 u32 max_blocksize = svc_max_payload(rqstp); 360 u32 max_blocksize = svc_max_payload(rqstp);
361 struct kvec *head = rqstp->rq_arg.head;
362 struct kvec *tail = rqstp->rq_arg.tail;
361 363
362 p = decode_fh(p, &args->fh); 364 p = decode_fh(p, &args->fh);
363 if (!p) 365 if (!p)
@@ -367,6 +369,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
367 args->count = ntohl(*p++); 369 args->count = ntohl(*p++);
368 args->stable = ntohl(*p++); 370 args->stable = ntohl(*p++);
369 len = args->len = ntohl(*p++); 371 len = args->len = ntohl(*p++);
372 if ((void *)p > head->iov_base + head->iov_len)
373 return 0;
370 /* 374 /*
371 * The count must equal the amount of data passed. 375 * The count must equal the amount of data passed.
372 */ 376 */
@@ -377,9 +381,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
377 * Check to make sure that we got the right number of 381 * Check to make sure that we got the right number of
378 * bytes. 382 * bytes.
379 */ 383 */
380 hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; 384 hdr = (void*)p - head->iov_base;
381 dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len 385 dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr;
382 + rqstp->rq_arg.tail[0].iov_len - hdr;
383 /* 386 /*
384 * Round the length of the data which was specified up to 387 * Round the length of the data which was specified up to
385 * the next multiple of XDR units and then compare that 388 * the next multiple of XDR units and then compare that
@@ -396,7 +399,7 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
396 len = args->len = max_blocksize; 399 len = args->len = max_blocksize;
397 } 400 }
398 rqstp->rq_vec[0].iov_base = (void*)p; 401 rqstp->rq_vec[0].iov_base = (void*)p;
399 rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; 402 rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
400 v = 0; 403 v = 0;
401 while (len > rqstp->rq_vec[v].iov_len) { 404 while (len > rqstp->rq_vec[v].iov_len) {
402 len -= rqstp->rq_vec[v].iov_len; 405 len -= rqstp->rq_vec[v].iov_len;
@@ -471,6 +474,8 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p,
471 /* first copy and check from the first page */ 474 /* first copy and check from the first page */
472 old = (char*)p; 475 old = (char*)p;
473 vec = &rqstp->rq_arg.head[0]; 476 vec = &rqstp->rq_arg.head[0];
477 if ((void *)old > vec->iov_base + vec->iov_len)
478 return 0;
474 avail = vec->iov_len - (old - (char*)vec->iov_base); 479 avail = vec->iov_len - (old - (char*)vec->iov_base);
475 while (len && avail && *old) { 480 while (len && avail && *old) {
476 *new++ = *old++; 481 *new++ = *old++;
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 31e1f9593457..59979f0bbd4b 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -747,6 +747,37 @@ static __be32 map_new_errors(u32 vers, __be32 nfserr)
747 return nfserr; 747 return nfserr;
748} 748}
749 749
750/*
751 * A write procedure can have a large argument, and a read procedure can
752 * have a large reply, but no NFSv2 or NFSv3 procedure has argument and
753 * reply that can both be larger than a page. The xdr code has taken
754 * advantage of this assumption to be a sloppy about bounds checking in
755 * some cases. Pending a rewrite of the NFSv2/v3 xdr code to fix that
756 * problem, we enforce these assumptions here:
757 */
758static bool nfs_request_too_big(struct svc_rqst *rqstp,
759 struct svc_procedure *proc)
760{
761 /*
762 * The ACL code has more careful bounds-checking and is not
763 * susceptible to this problem:
764 */
765 if (rqstp->rq_prog != NFS_PROGRAM)
766 return false;
767 /*
768 * Ditto NFSv4 (which can in theory have argument and reply both
769 * more than a page):
770 */
771 if (rqstp->rq_vers >= 4)
772 return false;
773 /* The reply will be small, we're OK: */
774 if (proc->pc_xdrressize > 0 &&
775 proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE))
776 return false;
777
778 return rqstp->rq_arg.len > PAGE_SIZE;
779}
780
750int 781int
751nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) 782nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
752{ 783{
@@ -759,6 +790,11 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
759 rqstp->rq_vers, rqstp->rq_proc); 790 rqstp->rq_vers, rqstp->rq_proc);
760 proc = rqstp->rq_procinfo; 791 proc = rqstp->rq_procinfo;
761 792
793 if (nfs_request_too_big(rqstp, proc)) {
794 dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers);
795 *statp = rpc_garbage_args;
796 return 1;
797 }
762 /* 798 /*
763 * Give the xdr decoder a chance to change this if it wants 799 * Give the xdr decoder a chance to change this if it wants
764 * (necessary in the NFSv4.0 compound case) 800 * (necessary in the NFSv4.0 compound case)
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index 41b468a6a90f..de07ff625777 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -280,6 +280,7 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
280 struct nfsd_writeargs *args) 280 struct nfsd_writeargs *args)
281{ 281{
282 unsigned int len, hdr, dlen; 282 unsigned int len, hdr, dlen;
283 struct kvec *head = rqstp->rq_arg.head;
283 int v; 284 int v;
284 285
285 p = decode_fh(p, &args->fh); 286 p = decode_fh(p, &args->fh);
@@ -300,9 +301,10 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
300 * Check to make sure that we got the right number of 301 * Check to make sure that we got the right number of
301 * bytes. 302 * bytes.
302 */ 303 */
303 hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; 304 hdr = (void*)p - head->iov_base;
304 dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len 305 if (hdr > head->iov_len)
305 - hdr; 306 return 0;
307 dlen = head->iov_len + rqstp->rq_arg.page_len - hdr;
306 308
307 /* 309 /*
308 * Round the length of the data which was specified up to 310 * Round the length of the data which was specified up to
@@ -316,7 +318,7 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
316 return 0; 318 return 0;
317 319
318 rqstp->rq_vec[0].iov_base = (void*)p; 320 rqstp->rq_vec[0].iov_base = (void*)p;
319 rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; 321 rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
320 v = 0; 322 v = 0;
321 while (len > rqstp->rq_vec[v].iov_len) { 323 while (len > rqstp->rq_vec[v].iov_len) {
322 len -= rqstp->rq_vec[v].iov_len; 324 len -= rqstp->rq_vec[v].iov_len;