Skip to content

Commit 78a4712

Browse files
Qihang Tanggregkh
authored andcommitted
packet: use consistent hard_header_len in non-ring send paths
[ Upstream commit 03390aa ] packet_snd() reads dev->hard_header_len multiple times while allocating and constructing an skb. Device reconfiguration can change this value concurrently, for example through bonding device type changes. For SOCK_RAW, packet_snd() can save a larger value in reserve and later allocate headroom using a smaller value. Moving skb->data back by reserve then places it before skb->head, and the following copy from userspace can attempt an out-of-bounds write. packet_sendmsg_spkt() has the same issue because it calculates its reservation and header offset from separate reads before dropping the RCU read lock to allocate the skb. Add LL_RESERVED_SPACE_EX() for callers that already saved a header length. Read hard_header_len once in packet_snd() and use it for allocation and construction. In packet_sendmsg_spkt(), preserve the allocation-time value through the device lookup retry. The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here. Fixes: b84bbaf ("packet: in packet_snd start writing at link layer allocation") 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-3-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Stable-dep-of: 21b5953 ("packet: use consistent hard_header_len in TX_RING send path") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 44bd0ec commit 78a4712

2 files changed

Lines changed: 20 additions & 12 deletions

File tree

‎include/linux/netdevice.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,11 @@ struct hh_cache {
274274
* We could use other alignment values, but we must maintain the
275275
* relationship HH alignment <= LL alignment.
276276
*/
277-
#define LL_RESERVED_SPACE(dev) \
278-
((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \
277+
#define LL_RESERVED_SPACE_EX(dev, hlen) \
278+
((((hlen) + READ_ONCE((dev)->needed_headroom)) \
279279
& ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
280+
#define LL_RESERVED_SPACE(dev) \
281+
LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len)
280282
#define LL_RESERVED_SPACE_EXTRA(dev,extra) \
281283
((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \
282284
& ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)

‎net/packet/af_packet.c‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,8 +1972,9 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
19721972
struct net_device *dev;
19731973
struct sockcm_cookie sockc;
19741974
__be16 proto = 0;
1975-
int err;
1975+
int hard_header_len;
19761976
int extra_len = 0;
1977+
int err;
19771978

19781979
/*
19791980
* Get and verify the address.
@@ -2016,14 +2017,18 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
20162017
extra_len = 4; /* We're doing our own CRC */
20172018
}
20182019

2020+
/* Keep the allocation-time header length across retry. */
2021+
if (!skb)
2022+
hard_header_len = READ_ONCE(dev->hard_header_len);
2023+
20192024
err = -EMSGSIZE;
2020-
if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len)
2025+
if (len > dev->mtu + hard_header_len + VLAN_HLEN + extra_len)
20212026
goto out_unlock;
20222027

20232028
if (!skb) {
2024-
size_t reserved = LL_RESERVED_SPACE(dev);
2029+
size_t reserved = LL_RESERVED_SPACE_EX(dev, hard_header_len);
20252030
int tlen = dev->needed_tailroom;
2026-
unsigned int hhlen = dev->header_ops ? dev->hard_header_len : 0;
2031+
unsigned int hhlen = dev->header_ops ? hard_header_len : 0;
20272032

20282033
rcu_read_unlock();
20292034
skb = sock_wmalloc(sk, len + reserved + tlen, 0, GFP_KERNEL);
@@ -2053,7 +2058,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
20532058
err = -EINVAL;
20542059
goto out_unlock;
20552060
}
2056-
if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
2061+
if (len > (dev->mtu + hard_header_len + extra_len) &&
20572062
!packet_extra_vlan_len_allowed(dev, skb)) {
20582063
err = -EMSGSIZE;
20592064
goto out_unlock;
@@ -2969,7 +2974,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
29692974
int offset = 0;
29702975
struct packet_sock *po = pkt_sk(sk);
29712976
bool has_vnet_hdr = false;
2972-
int hlen, tlen, linear;
2977+
int hard_header_len, hlen, tlen, linear;
29732978
int extra_len = 0;
29742979

29752980
/*
@@ -3010,8 +3015,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
30103015
goto out_unlock;
30113016
}
30123017

3018+
hard_header_len = READ_ONCE(dev->hard_header_len);
30133019
if (sock->type == SOCK_RAW)
3014-
reserve = dev->hard_header_len;
3020+
reserve = hard_header_len;
30153021
if (po->has_vnet_hdr) {
30163022
err = packet_snd_vnet_parse(msg, &len, &vnet_hdr);
30173023
if (err)
@@ -3033,10 +3039,10 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
30333039
goto out_unlock;
30343040

30353041
err = -ENOBUFS;
3036-
hlen = LL_RESERVED_SPACE(dev);
3042+
hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len);
30373043
tlen = dev->needed_tailroom;
30383044
linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len);
3039-
linear = max(linear, min_t(int, len, dev->hard_header_len));
3045+
linear = max(linear, min_t(int, len, hard_header_len));
30403046
skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear,
30413047
msg->msg_flags & MSG_DONTWAIT, &err);
30423048
if (skb == NULL)
@@ -3052,7 +3058,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
30523058
} else if (reserve) {
30533059
skb_reserve(skb, -reserve);
30543060
if (len < reserve + sizeof(struct ipv6hdr) &&
3055-
dev->min_header_len != dev->hard_header_len)
3061+
dev->min_header_len != hard_header_len)
30563062
skb_reset_network_header(skb);
30573063
}
30583064

0 commit comments

Comments
 (0)