|
|
770e482 |
From: "J. Bruce Fields" <bfields@redhat.com>
|
|
|
770e482 |
Date: 2017-04-14 15:04:40
|
|
|
770e482 |
Subject: [PATCH] nfsd: check for oversized NFSv2/v3 arguments
|
|
|
770e482 |
|
|
|
770e482 |
A client can append random data to the end of an NFSv2 or NFSv3 RPC call
|
|
|
770e482 |
without our complaining; we'll just stop parsing at the end of the
|
|
|
770e482 |
expected data and ignore the rest.
|
|
|
770e482 |
|
|
|
770e482 |
Encoded arguments and replies are stored together in an array of pages,
|
|
|
770e482 |
and if a call is too large it could leave inadequate space for the
|
|
|
770e482 |
reply. This is normally OK because NFS RPC's typically have either
|
|
|
770e482 |
short arguments and long replies (like READ) or long arguments and short
|
|
|
770e482 |
replies (like WRITE). But a client that sends an incorrectly long reply
|
|
|
770e482 |
can violate those assumptions. This was observed to cause crashes.
|
|
|
770e482 |
|
|
|
770e482 |
So, insist that the argument not be any longer than we expect.
|
|
|
770e482 |
|
|
|
770e482 |
Also, several operations increment rq_next_page in the decode routine
|
|
|
770e482 |
before checking the argument size, which can leave rq_next_page pointing
|
|
|
770e482 |
well past the end of the page array, causing trouble later in
|
|
|
770e482 |
svc_free_pages.
|
|
|
770e482 |
|
|
|
770e482 |
As followup we may also want to rewrite the encoding routines to check
|
|
|
770e482 |
more carefully that they aren't running off the end of the page array.
|
|
|
770e482 |
|
|
|
770e482 |
Reported-by: Tuomas Haanpää <thaan@synopsys.com>
|
|
|
770e482 |
Reported-by: Ari Kauppi <ari@synopsys.com>
|
|
|
770e482 |
Cc: stable@vger.kernel.org
|
|
|
770e482 |
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
|
|
|
770e482 |
---
|
|
|
770e482 |
fs/nfsd/nfs3xdr.c | 23 +++++++++++++++++------
|
|
|
770e482 |
fs/nfsd/nfsxdr.c | 13 ++++++++++---
|
|
|
770e482 |
include/linux/sunrpc/svc.h | 3 +--
|
|
|
770e482 |
3 files changed, 28 insertions(+), 11 deletions(-)
|
|
|
770e482 |
|
|
|
770e482 |
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
|
|
|
770e482 |
index dba2ff8eaa68..be66bcadfaea 100644
|
|
|
770e482 |
--- a/fs/nfsd/nfs3xdr.c
|
|
|
770e482 |
+++ b/fs/nfsd/nfs3xdr.c
|
|
|
770e482 |
@@ -334,8 +334,11 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
if (!p)
|
|
|
770e482 |
return 0;
|
|
|
770e482 |
p = xdr_decode_hyper(p, &args->offset);
|
|
|
770e482 |
-
|
|
|
770e482 |
args->count = ntohl(*p++);
|
|
|
770e482 |
+
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
+
|
|
|
770e482 |
len = min(args->count, max_blocksize);
|
|
|
770e482 |
|
|
|
770e482 |
/* set up the kvec */
|
|
|
770e482 |
@@ -349,7 +352,7 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
v++;
|
|
|
770e482 |
}
|
|
|
770e482 |
args->vlen = v;
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
int
|
|
|
770e482 |
@@ -536,9 +539,11 @@ nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
p = decode_fh(p, &args->fh);
|
|
|
770e482 |
if (!p)
|
|
|
770e482 |
return 0;
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
args->buffer = page_address(*(rqstp->rq_next_page++));
|
|
|
770e482 |
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
int
|
|
|
770e482 |
@@ -564,10 +569,14 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
args->verf = p; p += 2;
|
|
|
770e482 |
args->dircount = ~0;
|
|
|
770e482 |
args->count = ntohl(*p++);
|
|
|
770e482 |
+
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
+
|
|
|
770e482 |
args->count = min_t(u32, args->count, PAGE_SIZE);
|
|
|
770e482 |
args->buffer = page_address(*(rqstp->rq_next_page++));
|
|
|
770e482 |
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
int
|
|
|
770e482 |
@@ -585,6 +594,9 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
args->dircount = ntohl(*p++);
|
|
|
770e482 |
args->count = ntohl(*p++);
|
|
|
770e482 |
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
+
|
|
|
770e482 |
len = args->count = min(args->count, max_blocksize);
|
|
|
770e482 |
while (len > 0) {
|
|
|
770e482 |
struct page *p = *(rqstp->rq_next_page++);
|
|
|
770e482 |
@@ -592,8 +604,7 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
args->buffer = page_address(p);
|
|
|
770e482 |
len -= PAGE_SIZE;
|
|
|
770e482 |
}
|
|
|
770e482 |
-
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
int
|
|
|
770e482 |
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
|
|
|
770e482 |
index 41b468a6a90f..79268369f7b3 100644
|
|
|
770e482 |
--- a/fs/nfsd/nfsxdr.c
|
|
|
770e482 |
+++ b/fs/nfsd/nfsxdr.c
|
|
|
770e482 |
@@ -257,6 +257,9 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
len = args->count = ntohl(*p++);
|
|
|
770e482 |
p++; /* totalcount - unused */
|
|
|
770e482 |
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
+
|
|
|
770e482 |
len = min_t(unsigned int, len, NFSSVC_MAXBLKSIZE_V2);
|
|
|
770e482 |
|
|
|
770e482 |
/* set up somewhere to store response.
|
|
|
770e482 |
@@ -272,7 +275,7 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
v++;
|
|
|
770e482 |
}
|
|
|
770e482 |
args->vlen = v;
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
int
|
|
|
770e482 |
@@ -360,9 +363,11 @@ nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readli
|
|
|
770e482 |
p = decode_fh(p, &args->fh);
|
|
|
770e482 |
if (!p)
|
|
|
770e482 |
return 0;
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
args->buffer = page_address(*(rqstp->rq_next_page++));
|
|
|
770e482 |
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
int
|
|
|
770e482 |
@@ -400,9 +405,11 @@ nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
|
|
|
770e482 |
args->cookie = ntohl(*p++);
|
|
|
770e482 |
args->count = ntohl(*p++);
|
|
|
770e482 |
args->count = min_t(u32, args->count, PAGE_SIZE);
|
|
|
770e482 |
+ if (!xdr_argsize_check(rqstp, p))
|
|
|
770e482 |
+ return 0;
|
|
|
770e482 |
args->buffer = page_address(*(rqstp->rq_next_page++));
|
|
|
770e482 |
|
|
|
770e482 |
- return xdr_argsize_check(rqstp, p);
|
|
|
770e482 |
+ return 1;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
/*
|
|
|
770e482 |
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
|
|
|
770e482 |
index e770abeed32d..6ef19cf658b4 100644
|
|
|
770e482 |
--- a/include/linux/sunrpc/svc.h
|
|
|
770e482 |
+++ b/include/linux/sunrpc/svc.h
|
|
|
770e482 |
@@ -336,8 +336,7 @@ xdr_argsize_check(struct svc_rqst *rqstp, __be32 *p)
|
|
|
770e482 |
{
|
|
|
770e482 |
char *cp = (char *)p;
|
|
|
770e482 |
struct kvec *vec = &rqstp->rq_arg.head[0];
|
|
|
770e482 |
- return cp >= (char*)vec->iov_base
|
|
|
770e482 |
- && cp <= (char*)vec->iov_base + vec->iov_len;
|
|
|
770e482 |
+ return cp == (char *)vec->iov_base + vec->iov_len;
|
|
|
770e482 |
}
|
|
|
770e482 |
|
|
|
770e482 |
static inline int
|
|
|
770e482 |
--
|
|
|
770e482 |
2.9.3
|
|
|
770e482 |
|
|
|
770e482 |
--
|
|
|
770e482 |
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
|
|
|
770e482 |
the body of a message to majordomo@vger.kernel.org
|
|
|
770e482 |
More majordomo info at http://vger.kernel.org/majordomo-info.html
|