[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