From 04334ef05beaf9334f454fc2f0c607de19102404 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Mon, 23 Jan 2012 13:09:16 +0000 Subject: [PATCH] fix CVE-2012-0056 --- CVE-2012-0056.patch | 268 ++++++++++++++++++++++++++++++++++++++++++++ PKGBUILD | 12 +- 2 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 CVE-2012-0056.patch diff --git a/CVE-2012-0056.patch b/CVE-2012-0056.patch new file mode 100644 index 0000000..6a83fef --- /dev/null +++ b/CVE-2012-0056.patch @@ -0,0 +1,268 @@ +From e268337dfe26dfc7efd422a804dbb27977a3cccc Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Tue, 17 Jan 2012 15:21:19 -0800 +Subject: [PATCH] proc: clean up and fix /proc//mem handling +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Jüri Aedla reported that the /proc//mem handling really isn't very +robust, and it also doesn't match the permission checking of any of the +other related files. + +This changes it to do the permission checks at open time, and instead of +tracking the process, it tracks the VM at the time of the open. That +simplifies the code a lot, but does mean that if you hold the file +descriptor open over an execve(), you'll continue to read from the _old_ +VM. + +That is different from our previous behavior, but much simpler. If +somebody actually finds a load where this matters, we'll need to revert +this commit. + +I suspect that nobody will ever notice - because the process mapping +addresses will also have changed as part of the execve. So you cannot +actually usefully access the fd across a VM change simply because all +the offsets for IO would have changed too. + +Reported-by: Jüri Aedla +Cc: Al Viro +Signed-off-by: Linus Torvalds +--- + fs/proc/base.c | 145 +++++++++++++++----------------------------------------- + 1 files changed, 39 insertions(+), 106 deletions(-) + +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 5485a53..662ddf2 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -198,65 +198,7 @@ static int proc_root_link(struct dentry *dentry, struct path *path) + return result; + } + +-static struct mm_struct *__check_mem_permission(struct task_struct *task) +-{ +- struct mm_struct *mm; +- +- mm = get_task_mm(task); +- if (!mm) +- return ERR_PTR(-EINVAL); +- +- /* +- * A task can always look at itself, in case it chooses +- * to use system calls instead of load instructions. +- */ +- if (task == current) +- return mm; +- +- /* +- * If current is actively ptrace'ing, and would also be +- * permitted to freshly attach with ptrace now, permit it. +- */ +- if (task_is_stopped_or_traced(task)) { +- int match; +- rcu_read_lock(); +- match = (ptrace_parent(task) == current); +- rcu_read_unlock(); +- if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) +- return mm; +- } +- +- /* +- * No one else is allowed. +- */ +- mmput(mm); +- return ERR_PTR(-EPERM); +-} +- +-/* +- * If current may access user memory in @task return a reference to the +- * corresponding mm, otherwise ERR_PTR. +- */ +-static struct mm_struct *check_mem_permission(struct task_struct *task) +-{ +- struct mm_struct *mm; +- int err; +- +- /* +- * Avoid racing if task exec's as we might get a new mm but validate +- * against old credentials. +- */ +- err = mutex_lock_killable(&task->signal->cred_guard_mutex); +- if (err) +- return ERR_PTR(err); +- +- mm = __check_mem_permission(task); +- mutex_unlock(&task->signal->cred_guard_mutex); +- +- return mm; +-} +- +-struct mm_struct *mm_for_maps(struct task_struct *task) ++static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) + { + struct mm_struct *mm; + int err; +@@ -267,7 +209,7 @@ struct mm_struct *mm_for_maps(struct task_struct *task) + + mm = get_task_mm(task); + if (mm && mm != current->mm && +- !ptrace_may_access(task, PTRACE_MODE_READ)) { ++ !ptrace_may_access(task, mode)) { + mmput(mm); + mm = ERR_PTR(-EACCES); + } +@@ -276,6 +218,11 @@ struct mm_struct *mm_for_maps(struct task_struct *task) + return mm; + } + ++struct mm_struct *mm_for_maps(struct task_struct *task) ++{ ++ return mm_access(task, PTRACE_MODE_READ); ++} ++ + static int proc_pid_cmdline(struct task_struct *task, char * buffer) + { + int res = 0; +@@ -752,38 +699,39 @@ static const struct file_operations proc_single_file_operations = { + + static int mem_open(struct inode* inode, struct file* file) + { +- file->private_data = (void*)((long)current->self_exec_id); ++ struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); ++ struct mm_struct *mm; ++ ++ if (!task) ++ return -ESRCH; ++ ++ mm = mm_access(task, PTRACE_MODE_ATTACH); ++ put_task_struct(task); ++ ++ if (IS_ERR(mm)) ++ return PTR_ERR(mm); ++ + /* OK to pass negative loff_t, we can catch out-of-range */ + file->f_mode |= FMODE_UNSIGNED_OFFSET; ++ file->private_data = mm; ++ + return 0; + } + + static ssize_t mem_read(struct file * file, char __user * buf, + size_t count, loff_t *ppos) + { +- struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); ++ int ret; + char *page; + unsigned long src = *ppos; +- int ret = -ESRCH; +- struct mm_struct *mm; ++ struct mm_struct *mm = file->private_data; + +- if (!task) +- goto out_no_task; ++ if (!mm) ++ return 0; + +- ret = -ENOMEM; + page = (char *)__get_free_page(GFP_TEMPORARY); + if (!page) +- goto out; +- +- mm = check_mem_permission(task); +- ret = PTR_ERR(mm); +- if (IS_ERR(mm)) +- goto out_free; +- +- ret = -EIO; +- +- if (file->private_data != (void*)((long)current->self_exec_id)) +- goto out_put; ++ return -ENOMEM; + + ret = 0; + +@@ -810,13 +758,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, + } + *ppos = src; + +-out_put: +- mmput(mm); +-out_free: + free_page((unsigned long) page); +-out: +- put_task_struct(task); +-out_no_task: + return ret; + } + +@@ -825,27 +767,15 @@ static ssize_t mem_write(struct file * file, const char __user *buf, + { + int copied; + char *page; +- struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); + unsigned long dst = *ppos; +- struct mm_struct *mm; ++ struct mm_struct *mm = file->private_data; + +- copied = -ESRCH; +- if (!task) +- goto out_no_task; ++ if (!mm) ++ return 0; + +- copied = -ENOMEM; + page = (char *)__get_free_page(GFP_TEMPORARY); + if (!page) +- goto out_task; +- +- mm = check_mem_permission(task); +- copied = PTR_ERR(mm); +- if (IS_ERR(mm)) +- goto out_free; +- +- copied = -EIO; +- if (file->private_data != (void *)((long)current->self_exec_id)) +- goto out_mm; ++ return -ENOMEM; + + copied = 0; + while (count > 0) { +@@ -869,13 +799,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf, + } + *ppos = dst; + +-out_mm: +- mmput(mm); +-out_free: + free_page((unsigned long) page); +-out_task: +- put_task_struct(task); +-out_no_task: + return copied; + } + +@@ -895,11 +819,20 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) + return file->f_pos; + } + ++static int mem_release(struct inode *inode, struct file *file) ++{ ++ struct mm_struct *mm = file->private_data; ++ ++ mmput(mm); ++ return 0; ++} ++ + static const struct file_operations proc_mem_operations = { + .llseek = mem_lseek, + .read = mem_read, + .write = mem_write, + .open = mem_open, ++ .release = mem_release, + }; + + static ssize_t environ_read(struct file *file, char __user *buf, +-- +1.7.6.5 + diff --git a/PKGBUILD b/PKGBUILD index 6434036..2fc077e 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -8,7 +8,7 @@ pkgname=('linux' 'linux-headers' 'linux-docs') # Build stock -ARCH kernel _kernelname=${pkgname#linux} _basekernel=3.2 pkgver=${_basekernel}.1 -pkgrel=1 +pkgrel=2 arch=('i686' 'x86_64') url="http://www.kernel.org/" license=('GPL2') @@ -22,7 +22,8 @@ source=("http://www.kernel.org/pub/linux/kernel/v3.x/linux-3.2.tar.xz" "${pkgname}.preset" 'change-default-console-loglevel.patch' 'i915-fix-ghost-tv-output.patch' - 'i915-gpu-finish.patch') + 'i915-gpu-finish.patch' + 'CVE-2012-0056.patch') md5sums=('364066fa18767ec0ae5f4e4abcf9dc51' '62ac6ac9b870162f693ecf5e8606423a' 'cbd469a1ba0bc8caa765caa42d429ea9' @@ -30,7 +31,8 @@ md5sums=('364066fa18767ec0ae5f4e4abcf9dc51' 'eb14dcfd80c00852ef81ded6e826826a' '9d3c56a4b999c8bfbd4018089a62f662' '263725f20c0b9eb9c353040792d644e5' - '4cd79aa147825837dc8bc9f6b736c0a0') + '4cd79aa147825837dc8bc9f6b736c0a0' + 'a050d76e56d2ce0715c8ff663ae7f436') build() { cd "${srcdir}/linux-${_basekernel}" @@ -38,6 +40,10 @@ build() { # add upstream patch patch -p1 -i "${srcdir}/patch-${pkgver}" + # patch for CVE-2012-0056 + # see http://blog.zx2c4.com/749 for details + patch -p1 -i "${srcdir}/CVE-2012-0056.patch" + # add latest fixes from stable queue, if needed # http://git.kernel.org/?p=linux/kernel/git/stable/stable-queue.git