]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
packet: use consistent hard_header_len in TX_RING send path
authorQihang Tang <q.h.hack.winter@gmail.com>
Wed, 5 Aug 2026 12:57:29 +0000 (20:57 +0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Aug 2026 16:27:21 +0000 (09:27 -0700)
tpacket_snd() reads dev->hard_header_len independently for skb
allocation and header construction in tpacket_fill_skb(). Concurrent
netdevice reconfiguration can therefore make the reserved headroom
smaller than the amount later pushed, or make copylen - hard_header_len
negative.

Snapshot hard_header_len once before processing ring frames and use it
for the frame limit, headroom allocation, copy length, and skb
construction. Pass the snapshot to tpacket_fill_skb().

The separate SOCK_DGRAM consistency problem between hard_header_len and
header_ops->create is not addressed here.

Fixes: 69e3c75f4d54 ("net: TX_RING and packet mmap")
Cc: stable@vger.kernel.org
Signed-off-by: Qihang Tang <q.h.hack.winter@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260805125729.19220-4-q.h.hack.winter@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/packet/af_packet.c

index b7af45c809e4e2eb8643aa06cc3d701bb6cd925e..435756877abaa1ab4c0edcc2346bcb2de3625605 100644 (file)
@@ -2587,6 +2587,7 @@ static int packet_snd_vnet_parse(struct msghdr *msg, size_t *len,
 static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
                void *frame, struct net_device *dev, void *data, int tp_len,
                __be16 proto, unsigned char *addr, int hlen, int copylen,
+               int hard_header_len,
                const struct sockcm_cookie *sockc)
 {
        union tpacket_uhdr ph;
@@ -2618,8 +2619,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
        } else if (copylen) {
                int hdrlen = min_t(int, copylen, tp_len);
 
-               skb_push(skb, dev->hard_header_len);
-               skb_put(skb, copylen - dev->hard_header_len);
+               skb_push(skb, hard_header_len);
+               skb_put(skb, copylen - hard_header_len);
                err = skb_store_bits(skb, 0, data, hdrlen);
                if (unlikely(err))
                        return err;
@@ -2750,7 +2751,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
        void *data;
        int len_sum = 0;
        int status = TP_STATUS_AVAILABLE;
-       int hlen, tlen, copylen = 0;
+       int hard_header_len, hlen, tlen, copylen = 0;
        long timeo;
 
        mutex_lock(&po->pg_vec_lock);
@@ -2797,8 +2798,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                        goto out_put;
        }
 
+       hard_header_len = READ_ONCE(dev->hard_header_len);
        if (po->sk.sk_socket->type == SOCK_RAW)
-               reserve = dev->hard_header_len;
+               reserve = hard_header_len;
        size_max = po->tx_ring.frame_size
                - (po->tp_hdrlen - sizeof(struct sockaddr_ll));
 
@@ -2835,7 +2837,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                        goto tpacket_error;
 
                status = TP_STATUS_SEND_REQUEST;
-               hlen = LL_RESERVED_SPACE(dev);
+               hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len);
                tlen = dev->needed_tailroom;
                if (vnet_hdr_sz) {
                        data += vnet_hdr_sz;
@@ -2853,10 +2855,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                                                    vnet_hdr.hdr_len);
                        has_vnet_hdr = true;
                }
-               copylen = max_t(int, copylen, dev->hard_header_len);
+               copylen = max_t(int, copylen, hard_header_len);
                skb = sock_alloc_send_skb(&po->sk,
                                hlen + tlen + sizeof(struct sockaddr_ll) +
-                               (copylen - dev->hard_header_len),
+                               (copylen - hard_header_len),
                                !need_wait, &err);
 
                if (unlikely(skb == NULL)) {
@@ -2866,7 +2868,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                        goto out_status;
                }
                tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto,
-                                         addr, hlen, copylen, &sockc);
+                                         addr, hlen, copylen, hard_header_len,
+                                         &sockc);
                if (likely(tp_len >= 0) &&
                    tp_len > dev->mtu + reserve &&
                    !vnet_hdr_sz &&