Commit 79f632c7 authored by Benjamin Poirier's avatar Benjamin Poirier Committed by David S. Miller

unix/stream: fix peeking with an offset larger than data in queue

Currently, peeking on a unix stream socket with an offset larger than len of
the data in the sk receive queue returns immediately with bogus data.

This patch fixes this so that the behavior is the same as peeking with no
offset on an empty queue: the caller blocks.
Signed-off-by: default avatarBenjamin Poirier <bpoirier@suse.de>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 39cc8613
...@@ -1859,10 +1859,10 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1859,10 +1859,10 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
} }
/* /*
* Sleep until data has arrive. But check for races.. * Sleep until more data has arrived. But check for races..
*/ */
static long unix_stream_data_wait(struct sock *sk, long timeo,
static long unix_stream_data_wait(struct sock *sk, long timeo) struct sk_buff *last)
{ {
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
...@@ -1871,7 +1871,7 @@ static long unix_stream_data_wait(struct sock *sk, long timeo) ...@@ -1871,7 +1871,7 @@ static long unix_stream_data_wait(struct sock *sk, long timeo)
for (;;) { for (;;) {
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
if (!skb_queue_empty(&sk->sk_receive_queue) || if (skb_peek_tail(&sk->sk_receive_queue) != last ||
sk->sk_err || sk->sk_err ||
(sk->sk_shutdown & RCV_SHUTDOWN) || (sk->sk_shutdown & RCV_SHUTDOWN) ||
signal_pending(current) || signal_pending(current) ||
...@@ -1890,8 +1890,6 @@ static long unix_stream_data_wait(struct sock *sk, long timeo) ...@@ -1890,8 +1890,6 @@ static long unix_stream_data_wait(struct sock *sk, long timeo)
return timeo; return timeo;
} }
static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, struct msghdr *msg, size_t size,
int flags) int flags)
...@@ -1936,14 +1934,12 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1936,14 +1934,12 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
goto out; goto out;
} }
skip = sk_peek_offset(sk, flags);
do { do {
int chunk; int chunk;
struct sk_buff *skb; struct sk_buff *skb, *last;
unix_state_lock(sk); unix_state_lock(sk);
skb = skb_peek(&sk->sk_receive_queue); last = skb = skb_peek(&sk->sk_receive_queue);
again: again:
if (skb == NULL) { if (skb == NULL) {
unix_sk(sk)->recursion_level = 0; unix_sk(sk)->recursion_level = 0;
...@@ -1966,7 +1962,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1966,7 +1962,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
break; break;
mutex_unlock(&u->readlock); mutex_unlock(&u->readlock);
timeo = unix_stream_data_wait(sk, timeo); timeo = unix_stream_data_wait(sk, timeo, last);
if (signal_pending(current) if (signal_pending(current)
|| mutex_lock_interruptible(&u->readlock)) { || mutex_lock_interruptible(&u->readlock)) {
...@@ -1980,9 +1976,12 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1980,9 +1976,12 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
break; break;
} }
if (skip >= skb->len) { skip = sk_peek_offset(sk, flags);
while (skip >= skb->len) {
skip -= skb->len; skip -= skb->len;
last = skb;
skb = skb_peek_next(skb, &sk->sk_receive_queue); skb = skb_peek_next(skb, &sk->sk_receive_queue);
if (!skb)
goto again; goto again;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment