[[stable] Patch "af_unix: limit recursion level" has been added to the 
2.6.36-stable tree] On 20/12/2010 (Mon 11:42) [email protected] wrote:

> 
> This is a note to let you know that I've just added the patch titled
> 
>     af_unix: limit recursion level
> 
> to the 2.6.36-stable tree which can be found at:
>     
> http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
> 
> The filename of the patch is:
>      af_unix-limit-recursion-level.patch
> and it can be found in the queue-2.6.36 subdirectory.
> 
> If you, or anyone else, feels it should not be added to the stable tree,
> please let <[email protected]> know about it.
> 
> 
> From a1e61b5127af75c0126e38533c25936c422c1191 Mon Sep 17 00:00:00 2001
> From: Eric Dumazet <[email protected]>
> Date: Thu, 25 Nov 2010 04:11:39 +0000
> Subject: af_unix: limit recursion level
> 
> 
> From: Eric Dumazet <[email protected]>
> 
> [ Upstream commit 25888e30319f8896fc656fc68643e6a078263060 ]

Hey Greg,

I had to manually apply this commit for .34 -- I checked that this 34
version applies clean to 32, and so I'm attaching it here, since it
seems like a key fix to block potential memory eaters -- probably
something that you would want for the 32-longterm as well.

It was pretty straigtforward, but if Eric wants to double check I didn't
munge something, well that would be great too.

Paul.


From 56aa6f65cdeefbc3f99c4b59c85786644cf22048 Mon Sep 17 00:00:00 2001
From: Eric Dumazet <[email protected]>
Date: Thu, 25 Nov 2010 04:11:39 +0000
Subject: [PATCH] af_unix: limit recursion level
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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: Марк Коренберг <[email protected]>
Signed-off-by: Eric Dumazet <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Paul Gortmaker <[email protected]>
---
 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 8c34e3b..23068e5 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1324,9 +1324,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
@@ -1337,10 +1353,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;
 }
 
 /*
@@ -1362,6 +1380,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;
 
        if (NULL == siocb->scm)
                siocb->scm = &tmp_scm;
@@ -1402,8 +1421,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);
 
@@ -1484,6 +1504,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);
@@ -1514,6 +1536,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;
 
        if (NULL == siocb->scm)
                siocb->scm = &tmp_scm;
@@ -1578,10 +1601,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;
                }
 
@@ -1598,6 +1622,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;
@@ -1814,6 +1840,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 ef5aa55..493e0e6 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -96,7 +96,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.3.3

_______________________________________________
stable mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/stable

Reply via email to