sock: correct sk_wmem_queued accounting on efault in tcp zerocopy
Syzkaller hits WARN_ON(sk->sk_wmem_queued) in sk_stream_kill_queues
after triggering an EFAULT in __zerocopy_sg_from_iter.
On this error, skb_zerocopy_stream_iter resets the skb to its state
before the operation with __pskb_trim. It cannot kfree_skb like
datagram callers, as the skb may have data from a previous send call.
__pskb_trim calls skb_condense for unowned skbs, which adjusts their
truesize. These tcp skbuffs are owned and their truesize must add up
to sk_wmem_queued. But they match because their skb->sk is NULL until
tcp_transmit_skb.
Temporarily set skb->sk when calling __pskb_trim to signal that the
skbuffs are owned and avoid the skb_condense path.
Fixes: 52267790ef
("sock: add MSG_ZEROCOPY")
Signed-off-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d2b2762433
commit
54d4311764
|
@ -1124,9 +1124,13 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
|
||||||
|
|
||||||
err = __zerocopy_sg_from_iter(sk, skb, &msg->msg_iter, len);
|
err = __zerocopy_sg_from_iter(sk, skb, &msg->msg_iter, len);
|
||||||
if (err == -EFAULT || (err == -EMSGSIZE && skb->len == orig_len)) {
|
if (err == -EFAULT || (err == -EMSGSIZE && skb->len == orig_len)) {
|
||||||
|
struct sock *save_sk = skb->sk;
|
||||||
|
|
||||||
/* Streams do not free skb on error. Reset to prev state. */
|
/* Streams do not free skb on error. Reset to prev state. */
|
||||||
msg->msg_iter = orig_iter;
|
msg->msg_iter = orig_iter;
|
||||||
|
skb->sk = sk;
|
||||||
___pskb_trim(skb, orig_len);
|
___pskb_trim(skb, orig_len);
|
||||||
|
skb->sk = save_sk;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue