Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+51 -8
+9 -4
fs/nfsd/nfs3xdr.c
··· 358 358 { 359 359 unsigned int len, v, hdr, dlen; 360 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 364 p = decode_fh(p, &args->fh); 363 365 if (!p) ··· 369 367 args->count = ntohl(*p++); 370 368 args->stable = ntohl(*p++); 371 369 len = args->len = ntohl(*p++); 370 + if ((void *)p > head->iov_base + head->iov_len) 371 + return 0; 372 372 /* 373 373 * The count must equal the amount of data passed. 374 374 */ ··· 381 377 * Check to make sure that we got the right number of 382 378 * bytes. 383 379 */ 384 - hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; 385 - dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len 386 - + rqstp->rq_arg.tail[0].iov_len - hdr; 380 + hdr = (void*)p - head->iov_base; 381 + dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr; 387 382 /* 388 383 * Round the length of the data which was specified up to 389 384 * the next multiple of XDR units and then compare that ··· 399 396 len = args->len = max_blocksize; 400 397 } 401 398 rqstp->rq_vec[0].iov_base = (void*)p; 402 - rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; 399 + rqstp->rq_vec[0].iov_len = head->iov_len - hdr; 403 400 v = 0; 404 401 while (len > rqstp->rq_vec[v].iov_len) { 405 402 len -= rqstp->rq_vec[v].iov_len; ··· 474 471 /* first copy and check from the first page */ 475 472 old = (char*)p; 476 473 vec = &rqstp->rq_arg.head[0]; 474 + if ((void *)old > vec->iov_base + vec->iov_len) 475 + return 0; 477 476 avail = vec->iov_len - (old - (char*)vec->iov_base); 478 477 while (len && avail && *old) { 479 478 *new++ = *old++;
+36
fs/nfsd/nfssvc.c
··· 747 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 + */ 758 + static 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 + 750 781 int 751 782 nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) 752 783 { ··· 790 759 rqstp->rq_vers, rqstp->rq_proc); 791 760 proc = rqstp->rq_procinfo; 792 761 762 + if (nfs_request_too_big(rqstp, proc)) { 763 + dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers); 764 + *statp = rpc_garbage_args; 765 + return 1; 766 + } 793 767 /* 794 768 * Give the xdr decoder a chance to change this if it wants 795 769 * (necessary in the NFSv4.0 compound case)
+6 -4
fs/nfsd/nfsxdr.c
··· 280 280 struct nfsd_writeargs *args) 281 281 { 282 282 unsigned int len, hdr, dlen; 283 + struct kvec *head = rqstp->rq_arg.head; 283 284 int v; 284 285 285 286 p = decode_fh(p, &args->fh); ··· 301 300 * Check to make sure that we got the right number of 302 301 * bytes. 303 302 */ 304 - hdr = (void*)p - rqstp->rq_arg.head[0].iov_base; 305 - dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len 306 - - hdr; 303 + hdr = (void*)p - head->iov_base; 304 + if (hdr > head->iov_len) 305 + return 0; 306 + dlen = head->iov_len + rqstp->rq_arg.page_len - hdr; 307 307 308 308 /* 309 309 * Round the length of the data which was specified up to ··· 318 316 return 0; 319 317 320 318 rqstp->rq_vec[0].iov_base = (void*)p; 321 - rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; 319 + rqstp->rq_vec[0].iov_len = head->iov_len - hdr; 322 320 v = 0; 323 321 while (len > rqstp->rq_vec[v].iov_len) { 324 322 len -= rqstp->rq_vec[v].iov_len;