| From 28c2b2d688b98135d776d988b9bfcd4e935b17ac Mon Sep 17 00:00:00 2001 |
| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Date: Sat, 4 Sep 2010 01:34:28 +0000 |
| Subject: UNIX: Do not loop forever at unix_autobind(). |
| |
| |
| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| |
| [ Upstream commit a9117426d0fcc05a194f728159a2d43df43c7add ] |
| |
| We assumed that unix_autobind() never fails if kzalloc() succeeded. |
| But unix_autobind() allows only 1048576 names. If /proc/sys/fs/file-max is |
| larger than 1048576 (e.g. systems with more than 10GB of RAM), a local user can |
| consume all names using fork()/socket()/bind(). |
| |
| If all names are in use, those who call bind() with addr_len == sizeof(short) |
| or connect()/sendmsg() with setsockopt(SO_PASSCRED) will continue |
| |
| while (1) |
| yield(); |
| |
| loop at unix_autobind() till a name becomes available. |
| This patch adds a loop counter in order to give up after 1048576 attempts. |
| |
| Calling yield() for once per 256 attempts may not be sufficient when many names |
| are already in use, for __unix_find_socket_byname() can take long time under |
| such circumstance. Therefore, this patch also adds cond_resched() call. |
| |
| Note that currently a local user can consume 2GB of kernel memory if the user |
| is allowed to create and autobind 1048576 UNIX domain sockets. We should |
| consider adding some restriction for autobind operation. |
| |
| Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| net/unix/af_unix.c | 15 ++++++++++++--- |
| 1 file changed, 12 insertions(+), 3 deletions(-) |
| |
| --- a/net/unix/af_unix.c |
| +++ b/net/unix/af_unix.c |
| @@ -671,6 +671,7 @@ static int unix_autobind(struct socket * |
| static u32 ordernum = 1; |
| struct unix_address *addr; |
| int err; |
| + unsigned int retries = 0; |
| |
| mutex_lock(&u->readlock); |
| |
| @@ -696,9 +697,17 @@ retry: |
| if (__unix_find_socket_byname(net, addr->name, addr->len, sock->type, |
| addr->hash)) { |
| spin_unlock(&unix_table_lock); |
| - /* Sanity yield. It is unusual case, but yet... */ |
| - if (!(ordernum&0xFF)) |
| - yield(); |
| + /* |
| + * __unix_find_socket_byname() may take long time if many names |
| + * are already in use. |
| + */ |
| + cond_resched(); |
| + /* Give up if all names seems to be in use. */ |
| + if (retries++ == 0xFFFFF) { |
| + err = -ENOSPC; |
| + kfree(addr); |
| + goto out; |
| + } |
| goto retry; |
| } |
| addr->hash ^= sk->sk_type; |