From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Konstantin Khorenko <khorenko@virtuozzo.com>
Cc: devel@openvz.org
Subject: [Devel] [PATCH vz7] ms/ipv4: validate checksum_start before completing checksum
Date: Mon, 09 Nov 2026 19:04:34 +0100 [thread overview]
Message-ID: <20261109-b4-prep-khorenko-vstor-154184-ipv4-checksum_start-v1-1-29d1d4321a63@virtuozzo.com> (raw)
From: "Michael S. Tsirkin" <mst@redhat.com>
If a packet with bad checksum metadata gets into the ipv4 stack,
skb_checksum_help can corrupt the network header and cause a bunch of
mischief.
This was discovered and reported by Paulos, and has been reporoduced
by others independently since.
We really shouldn't allow such packets in, but as a defence
in depth measure, let's also check before we complete the checksum.
A more complete validation at input is forthcoming, but needs more work.
Cc: stable@vger.kernel.org
Fixes: f43798c27684 ("tun: Allow GSO using virtio_net_hdr")
Fixes: bfd5f4a3d605 ("packet: Add GSO/csum offload support.")
Reported-by: Paulos Yibelo <habte.yibelo@gmail.com>
Closes: https://lore.kernel.org/netdev/20260922030310.8684-2-habte.yibelo@gmail.com/
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/0a012b4923e189c4c593ef4f471e5ff0edbe9030.1791412497.git.mst@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit 6785011f8b16abf40b1e9d8b2e332232b3e68822)
Backport notes:
- drop the net/bridge/netfilter/nf_conntrack_bridge.c hunk;
- add nf_queue_checksum_help() to nfnetlink_queue_core.c with the IPv4
check only, and call it from nfqnl_build_packet_message().
nf_conntrack_bridge.c came with ms commit 3c171f496ef5 ("netfilter:
bridge: add connection tracking system") in v5.3 and does not exist
here. nf_queue_checksum_help() came with ms commit 26a77d02891a
("netfilter: nfnetlink_queue: unbreak SCTP traffic"), so here
nfqnl_build_packet_message() still calls skb_checksum_help() directly.
The helper is added without the SCTP branch of that commit.
The four guarded sites are all skb_checksum_help() callers under
net/ipv4, net/netfilter and net/xfrm in this tree. RHEL7
kernel-3.10.0-1160.169.1.el7 carries the same change.
CVE-2026-99999
https://virtuozzo.atlassian.net/browse/VSTOR-154184
Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
CVE-2026-99999 (PwnEscape) fix for the vz7 kernel.
https://virtuozzo.atlassian.net/browse/VSTOR-154184
Assisted-by: LLM
---
include/net/ip.h | 7 +++++++
net/ipv4/ip_output.c | 10 +++++++---
net/netfilter/nfnetlink_queue_core.c | 13 ++++++++++++-
net/netfilter/xt_CHECKSUM.c | 6 +++++-
net/xfrm/xfrm_output.c | 16 +++++++++++-----
5 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/include/net/ip.h b/include/net/ip.h
index b321d2a5fc3df..138e1c2152a64 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -57,6 +57,13 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb)
return ip_hdr(skb)->ihl * 4;
}
+static inline int ip_check_csum_start(const struct sk_buff *skb)
+{
+ if (unlikely(skb->csum_start < skb->network_header + ip_hdrlen(skb)))
+ return -EINVAL;
+ return 0;
+}
+
struct ipcm_cookie {
__be32 addr;
int oif;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index f7195e5f72e5d..7d66717027b4a 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -539,9 +539,13 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
dev = rt->dst.dev;
/* for offloaded checksums cleanup checksum before fragmentation */
- if (skb->ip_summed == CHECKSUM_PARTIAL &&
- (err = skb_checksum_help(skb)))
- goto fail;
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ err = ip_check_csum_start(skb);
+ if (!err)
+ err = skb_checksum_help(skb);
+ if (err)
+ goto fail;
+ }
/*
* Point into the IP datagram header.
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index ff2d4b31b353e..591c3f3b9bc21 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -29,6 +29,7 @@
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <linux/list.h>
+#include <net/ip.h>
#include <net/sock.h>
#include <net/netfilter/nf_queue.h>
#include <net/netns/generic.h>
@@ -245,6 +246,16 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
return flags ? nla_put_be32(nlskb, NFQA_SKB_INFO, htonl(flags)) : 0;
}
+static int nf_queue_checksum_help(struct sk_buff *entskb)
+{
+ if (entskb->protocol == htons(ETH_P_IP)) {
+ if (!pskb_network_may_pull(entskb, sizeof(struct iphdr)) ||
+ ip_check_csum_start(entskb))
+ return -EINVAL;
+ }
+ return skb_checksum_help(entskb);
+}
+
static struct sk_buff *
nfqnl_build_packet_message(struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
@@ -297,7 +308,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
case NFQNL_COPY_PACKET:
if (!(queue->flags & NFQA_CFG_F_GSO) &&
entskb->ip_summed == CHECKSUM_PARTIAL &&
- skb_checksum_help(entskb))
+ nf_queue_checksum_help(entskb))
return NULL;
data_len = ACCESS_ONCE(queue->copy_range);
diff --git a/net/netfilter/xt_CHECKSUM.c b/net/netfilter/xt_CHECKSUM.c
index db286fc0bc32a..1d4efe9c59fcb 100644
--- a/net/netfilter/xt_CHECKSUM.c
+++ b/net/netfilter/xt_CHECKSUM.c
@@ -18,6 +18,7 @@
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <net/ip.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael S. Tsirkin <mst@redhat.com>");
@@ -28,8 +29,11 @@ MODULE_ALIAS("ip6t_CHECKSUM");
static unsigned int
checksum_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
- if (skb->ip_summed == CHECKSUM_PARTIAL && !skb_is_gso(skb))
+ if (skb->ip_summed == CHECKSUM_PARTIAL && !skb_is_gso(skb)) {
+ if (xt_family(par) == NFPROTO_IPV4 && ip_check_csum_start(skb))
+ return NF_DROP;
skb_checksum_help(skb);
+ }
return XT_CONTINUE;
}
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 65a0e1a9ac985..a1d6c922f43e0 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -197,15 +197,21 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
return xfrm_output_gso(sk, skb);
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- err = skb_checksum_help(skb);
- if (err) {
- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
- kfree_skb(skb);
- return err;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ err = ip_check_csum_start(skb);
+ if (err)
+ goto error;
}
+ err = skb_checksum_help(skb);
+ if (err)
+ goto error;
}
return xfrm_output2(sk, skb);
+error:
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
+ kfree_skb(skb);
+ return err;
}
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
---
base-commit: 78c10c447efc7633a8e4260fe7dc33e1cb4bb4d2
change-id: 20261109-b4-prep-khorenko-vstor-154184-ipv4-checksum_start-6f6b55915d5c
Best regards,
--
Konstantin Khorenko,
Virtuozzo Linux Kernel Team
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
reply other threads:[~2026-11-09 18:06 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109-b4-prep-khorenko-vstor-154184-ipv4-checksum_start-v1-1-29d1d4321a63@virtuozzo.com \
--to=khorenko@virtuozzo.com \
--cc=devel@openvz.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.