Messages in this thread Patch in this message | | | From | Lars Heete <> | Subject | Re: [patch] fix for accept on unix-domain sockets | Date | Fri, 29 Oct 1999 08:00:03 +0200 |
| |
On Thu, 28 Oct 1999, kuznet@ms2.inr.ac.ru wrote: >> The reason is, that skb_recv_datagram is used get incoming connection requests, but this checks >> if a SOCK_STREAM socket is in TCP_ESTABLISHED state before waiting. > >Opla! Thank you. > >The patch is incorrect (racy), but you can make correct one >if copy part of skb_recv_datagram, omitting offending line >and some useless checks (f.e. sk->shutdown, socket_error() etc)
Ok, here is my corrected version. Is it possible to make a generic race-free skb_wait_datagram function, because some other protocols might need this too for their accept call (ax25, x25, irda, rose, spx)
Lars--- linux.orig/net/unix/af_unix.c Thu Oct 28 18:25:11 1999 +++ linux/net/unix/af_unix.c Fri Oct 29 07:56:51 1999 @@ -942,10 +942,46 @@ sockb->state=SS_CONNECTED; } return 0; } +/* + * Wait for a packet.. + */ + +static int unix_wait_for_connect(struct sock * sk, int *err) +{ + int error; + + DECLARE_WAITQUEUE(wait, current); + + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(sk->sleep, &wait); + + if (!skb_queue_empty(&sk->receive_queue)) + goto ready; + + /* handle signals */ + error = -ERESTARTSYS; + if (signal_pending(current)) + goto out; + + schedule(); + +ready: + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); + return 0; + +out: + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); + *err = error; + return error; +} + + static int unix_accept(struct socket *sock, struct socket *newsock, int flags) { unix_socket *sk = sock->sk; unix_socket *tsk; struct sk_buff *skb; @@ -959,20 +995,28 @@ if (sk->state!=TCP_LISTEN) goto out; /* If socket state is TCP_LISTEN it cannot change, so that no locks are necessary. + + It's not possible to use skb_recv_datagram here, + because this checks if the socket is connected. */ - - skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err); - if (!skb) - goto out; + + while ((skb = skb_dequeue(&sk->receive_queue)) == NULL) { + if (flags&O_NONBLOCK) { + err = -EAGAIN; + goto out; + } + if (unix_wait_for_connect(sk, &err) != 0) + goto out; + } tsk = skb->sk; + kfree_skb(skb); if (skb_queue_len(&sk->receive_queue) <= sk->max_ack_backlog/2) wake_up_interruptible(&sk->protinfo.af_unix.peer_wait); - skb_free_datagram(sk, skb); /* attach accepted sock to socket */ unix_state_wlock(tsk); newsock->state = SS_CONNECTED; newsock->sk = tsk; | |