diff options
author | Benjamin Poirier <bpoirier@suse.de> | 2013-04-29 07:42:13 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-04-30 00:43:54 -0400 |
commit | 39cc86130bc045d87f525ce7742da308ff757cec (patch) | |
tree | 28e803dce3877b9f1eb7c8ad74fe58c29764a422 /net/core | |
parent | add05ad4e9f5c4efee9b98535db5efa32b0d0492 (diff) |
unix/dgram: fix peeking with an offset larger than data in queue
Currently, peeking on a unix datagram socket with an offset larger than len of
the data in the sk receive queue returns immediately with bogus data. That's
because *off is not reset between each skb_queue_walk().
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: Benjamin Poirier <bpoirier@suse.de>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/datagram.c | 21 |
1 files changed, 13 insertions, 8 deletions
diff --git a/net/core/datagram.c b/net/core/datagram.c index 99c4f525b1d9..b5d48ac2a9c1 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -78,9 +78,10 @@ static int receiver_wake_function(wait_queue_t *wait, unsigned int mode, int syn | |||
78 | return autoremove_wake_function(wait, mode, sync, key); | 78 | return autoremove_wake_function(wait, mode, sync, key); |
79 | } | 79 | } |
80 | /* | 80 | /* |
81 | * Wait for a packet.. | 81 | * Wait for the last received packet to be different from skb |
82 | */ | 82 | */ |
83 | static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) | 83 | static int wait_for_more_packets(struct sock *sk, int *err, long *timeo_p, |
84 | const struct sk_buff *skb) | ||
84 | { | 85 | { |
85 | int error; | 86 | int error; |
86 | DEFINE_WAIT_FUNC(wait, receiver_wake_function); | 87 | DEFINE_WAIT_FUNC(wait, receiver_wake_function); |
@@ -92,7 +93,7 @@ static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) | |||
92 | if (error) | 93 | if (error) |
93 | goto out_err; | 94 | goto out_err; |
94 | 95 | ||
95 | if (!skb_queue_empty(&sk->sk_receive_queue)) | 96 | if (sk->sk_receive_queue.prev != skb) |
96 | goto out; | 97 | goto out; |
97 | 98 | ||
98 | /* Socket shut down? */ | 99 | /* Socket shut down? */ |
@@ -131,9 +132,9 @@ out_noerr: | |||
131 | * __skb_recv_datagram - Receive a datagram skbuff | 132 | * __skb_recv_datagram - Receive a datagram skbuff |
132 | * @sk: socket | 133 | * @sk: socket |
133 | * @flags: MSG_ flags | 134 | * @flags: MSG_ flags |
135 | * @peeked: returns non-zero if this packet has been seen before | ||
134 | * @off: an offset in bytes to peek skb from. Returns an offset | 136 | * @off: an offset in bytes to peek skb from. Returns an offset |
135 | * within an skb where data actually starts | 137 | * within an skb where data actually starts |
136 | * @peeked: returns non-zero if this packet has been seen before | ||
137 | * @err: error code returned | 138 | * @err: error code returned |
138 | * | 139 | * |
139 | * Get a datagram skbuff, understands the peeking, nonblocking wakeups | 140 | * Get a datagram skbuff, understands the peeking, nonblocking wakeups |
@@ -161,7 +162,7 @@ out_noerr: | |||
161 | struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | 162 | struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, |
162 | int *peeked, int *off, int *err) | 163 | int *peeked, int *off, int *err) |
163 | { | 164 | { |
164 | struct sk_buff *skb; | 165 | struct sk_buff *skb, *last; |
165 | long timeo; | 166 | long timeo; |
166 | /* | 167 | /* |
167 | * Caller is allowed not to check sk->sk_err before skb_recv_datagram() | 168 | * Caller is allowed not to check sk->sk_err before skb_recv_datagram() |
@@ -182,14 +183,17 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | |||
182 | */ | 183 | */ |
183 | unsigned long cpu_flags; | 184 | unsigned long cpu_flags; |
184 | struct sk_buff_head *queue = &sk->sk_receive_queue; | 185 | struct sk_buff_head *queue = &sk->sk_receive_queue; |
186 | int _off = *off; | ||
185 | 187 | ||
188 | last = (struct sk_buff *)queue; | ||
186 | spin_lock_irqsave(&queue->lock, cpu_flags); | 189 | spin_lock_irqsave(&queue->lock, cpu_flags); |
187 | skb_queue_walk(queue, skb) { | 190 | skb_queue_walk(queue, skb) { |
191 | last = skb; | ||
188 | *peeked = skb->peeked; | 192 | *peeked = skb->peeked; |
189 | if (flags & MSG_PEEK) { | 193 | if (flags & MSG_PEEK) { |
190 | if (*off >= skb->len && (skb->len || *off || | 194 | if (_off >= skb->len && (skb->len || _off || |
191 | skb->peeked)) { | 195 | skb->peeked)) { |
192 | *off -= skb->len; | 196 | _off -= skb->len; |
193 | continue; | 197 | continue; |
194 | } | 198 | } |
195 | skb->peeked = 1; | 199 | skb->peeked = 1; |
@@ -198,6 +202,7 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | |||
198 | __skb_unlink(skb, queue); | 202 | __skb_unlink(skb, queue); |
199 | 203 | ||
200 | spin_unlock_irqrestore(&queue->lock, cpu_flags); | 204 | spin_unlock_irqrestore(&queue->lock, cpu_flags); |
205 | *off = _off; | ||
201 | return skb; | 206 | return skb; |
202 | } | 207 | } |
203 | spin_unlock_irqrestore(&queue->lock, cpu_flags); | 208 | spin_unlock_irqrestore(&queue->lock, cpu_flags); |
@@ -207,7 +212,7 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, | |||
207 | if (!timeo) | 212 | if (!timeo) |
208 | goto no_packet; | 213 | goto no_packet; |
209 | 214 | ||
210 | } while (!wait_for_packet(sk, err, &timeo)); | 215 | } while (!wait_for_more_packets(sk, err, &timeo, last)); |
211 | 216 | ||
212 | return NULL; | 217 | return NULL; |
213 | 218 | ||