[kernel] r12386 - in dists/etch-security/linux-2.6.24/debian: . config patches/bugfix patches/series

Dann Frazier dannf at alioth.debian.org
Mon Nov 17 06:13:53 UTC 2008


Author: dannf
Date: Mon Nov 17 06:13:52 2008
New Revision: 12386

Log:
unix domain sockets: fix recursive descent in __scm_destroy()
and garbage collector counting bug (CVE-2008-5029)


Added:
   dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-counting-gc.patch
   dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-recursive-descent-abi-ignore.patch
   dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-recursive-descent.patch
   dists/etch-security/linux-2.6.24/debian/patches/series/6~etchnhalf.7
Modified:
   dists/etch-security/linux-2.6.24/debian/changelog
   dists/etch-security/linux-2.6.24/debian/config/defines

Modified: dists/etch-security/linux-2.6.24/debian/changelog
==============================================================================
--- dists/etch-security/linux-2.6.24/debian/changelog	(original)
+++ dists/etch-security/linux-2.6.24/debian/changelog	Mon Nov 17 06:13:52 2008
@@ -1,3 +1,11 @@
+linux-2.6.24 (2.6.24-6~etchnhalf.7) UNRELEASED; urgency=high
+
+  [ Alexander Prinsier ]
+  * unix domain sockets: fix recursive descent in __scm_destroy()
+    and garbage collector counting bug (CVE-2008-5029)
+
+ -- dann frazier <dannf at debian.org>  Mon, 13 Oct 2008 00:01:20 -0600
+
 linux-2.6.24 (2.6.24-6~etchnhalf.6) stable-security; urgency=high
 
   * Add missing capability checks in sbni_ioctl (CVE-2008-3525)

Modified: dists/etch-security/linux-2.6.24/debian/config/defines
==============================================================================
--- dists/etch-security/linux-2.6.24/debian/config/defines	(original)
+++ dists/etch-security/linux-2.6.24/debian/config/defines	Mon Nov 17 06:13:52 2008
@@ -1,5 +1,10 @@
 [abi]
 abiname: etchnhalf.1
+ignore-changes:
+ __scm_destroy
+ __scm_send
+ scm_detach_fds
+ scm_fp_dup
 
 [base]
 arches:

Added: dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-counting-gc.patch
==============================================================================
--- (empty file)
+++ dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-counting-gc.patch	Mon Nov 17 06:13:52 2008
@@ -0,0 +1,204 @@
+commit 6209344f5a3795d34b7f2c0061f49802283b6bdd
+Author: Miklos Szeredi <mszeredi at suse.cz>
+Date:   Sun Nov 9 15:23:57 2008 +0100
+
+    net: unix: fix inflight counting bug in garbage collector
+    
+    Previously I assumed that the receive queues of candidates don't
+    change during the GC.  This is only half true, nothing can be received
+    from the queues (see comment in unix_gc()), but buffers could be added
+    through the other half of the socket pair, which may still have file
+    descriptors referring to it.
+    
+    This can result in inc_inflight_move_tail() erronously increasing the
+    "inflight" counter for a unix socket for which dec_inflight() wasn't
+    previously called.  This in turn can trigger the "BUG_ON(total_refs <
+    inflight_refs)" in a later garbage collection run.
+    
+    Fix this by only manipulating the "inflight" counter for sockets which
+    are candidates themselves.  Duplicating the file references in
+    unix_attach_fds() is also needed to prevent a socket becoming a
+    candidate for GC while the skb that contains it is not yet queued.
+    
+    Reported-by: Andrea Bittau <a.bittau at cs.ucl.ac.uk>
+    Signed-off-by: Miklos Szeredi <mszeredi at suse.cz>
+    CC: stable at kernel.org
+    Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+
+    Backported to Debian's 2.6.24 by Alexander Prinsier <aphexer at mailhaven.com>
+
+diff --git a/include/net/af_unix.h b/include/net/af_unix.h
+index 7dd29b7..c29ff1d 100644
+--- a/include/net/af_unix.h
++++ b/include/net/af_unix.h
+@@ -54,6 +54,7 @@ struct unix_sock {
+         atomic_t                inflight;
+         spinlock_t		lock;
+ 	unsigned int		gc_candidate : 1;
++	unsigned int		gc_maybe_cycle : 1;
+         wait_queue_head_t       peer_wait;
+ };
+ #define unix_sk(__sk) ((struct unix_sock *)__sk)
+diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
+index 4d3c607..eb90f77 100644
+--- a/net/unix/af_unix.c
++++ b/net/unix/af_unix.c
+@@ -1311,14 +1311,23 @@ static void unix_destruct_fds(struct sk_buff *skb)
+ 	sock_wfree(skb);
+ }
+ 
+-static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
++static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+ {
+ 	int i;
++
++	/*
++	 * Need to duplicate file references for the sake of garbage
++	 * collection.  Otherwise a socket in the fps might become a
++	 * candidate for GC while the skb is not yet queued.
++	 */
++	UNIXCB(skb).fp = scm_fp_dup(scm->fp);
++	if (!UNIXCB(skb).fp)
++		return -ENOMEM;
++
+ 	for (i=scm->fp->count-1; i>=0; i--)
+ 		unix_inflight(scm->fp->fp[i]);
+-	UNIXCB(skb).fp = scm->fp;
+ 	skb->destructor = unix_destruct_fds;
+-	scm->fp = NULL;
++	return 0;
+ }
+ 
+ /*
+@@ -1376,8 +1385,11 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 		goto out;
+ 
+ 	memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
+-	if (siocb->scm->fp)
+-		unix_attach_fds(siocb->scm, skb);
++	if (siocb->scm->fp) {
++		err = unix_attach_fds(siocb->scm, skb);
++		if (err)
++			goto out_free;
++	}
+ 	unix_get_secdata(siocb->scm, skb);
+ 
+ 	skb_reset_transport_header(skb);
+@@ -1548,8 +1560,13 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 		size = min_t(int, size, skb_tailroom(skb));
+ 
+ 		memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
+-		if (siocb->scm->fp)
+-			unix_attach_fds(siocb->scm, skb);
++		if (siocb->scm->fp) {
++			err = unix_attach_fds(siocb->scm, skb);
++			if (err) {
++				kfree_skb(skb);
++				goto out_err;
++			}
++		}
+ 
+ 		if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) {
+ 			kfree_skb(skb);
+diff --git a/net/unix/garbage.c b/net/unix/garbage.c
+index 2a27b84..6d4a9a8 100644
+--- a/net/unix/garbage.c
++++ b/net/unix/garbage.c
+@@ -186,8 +186,17 @@ static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
+ 				 */
+ 				struct sock *sk = unix_get_socket(*fp++);
+ 				if (sk) {
+-					hit = true;
+-					func(unix_sk(sk));
++					struct unix_sock *u = unix_sk(sk);
++
++					/*
++					 * Ignore non-candidates, they could
++					 * have been added to the queues after
++					 * starting the garbage collection
++					 */
++					if (u->gc_candidate) {
++						hit = true;
++						func(u);
++					}
+ 				}
+ 			}
+ 			if (hit && hitlist != NULL) {
+@@ -249,11 +258,11 @@ static void inc_inflight_move_tail(struct unix_sock *u)
+ {
+ 	atomic_inc(&u->inflight);
+ 	/*
+-	 * If this is still a candidate, move it to the end of the
+-	 * list, so that it's checked even if it was already passed
+-	 * over
++	 * If this still might be part of a cycle, move it to the end
++	 * of the list, so that it's checked even if it was already
++	 * passed over
+ 	 */
+-	if (u->gc_candidate)
++	if (u->gc_maybe_cycle)
+ 		list_move_tail(&u->link, &gc_candidates);
+ }
+ 
+@@ -267,6 +276,7 @@ void unix_gc(void)
+ 	struct unix_sock *next;
+ 	struct sk_buff_head hitlist;
+ 	struct list_head cursor;
++	LIST_HEAD(not_cycle_list);
+ 
+ 	spin_lock(&unix_gc_lock);
+ 
+@@ -282,10 +292,14 @@ void unix_gc(void)
+ 	 *
+ 	 * Holding unix_gc_lock will protect these candidates from
+ 	 * being detached, and hence from gaining an external
+-	 * reference.  This also means, that since there are no
+-	 * possible receivers, the receive queues of these sockets are
+-	 * static during the GC, even though the dequeue is done
+-	 * before the detach without atomicity guarantees.
++	 * reference.  Since there are no possible receivers, all
++	 * buffers currently on the candidates' queues stay there
++	 * during the garbage collection.
++	 *
++	 * We also know that no new candidate can be added onto the
++	 * receive queues.  Other, non candidate sockets _can_ be
++	 * added to queue, so we must make sure only to touch
++	 * candidates.
+ 	 */
+ 	list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
+ 		int total_refs;
+@@ -299,6 +313,7 @@ void unix_gc(void)
+ 		if (total_refs == inflight_refs) {
+ 			list_move_tail(&u->link, &gc_candidates);
+ 			u->gc_candidate = 1;
++			u->gc_maybe_cycle = 1;
+ 		}
+ 	}
+ 
+@@ -325,14 +340,24 @@ void unix_gc(void)
+ 		list_move(&cursor, &u->link);
+ 
+ 		if (atomic_read(&u->inflight) > 0) {
+-			list_move_tail(&u->link, &gc_inflight_list);
+-			u->gc_candidate = 0;
++			list_move_tail(&u->link, &not_cycle_list);
++			u->gc_maybe_cycle = 0;
+ 			scan_children(&u->sk, inc_inflight_move_tail, NULL);
+ 		}
+ 	}
+ 	list_del(&cursor);
+ 
+ 	/*
++	 * not_cycle_list contains those sockets which do not make up a
++	 * cycle.  Restore these to the inflight list.
++	 */
++	while (!list_empty(&not_cycle_list)) {
++		u = list_entry(not_cycle_list.next, struct unix_sock, link);
++		u->gc_candidate = 0;
++		list_move_tail(&u->link, &gc_inflight_list);
++	}
++
++	/*
+ 	 * Now gc_candidates contains only garbage.  Restore original
+ 	 * inflight counters for these as well, and remove the skbuffs
+ 	 * which are creating the cycle(s).

Added: dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-recursive-descent-abi-ignore.patch
==============================================================================
--- (empty file)
+++ dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-recursive-descent-abi-ignore.patch	Mon Nov 17 06:13:52 2008
@@ -0,0 +1,12 @@
+--- source/include/linux/sched.h.orig	2008-11-14 17:10:09.000000000 -0700
++++ source/include/linux/sched.h	2008-11-14 17:08:36.000000000 -0700
+@@ -1179,7 +1179,9 @@
+ #endif
+ 	struct prop_local_single dirties;
+ 
++#ifndef __GENKSYMS__
+ 	struct list_head        *scm_work_list;
++#endif
+ };
+ 
+ /*

Added: dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-recursive-descent.patch
==============================================================================
--- (empty file)
+++ dists/etch-security/linux-2.6.24/debian/patches/bugfix/unix-domain-recursive-descent.patch	Mon Nov 17 06:13:52 2008
@@ -0,0 +1,99 @@
+commit f8d570a4745835f2238a33b537218a1bb03fc671
+Author: David Miller <davem at davemloft.net>
+Date:   Thu Nov 6 00:37:40 2008 -0800
+
+    net: Fix recursive descent in __scm_destroy().
+    
+    __scm_destroy() walks the list of file descriptors in the scm_fp_list
+    pointed to by the scm_cookie argument.
+    
+    Those, in turn, can close sockets and invoke __scm_destroy() again.
+    
+    There is nothing which limits how deeply this can occur.
+    
+    The idea for how to fix this is from Linus.  Basically, we do all of
+    the fput()s at the top level by collecting all of the scm_fp_list
+    objects hit by an fput().  Inside of the initial __scm_destroy() we
+    keep running the list until it is empty.
+    
+    Signed-off-by: David S. Miller <davem at davemloft.net>
+    Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+
+    Backported to Debian's 2.6.24 by Alexander Prinsier <aphexer at mailhaven.com>
+
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -1178,6 +1178,8 @@ struct task_struct {
+ 	int make_it_fail;
+ #endif
+ 	struct prop_local_single dirties;
++
++	struct list_head        *scm_work_list;
+ };
+ 
+ /*
+diff --git a/include/net/scm.h b/include/net/scm.h
+index 06df126..33e9986 100644
+--- a/include/net/scm.h
++++ b/include/net/scm.h
+@@ -14,8 +14,9 @@
+ 
+ struct scm_fp_list
+ {
+-	int		count;
+-	struct file	*fp[SCM_MAX_FD];
++	struct list_head	list;
++	int			count;
++	struct file		*fp[SCM_MAX_FD];
+ };
+ 
+ struct scm_cookie
+diff --git a/net/core/scm.c b/net/core/scm.c
+index 10f5c65..ab242cc 100644
+--- a/net/core/scm.c
++++ b/net/core/scm.c
+@@ -75,6 +75,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+ 		if (!fpl)
+ 			return -ENOMEM;
+ 		*fplp = fpl;
++		INIT_LIST_HEAD(&fpl->list);
+ 		fpl->count = 0;
+ 	}
+ 	fpp = &fpl->fp[fpl->count];
+@@ -106,9 +107,25 @@ void __scm_destroy(struct scm_cookie *scm)
+ 
+ 	if (fpl) {
+ 		scm->fp = NULL;
+-		for (i=fpl->count-1; i>=0; i--)
+-			fput(fpl->fp[i]);
+-		kfree(fpl);
++		if (current->scm_work_list) {
++			list_add_tail(&fpl->list, current->scm_work_list);
++		} else {
++			LIST_HEAD(work_list);
++
++			current->scm_work_list = &work_list;
++
++			list_add(&fpl->list, &work_list);
++			while (!list_empty(&work_list)) {
++				fpl = list_first_entry(&work_list, struct scm_fp_list, list);
++
++				list_del(&fpl->list);
++				for (i=fpl->count-1; i>=0; i--)
++					fput(fpl->fp[i]);
++				kfree(fpl);
++			}
++
++			current->scm_work_list = NULL;
++		}
+ 	}
+ }
+ 
+@@ -284,6 +301,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
+ 
+ 	new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
+ 	if (new_fpl) {
++		INIT_LIST_HEAD(&new_fpl->list);
+ 		for (i=fpl->count-1; i>=0; i--)
+ 			get_file(fpl->fp[i]);
+ 		memcpy(new_fpl, fpl, sizeof(*fpl));

Added: dists/etch-security/linux-2.6.24/debian/patches/series/6~etchnhalf.7
==============================================================================
--- (empty file)
+++ dists/etch-security/linux-2.6.24/debian/patches/series/6~etchnhalf.7	Mon Nov 17 06:13:52 2008
@@ -0,0 +1,3 @@
++ bugfix/unix-domain-recursive-descent.patch
++ bugfix/unix-domain-counting-gc.patch
++ bugfix/unix-domain-recursive-descent-abi-ignore.patch



More information about the Kernel-svn-changes mailing list