Commit 5794d21e authored by Sachin Prabhu's avatar Sachin Prabhu Committed by Trond Myklebust

Avoid beyond bounds copy while caching ACL

When attempting to cache ACLs returned from the server, if the bitmap
size + the ACL size is greater than a PAGE_SIZE but the ACL size itself
is smaller than a PAGE_SIZE, we can read past the buffer page boundary.
Signed-off-by: default avatarSachin Prabhu <sprabhu@redhat.com>
Reported-by: default avatarJian Li <jiali@redhat.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 5a006899
...@@ -3628,16 +3628,16 @@ static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_ ...@@ -3628,16 +3628,16 @@ static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_
return ret; return ret;
} }
static void nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len) static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
{ {
struct nfs4_cached_acl *acl; struct nfs4_cached_acl *acl;
if (buf && acl_len <= PAGE_SIZE) { if (pages && acl_len <= PAGE_SIZE) {
acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL); acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
if (acl == NULL) if (acl == NULL)
goto out; goto out;
acl->cached = 1; acl->cached = 1;
memcpy(acl->data, buf, acl_len); _copy_from_pages(acl->data, pages, pgbase, acl_len);
} else { } else {
acl = kmalloc(sizeof(*acl), GFP_KERNEL); acl = kmalloc(sizeof(*acl), GFP_KERNEL);
if (acl == NULL) if (acl == NULL)
...@@ -3670,7 +3670,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu ...@@ -3670,7 +3670,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
struct nfs_getaclres res = { struct nfs_getaclres res = {
.acl_len = buflen, .acl_len = buflen,
}; };
void *resp_buf;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
.rpc_argp = &args, .rpc_argp = &args,
...@@ -3705,7 +3704,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu ...@@ -3705,7 +3704,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
* the page we send as a guess */ * the page we send as a guess */
if (buf == NULL) if (buf == NULL)
res.acl_flags |= NFS4_ACL_LEN_REQUEST; res.acl_flags |= NFS4_ACL_LEN_REQUEST;
resp_buf = page_address(pages[0]);
dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n", dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n",
__func__, buf, buflen, npages, args.acl_len); __func__, buf, buflen, npages, args.acl_len);
...@@ -3716,9 +3714,9 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu ...@@ -3716,9 +3714,9 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
acl_len = res.acl_len - res.acl_data_offset; acl_len = res.acl_len - res.acl_data_offset;
if (acl_len > args.acl_len) if (acl_len > args.acl_len)
nfs4_write_cached_acl(inode, NULL, acl_len); nfs4_write_cached_acl(inode, NULL, 0, acl_len);
else else
nfs4_write_cached_acl(inode, resp_buf + res.acl_data_offset, nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
acl_len); acl_len);
if (buf) { if (buf) {
ret = -ERANGE; ret = -ERANGE;
......
...@@ -4940,7 +4940,7 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, ...@@ -4940,7 +4940,7 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
res->acl_len = attrlen; res->acl_len = attrlen;
goto out; goto out;
} }
dprintk("NFS: acl reply: attrlen %zu > page_len %u\n", dprintk("NFS: acl reply: attrlen %u > page_len %zu\n",
attrlen, page_len); attrlen, page_len);
return -EINVAL; return -EINVAL;
} }
......
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