[kernel] r16948 - in dists/squeeze/linux-2.6/debian: . patches/bugfix/all patches/debian patches/series

Ben Hutchings benh at alioth.debian.org
Mon Feb 28 04:05:29 UTC 2011


Author: benh
Date: Mon Feb 28 04:05:25 2011
New Revision: 16948

Log:
af_unix: Limit recursion level of passing sockets through sockets

This fixes a variant of CVE-2010-4249, which does not seem to have
been assigned its own CVE yet.

Added:
   dists/squeeze/linux-2.6/debian/patches/bugfix/all/af_unix-limit-recursion-level.patch
   dists/squeeze/linux-2.6/debian/patches/debian/af_unix-Avoid-ABI-change-from-introduction-of-recursion-limit.patch
Modified:
   dists/squeeze/linux-2.6/debian/changelog
   dists/squeeze/linux-2.6/debian/patches/series/31

Modified: dists/squeeze/linux-2.6/debian/changelog
==============================================================================
--- dists/squeeze/linux-2.6/debian/changelog	Mon Feb 28 03:37:49 2011	(r16947)
+++ dists/squeeze/linux-2.6/debian/changelog	Mon Feb 28 04:05:25 2011	(r16948)
@@ -31,6 +31,8 @@
   * virtio_net: Further fixes for out-of-memory conditions (Closes: #603835)
     - Fix OOM handling on TX
     - Add schedule check to napi_enable call
+  * af_unix: Limit recursion level of passing sockets through sockets
+    (variant of CVE-2010-4249)
 
   [ dann frazier ]
   * xfs: fix information leak using stale NFS handle (CVE-2010-2943)

Added: dists/squeeze/linux-2.6/debian/patches/bugfix/all/af_unix-limit-recursion-level.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze/linux-2.6/debian/patches/bugfix/all/af_unix-limit-recursion-level.patch	Mon Feb 28 04:05:25 2011	(r16948)
@@ -0,0 +1,182 @@
+From: Eric Dumazet <eric.dumazet at gmail.com>
+Date: Thu, 25 Nov 2010 04:11:39 +0000
+Subject: [PATCH] af_unix: limit recursion level
+
+commit 25888e30319f8896fc656fc68643e6a078263060 upstream.
+
+Its easy to eat all kernel memory and trigger NMI watchdog, using an
+exploit program that queues unix sockets on top of others.
+
+lkml ref : http://lkml.org/lkml/2010/11/25/8
+
+This mechanism is used in applications, one choice we have is to have a
+recursion limit.
+
+Other limits might be needed as well (if we queue other types of files),
+since the passfd mechanism is currently limited by socket receive queue
+sizes only.
+
+Add a recursion_level to unix socket, allowing up to 4 levels.
+
+Each time we send an unix socket through sendfd mechanism, we copy its
+recursion level (plus one) to receiver. This recursion level is cleared
+when socket receive queue is emptied.
+
+Reported-by: Марк Коренберг <socketpair at gmail.com>
+Signed-off-by: Eric Dumazet <eric.dumazet at gmail.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Adjust for 2.6.32]
+---
+ include/net/af_unix.h |    2 ++
+ net/unix/af_unix.c    |   37 ++++++++++++++++++++++++++++++++-----
+ net/unix/garbage.c    |    2 +-
+ 3 files changed, 35 insertions(+), 6 deletions(-)
+
+diff --git a/include/net/af_unix.h b/include/net/af_unix.h
+index 1614d78..861045f 100644
+--- a/include/net/af_unix.h
++++ b/include/net/af_unix.h
+@@ -10,6 +10,7 @@ extern void unix_inflight(struct file *fp);
+ extern void unix_notinflight(struct file *fp);
+ extern void unix_gc(void);
+ extern void wait_for_unix_gc(void);
++extern struct sock *unix_get_socket(struct file *filp);
+ 
+ #define UNIX_HASH_SIZE	256
+ 
+@@ -56,6 +57,7 @@ struct unix_sock {
+         spinlock_t		lock;
+ 	unsigned int		gc_candidate : 1;
+ 	unsigned int		gc_maybe_cycle : 1;
++	unsigned char		recursion_level;
+         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 065dc66..e707e26 100644
+--- a/net/unix/af_unix.c
++++ b/net/unix/af_unix.c
+@@ -1323,9 +1323,25 @@ static void unix_destruct_fds(struct sk_buff *skb)
+ 	sock_wfree(skb);
+ }
+ 
++#define MAX_RECURSION_LEVEL 4
++
+ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+ {
+ 	int i;
++	unsigned char max_level = 0;
++	int unix_sock_count = 0;
++
++	for (i = scm->fp->count - 1; i >= 0; i--) {
++		struct sock *sk = unix_get_socket(scm->fp->fp[i]);
++
++		if (sk) {
++			unix_sock_count++;
++			max_level = max(max_level,
++					unix_sk(sk)->recursion_level);
++		}
++	}
++	if (unlikely(max_level > MAX_RECURSION_LEVEL))
++		return -ETOOMANYREFS;
+ 
+ 	/*
+ 	 * Need to duplicate file references for the sake of garbage
+@@ -1336,10 +1352,12 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+ 	if (!UNIXCB(skb).fp)
+ 		return -ENOMEM;
+ 
+-	for (i = scm->fp->count-1; i >= 0; i--)
+-		unix_inflight(scm->fp->fp[i]);
++	if (unix_sock_count) {
++		for (i = scm->fp->count - 1; i >= 0; i--)
++			unix_inflight(scm->fp->fp[i]);
++	}
+ 	skb->destructor = unix_destruct_fds;
+-	return 0;
++	return max_level;
+ }
+ 
+ /*
+@@ -1361,6 +1379,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 	struct sk_buff *skb;
+ 	long timeo;
+ 	struct scm_cookie tmp_scm;
++	int max_level = 0;
+ 
+ 	if (NULL == siocb->scm)
+ 		siocb->scm = &tmp_scm;
+@@ -1401,8 +1420,9 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 	memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
+ 	if (siocb->scm->fp) {
+ 		err = unix_attach_fds(siocb->scm, skb);
+-		if (err)
++		if (err < 0)
+ 			goto out_free;
++		max_level = err + 1;
+ 	}
+ 	unix_get_secdata(siocb->scm, skb);
+ 
+@@ -1483,6 +1503,8 @@ restart:
+ 	}
+ 
+ 	skb_queue_tail(&other->sk_receive_queue, skb);
++	if (max_level > unix_sk(other)->recursion_level)
++		unix_sk(other)->recursion_level = max_level;
+ 	unix_state_unlock(other);
+ 	other->sk_data_ready(other, len);
+ 	sock_put(other);
+@@ -1513,6 +1535,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 	int sent = 0;
+ 	struct scm_cookie tmp_scm;
+ 	bool fds_sent = false;
++	int max_level = 0;
+ 
+ 	if (NULL == siocb->scm)
+ 		siocb->scm = &tmp_scm;
+@@ -1577,10 +1600,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 		/* Only send the fds in the first buffer */
+ 		if (siocb->scm->fp && !fds_sent) {
+ 			err = unix_attach_fds(siocb->scm, skb);
+-			if (err) {
++			if (err < 0) {
+ 				kfree_skb(skb);
+ 				goto out_err;
+ 			}
++			max_level = err + 1;
+ 			fds_sent = true;
+ 		}
+ 
+@@ -1597,6 +1621,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ 			goto pipe_err_free;
+ 
+ 		skb_queue_tail(&other->sk_receive_queue, skb);
++		if (max_level > unix_sk(other)->recursion_level)
++			unix_sk(other)->recursion_level = max_level;
+ 		unix_state_unlock(other);
+ 		other->sk_data_ready(other, size);
+ 		sent += size;
+@@ -1813,6 +1839,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
+ 		unix_state_lock(sk);
+ 		skb = skb_dequeue(&sk->sk_receive_queue);
+ 		if (skb == NULL) {
++			unix_sk(sk)->recursion_level = 0;
+ 			if (copied >= target)
+ 				goto unlock;
+ 
+diff --git a/net/unix/garbage.c b/net/unix/garbage.c
+index 736e6f9..cb72e91 100644
+--- a/net/unix/garbage.c
++++ b/net/unix/garbage.c
+@@ -97,7 +97,7 @@ static DECLARE_WAIT_QUEUE_HEAD(unix_gc_wait);
+ unsigned int unix_tot_inflight;
+ 
+ 
+-static struct sock *unix_get_socket(struct file *filp)
++struct sock *unix_get_socket(struct file *filp)
+ {
+ 	struct sock *u_sock = NULL;
+ 	struct inode *inode = filp->f_path.dentry->d_inode;
+-- 
+1.7.4.1
+

Added: dists/squeeze/linux-2.6/debian/patches/debian/af_unix-Avoid-ABI-change-from-introduction-of-recursion-limit.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/squeeze/linux-2.6/debian/patches/debian/af_unix-Avoid-ABI-change-from-introduction-of-recursion-limit.patch	Mon Feb 28 04:05:25 2011	(r16948)
@@ -0,0 +1,44 @@
+From: Ben Hutchings <ben at decadent.org.uk>
+Date: Mon, 28 Feb 2011 03:31:55 +0000
+Subject: [PATCH] af_unix: Avoid ABI change from introduction of recursion limit
+
+---
+ include/net/af_unix.h |    5 ++++-
+ net/unix/af_unix.c    |    5 +++--
+ 2 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/include/net/af_unix.h b/include/net/af_unix.h
+index 861045f..12e5f05 100644
+--- a/include/net/af_unix.h
++++ b/include/net/af_unix.h
+@@ -57,7 +57,10 @@ struct unix_sock {
+         spinlock_t		lock;
+ 	unsigned int		gc_candidate : 1;
+ 	unsigned int		gc_maybe_cycle : 1;
+-	unsigned char		recursion_level;
++#ifndef __GENKSYSMS__
++	unsigned int		: 6;
++	unsigned int		recursion_level : 8;
++#endif
+         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 e707e26..e64ee65 100644
+--- a/net/unix/af_unix.c
++++ b/net/unix/af_unix.c
+@@ -1336,8 +1336,9 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+ 
+ 		if (sk) {
+ 			unix_sock_count++;
+-			max_level = max(max_level,
+-					unix_sk(sk)->recursion_level);
++			max_level = max_t(unsigned char,
++					  max_level,
++					  unix_sk(sk)->recursion_level);
+ 		}
+ 	}
+ 	if (unlikely(max_level > MAX_RECURSION_LEVEL))
+-- 
+1.7.4.1
+

Modified: dists/squeeze/linux-2.6/debian/patches/series/31
==============================================================================
--- dists/squeeze/linux-2.6/debian/patches/series/31	Mon Feb 28 03:37:49 2011	(r16947)
+++ dists/squeeze/linux-2.6/debian/patches/series/31	Mon Feb 28 04:05:25 2011	(r16948)
@@ -36,3 +36,5 @@
 + bugfix/s390/remove-task_show_regs.patch
 + bugfix/all/can-use-inode-instead-of-kernel-address-for-proc-file.patch
 + bugfix/all/revert-USB-prevent-buggy-hubs-from-crashing-the-USB.patch
++ bugfix/all/af_unix-limit-recursion-level.patch
++ debian/af_unix-Avoid-ABI-change-from-introduction-of-recursion-limit.patch



More information about the Kernel-svn-changes mailing list