From: Neil Horman <nhorman@redhat.com> Date: Mon, 7 Mar 2011 15:26:54 -0500 Subject: [fs] nfs: fix use of slab alloc'd pages in skb frag list Message-id: <1299511614-11812-1-git-send-email-nhorman@redhat.com> Patchwork-id: 33830 O-Subject: [RHEL 5.7 PATCH] nfs: Fix NFS4 use of slab allocated pages in skb frag list (bz 675127) Bugzilla: 682643 CVE: CVE-2011-1090 RH-Acked-by: Steve Dickson <SteveD@redhat.com> RH-Acked-by: Jeff Layton <jlayton@redhat.com> From: Neil Horman <nhorman@tuxdriver.com> Backport of upstream commit e9e3d724e2145f5039b423c290ce2b2c3d8f94bc from linux-nfs.git. Fixes the NFS setacl path so that slab pages aren't inadvertently placed on the skb frag list when sending. Doing so results in a user triggerable oops. Tested by myself. diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index bae0d10..c25472a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -48,6 +48,7 @@ #include <linux/smp_lock.h> #include <linux/namei.h> #include <linux/mount.h> +#include <linux/mm.h> #include "nfs4_fs.h" #include "delegation.h" @@ -2669,6 +2670,35 @@ static void buf_to_pages(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 *newpage, **spages; + int rc = 0; + size_t len; + spages = pages; + + do { + len = min(PAGE_CACHE_SIZE, buflen); + newpage = alloc_page(GFP_KERNEL); + + if (newpage == NULL) + goto unwind; + memcpy(page_address(newpage), buf, len); + buf += len; + buflen -= len; + *pages++ = newpage; + rc++; + } while (buflen != 0); + + return rc; + +unwind: + for(; rc > 0; rc--) + __free_page(spages[rc-1]); + return -ENOMEM; +} + struct nfs4_cached_acl { int cached; size_t len; @@ -2832,13 +2862,23 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl .rpc_argp = &arg, .rpc_resp = NULL, }; - int ret; + int ret, i; if (!nfs4_server_supports_acls(server)) return -EOPNOTSUPP; + i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); + if (i < 0) + return i; nfs_inode_return_delegation(inode); - buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); + + /* + * Free each page after tx, so the only ref left is + * held by the network stack + */ + for (; i > 0; i--) + put_page(pages[i-1]); + if (ret == 0) nfs4_write_cached_acl(inode, buf, buflen); return ret;