Additional fixes for CVE-2011-4131 (rhbz 822874 822869)

This commit is contained in:
Josh Boyer 2012-05-18 09:19:40 -04:00
parent 5a13292239
commit d1a2aa668f
3 changed files with 216 additions and 1 deletions

View File

@ -54,7 +54,7 @@ Summary: The Linux kernel
# For non-released -rc kernels, this will be appended after the rcX and
# gitX tags, so a 3 here would become part of release "0.rcX.gitX.3"
#
%global baserelease 4
%global baserelease 5
%global fedora_build %{baserelease}
# base_sublevel is the kernel version we're starting with and patching
@ -727,6 +727,10 @@ Patch4113: NFS-optimise-away-unnecessary-setattrs-for-open-O_TRUNC.patch
Patch4114: NFSv4-fix-open-O_TRUNC-and-ftruncate-error-handling.patch
Patch4115: NFSv4-Rate-limit-the-state-manager-for-lock-reclaim-.patch
#rhbz 822874
Patch4116: nfs-Avoid-reading-past-buffer-when-calling-GETACL.patch
Patch4117: nfs-Avoid-beyond-bounds-copy-while-caching-ACL.patch
# patches headed upstream
Patch12016: disable-i8042-check-on-apple-mac.patch
@ -1343,6 +1347,9 @@ ApplyPatch NFS-optimise-away-unnecessary-setattrs-for-open-O_TRUNC.patch
ApplyPatch NFSv4-fix-open-O_TRUNC-and-ftruncate-error-handling.patch
ApplyPatch NFSv4-Rate-limit-the-state-manager-for-lock-reclaim-.patch
ApplyPatch nfs-Avoid-reading-past-buffer-when-calling-GETACL.patch
ApplyPatch nfs-Avoid-beyond-bounds-copy-while-caching-ACL.patch
# USB
# WMI
@ -2235,6 +2242,9 @@ fi
# and build.
%changelog
* Fri May 18 2012 Josh Boyer <jwboyer@redhat.com>
- Additional fixes for CVE-2011-4131 (rhbz 822874 822869)
* Thu May 17 2012 Josh Boyer <jwboyer@redhat.com>
- Fix rtlwifi async firmware load race condition (rhbz 822120)

View File

@ -0,0 +1,85 @@
From 5794d21ef4639f0e33440927bb903f9598c21e92 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu@redhat.com>
Date: Tue, 17 Apr 2012 14:36:40 +0100
Subject: [PATCH] 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: Sachin Prabhu <sprabhu@redhat.com>
Reported-by: Jian Li <jiali@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/nfs4proc.c | 12 +++++-------
fs/nfs/nfs4xdr.c | 2 +-
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index f5f125f..2ce0698 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -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;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 9312dd7..203c096 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -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;
}
--
1.7.7.6

View File

@ -0,0 +1,120 @@
From 5a00689930ab975fdd1b37b034475017e460cf2a Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu@redhat.com>
Date: Tue, 17 Apr 2012 14:35:39 +0100
Subject: [PATCH] Avoid reading past buffer when calling GETACL
Bug noticed in commit
bf118a342f10dafe44b14451a1392c3254629a1f
When calling GETACL, if the size of the bitmap array, the length
attribute and the acl returned by the server is greater than the
allocated buffer(args.acl_len), we can Oops with a General Protection
fault at _copy_from_pages() when we attempt to read past the pages
allocated.
This patch allocates an extra PAGE for the bitmap and checks to see that
the bitmap + attribute_length + ACLs don't exceed the buffer space
allocated to it.
Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
Reported-by: Jian Li <jiali@redhat.com>
[Trond: Fixed a size_t vs unsigned int printk() warning]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
---
fs/nfs/nfs4proc.c | 16 ++++++++++------
fs/nfs/nfs4xdr.c | 18 +++++++++++-------
2 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 60d5f4c..f5f125f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3684,19 +3684,23 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
if (npages == 0)
npages = 1;
+ /* Add an extra page to handle the bitmap returned */
+ npages++;
+
for (i = 0; i < npages; i++) {
pages[i] = alloc_page(GFP_KERNEL);
if (!pages[i])
goto out_free;
}
- if (npages > 1) {
- /* for decoding across pages */
- res.acl_scratch = alloc_page(GFP_KERNEL);
- if (!res.acl_scratch)
- goto out_free;
- }
+
+ /* for decoding across pages */
+ res.acl_scratch = alloc_page(GFP_KERNEL);
+ if (!res.acl_scratch)
+ goto out_free;
+
args.acl_len = npages * PAGE_SIZE;
args.acl_pgbase = 0;
+
/* Let decode_getfacl know not to fail if the ACL data is larger than
* the page we send as a guess */
if (buf == NULL)
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 77fc5f9..9312dd7 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4902,11 +4902,19 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
bitmap[3] = {0};
struct kvec *iov = req->rq_rcv_buf.head;
int status;
+ size_t page_len = xdr->buf->page_len;
res->acl_len = 0;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto out;
+
bm_p = xdr->p;
+ res->acl_data_offset = be32_to_cpup(bm_p) + 2;
+ res->acl_data_offset <<= 2;
+ /* Check if the acl data starts beyond the allocated buffer */
+ if (res->acl_data_offset > page_len)
+ return -ERANGE;
+
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
goto out;
if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
@@ -4916,28 +4924,24 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
size_t hdrlen;
- u32 recvd;
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
* are stored with the acl data to handle the problem of
* variable length bitmaps.*/
xdr->p = bm_p;
- res->acl_data_offset = be32_to_cpup(bm_p) + 2;
- res->acl_data_offset <<= 2;
/* We ignore &savep and don't do consistency checks on
* the attr length. Let userspace figure it out.... */
hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
attrlen += res->acl_data_offset;
- recvd = req->rq_rcv_buf.len - hdrlen;
- if (attrlen > recvd) {
+ if (attrlen > page_len) {
if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
/* getxattr interface called with a NULL buf */
res->acl_len = attrlen;
goto out;
}
- dprintk("NFS: acl reply: attrlen %u > recvd %u\n",
- attrlen, recvd);
+ dprintk("NFS: acl reply: attrlen %zu > page_len %u\n",
+ attrlen, page_len);
return -EINVAL;
}
xdr_read_pages(xdr, attrlen);
--
1.7.7.6