diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2005-11-03 17:56:56 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@mandriva.com> | 2005-11-05 18:05:20 -0500 |
commit | 6151b31c9616d71f714fc7ef8e2306f67f3b94c3 (patch) | |
tree | 59ac0ad477d80c53bbf173b5fc863ab239f0705c /net/core | |
parent | eb229c4cdc3389682cda20adb015ba767950a220 (diff) |
[NET]: Fix race condition in sk_stream_wait_connect
When sk_stream_wait_connect detects a state transition to ESTABLISHED
or CLOSE_WAIT prior to it going to sleep, it will return without
calling finish_wait and decrementing sk_write_pending.
This may result in crashes and other unintended behaviour.
The fix is to always call finish_wait and update sk_write_pending since
it is safe to do so even if the wait entry is no longer on the queue.
This bug was tracked down with the help of Alex Sidorenko and the
fix is also based on his suggestion.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/stream.c | 12 |
1 files changed, 6 insertions, 6 deletions
diff --git a/net/core/stream.c b/net/core/stream.c index ac9edfdf8742..15bfd03e8024 100644 --- a/net/core/stream.c +++ b/net/core/stream.c | |||
@@ -52,8 +52,9 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p) | |||
52 | { | 52 | { |
53 | struct task_struct *tsk = current; | 53 | struct task_struct *tsk = current; |
54 | DEFINE_WAIT(wait); | 54 | DEFINE_WAIT(wait); |
55 | int done; | ||
55 | 56 | ||
56 | while (1) { | 57 | do { |
57 | if (sk->sk_err) | 58 | if (sk->sk_err) |
58 | return sock_error(sk); | 59 | return sock_error(sk); |
59 | if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) | 60 | if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) |
@@ -65,13 +66,12 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p) | |||
65 | 66 | ||
66 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); | 67 | prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); |
67 | sk->sk_write_pending++; | 68 | sk->sk_write_pending++; |
68 | if (sk_wait_event(sk, timeo_p, | 69 | done = sk_wait_event(sk, timeo_p, |
69 | !((1 << sk->sk_state) & | 70 | !((1 << sk->sk_state) & |
70 | ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)))) | 71 | ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))); |
71 | break; | ||
72 | finish_wait(sk->sk_sleep, &wait); | 72 | finish_wait(sk->sk_sleep, &wait); |
73 | sk->sk_write_pending--; | 73 | sk->sk_write_pending--; |
74 | } | 74 | } while (!done); |
75 | return 0; | 75 | return 0; |
76 | } | 76 | } |
77 | 77 | ||