From: Eric Dumazet <edumazet@google.com>
To: davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com
Cc: horms@kernel.org, dsahern@kernel.org, idosch@nvidia.com,
netdev@vger.kernel.org, eric.dumazet@gmail.com,
Eric Dumazet <edumazet@google.com>
Subject: [PATCH net v2 5/5] gre: fix out-of-bounds read of erspan metadata in collect_md mode
Date: Wed, 16 Sep 2026 10:01:55 +0000 [thread overview]
Message-ID: <20260916100155.1398403-6-edumazet@google.com> (raw)
In-Reply-To: <20260916100155.1398403-1-edumazet@google.com>
erspan_rcv() and ip6erspan_rcv() copy the ERSPAN metadata out of the
packet for collect_md tunnels:
pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
sizeof(*ershdr));
...
memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
ERSPAN_V2_MDSIZE);
Both read 8 bytes at 12 bytes from the start of the GRE header, but only
ask pskb_may_pull() for erspan_hdr_len(ver) bytes beyond it, which type I
support made 0 for version 0. Two ways to get there:
- An ERSPAN type I packet has a 4 byte GRE header and no ERSPAN header
at all, so erspan_rcv() sets ver = 0 and only pulls the GRE header. It
still falls back to a collect_md tunnel through itn->collect_md_tun,
and there the ternary above picks ERSPAN_V2_MDSIZE.
- A packet with an 8 byte GRE header and ershdr->ver == 0 is malformed,
yet neither erspan_rcv() nor ip6erspan_rcv() validates the version
before using it, so erspan_hdr_len(0) pulls nothing either. A version
above 2 is not an out-of-bounds read, but it does store a version that
the transmit side (erspan_fb_xmit(), ip6erspan_tunnel_xmit()) rejects.
Reject a base header whose version is neither 1 nor 2, and skip the
metadata extraction for type I, which has none: ip_tun_rx_dst() hands
out a zeroed option area, so md->version = 0 alone describes it.
Fixes: f989d546a2d5 ("erspan: Add type I version 0 support.")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/ip_gre.c | 34 ++++++++++++++++++++++------------
net/ipv6/ip6_gre.c | 2 ++
2 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 696884f53cdcc65fe87cf04f357f1cc45a7e0736..92f3a52d20d38186c3ce87824a8e15c9e00a925f 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -274,7 +274,6 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
struct ip_tunnel_net *itn;
struct ip_tunnel *tunnel;
const struct iphdr *iph;
- struct erspan_md2 *md2;
int ver;
int len;
@@ -294,6 +293,9 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
ver = ershdr->ver;
+ if (unlikely(ver != 1 && ver != 2))
+ return PACKET_REJECT;
+
iph = ip_hdr(skb);
__set_bit(IP_TUNNEL_KEY_BIT, flags);
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, flags,
@@ -318,6 +320,7 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
if (tunnel->collect_md) {
struct erspan_metadata *pkt_md, *md;
struct ip_tunnel_info *info;
+ struct erspan_md2 *md2;
unsigned char *gh;
__be64 tun_id;
@@ -334,19 +337,26 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
info = &tun_dst->u.tun_info;
info->options_len = sizeof(*md);
- /* skb can be uncloned in __iptunnel_pull_header, so
- * old pkt_md is no longer valid and we need to reset
- * it
- */
- gh = skb_network_header(skb) +
- skb_network_header_len(skb);
- pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
- sizeof(*ershdr));
md = ip_tunnel_info_opts(&tun_dst->u.tun_info);
md->version = ver;
- md2 = &md->u.md2;
- memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
- ERSPAN_V2_MDSIZE);
+
+ /* Type I has no ERSPAN header, thus no metadata to
+ * extract: reading it would go past the @len bytes
+ * pulled above. ip_tun_rx_dst() zeroed @md for us.
+ */
+ if (!is_erspan_type1(gre_hdr_len)) {
+ /* skb can be uncloned in __iptunnel_pull_header, so
+ * old pkt_md is no longer valid and we need to reset
+ * it
+ */
+ gh = skb_network_header(skb) +
+ skb_network_header_len(skb);
+ pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
+ sizeof(*ershdr));
+ md2 = &md->u.md2;
+ memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
+ ERSPAN_V2_MDSIZE);
+ }
__set_bit(IP_TUNNEL_ERSPAN_OPT_BIT,
info->key.tun_flags);
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 8ebda0b6a78b2236b439f5499d84f34f652fcbe2..a59fb82c74dad7f0c1d1128338e7bed1e3c7116f 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -503,6 +503,8 @@ static int ip6erspan_rcv(struct sk_buff *skb,
ipv6h = ipv6_hdr(skb);
ershdr = (struct erspan_base_hdr *)skb->data;
ver = ershdr->ver;
+ if (unlikely(ver != 1 && ver != 2))
+ return PACKET_REJECT;
tunnel = ip6gre_tunnel_lookup(skb->dev,
&ipv6h->saddr, &ipv6h->daddr, tpi->key,
--
2.55.0.1032.g73a4cd73de-goog
next prev parent reply other threads:[~2026-09-16 10:02 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-12 15:09 [PATCH net 0/3] ip_gre: fix header lengths and validation on changelink Eric Dumazet
2026-09-12 15:09 ` [PATCH net 1/3] ip_gre: validate netlink attributes before changing the tunnel Eric Dumazet
2026-09-15 12:11 ` netdev-bot+sashiko
2026-09-12 15:09 ` [PATCH net 2/3] ip_gre: compute tunnel lengths absolutely instead of by delta Eric Dumazet
2026-09-15 12:11 ` netdev-bot+sashiko
2026-09-12 15:09 ` [PATCH net 3/3] ip_gre: recompute erspan header lengths after a change Eric Dumazet
2026-09-15 12:11 ` netdev-bot+sashiko
2026-09-15 13:31 ` [PATCH net 0/3] ip_gre: fix header lengths and validation on changelink Eric Dumazet
2026-09-16 10:01 ` [PATCH net v2 0/5] ip_tunnel, ip_gre: fix changelink lengths and ERSPAN receive Eric Dumazet
2026-09-16 10:01 ` [PATCH net v2 1/5] ip_tunnel: do not clear the active encap before validating the new one Eric Dumazet
2026-09-20 10:48 ` netdev-bot+sashiko
2026-09-16 10:01 ` [PATCH net v2 2/5] ip_gre: validate netlink attributes before changing the tunnel Eric Dumazet
2026-09-20 10:48 ` netdev-bot+sashiko
2026-09-16 10:01 ` [PATCH net v2 3/5] ip_gre: compute tunnel lengths absolutely instead of by delta Eric Dumazet
2026-09-20 10:48 ` netdev-bot+sashiko
2026-09-16 10:01 ` [PATCH net v2 4/5] ip_gre: recompute erspan header lengths after a change Eric Dumazet
2026-09-20 10:48 ` netdev-bot+sashiko
2026-09-16 10:01 ` Eric Dumazet [this message]
2026-09-20 10:48 ` [PATCH net v2 5/5] gre: fix out-of-bounds read of erspan metadata in collect_md mode netdev-bot+sashiko
2026-09-16 22:45 ` [PATCH net v2 0/5] ip_tunnel, ip_gre: fix changelink lengths and ERSPAN receive Jakub Kicinski
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=20260916100155.1398403-6-edumazet@google.com \
--to=edumazet@google.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=eric.dumazet@gmail.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/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.