diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /net/unix/af_unix.c | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r-- | net/unix/af_unix.c | 95 |
1 files changed, 87 insertions, 8 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index ae33770fe..21614a3c6 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.73 1999/01/15 06:55:48 davem Exp $ + * Version: $Id: af_unix.c,v 1.76 1999/05/08 05:54:55 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -33,6 +33,16 @@ * Lots of bug fixes. * Alexey Kuznetosv : Repaired (I hope) bugs introduces * by above two patches. + * Andrea Arcangeli : If possible we block in connect(2) + * if the max backlog of the listen socket + * is been reached. This won't break + * old apps and it will avoid huge amount + * of socks hashed (this for unix_gc() + * performances reasons). + * Security fix that limits the max + * number of socks to 2*max_files and + * the number of skb queueable in the + * dgram receiver. * * Known differences from reference BSD that was tested: * @@ -100,8 +110,12 @@ int sysctl_unix_delete_delay = HZ; int sysctl_unix_destroy_delay = 10*HZ; +int sysctl_unix_max_dgram_qlen = 10; unix_socket *unix_socket_table[UNIX_HASH_SIZE+1]; +static atomic_t unix_nr_socks = ATOMIC_INIT(0); +static struct wait_queue * unix_ack_wqueue = NULL; +static struct wait_queue * unix_dgram_wqueue = NULL; #define unix_sockets_unbound (unix_socket_table[UNIX_HASH_SIZE]) @@ -263,6 +277,8 @@ static void unix_destroy_timer(unsigned long data) unix_socket *sk=(unix_socket *)data; if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0) { + atomic_dec(&unix_nr_socks); + sk_free(sk); /* socket destroyed, decrement count */ @@ -295,13 +311,18 @@ static int unix_release_sock (unix_socket *sk) sk->dead=1; sk->socket = NULL; + if (sk->state == TCP_LISTEN) + wake_up_interruptible(&unix_ack_wqueue); + if (sk->type == SOCK_DGRAM) + wake_up_interruptible(&unix_dgram_wqueue); + skpair=unix_peer(sk); if (skpair!=NULL) { if (sk->type==SOCK_STREAM && unix_our_peer(sk, skpair)) { - skpair->state_change(skpair); + skpair->data_ready(skpair,0); skpair->shutdown=SHUTDOWN_MASK; /* No more writes*/ } unix_unlock(skpair); /* It may now die */ @@ -347,6 +368,8 @@ static void unix_destroy_socket(unix_socket *sk) if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0) { + atomic_dec(&unix_nr_socks); + sk_free(sk); /* socket destroyed, decrement count */ @@ -371,6 +394,8 @@ static int unix_listen(struct socket *sock, int backlog) return -EOPNOTSUPP; /* Only stream sockets accept */ if (!sk->protinfo.af_unix.addr) return -EINVAL; /* No listens on an unbound socket */ + if ((unsigned) backlog > SOMAXCONN) + backlog = SOMAXCONN; sk->max_ack_backlog=backlog; sk->state=TCP_LISTEN; sock->flags |= SO_ACCEPTCON; @@ -388,6 +413,9 @@ static struct sock * unix_create1(struct socket *sock, int stream) { struct sock *sk; + if (atomic_read(&unix_nr_socks) >= 2*max_files) + return NULL; + MOD_INC_USE_COUNT; sk = sk_alloc(PF_UNIX, GFP_KERNEL, 1); if (!sk) { @@ -395,6 +423,8 @@ static struct sock * unix_create1(struct socket *sock, int stream) return NULL; } + atomic_inc(&unix_nr_socks); + sock_init_data(sock,sk); if (stream) @@ -673,9 +703,25 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, we will have to recheck all again in any case. */ +restart: /* Find listening sock */ other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err); + if (!other) + return -ECONNREFUSED; + + while (other->ack_backlog >= other->max_ack_backlog) { + unix_unlock(other); + if (other->dead || other->state != TCP_LISTEN) + return -ECONNREFUSED; + if (flags & O_NONBLOCK) + return -EAGAIN; + interruptible_sleep_on(&unix_ack_wqueue); + if (signal_pending(current)) + return -ERESTARTSYS; + goto restart; + } + /* create new sock for complete connection */ newsk = unix_create1(NULL, 1); @@ -704,7 +750,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, /* Check that listener is in valid state. */ err = -ECONNREFUSED; - if (other == NULL || other->dead || other->state != TCP_LISTEN) + if (other->dead || other->state != TCP_LISTEN) goto out; err = -ENOMEM; @@ -815,11 +861,10 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags) continue; } tsk = skb->sk; - sk->ack_backlog--; + if (sk->max_ack_backlog == sk->ack_backlog--) + wake_up_interruptible(&unix_ack_wqueue); kfree_skb(skb); - if (!tsk->dead) - break; - unix_release_sock(tsk); + break; } @@ -947,6 +992,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, * Check with 1003.1g - what should * datagram error */ + dead: unix_unlock(other); unix_peer(sk)=NULL; other = NULL; @@ -964,6 +1010,29 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, goto out_unlock; } + while (skb_queue_len(&other->receive_queue) >= + sysctl_unix_max_dgram_qlen) + { + if (sock->file->f_flags & O_NONBLOCK) + { + err = -EAGAIN; + goto out_unlock; + } + interruptible_sleep_on(&unix_dgram_wqueue); + if (other->dead) + goto dead; + if (sk->shutdown & SEND_SHUTDOWN) + { + err = -EPIPE; + goto out_unlock; + } + if (signal_pending(current)) + { + err = -ERESTARTSYS; + goto out_unlock; + } + } + skb_queue_tail(&other->receive_queue, skb); other->data_ready(other,len); @@ -1126,6 +1195,13 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, int size, if (!skb) goto out; + /* + * sysctl_unix_max_dgram_qlen may change over the time we blocked + * in the waitqueue so we must wakeup every time we shrink the + * receiver queue. -arca + */ + wake_up_interruptible(&unix_dgram_wqueue); + if (msg->msg_name) { msg->msg_namelen = sizeof(short); @@ -1333,7 +1409,10 @@ static int unix_shutdown(struct socket *sock, int mode) if (mode&SEND_SHUTDOWN) peer_mode |= RCV_SHUTDOWN; other->shutdown |= peer_mode; - other->state_change(other); + if (peer_mode&RCV_SHUTDOWN) + other->data_ready(other,0); + else + other->state_change(other); } } return 0; |