]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xsk: Free skb when TX metadata options are invalid
authorFelix Maurer <fmaurer@redhat.com>
Thu, 14 Nov 2024 11:30:05 +0000 (12:30 +0100)
committerJakub Kicinski <kuba@kernel.org>
Fri, 15 Nov 2024 22:26:40 +0000 (14:26 -0800)
When a new skb is allocated for transmitting an xsk descriptor, i.e., for
every non-multibuf descriptor or the first frag of a multibuf descriptor,
but the descriptor is later found to have invalid options set for the TX
metadata, the new skb is never freed. This can leak skbs until the send
buffer is full which makes sending more packets impossible.

Fix this by freeing the skb in the error path if we are currently dealing
with the first frag, i.e., an skb allocated in this iteration of
xsk_build_skb.

Fixes: 48eb03dd2630 ("xsk: Add TX timestamp and TX checksum offload support")
Reported-by: Michal Schmidt <mschmidt@redhat.com>
Signed-off-by: Felix Maurer <fmaurer@redhat.com>
Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Acked-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://patch.msgid.link/edb9b00fb19e680dff5a3350cd7581c5927975a8.1731581697.git.fmaurer@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/xdp/xsk.c

index 1140b2a120caecbb7c6b147aec19f0790494ed36..b57d5d2904eb4609efaaac2c1c92dc8b44a2be49 100644 (file)
@@ -675,6 +675,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
                len = desc->len;
 
                if (!skb) {
+                       first_frag = true;
+
                        hr = max(NET_SKB_PAD, L1_CACHE_ALIGN(dev->needed_headroom));
                        tr = dev->needed_tailroom;
                        skb = sock_alloc_send_skb(&xs->sk, hr + len + tr, 1, &err);
@@ -685,12 +687,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
                        skb_put(skb, len);
 
                        err = skb_store_bits(skb, 0, buffer, len);
-                       if (unlikely(err)) {
-                               kfree_skb(skb);
+                       if (unlikely(err))
                                goto free_err;
-                       }
-
-                       first_frag = true;
                } else {
                        int nr_frags = skb_shinfo(skb)->nr_frags;
                        struct page *page;
@@ -758,6 +756,9 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
        return skb;
 
 free_err:
+       if (first_frag && skb)
+               kfree_skb(skb);
+
        if (err == -EOVERFLOW) {
                /* Drop the packet */
                xsk_set_destructor_arg(xs->skb);