[kernel] r6587 - in
dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian:
patches patches/series
Dann Frazier
dannf at costa.debian.org
Wed May 17 05:48:06 UTC 2006
Author: dannf
Date: Wed May 17 05:48:03 2006
New Revision: 6587
Added:
dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/perfmon-exit-race.dpatch
Modified:
dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/changelog
dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/series/2.6.8-16sarge3
Log:
* perfmon-exit-race.dpatch
[SECURITY] [ia64] Fix local denial of service vulnerability (oops) in
the ia64 perfmon subsystem
See CVE-2006-0558
Modified: dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/changelog
==============================================================================
--- dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/changelog (original)
+++ dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/changelog Wed May 17 05:48:03 2006
@@ -16,8 +16,12 @@
[SECURITY] Fix directory traversal vulnerability in smbfs that permits
local users to escape chroot restrictions
See CVE-2006-1864
+ * perfmon-exit-race.dpatch
+ [SECURITY] [ia64] Fix local denial of service vulnerability (oops) in
+ the ia64 perfmon subsystem
+ See CVE-2006-0558
- -- dann frazier <dannf at debian.org> Tue, 16 May 2006 22:46:36 -0500
+ -- dann frazier <dannf at debian.org> Tue, 16 May 2006 23:25:43 -0600
kernel-source-2.6.8 (2.6.8-16sarge2) stable-security; urgency=high
Added: dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/perfmon-exit-race.dpatch
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/perfmon-exit-race.dpatch Wed May 17 05:48:03 2006
@@ -0,0 +1,280 @@
+The problem occurs during exit processing when a task has
+done a pfm_context_create() (and pfm_smpl_buffer_alloc()).
+
+To trigger the problem requires:
+ - that the task be interrupted (so that it does not unmap its smpl buffer)
+ - that there is another process accessing its mm_struct
+
+At exit:
+ - sets its task->mm to null
+ - calls mmput(). But the task's vmas are not unmapped because the mm_users
+ count is still 1
+ though pfm_flush() is unable to unmap the buffer vma, but it does
+ "put" the buffer pages when it frees the kernel mapping (vm_struct).
+ - closes the pfm file with pfm_flush() and pfm_close(), which un-reserve
+ the smpl buffer pages, but the buffer vma is not released.
+
+When that external task does the final mmput() on this mm_struct,
+ the sampling buffer's vma is unmapped. This does the second put on its
+ pages and trips the BUG_ON(page_count(p) == 0) in put_page_testzero(p)
+
+This patch maintains a list of active pfm tasks and their mm_structs.
+The list allows pfm_flush() to locate the exiting task's mm_struct so that
+it can unmap the vma.
+(this could be done without a list, but would require another field in the
+ task_struct)
+(this fix is not necessary for Stephane Eranian's perfmon2)
+
+Diffed against 2.6.15-rc6
+
+Signed-off-by: Cliff Wickman <cpw at sgi.com>
+
+# Backported to Debian's 2.6.8 by dann frazier <dannf at debian.org>
+
+diff -urN kernel-source-2.6.8.orig/arch/ia64/kernel/perfmon.c kernel-source-2.6.8/arch/ia64/kernel/perfmon.c
+--- kernel-source-2.6.8.orig/arch/ia64/kernel/perfmon.c 2004-08-14 00:38:04.000000000 -0500
++++ kernel-source-2.6.8/arch/ia64/kernel/perfmon.c 2006-05-16 19:58:17.866972008 -0500
+@@ -540,6 +540,21 @@
+ #define pfm_get_cpu_var(v) __ia64_per_cpu_var(v)
+ #define pfm_get_cpu_data(a,b) per_cpu(a, b)
+
++struct mm_struct *pfm_lookup_mm(void);
++void pfm_remove_task_from_list(struct task_struct *);
++void pfm_record_tm(struct task_struct *);
++/* global: for exit_mmap() to call us back when the mm_struct is removed */
++int pfm_check_list=0;
++int pfm_num_in_list;
++EXPORT_SYMBOL(pfm_check_list);
++struct pfm_task_mm {
++ struct list_head pfm_tm_list;
++ struct task_struct *pfm_taskp;
++ struct mm_struct *pfm_mm;
++};
++rwlock_t pfm_tmlist_lock;
++struct list_head pfm_tm_list_head;
++
+ static inline void
+ pfm_put_task(struct task_struct *task)
+ {
+@@ -1403,13 +1418,13 @@
+ * a PROTECT_CTX() section.
+ */
+ static int
+-pfm_remove_smpl_mapping(struct task_struct *task, void *vaddr, unsigned long size)
++pfm_remove_smpl_mapping(struct task_struct *task, struct mm_struct *mm, void *vaddr, unsigned long size)
+ {
+ int r;
+
+ /* sanity checks */
+- if (task->mm == NULL || size == 0UL || vaddr == NULL) {
+- printk(KERN_ERR "perfmon: pfm_remove_smpl_mapping [%d] invalid context mm=%p\n", task->pid, task->mm);
++ if (mm == NULL || size == 0UL || vaddr == NULL) {
++ printk(KERN_ERR "perfmon: pfm_remove_smpl_mapping [%d] invalid context mm=%p\n", task->pid, mm);
+ return -EINVAL;
+ }
+
+@@ -1418,13 +1433,13 @@
+ /*
+ * does the actual unmapping
+ */
+- down_write(&task->mm->mmap_sem);
++ down_write(&mm->mmap_sem);
+
+ DPRINT(("down_write done smpl_vaddr=%p size=%lu\n", vaddr, size));
+
+- r = pfm_do_munmap(task->mm, (unsigned long)vaddr, size, 0);
++ r = pfm_do_munmap(mm, (unsigned long)vaddr, size, 0);
+
+- up_write(&task->mm->mmap_sem);
++ up_write(&mm->mmap_sem);
+ if (r !=0) {
+ printk(KERN_ERR "perfmon: [%d] unable to unmap sampling buffer @%p size=%lu\n", task->pid, vaddr, size);
+ }
+@@ -1502,6 +1517,12 @@
+ else
+ err = 0;
+ }
++
++ /* initialize task/mm list */
++ rwlock_init(&pfm_tmlist_lock);
++ INIT_LIST_HEAD(&pfm_tm_list_head);
++ pfm_num_in_list = 0;
++
+ return err;
+ }
+
+@@ -1781,6 +1802,7 @@
+ {
+ pfm_context_t *ctx;
+ struct task_struct *task;
++ struct mm_struct *mm;
+ struct pt_regs *regs;
+ unsigned long flags;
+ unsigned long smpl_buf_size = 0UL;
+@@ -1884,11 +1906,14 @@
+ * ctx_smpl_vaddr must never be cleared because it is needed
+ * by every task with access to the context
+ *
+- * When called from do_exit(), the mm context is gone already, therefore
+- * mm is NULL, i.e., the VMA is already gone and we do not have to
+- * do anything here
++ * When called from do_exit(), the mm context may still be present
++ * if mm_users is not zero. But the task mm pointer is set to null
++ * at exit, so we have to use active_mm here.
++ * (we are depending on the assumption that active_mm is not cleared
++ * at exit)
+ */
+- if (ctx->ctx_smpl_vaddr && current->mm) {
++ mm = pfm_lookup_mm();
++ if (ctx->ctx_smpl_vaddr && mm && current->active_mm==mm) {
+ smpl_buf_vaddr = ctx->ctx_smpl_vaddr;
+ smpl_buf_size = ctx->ctx_smpl_size;
+ }
+@@ -1901,7 +1926,7 @@
+ * because some VM function reenables interrupts.
+ *
+ */
+- if (smpl_buf_vaddr) pfm_remove_smpl_mapping(current, smpl_buf_vaddr, smpl_buf_size);
++ if (smpl_buf_vaddr) pfm_remove_smpl_mapping(current, mm, smpl_buf_vaddr, smpl_buf_size);
+
+ return 0;
+ }
+@@ -1939,6 +1964,12 @@
+ DPRINT(("bad magic\n"));
+ return -EBADF;
+ }
++
++ /* It is possible that this task went thru do_exit with
++ mm_users > 1, in which case this task was not removed from
++ the list of pfm tasks with valid mm_struct's. So make sure the
++ task is removed from the list. */
++ pfm_remove_task_from_list(current);
+
+ ctx = (pfm_context_t *)filp->private_data;
+ if (ctx == NULL) {
+@@ -4325,6 +4356,9 @@
+ */
+ ctx->ctx_task = task;
+
++ /* record the task/mm for this task, as it now has a context */
++ pfm_record_tm(task);
++
+ if (is_system) {
+ /*
+ * we load as stopped
+@@ -6672,6 +6706,88 @@
+ * the psr bits are already set properly in copy_threads()
+ */
+ }
++
++/*
++ * record task_struct and mm_struct in the list of valid pfm mm_structs
++ */
++void
++pfm_record_tm(struct task_struct *task)
++{
++ struct pfm_task_mm *tm;
++ struct list_head *this, *next;
++
++ read_lock(&pfm_tmlist_lock);
++ list_for_each_safe(this, next, &pfm_tm_list_head) {
++ tm = list_entry(this, struct pfm_task_mm, pfm_tm_list);
++ if (tm->pfm_taskp == task) {
++ /* don't record it twice */
++ read_unlock(&pfm_tmlist_lock);
++ return;
++ }
++ }
++
++ tm = kmalloc(sizeof(struct pfm_task_mm), GFP_KERNEL);
++ if (!tm) {
++ read_unlock(&pfm_tmlist_lock);
++ return;
++ }
++ tm->pfm_taskp = task;
++ tm->pfm_mm = task->mm;
++ list_add_tail(&tm->pfm_tm_list, &pfm_tm_list_head);
++ pfm_num_in_list++;
++ read_unlock(&pfm_tmlist_lock);
++ return;
++}
++
++/*
++ * look up the current task in the list of valid pfm mm_structs
++ */
++struct mm_struct *
++pfm_lookup_mm()
++{
++ struct pfm_task_mm *tm;
++ struct list_head *this, *next;
++
++ read_lock(&pfm_tmlist_lock);
++ list_for_each_safe(this, next, &pfm_tm_list_head) {
++ tm = list_entry(this, struct pfm_task_mm, pfm_tm_list);
++ if (current == tm->pfm_taskp) {
++ read_unlock(&pfm_tmlist_lock);
++ return tm->pfm_mm;
++ }
++ }
++ read_unlock(&pfm_tmlist_lock);
++ return NULL;
++}
++
++/*
++ * called from exit_mmap when the task's mm_struct is removed
++ * (so that we do not use active_mm when it might point a freed mm_struct
++ * (or another task's mm_struct))
++ */
++void
++pfm_remove_task_from_list(struct task_struct *task)
++{
++ struct pfm_task_mm *tm;
++ struct list_head *this, *next;
++
++ write_lock(&pfm_tmlist_lock);
++ list_for_each_safe(this, next, &pfm_tm_list_head) {
++ tm = list_entry(this, struct pfm_task_mm, pfm_tm_list);
++ if (current == tm->pfm_taskp) {
++ list_del(this);
++ kfree(tm);
++ pfm_num_in_list--;
++ /* clear the flag when all have been removed */
++ if (pfm_num_in_list == 0)
++ pfm_check_list=0;
++ write_unlock(&pfm_tmlist_lock);
++ return;
++ }
++ }
++ write_unlock(&pfm_tmlist_lock);
++ return;
++}
+ #else /* !CONFIG_PERFMON */
+ asmlinkage long
+ sys_perfmonctl (int fd, int cmd, void *arg, int count, long arg5, long arg6, long arg7,
+diff -urN kernel-source-2.6.8.orig/kernel/exit.c kernel-source-2.6.8/kernel/exit.c
+--- kernel-source-2.6.8.orig/kernel/exit.c 2006-02-08 23:55:59.000000000 -0600
++++ kernel-source-2.6.8/kernel/exit.c 2006-05-16 19:58:17.857973376 -0500
+@@ -32,6 +32,11 @@
+ extern void sem_exit (void);
+ extern struct task_struct *child_reaper;
+
++#ifdef CONFIG_PERFMON
++void pfm_remove_task_from_list(struct task_struct *);
++extern int pfm_check_list;
++#endif
++
+ int getrusage(struct task_struct *, int, struct rusage __user *);
+
+ static void __unhash_process(struct task_struct *p)
+@@ -515,6 +520,11 @@
+ up_read(&mm->mmap_sem);
+ enter_lazy_tlb(mm, current);
+ task_unlock(tsk);
++#ifdef CONFIG_PERFMON
++ if (atomic_read(&mm->mm_users) == 1 && pfm_check_list) {
++ pfm_remove_task_from_list(current);
++ }
++#endif
+ mmput(mm);
+ }
+
Modified: dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/series/2.6.8-16sarge3
==============================================================================
--- dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/series/2.6.8-16sarge3 (original)
+++ dists/sarge-security/kernel/source/kernel-source-2.6.8-2.6.8/debian/patches/series/2.6.8-16sarge3 Wed May 17 05:48:03 2006
@@ -3,3 +3,4 @@
+ netfilter-do_replace-overflow.dpatch
+ sys_mbind-sanity-checking.dpatch
+ smbfs-chroot-escape.dpatch
++ perfmon-exit-race.dpatch
More information about the Kernel-svn-changes
mailing list