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. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJZAlLrAAoJECebzXlCjuG+lb8P/idTu9rLGaU1VYPInrdoXru0 iPY+p5inmGSYW2MfCGlS7disaCACgzPBKVqKjeNB1hfHn2JZCrfeBd0XvBYlc7TH JYmlHKSBjN/ZfnYMl0WrlVUCZstt4JVmxWjO3sZTCL3nImEbGA7d13yBDagxISWh gM9wOOiJwR5lzT/W3MezNCYj4n27/vVhODMP+Qhy0rpK08dBORIH0Bi7hwtVgdD9 cMVIXMbujmJrCz0Uhbo/DhoItcePRBrCLTXdY6WEeMmHxnXlyaA2XuA0EjjZHuMs +BsbAOsNy1BxY1c8Z2ignYgRymUvUBHeiJeIGZLbyKKM2OJMtE4BoqlSWjx08P3Y hTwTuaw8u7uxfAsvxepukZoonWj/uVY5tP2Hyq+K6CIKKJpB7vj+c3QGYD3dNnUu zDl/LG3ayZgpPXqfjKRnSzZE+St1/IwDnvaM2WN2B1mkuerVr8qDGq6xd4kB0QKX BcEKiwcb2ewfPaLlVnSXz6Wbuh2pB42BObJjC3qbOgMvQ7SBUM0UcZmFpJJ20uCR BX20aFzB/GHcd6fTRHpDrAxB4XGdVG/8Da5Ki2WhRnmmaeSXushhiKWImujY9B6i s4mZHu4gGGJdLzOT5u2HZl93STevriL70SA9nZPhPLQycnJMUAO0buU9UcNI7wXR GVu2F3IHd+anxDtDwOv4 =Glhl -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
8b5d11e4b0
|
@ -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++;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue