netdev_erspan_pop_header() reads the GRE protocol and flags, the
ERSPAN base header and the version 1 index or version 2 metadata
before it checks that the headers fit in the packet. The values are
discarded when the check fails, but a truncated packet still makes it
read past the end of the packet data.
Check the GRE and ERSPAN base headers against the packet size before
reading them, and the version specific metadata before reading it.
Fixes: 7dc18ae96d33 ("userspace: add erspan tunnel support.")
Assisted-by: claude-opus-5-5, Claude Code
Signed-off-by: Alejandro Pacheco <[email protected]>
---
lib/netdev-native-tnl.c | 32 ++++++++++++++++++++------------
tests/tunnel-push-pop.at | 20 ++++++++++++++++++++
2 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index dc14542ec..39b4a5b89 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -656,6 +656,11 @@ netdev_erspan_pop_header(struct dp_packet *packet)
goto err;
}
+ hlen = ulen + ERSPAN_GREHDR_LEN + sizeof *ersh;
+ if (hlen > dp_packet_size(packet)) {
+ goto err;
+ }
+
greh_protocol = ntohs(greh->protocol);
if (greh_protocol != ETH_TYPE_ERSPAN1 &&
greh_protocol != ETH_TYPE_ERSPAN2) {
@@ -667,6 +672,19 @@ netdev_erspan_pop_header(struct dp_packet *packet)
}
ersh = ERSPAN_HDR(greh);
+ if (ersh->ver == 1) {
+ hlen += ERSPAN_V1_MDSIZE;
+ } else if (ersh->ver == 2) {
+ hlen += ERSPAN_V2_MDSIZE;
+ } else {
+ VLOG_WARN_RL(&err_rl, "ERSPAN version error %d", ersh->ver);
+ goto err;
+ }
+
+ if (hlen > dp_packet_size(packet)) {
+ goto err;
+ }
+
tnl->tun_id = be16_to_be64(htons(get_sid(ersh)));
tnl->erspan_ver = ersh->ver;
@@ -674,22 +692,12 @@ netdev_erspan_pop_header(struct dp_packet *packet)
ovs_16aligned_be32 *index = ALIGNED_CAST(ovs_16aligned_be32 *,
ersh + 1);
tnl->erspan_idx = ntohl(get_16aligned_be32(index));
- tnl->flags |= FLOW_TNL_F_KEY;
- hlen = ulen + ERSPAN_GREHDR_LEN + sizeof *ersh + ERSPAN_V1_MDSIZE;
- } else if (ersh->ver == 2) {
+ } else {
struct erspan_md2 *md2 = ALIGNED_CAST(struct erspan_md2 *, ersh + 1);
tnl->erspan_dir = md2->dir;
tnl->erspan_hwid = get_hwid(md2);
- tnl->flags |= FLOW_TNL_F_KEY;
- hlen = ulen + ERSPAN_GREHDR_LEN + sizeof *ersh + ERSPAN_V2_MDSIZE;
- } else {
- VLOG_WARN_RL(&err_rl, "ERSPAN version error %d", ersh->ver);
- goto err;
- }
-
- if (hlen > dp_packet_size(packet)) {
- goto err;
}
+ tnl->flags |= FLOW_TNL_F_KEY;
tnl_ol_pop(packet, hlen);
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index 74e43fc82..27ae7853e 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -169,6 +169,26 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep
'port 3'], [0], [dnl
port 3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
])
+dnl ERSPAN v2 header truncated in its metadata is dropped.
+m4_define([ERSPAN_SHORT_PKT], m4_join([],
+dnl eth(src=00:1b:21:3c:ab:64,dst=aa:55:aa:55:00:00,type=0x0800)
+[aa55aa550000001b213cab640800],
+dnl ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tot_len=36)
+[4500002479464000402fbaaf0101025c01010258],
+dnl gre(flags=S,proto=0x22eb,seq=1)
+[100022eb00000001],
+dnl erspan(ver=2,sid=567)
+[20000237],
+dnl First 4 of the 8 bytes of ERSPAN v2 metadata.
+[10abcd01]))
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 ERSPAN_SHORT_PKT])
+AT_CHECK([ovs-appctl revalidator/wait])
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 3'], [0], [dnl
+ port 3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
+])
+CHECK_COVERAGE([datapath_drop_tunnel_pop_error], [1])
+
dnl Check ERSPAN encap in pcap file
dnl This ARP reply from p0 has two effects:
dnl 1. The ARP cache will learn that 1.1.2.92 is at f8:bc:12:44:34:b6.
--
2.53.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev