diff options
author | Sabrina Dubroca <sd@queasysnail.net> | 2015-07-24 12:19:25 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-07-27 04:06:53 -0400 |
commit | dfbafc995304ebb9a9b03f65083e6e9cea143b20 (patch) | |
tree | db3c54339548ef9e9e9b053e3c5d2f77c1dc560f | |
parent | 3d3af8859284f982ad62980b3787f05259f1dd10 (diff) |
tcp: fix recv with flags MSG_WAITALL | MSG_PEEK
Currently, tcp_recvmsg enters a busy loop in sk_wait_data if called
with flags = MSG_WAITALL | MSG_PEEK.
sk_wait_data waits for sk_receive_queue not empty, but in this case,
the receive queue is not empty, but does not contain any skb that we
can use.
Add a "last skb seen on receive queue" argument to sk_wait_data, so
that it sleeps until the receive queue has new skbs.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=99461
Link: https://sourceware.org/bugzilla/show_bug.cgi?id=18493
Link: https://bugzilla.redhat.com/show_bug.cgi?id=1205258
Reported-by: Enrico Scholz <rh-bugzilla@ensc.de>
Reported-by: Dan Searle <dan@censornet.com>
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sock.h | 2 | ||||
-rw-r--r-- | net/core/sock.c | 5 | ||||
-rw-r--r-- | net/dccp/proto.c | 2 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 11 | ||||
-rw-r--r-- | net/llc/af_llc.c | 4 |
5 files changed, 14 insertions, 10 deletions
diff --git a/include/net/sock.h b/include/net/sock.h index 05a8c1aea251..f21f0708ec59 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -902,7 +902,7 @@ void sk_stream_kill_queues(struct sock *sk); | |||
902 | void sk_set_memalloc(struct sock *sk); | 902 | void sk_set_memalloc(struct sock *sk); |
903 | void sk_clear_memalloc(struct sock *sk); | 903 | void sk_clear_memalloc(struct sock *sk); |
904 | 904 | ||
905 | int sk_wait_data(struct sock *sk, long *timeo); | 905 | int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb); |
906 | 906 | ||
907 | struct request_sock_ops; | 907 | struct request_sock_ops; |
908 | struct timewait_sock_ops; | 908 | struct timewait_sock_ops; |
diff --git a/net/core/sock.c b/net/core/sock.c index 08f16db46070..8a14f1285fc4 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -1967,20 +1967,21 @@ static void __release_sock(struct sock *sk) | |||
1967 | * sk_wait_data - wait for data to arrive at sk_receive_queue | 1967 | * sk_wait_data - wait for data to arrive at sk_receive_queue |
1968 | * @sk: sock to wait on | 1968 | * @sk: sock to wait on |
1969 | * @timeo: for how long | 1969 | * @timeo: for how long |
1970 | * @skb: last skb seen on sk_receive_queue | ||
1970 | * | 1971 | * |
1971 | * Now socket state including sk->sk_err is changed only under lock, | 1972 | * Now socket state including sk->sk_err is changed only under lock, |
1972 | * hence we may omit checks after joining wait queue. | 1973 | * hence we may omit checks after joining wait queue. |
1973 | * We check receive queue before schedule() only as optimization; | 1974 | * We check receive queue before schedule() only as optimization; |
1974 | * it is very likely that release_sock() added new data. | 1975 | * it is very likely that release_sock() added new data. |
1975 | */ | 1976 | */ |
1976 | int sk_wait_data(struct sock *sk, long *timeo) | 1977 | int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb) |
1977 | { | 1978 | { |
1978 | int rc; | 1979 | int rc; |
1979 | DEFINE_WAIT(wait); | 1980 | DEFINE_WAIT(wait); |
1980 | 1981 | ||
1981 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | 1982 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); |
1982 | set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); | 1983 | set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); |
1983 | rc = sk_wait_event(sk, timeo, !skb_queue_empty(&sk->sk_receive_queue)); | 1984 | rc = sk_wait_event(sk, timeo, skb_peek_tail(&sk->sk_receive_queue) != skb); |
1984 | clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); | 1985 | clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); |
1985 | finish_wait(sk_sleep(sk), &wait); | 1986 | finish_wait(sk_sleep(sk), &wait); |
1986 | return rc; | 1987 | return rc; |
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 52a94016526d..b5cf13a28009 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -886,7 +886,7 @@ verify_sock_status: | |||
886 | break; | 886 | break; |
887 | } | 887 | } |
888 | 888 | ||
889 | sk_wait_data(sk, &timeo); | 889 | sk_wait_data(sk, &timeo, NULL); |
890 | continue; | 890 | continue; |
891 | found_ok_skb: | 891 | found_ok_skb: |
892 | if (len > skb->len) | 892 | if (len > skb->len) |
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 7f4056785acc..45534a5ab430 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -780,7 +780,7 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, | |||
780 | ret = -EAGAIN; | 780 | ret = -EAGAIN; |
781 | break; | 781 | break; |
782 | } | 782 | } |
783 | sk_wait_data(sk, &timeo); | 783 | sk_wait_data(sk, &timeo, NULL); |
784 | if (signal_pending(current)) { | 784 | if (signal_pending(current)) { |
785 | ret = sock_intr_errno(timeo); | 785 | ret = sock_intr_errno(timeo); |
786 | break; | 786 | break; |
@@ -1575,7 +1575,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, | |||
1575 | int target; /* Read at least this many bytes */ | 1575 | int target; /* Read at least this many bytes */ |
1576 | long timeo; | 1576 | long timeo; |
1577 | struct task_struct *user_recv = NULL; | 1577 | struct task_struct *user_recv = NULL; |
1578 | struct sk_buff *skb; | 1578 | struct sk_buff *skb, *last; |
1579 | u32 urg_hole = 0; | 1579 | u32 urg_hole = 0; |
1580 | 1580 | ||
1581 | if (unlikely(flags & MSG_ERRQUEUE)) | 1581 | if (unlikely(flags & MSG_ERRQUEUE)) |
@@ -1635,7 +1635,9 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, | |||
1635 | 1635 | ||
1636 | /* Next get a buffer. */ | 1636 | /* Next get a buffer. */ |
1637 | 1637 | ||
1638 | last = skb_peek_tail(&sk->sk_receive_queue); | ||
1638 | skb_queue_walk(&sk->sk_receive_queue, skb) { | 1639 | skb_queue_walk(&sk->sk_receive_queue, skb) { |
1640 | last = skb; | ||
1639 | /* Now that we have two receive queues this | 1641 | /* Now that we have two receive queues this |
1640 | * shouldn't happen. | 1642 | * shouldn't happen. |
1641 | */ | 1643 | */ |
@@ -1754,8 +1756,9 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, | |||
1754 | /* Do not sleep, just process backlog. */ | 1756 | /* Do not sleep, just process backlog. */ |
1755 | release_sock(sk); | 1757 | release_sock(sk); |
1756 | lock_sock(sk); | 1758 | lock_sock(sk); |
1757 | } else | 1759 | } else { |
1758 | sk_wait_data(sk, &timeo); | 1760 | sk_wait_data(sk, &timeo, last); |
1761 | } | ||
1759 | 1762 | ||
1760 | if (user_recv) { | 1763 | if (user_recv) { |
1761 | int chunk; | 1764 | int chunk; |
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 8fd9febaa5ba..8dab4e569571 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c | |||
@@ -613,7 +613,7 @@ static int llc_wait_data(struct sock *sk, long timeo) | |||
613 | if (signal_pending(current)) | 613 | if (signal_pending(current)) |
614 | break; | 614 | break; |
615 | rc = 0; | 615 | rc = 0; |
616 | if (sk_wait_data(sk, &timeo)) | 616 | if (sk_wait_data(sk, &timeo, NULL)) |
617 | break; | 617 | break; |
618 | } | 618 | } |
619 | return rc; | 619 | return rc; |
@@ -802,7 +802,7 @@ static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, | |||
802 | release_sock(sk); | 802 | release_sock(sk); |
803 | lock_sock(sk); | 803 | lock_sock(sk); |
804 | } else | 804 | } else |
805 | sk_wait_data(sk, &timeo); | 805 | sk_wait_data(sk, &timeo, NULL); |
806 | 806 | ||
807 | if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) { | 807 | if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) { |
808 | net_dbg_ratelimited("LLC(%s:%d): Application bug, race in MSG_PEEK\n", | 808 | net_dbg_ratelimited("LLC(%s:%d): Application bug, race in MSG_PEEK\n", |