From: Matthew Dawson <hidden> Date: 2017-08-18 02:12:26
Due to commit e6afc8ace6dd5cef5e812f26c72579da8806f5ac ("udp: remove
headers from UDP packets before queueing"), when udp packets are being
peeked the requested extra offset is always 0 as there is no need to skip
the udp header. However, when the offset is 0 and the next skb is
of length 0, it is only returned once. The behaviour can be seen with
the following python script:
from socket import *;
f=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
g=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
f.bind(('::', 0));
addr=('::1', f.getsockname()[1]);
g.sendto(b'', addr)
g.sendto(b'b', addr)
print(f.recvfrom(10, MSG_PEEK));
print(f.recvfrom(10, MSG_PEEK));
Where the expected output should be the empty string twice.
Instead, make sk_peek_offset return negative values, and pass those values
to __skb_try_recv_datagram/__skb_try_recv_from_queue. If the passed offset
to __skb_try_recv_from_queue is negative, the checked skb is never skipped.
__skb_try_recv_from_queue will then ensure the offset is reset back to 0
if a peek is requested without an offset, unless no packets are found.
Also simplify the if condition in __skb_try_recv_from_queue. If _off is
greater then 0, and off is greater then or equal to skb->len, then
(_off || skb->len) must always be true assuming skb->len >= 0 is always
true.
Also remove a redundant check around a call to sk_peek_offset in af_unix.c,
as it double checked if MSG_PEEK was set in the flags.
V2:
- Moved the negative fixup into __skb_try_recv_from_queue, and remove now
redundant checks
- Fix peeking in udp{,v6}_recvmsg to report the right value when the
offset is 0
Signed-off-by: Matthew Dawson <redacted>
---
include/net/sock.h | 4 +---
net/core/datagram.c | 12 +++++++++---
net/ipv4/udp.c | 3 ++-
net/ipv6/udp.c | 3 ++-
net/unix/af_unix.c | 5 +----
5 files changed, 15 insertions(+), 12 deletions(-)
@@ -507,9 +507,7 @@ int sk_set_peek_off(struct sock *sk, int val);staticinlineintsk_peek_offset(structsock*sk,intflags){if(unlikely(flags&MSG_PEEK)){-s32off=READ_ONCE(sk->sk_peek_off);-if(off>=0)-returnoff;+returnREAD_ONCE(sk->sk_peek_off);}return0;
From: Paolo Abeni <pabeni@redhat.com> Date: 2017-08-18 14:05:21
On Thu, 2017-08-17 at 22:11 -0400, Matthew Dawson wrote:
quoted hunk
Due to commit e6afc8ace6dd5cef5e812f26c72579da8806f5ac ("udp: remove
headers from UDP packets before queueing"), when udp packets are being
peeked the requested extra offset is always 0 as there is no need to skip
the udp header. However, when the offset is 0 and the next skb is
of length 0, it is only returned once. The behaviour can be seen with
the following python script:
from socket import *;
f=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
g=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
f.bind(('::', 0));
addr=('::1', f.getsockname()[1]);
g.sendto(b'', addr)
g.sendto(b'b', addr)
print(f.recvfrom(10, MSG_PEEK));
print(f.recvfrom(10, MSG_PEEK));
Where the expected output should be the empty string twice.
Instead, make sk_peek_offset return negative values, and pass those values
to __skb_try_recv_datagram/__skb_try_recv_from_queue. If the passed offset
to __skb_try_recv_from_queue is negative, the checked skb is never skipped.
__skb_try_recv_from_queue will then ensure the offset is reset back to 0
if a peek is requested without an offset, unless no packets are found.
Also simplify the if condition in __skb_try_recv_from_queue. If _off is
greater then 0, and off is greater then or equal to skb->len, then
(_off || skb->len) must always be true assuming skb->len >= 0 is always
true.
Also remove a redundant check around a call to sk_peek_offset in af_unix.c,
as it double checked if MSG_PEEK was set in the flags.
V2:
- Moved the negative fixup into __skb_try_recv_from_queue, and remove now
redundant checks
- Fix peeking in udp{,v6}_recvmsg to report the right value when the
offset is 0
Signed-off-by: Matthew Dawson <redacted>
---
include/net/sock.h | 4 +---
net/core/datagram.c | 12 +++++++++---
net/ipv4/udp.c | 3 ++-
net/ipv6/udp.c | 3 ++-
net/unix/af_unix.c | 5 +----
5 files changed, 15 insertions(+), 12 deletions(-)
@@ -507,9 +507,7 @@ int sk_set_peek_off(struct sock *sk, int val);staticinlineintsk_peek_offset(structsock*sk,intflags){if(unlikely(flags&MSG_PEEK)){-s32off=READ_ONCE(sk->sk_peek_off);-if(off>=0)-returnoff;+returnREAD_ONCE(sk->sk_peek_off);}return0;
@@ -2304,10 +2304,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,*/mutex_lock(&u->iolock);-if(flags&MSG_PEEK)-skip=sk_peek_offset(sk,flags);-else-skip=0;+skip=max(sk_peek_offset(sk,flags),0);do{intchunk;
later we have:
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
without any call to __skb_try_recv_from_queue(), so we will get
bad/unexpected values from the above assignment when 'skip' is
negative.
Overall I still think that adding/using an explicit MSG_PEEK_OFF bit
would produce a simpler code, but is just a personal preference.
Paolo
From: Matthew Dawson <hidden> Date: 2017-08-18 16:39:28
On Friday, August 18, 2017 10:05:18 AM EDT Paolo Abeni wrote:
On Thu, 2017-08-17 at 22:11 -0400, Matthew Dawson wrote:
quoted
Due to commit e6afc8ace6dd5cef5e812f26c72579da8806f5ac ("udp: remove
headers from UDP packets before queueing"), when udp packets are being
peeked the requested extra offset is always 0 as there is no need to skip
the udp header. However, when the offset is 0 and the next skb is
of length 0, it is only returned once. The behaviour can be seen with
the following python script:
from socket import *;
f=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
g=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
f.bind(('::', 0));
addr=('::1', f.getsockname()[1]);
g.sendto(b'', addr)
g.sendto(b'b', addr)
print(f.recvfrom(10, MSG_PEEK));
print(f.recvfrom(10, MSG_PEEK));
Where the expected output should be the empty string twice.
Instead, make sk_peek_offset return negative values, and pass those values
to __skb_try_recv_datagram/__skb_try_recv_from_queue. If the passed
offset
to __skb_try_recv_from_queue is negative, the checked skb is never
skipped.
__skb_try_recv_from_queue will then ensure the offset is reset back to 0
if a peek is requested without an offset, unless no packets are found.
Also simplify the if condition in __skb_try_recv_from_queue. If _off is
greater then 0, and off is greater then or equal to skb->len, then
(_off || skb->len) must always be true assuming skb->len >= 0 is always
true.
Also remove a redundant check around a call to sk_peek_offset in
af_unix.c,
as it double checked if MSG_PEEK was set in the flags.
V2:
- Moved the negative fixup into __skb_try_recv_from_queue, and remove now
redundant checks
- Fix peeking in udp{,v6}_recvmsg to report the right value when the
offset is 0
Signed-off-by: Matthew Dawson <redacted>
---
include/net/sock.h | 4 +---
net/core/datagram.c | 12 +++++++++---
net/ipv4/udp.c | 3 ++-
net/ipv6/udp.c | 3 ++-
net/unix/af_unix.c | 5 +----
5 files changed, 15 insertions(+), 12 deletions(-)
@@ -507,9 +507,7 @@ int sk_set_peek_off(struct sock *sk, int val);staticinlineintsk_peek_offset(structsock*sk,intflags){if(unlikely(flags&MSG_PEEK)){-s32off=READ_ONCE(sk->sk_peek_off);-if(off>=0)-returnoff;+returnREAD_ONCE(sk->sk_peek_off);}return0;
@@ -2304,10 +2304,7 @@ static int unix_stream_read_generic(struct
unix_stream_read_state *state,>
*/
mutex_lock(&u->iolock);
- if (flags & MSG_PEEK)
- skip = sk_peek_offset(sk, flags);
- else
- skip = 0;
+ skip = max(sk_peek_offset(sk, flags), 0);
do {
int chunk;
later we have:
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
without any call to __skb_try_recv_from_queue(), so we will get
bad/unexpected values from the above assignment when 'skip' is
negative.
The assignment to skip should ensure it is never less then zero, thanks to the
max(sk...(), 0). Thus that shouldn't be an issue?
Overall I still think that adding/using an explicit MSG_PEEK_OFF bit
would produce a simpler code, but is just a personal preference.
I don't mind either way, that just seemed to be the preference I saw from the
discussion around the patch. I think either way will work, so whatever the
list prefers I'm happy with.
--
Matthew
@@ -2304,10 +2304,7 @@ static int unix_stream_read_generic(struct
unix_stream_read_state *state,>
*/
mutex_lock(&u->iolock);
- if (flags & MSG_PEEK)
- skip = sk_peek_offset(sk, flags);
- else
- skip = 0;
+ skip = max(sk_peek_offset(sk, flags), 0);
do {
int chunk;
later we have:
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
without any call to __skb_try_recv_from_queue(), so we will get
bad/unexpected values from the above assignment when 'skip' is
negative.
The assignment to skip should ensure it is never less then zero, thanks to the
max(sk...(), 0). Thus that shouldn't be an issue?
Right, I missed the max() call. Thanks for pointing it out.
I'm fine with the above.
quoted
Overall I still think that adding/using an explicit MSG_PEEK_OFF bit
would produce a simpler code, but is just a personal preference.
I don't mind either way, that just seemed to be the preference I saw from the
discussion around the patch. I think either way will work, so whatever the
list prefers I'm happy with.
I'm ok either way. Probably it's worth continue this way.
Paolo
@@ -2304,10 +2304,7 @@ static int unix_stream_read_generic(struct
unix_stream_read_state *state,>
*/
mutex_lock(&u->iolock);
- if (flags & MSG_PEEK)
- skip = sk_peek_offset(sk, flags);
- else
- skip = 0;
+ skip = max(sk_peek_offset(sk, flags), 0);
do {
int chunk;
later we have:
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
without any call to __skb_try_recv_from_queue(), so we will get
bad/unexpected values from the above assignment when 'skip' is
negative.
The assignment to skip should ensure it is never less then zero, thanks to the
max(sk...(), 0). Thus that shouldn't be an issue?
Right, I missed the max() call. Thanks for pointing it out.
I'm fine with the above.
quoted
quoted
Overall I still think that adding/using an explicit MSG_PEEK_OFF bit
would produce a simpler code, but is just a personal preference.
I don't mind either way, that just seemed to be the preference I saw from the
discussion around the patch. I think either way will work, so whatever the
list prefers I'm happy with.
I'm ok either way. Probably it's worth continue this way.
I don't think anyone cares too much, as long as this is fixed.
I have a slight subjective preference for directly passing the sk_peek_off
variable and relying on the negative value as signal for whether the
SO_PEEK_OFF mode is enabled or not, simply because that signal
already exists and we avoid an intermediate conversion step.
That said, I have a follow-on bug fix for __sk_queue_drop_skb where
that signal is not available, but the flags argument is. I believe that
we will need to take the same action in both cases, so that this is moot.
Just mentioning it in case that would sway opinion the other way.
As said, I'm fine with both. Will Ack this.
From: Willem de Bruijn <willemdebruijn.kernel@gmail.com> Date: 2017-08-18 17:53:35
On Thu, Aug 17, 2017 at 10:11 PM, Matthew Dawson [off-list ref] wrote:
Due to commit e6afc8ace6dd5cef5e812f26c72579da8806f5ac ("udp: remove
headers from UDP packets before queueing"), when udp packets are being
peeked the requested extra offset is always 0 as there is no need to skip
the udp header. However, when the offset is 0 and the next skb is
of length 0, it is only returned once. The behaviour can be seen with
the following python script:
from socket import *;
f=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
g=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
f.bind(('::', 0));
addr=('::1', f.getsockname()[1]);
g.sendto(b'', addr)
g.sendto(b'b', addr)
print(f.recvfrom(10, MSG_PEEK));
print(f.recvfrom(10, MSG_PEEK));
Where the expected output should be the empty string twice.
Instead, make sk_peek_offset return negative values, and pass those values
to __skb_try_recv_datagram/__skb_try_recv_from_queue. If the passed offset
to __skb_try_recv_from_queue is negative, the checked skb is never skipped.
__skb_try_recv_from_queue will then ensure the offset is reset back to 0
if a peek is requested without an offset, unless no packets are found.
Also simplify the if condition in __skb_try_recv_from_queue. If _off is
greater then 0, and off is greater then or equal to skb->len, then
(_off || skb->len) must always be true assuming skb->len >= 0 is always
true.
Also remove a redundant check around a call to sk_peek_offset in af_unix.c,
as it double checked if MSG_PEEK was set in the flags.
V2:
- Moved the negative fixup into __skb_try_recv_from_queue, and remove now
redundant checks
- Fix peeking in udp{,v6}_recvmsg to report the right value when the
offset is 0
Signed-off-by: Matthew Dawson <redacted>