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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox