Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b5d11e4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 19ac4474 13bf9fbf
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -358,6 +358,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
{
	unsigned int len, v, hdr, dlen;
	u32 max_blocksize = svc_max_payload(rqstp);
	struct kvec *head = rqstp->rq_arg.head;
	struct kvec *tail = rqstp->rq_arg.tail;

	p = decode_fh(p, &args->fh);
	if (!p)
@@ -367,6 +369,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
	args->count = ntohl(*p++);
	args->stable = ntohl(*p++);
	len = args->len = ntohl(*p++);
	if ((void *)p > head->iov_base + head->iov_len)
		return 0;
	/*
	 * The count must equal the amount of data passed.
	 */
@@ -377,9 +381,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
	 * Check to make sure that we got the right number of
	 * bytes.
	 */
	hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
	dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len
		+ rqstp->rq_arg.tail[0].iov_len - hdr;
	hdr = (void*)p - head->iov_base;
	dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr;
	/*
	 * Round the length of the data which was specified up to
	 * the next multiple of XDR units and then compare that
@@ -396,7 +399,7 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
		len = args->len = max_blocksize;
	}
	rqstp->rq_vec[0].iov_base = (void*)p;
	rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
	rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
	v = 0;
	while (len > rqstp->rq_vec[v].iov_len) {
		len -= rqstp->rq_vec[v].iov_len;
@@ -471,6 +474,8 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p,
	/* first copy and check from the first page */
	old = (char*)p;
	vec = &rqstp->rq_arg.head[0];
	if ((void *)old > vec->iov_base + vec->iov_len)
		return 0;
	avail = vec->iov_len - (old - (char*)vec->iov_base);
	while (len && avail && *old) {
		*new++ = *old++;
+36 −0
Original line number Diff line number Diff line
@@ -747,6 +747,37 @@ static __be32 map_new_errors(u32 vers, __be32 nfserr)
	return nfserr;
}

/*
 * A write procedure can have a large argument, and a read procedure can
 * have a large reply, but no NFSv2 or NFSv3 procedure has argument and
 * reply that can both be larger than a page.  The xdr code has taken
 * advantage of this assumption to be a sloppy about bounds checking in
 * some cases.  Pending a rewrite of the NFSv2/v3 xdr code to fix that
 * problem, we enforce these assumptions here:
 */
static bool nfs_request_too_big(struct svc_rqst *rqstp,
				struct svc_procedure *proc)
{
	/*
	 * The ACL code has more careful bounds-checking and is not
	 * susceptible to this problem:
	 */
	if (rqstp->rq_prog != NFS_PROGRAM)
		return false;
	/*
	 * Ditto NFSv4 (which can in theory have argument and reply both
	 * more than a page):
	 */
	if (rqstp->rq_vers >= 4)
		return false;
	/* The reply will be small, we're OK: */
	if (proc->pc_xdrressize > 0 &&
	    proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE))
		return false;

	return rqstp->rq_arg.len > PAGE_SIZE;
}

int
nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{
@@ -759,6 +790,11 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
				rqstp->rq_vers, rqstp->rq_proc);
	proc = rqstp->rq_procinfo;

	if (nfs_request_too_big(rqstp, proc)) {
		dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers);
		*statp = rpc_garbage_args;
		return 1;
	}
	/*
	 * Give the xdr decoder a chance to change this if it wants
	 * (necessary in the NFSv4.0 compound case)
+6 −4
Original line number Diff line number Diff line
@@ -280,6 +280,7 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
					struct nfsd_writeargs *args)
{
	unsigned int len, hdr, dlen;
	struct kvec *head = rqstp->rq_arg.head;
	int v;

	p = decode_fh(p, &args->fh);
@@ -300,9 +301,10 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
	 * Check to make sure that we got the right number of
	 * bytes.
	 */
	hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
	dlen = rqstp->rq_arg.head[0].iov_len + rqstp->rq_arg.page_len
		- hdr;
	hdr = (void*)p - head->iov_base;
	if (hdr > head->iov_len)
		return 0;
	dlen = head->iov_len + rqstp->rq_arg.page_len - hdr;

	/*
	 * Round the length of the data which was specified up to
@@ -316,7 +318,7 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
		return 0;

	rqstp->rq_vec[0].iov_base = (void*)p;
	rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
	rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
	v = 0;
	while (len > rqstp->rq_vec[v].iov_len) {
		len -= rqstp->rq_vec[v].iov_len;