mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd()
In tpacket_snd(), when PACKET_VNET_HDR is enabled, vnet_hdr points
directly into the mmap'd TX ring buffer shared with userspace. The
kernel validates the header via __packet_snd_vnet_parse() but then
re-reads all fields later in virtio_net_hdr_to_skb(). A concurrent
userspace thread can modify the vnet_hdr fields between validation
and use, bypassing all safety checks.
The non-TPACKET path (packet_snd()) already correctly copies vnet_hdr
to a stack-local variable. All other vnet_hdr consumers in the kernel
(tun.c, tap.c, virtio_net.c) also use stack copies. The TPACKET TX
path is the only caller of virtio_net_hdr_to_skb() that reads directly
from user-controlled shared memory.
Fix this by copying vnet_hdr from the mmap'd ring buffer to a
stack-local variable before validation and use, consistent with the
approach used in packet_snd() and all other callers.
Fixes: 1d036d25e5 ("packet: tpacket_snd gso and checksum offload")
Signed-off-by: Bingquan Chen <patzilla007@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260418112006.78823-1-patzilla007@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
3bfcf39608
commit
2c054e17d9
|
|
@ -2718,7 +2718,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
|
|||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
struct net_device *dev;
|
||||
struct virtio_net_hdr *vnet_hdr = NULL;
|
||||
struct virtio_net_hdr vnet_hdr;
|
||||
bool has_vnet_hdr = false;
|
||||
struct sockcm_cookie sockc;
|
||||
__be16 proto;
|
||||
int err, reserve = 0;
|
||||
|
|
@ -2819,16 +2820,20 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
|
|||
hlen = LL_RESERVED_SPACE(dev);
|
||||
tlen = dev->needed_tailroom;
|
||||
if (vnet_hdr_sz) {
|
||||
vnet_hdr = data;
|
||||
data += vnet_hdr_sz;
|
||||
tp_len -= vnet_hdr_sz;
|
||||
if (tp_len < 0 ||
|
||||
__packet_snd_vnet_parse(vnet_hdr, tp_len)) {
|
||||
if (tp_len < 0) {
|
||||
tp_len = -EINVAL;
|
||||
goto tpacket_error;
|
||||
}
|
||||
memcpy(&vnet_hdr, data - vnet_hdr_sz, sizeof(vnet_hdr));
|
||||
if (__packet_snd_vnet_parse(&vnet_hdr, tp_len)) {
|
||||
tp_len = -EINVAL;
|
||||
goto tpacket_error;
|
||||
}
|
||||
copylen = __virtio16_to_cpu(vio_le(),
|
||||
vnet_hdr->hdr_len);
|
||||
vnet_hdr.hdr_len);
|
||||
has_vnet_hdr = true;
|
||||
}
|
||||
copylen = max_t(int, copylen, dev->hard_header_len);
|
||||
skb = sock_alloc_send_skb(&po->sk,
|
||||
|
|
@ -2865,12 +2870,12 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
|
|||
}
|
||||
}
|
||||
|
||||
if (vnet_hdr_sz) {
|
||||
if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) {
|
||||
if (has_vnet_hdr) {
|
||||
if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le())) {
|
||||
tp_len = -EINVAL;
|
||||
goto tpacket_error;
|
||||
}
|
||||
virtio_net_hdr_set_proto(skb, vnet_hdr);
|
||||
virtio_net_hdr_set_proto(skb, &vnet_hdr);
|
||||
}
|
||||
|
||||
skb->destructor = tpacket_destruct_skb;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user