mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 16:53:20 -04:00
Merge branch 'net-fix-hard_header_len-races-in-packet-send-paths'
Qihang Tang says: ==================== net: fix hard_header_len races in packet send paths The packet socket TX paths read dev->hard_header_len independently for skb allocation and header construction. Concurrent netdevice reconfiguration (e.g. bonding device type changes) can change this value in between, leading to mismatched headroom and copy length, and in the SOCK_RAW case to out-of-bounds writes. Patch 1 removes the CAP_SYS_RAWIO zero-padding branch in dev_validate_header(). That branch sizes a memset against the live dev->hard_header_len while operating on an skb whose headroom was allocated from an earlier hard_header_len read, so a concurrent increase can write past the reserved buffer. Removing it first keeps the later snapshot fixes bisect-safe: they do not replace an earlier skb_under_panic with a silent overwrite. Patches 2 and 3 snapshot hard_header_len once per send and use it consistently for allocation and construction, in the non-ring and TX_RING paths respectively. The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create remains out of scope, as noted in the commit messages. ==================== Link: https://patch.msgid.link/20260805125729.19220-1-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -300,9 +300,11 @@ struct hh_cache {
|
||||
* We could use other alignment values, but we must maintain the
|
||||
* relationship HH alignment <= LL alignment.
|
||||
*/
|
||||
#define LL_RESERVED_SPACE(dev) \
|
||||
((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \
|
||||
#define LL_RESERVED_SPACE_EX(dev, hlen) \
|
||||
((((hlen) + READ_ONCE((dev)->needed_headroom)) \
|
||||
& ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
|
||||
#define LL_RESERVED_SPACE(dev) \
|
||||
LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len)
|
||||
#define LL_RESERVED_SPACE_EXTRA(dev,extra) \
|
||||
((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \
|
||||
& ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
|
||||
@@ -3531,11 +3533,6 @@ static inline bool dev_validate_header(const struct net_device *dev,
|
||||
if (len < dev->min_header_len)
|
||||
return false;
|
||||
|
||||
if (capable(CAP_SYS_RAWIO)) {
|
||||
memset(ll_header + len, 0, dev->hard_header_len - len);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dev->header_ops && dev->header_ops->validate)
|
||||
return dev->header_ops->validate(ll_header, len);
|
||||
|
||||
|
||||
@@ -1966,8 +1966,9 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
|
||||
struct net_device *dev;
|
||||
struct sockcm_cookie sockc;
|
||||
__be16 proto = 0;
|
||||
int err;
|
||||
int hard_header_len;
|
||||
int extra_len = 0;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Get and verify the address.
|
||||
@@ -2010,14 +2011,18 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
|
||||
extra_len = 4; /* We're doing our own CRC */
|
||||
}
|
||||
|
||||
/* Keep the allocation-time header length across retry. */
|
||||
if (!skb)
|
||||
hard_header_len = READ_ONCE(dev->hard_header_len);
|
||||
|
||||
err = -EMSGSIZE;
|
||||
if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len)
|
||||
if (len > dev->mtu + hard_header_len + VLAN_HLEN + extra_len)
|
||||
goto out_unlock;
|
||||
|
||||
if (!skb) {
|
||||
size_t reserved = LL_RESERVED_SPACE(dev);
|
||||
size_t reserved = LL_RESERVED_SPACE_EX(dev, hard_header_len);
|
||||
int tlen = dev->needed_tailroom;
|
||||
unsigned int hhlen = dev->header_ops ? dev->hard_header_len : 0;
|
||||
unsigned int hhlen = dev->header_ops ? hard_header_len : 0;
|
||||
|
||||
rcu_read_unlock();
|
||||
skb = sock_wmalloc(sk, len + reserved + tlen, 0, GFP_KERNEL);
|
||||
@@ -2047,7 +2052,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
|
||||
if (len > (dev->mtu + hard_header_len + extra_len) &&
|
||||
!packet_extra_vlan_len_allowed(dev, skb)) {
|
||||
err = -EMSGSIZE;
|
||||
goto out_unlock;
|
||||
@@ -2582,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;
|
||||
@@ -2613,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;
|
||||
@@ -2745,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);
|
||||
@@ -2792,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));
|
||||
|
||||
@@ -2830,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;
|
||||
@@ -2848,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)) {
|
||||
@@ -2861,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 &&
|
||||
@@ -2969,7 +2977,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
int offset = 0;
|
||||
struct packet_sock *po = pkt_sk(sk);
|
||||
int vnet_hdr_sz = READ_ONCE(po->vnet_hdr_sz);
|
||||
int hlen, tlen, linear;
|
||||
int hard_header_len, hlen, tlen, linear;
|
||||
int extra_len = 0;
|
||||
|
||||
/*
|
||||
@@ -3009,8 +3017,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
hard_header_len = READ_ONCE(dev->hard_header_len);
|
||||
if (sock->type == SOCK_RAW)
|
||||
reserve = dev->hard_header_len;
|
||||
reserve = hard_header_len;
|
||||
if (vnet_hdr_sz) {
|
||||
err = packet_snd_vnet_parse(msg, &len, &vnet_hdr, vnet_hdr_sz);
|
||||
if (err)
|
||||
@@ -3031,10 +3040,10 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
goto out_unlock;
|
||||
|
||||
err = -ENOBUFS;
|
||||
hlen = LL_RESERVED_SPACE(dev);
|
||||
hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len);
|
||||
tlen = dev->needed_tailroom;
|
||||
linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len);
|
||||
linear = max(linear, min_t(int, len, dev->hard_header_len));
|
||||
linear = max(linear, min_t(int, len, hard_header_len));
|
||||
skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear,
|
||||
msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (skb == NULL)
|
||||
@@ -3050,7 +3059,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
} else if (reserve) {
|
||||
skb_reserve(skb, -reserve);
|
||||
if (len < reserve + sizeof(struct ipv6hdr) &&
|
||||
dev->min_header_len != dev->hard_header_len)
|
||||
dev->min_header_len != hard_header_len)
|
||||
skb_reset_network_header(skb);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user