[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, ¬_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(¬_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