From a14e8e55fc4736b4dc37890a641592f03ec3361c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 23 Jan 2012 15:02:16 +0000 Subject: [PATCH] proc: clean up and fix /proc//mem handling (CVE-2012-0056) svn path=/dists/trunk/linux-2.6/; revision=18605 --- debian/changelog | 3 +- ...ean-up-and-fix-proc-pid-mem-handling.patch | 269 ++++++++++++++++++ debian/patches/series/base | 1 + 3 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 debian/patches/bugfix/all/proc-clean-up-and-fix-proc-pid-mem-handling.patch diff --git a/debian/changelog b/debian/changelog index a28a8ca58..1d77a3a56 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -linux-2.6 (3.2.1-2) UNRELEASED; urgency=low +linux-2.6 (3.2.1-2) UNRELEASED; urgency=high [ Stefan Lippers-Hollmann ] * udeb: Add missing modules to nic-wireless-modules: @@ -13,6 +13,7 @@ linux-2.6 (3.2.1-2) UNRELEASED; urgency=low not always exist * Build linux-libc-dev without multiarch if dpkg does not support it, to support backports + * proc: clean up and fix /proc//mem handling (CVE-2012-0056) [ Aurelien Jarno ] * [x86] Backport KVM nested VMX fixes from 3.3 to fix warnings and diff --git a/debian/patches/bugfix/all/proc-clean-up-and-fix-proc-pid-mem-handling.patch b/debian/patches/bugfix/all/proc-clean-up-and-fix-proc-pid-mem-handling.patch new file mode 100644 index 000000000..2acee073d --- /dev/null +++ b/debian/patches/bugfix/all/proc-clean-up-and-fix-proc-pid-mem-handling.patch @@ -0,0 +1,269 @@ +From e268337dfe26dfc7efd422a804dbb27977a3cccc Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Tue, 17 Jan 2012 15:21:19 -0800 +Subject: proc: clean up and fix /proc//mem handling +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Linus Torvalds + +commit e268337dfe26dfc7efd422a804dbb27977a3cccc upstream. + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + fs/proc/base.c | 145 +++++++++++++++------------------------------------------ + 1 file changed, 39 insertions(+), 106 deletions(-) + +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -194,65 +194,7 @@ static int proc_root_link(struct inode * + 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; +@@ -263,7 +205,7 @@ struct mm_struct *mm_for_maps(struct tas + + 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); + } +@@ -272,6 +214,11 @@ struct mm_struct *mm_for_maps(struct tas + 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; +@@ -816,38 +763,39 @@ static const struct file_operations proc + + 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; + +@@ -874,13 +822,7 @@ static ssize_t mem_read(struct file * fi + } + *ppos = src; + +-out_put: +- mmput(mm); +-out_free: + free_page((unsigned long) page); +-out: +- put_task_struct(task); +-out_no_task: + return ret; + } + +@@ -889,27 +831,15 @@ static ssize_t mem_write(struct file * f + { + 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) { +@@ -933,13 +863,7 @@ static ssize_t mem_write(struct file * f + } + *ppos = dst; + +-out_mm: +- mmput(mm); +-out_free: + free_page((unsigned long) page); +-out_task: +- put_task_struct(task); +-out_no_task: + return copied; + } + +@@ -959,11 +883,20 @@ loff_t mem_lseek(struct file *file, loff + 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, diff --git a/debian/patches/series/base b/debian/patches/series/base index cd30b0ccb..39e288a58 100644 --- a/debian/patches/series/base +++ b/debian/patches/series/base @@ -75,3 +75,4 @@ + features/all/Input-ALPS-remove-assumptions-about-packet-size.patch + features/all/Input-ALPS-add-support-for-protocol-versions-3-and-4.patch + features/all/Input-ALPS-add-semi-MT-support-for-v3-protocol.patch ++ bugfix/all/proc-clean-up-and-fix-proc-pid-mem-handling.patch