aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWillem de Bruijn <willemb@google.com>2019-05-30 18:01:21 -0400
committerDavid S. Miller <davem@davemloft.net>2019-05-30 18:54:04 -0400
commit100f6d8e09905c59be45b6316f8f369c0be1b2d8 (patch)
tree4d3161cefeea7f8ff4dc614a5d0140dd29bd7793
parentb73484b2fc0d0ba84a13e9d86eb4adcae718163b (diff)
net: correct zerocopy refcnt with udp MSG_MORE
TCP zerocopy takes a uarg reference for every skb, plus one for the tcp_sendmsg_locked datapath temporarily, to avoid reaching refcnt zero as it builds, sends and frees skbs inside its inner loop. UDP and RAW zerocopy do not send inside the inner loop so do not need the extra sock_zerocopy_get + sock_zerocopy_put pair. Commit 52900d22288ed ("udp: elide zerocopy operation in hot path") introduced extra_uref to pass the initial reference taken in sock_zerocopy_alloc to the first generated skb. But, sock_zerocopy_realloc takes this extra reference at the start of every call. With MSG_MORE, no new skb may be generated to attach the extra_uref to, so refcnt is incorrectly 2 with only one skb. Do not take the extra ref if uarg && !tcp, which implies MSG_MORE. Update extra_uref accordingly. This conditional assignment triggers a false positive may be used uninitialized warning, so have to initialize extra_uref at define. Changes v1->v2: fix typo in Fixes SHA1 Fixes: 52900d22288e7 ("udp: elide zerocopy operation in hot path") Reported-by: syzbot <syzkaller@googlegroups.com> Diagnosed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/core/skbuff.c6
-rw-r--r--net/ipv4/ip_output.c4
-rw-r--r--net/ipv6/ip6_output.c4
3 files changed, 9 insertions, 5 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index e89be6282693..eaad23f9c7b5 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1036,7 +1036,11 @@ struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size,
1036 uarg->len++; 1036 uarg->len++;
1037 uarg->bytelen = bytelen; 1037 uarg->bytelen = bytelen;
1038 atomic_set(&sk->sk_zckey, ++next); 1038 atomic_set(&sk->sk_zckey, ++next);
1039 sock_zerocopy_get(uarg); 1039
1040 /* no extra ref when appending to datagram (MSG_MORE) */
1041 if (sk->sk_type == SOCK_STREAM)
1042 sock_zerocopy_get(uarg);
1043
1040 return uarg; 1044 return uarg;
1041 } 1045 }
1042 } 1046 }
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index bfd0ca554977..8c9189a41b13 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -878,7 +878,7 @@ static int __ip_append_data(struct sock *sk,
878 int csummode = CHECKSUM_NONE; 878 int csummode = CHECKSUM_NONE;
879 struct rtable *rt = (struct rtable *)cork->dst; 879 struct rtable *rt = (struct rtable *)cork->dst;
880 unsigned int wmem_alloc_delta = 0; 880 unsigned int wmem_alloc_delta = 0;
881 bool paged, extra_uref; 881 bool paged, extra_uref = false;
882 u32 tskey = 0; 882 u32 tskey = 0;
883 883
884 skb = skb_peek_tail(queue); 884 skb = skb_peek_tail(queue);
@@ -918,7 +918,7 @@ static int __ip_append_data(struct sock *sk,
918 uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb)); 918 uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
919 if (!uarg) 919 if (!uarg)
920 return -ENOBUFS; 920 return -ENOBUFS;
921 extra_uref = true; 921 extra_uref = !skb; /* only extra ref if !MSG_MORE */
922 if (rt->dst.dev->features & NETIF_F_SG && 922 if (rt->dst.dev->features & NETIF_F_SG &&
923 csummode == CHECKSUM_PARTIAL) { 923 csummode == CHECKSUM_PARTIAL) {
924 paged = true; 924 paged = true;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index adef2236abe2..f9e43323e667 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1275,7 +1275,7 @@ static int __ip6_append_data(struct sock *sk,
1275 int csummode = CHECKSUM_NONE; 1275 int csummode = CHECKSUM_NONE;
1276 unsigned int maxnonfragsize, headersize; 1276 unsigned int maxnonfragsize, headersize;
1277 unsigned int wmem_alloc_delta = 0; 1277 unsigned int wmem_alloc_delta = 0;
1278 bool paged, extra_uref; 1278 bool paged, extra_uref = false;
1279 1279
1280 skb = skb_peek_tail(queue); 1280 skb = skb_peek_tail(queue);
1281 if (!skb) { 1281 if (!skb) {
@@ -1344,7 +1344,7 @@ emsgsize:
1344 uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb)); 1344 uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
1345 if (!uarg) 1345 if (!uarg)
1346 return -ENOBUFS; 1346 return -ENOBUFS;
1347 extra_uref = true; 1347 extra_uref = !skb; /* only extra ref if !MSG_MORE */
1348 if (rt->dst.dev->features & NETIF_F_SG && 1348 if (rt->dst.dev->features & NETIF_F_SG &&
1349 csummode == CHECKSUM_PARTIAL) { 1349 csummode == CHECKSUM_PARTIAL) {
1350 paged = true; 1350 paged = true;