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

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

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
Loading
Loading
Loading
Loading
+5 −7
Original line number Diff line number Diff line
@@ -3628,16 +3628,16 @@ out:
	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;

	if (buf && acl_len <= PAGE_SIZE) {
	if (pages && acl_len <= PAGE_SIZE) {
		acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
		if (acl == NULL)
			goto out;
		acl->cached = 1;
		memcpy(acl->data, buf, acl_len);
		_copy_from_pages(acl->data, pages, pgbase, acl_len);
	} else {
		acl = kmalloc(sizeof(*acl), GFP_KERNEL);
		if (acl == NULL)
@@ -3670,7 +3670,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
	struct nfs_getaclres res = {
		.acl_len = buflen,
	};
	void *resp_buf;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
		.rpc_argp = &args,
@@ -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 */
	if (buf == NULL)
		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",
		__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

	acl_len = res.acl_len - res.acl_data_offset;
	if (acl_len > args.acl_len)
		nfs4_write_cached_acl(inode, NULL, acl_len);
		nfs4_write_cached_acl(inode, NULL, 0, acl_len);
	else
		nfs4_write_cached_acl(inode, resp_buf + res.acl_data_offset,
		nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
				      acl_len);
	if (buf) {
		ret = -ERANGE;
+1 −1
Original line number Diff line number Diff line
@@ -4940,7 +4940,7 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
				res->acl_len = attrlen;
				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);
			return -EINVAL;
		}