On Wed, Aug 16, 2017 at 4:20 PM, Paolo Abeni <pab...@redhat.com> wrote: > On Wed, 2017-08-16 at 11:18 -0400, Willem de Bruijn wrote: >> > If I read the above correctly, you are arguining in favor of the >> > addittional flag version, right? >> >> I was. Though if we are going to thread the argument from the caller >> to __skb_try_recv_from_queue to avoid rereading sk->sk_peek_off, >> on second thought it might be simpler to do it through off: > [...] >> This, of course, requires restricting sk_peek_off to protect against >> overflow. > > Ok, even if I'm not 100% sure overall this will be simpler when adding > also the overflow check.
Actually, it is safe even without the check. Overflow of the signed integer is benign here. >> If I'm not mistaken, the test in udp_recvmsg currently incorrectly sets >> peeking to false when peeking at offset zero: >> >> peeking = off = sk_peek_offset(sk, flags); > > I think you are right, does not look correct. By shifting the offset by two, we could even make both assignments become correct. Return 0 without peek, 1 on peek without SO_PEEK_OFF, 2+ otherwise, including overflow up to INT_MIN + 1. But the end result is more readable if we just separate those two assignments. @@ -1574,7 +1574,8 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, return ip_recv_error(sk, msg, len, addr_len); try_again: - peeking = off = sk_peek_offset(sk, flags); + peeking = flags & MSG_PEEK; + off = sk_peek_offset(sk, flags); skb = __skb_recv_udp(sk, flags, noblock, &peeked, &off, &err); if (!skb) return err; @@ -362,7 +362,8 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, return ipv6_recv_rxpmtu(sk, msg, len, addr_len); try_again: - peeking = off = sk_peek_offset(sk, flags); + peeking = flags & MSG_PEEK; + off = sk_peek_offset(sk, flags); skb = __skb_recv_udp(sk, flags, noblock, &peeked, &off, &err); if (!skb) return err; At which point there is also no longer a need for the variable shift at sk_peek_offset. Just pass the raw value down to __skb_try_recv_from_queue and disambiguate there: @@ -506,11 +506,8 @@ int sk_set_peek_off(struct sock *sk, int val); static inline int sk_peek_offset(struct sock *sk, int flags) { - if (unlikely(flags & MSG_PEEK)) { - s32 off = READ_ONCE(sk->sk_peek_off); - if (off >= 0) - return off; - } + if (unlikely(flags & MSG_PEEK)) + return READ_ONCE(sk->sk_peek_off); return 0; } @@ -169,14 +169,20 @@ struct sk_buff *__skb_try_recv_from_queue(struct sock *sk, int *peeked, int *off, int *err, struct sk_buff **last) { + bool peek_at_off = false; struct sk_buff *skb; - int _off = *off; + int _off = 0; + + if (flags & MSG_PEEK && (*off) >= 0) { + peek_at_off = true; + _off = *off; + } *last = queue->prev; skb_queue_walk(queue, skb) { if (flags & MSG_PEEK) { - if (_off >= skb->len && (skb->len || _off || - skb->peeked)) { + if (peek_at_off && _off >= skb->len && + (skb->len || _off || skb->peeked)) { _off -= skb->len; continue; }