Commit 21f498c2 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Fix range checking in __nfs4_get_acl_uncached and __nfs4_proc_set_acl

Ensure that the user supplied buffer size doesn't cause us to overflow
the 'pages' array.

Also fix up some confusion between the use of PAGE_SIZE and
PAGE_CACHE_SIZE when calculating buffer sizes. We're not using
the page cache for anything here.
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 872ece86
...@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server) ...@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
} }
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
* it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
* the stack. * the stack.
*/ */
#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT) #define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
static int buf_to_pages_noslab(const void *buf, size_t buflen, static int buf_to_pages_noslab(const void *buf, size_t buflen,
struct page **pages, unsigned int *pgbase) struct page **pages, unsigned int *pgbase)
...@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen, ...@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen,
spages = pages; spages = pages;
do { do {
len = min_t(size_t, PAGE_CACHE_SIZE, buflen); len = min_t(size_t, PAGE_SIZE, buflen);
newpage = alloc_page(GFP_KERNEL); newpage = alloc_page(GFP_KERNEL);
if (newpage == NULL) if (newpage == NULL)
...@@ -3782,17 +3782,16 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu ...@@ -3782,17 +3782,16 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = &res, .rpc_resp = &res,
}; };
int ret = -ENOMEM, npages, i; unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
int ret = -ENOMEM, i;
size_t acl_len = 0; size_t acl_len = 0;
npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
/* As long as we're doing a round trip to the server anyway, /* As long as we're doing a round trip to the server anyway,
* let's be prepared for a page of acl data. */ * let's be prepared for a page of acl data. */
if (npages == 0) if (npages == 0)
npages = 1; npages = 1;
if (npages > ARRAY_SIZE(pages))
/* Add an extra page to handle the bitmap returned */ return -ERANGE;
npages++;
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
pages[i] = alloc_page(GFP_KERNEL); pages[i] = alloc_page(GFP_KERNEL);
...@@ -3891,10 +3890,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl ...@@ -3891,10 +3890,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
}; };
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
int ret, i; int ret, i;
if (!nfs4_server_supports_acls(server)) if (!nfs4_server_supports_acls(server))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (npages > ARRAY_SIZE(pages))
return -ERANGE;
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
if (i < 0) if (i < 0)
return i; return i;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment