Skip to contentSkip to conversation

batman-adv: reject unrepresentable multicast TVLV offsets

PATCH Applied David Lee 3 participants 4 messages Aug 17 – Aug 20 1 message not in the archive

Subsystems (4)
lore
Share & tools
lore

David Lee

author
#0 lore raw

PATCH v3 1/1 batman-adv: reject unrepresentable multicast TVLV offsets

From: Kyle Zeng <[email protected]>

The network and transport header fields in struct sk_buff are 16-bit offsets from skb->head, and U16_MAX is reserved as the unset transport header value. batadv_tvlv_call_handler() sets both fields from a received multicast TVLV without checking whether the TVLV end is representable.

If the end offset exceeds the field's range, skb_set_transport_header() truncates it so that the transport header precedes the network header. The negative difference is then returned by skb_network_header_len() as a large u32. batadv_mcast_forw_packet() consequently accepts an oversized multicast tracker and accesses memory beyond the skb data.

Add skb_set_transport_header_careful(), an offset-aware counterpart to skb_reset_transport_header_careful(), which validates the final head-relative offset before assigning it. Use the new helper in batadv_tvlv_call_handler() and reject unrepresentable TVLVs before setting the network header.

Fixes 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding")
Assisted-by Codex:gpt-5.6-sol Codex:gpt-5.5-cyber

---
Changes in v3:
- Rename transport_offset to thoff so the comparisons fit on one line.

v2: https://lore.kernel.org/netdev/[email protected]/

Changes in v2:
- Add an offset-aware careful transport-header setter and make batman-adv handle its failure, as suggested by Sven Eckelmann.
- Restore Kyle Zeng as the patch author and correct the sign-off chain.
- Move the research credit below the commit-message separator.

v1: https://lore.kernel.org/all/[email protected]/

Bug found and triaged by OpenAI Security Research and validated by Trail of Bits.

Trail of Bits has a reproducer for this bug that triggers a KASAN use-after-free and can share if needed.

 include/linux/skbuff.h | 24 ++++++++++++++++++++++++
 net/batman-adv/tvlv.c  |  5 ++++-
 2 files changed, 28 insertions(+), 1 deletion(-)
include/linux/skbuff.h +24 -0
index 22eda1d54a0e..ed6a2bc23db5 100644
@@ -3126,6 +3126,30 @@ static inline void skb_set_transport_header(struct sk_buff *skb,
 	skb->transport_header += offset;
 }
 
+/**
+ * skb_set_transport_header_careful - conditionally set transport header
+ * @skb: buffer to alter
+ * @offset: offset to add to skb->data
+ *
+ * Hardened version of skb_set_transport_header().
+ *
+ * Returns: true if the operation was a success.
+ */
+static inline bool __must_check
+skb_set_transport_header_careful(struct sk_buff *skb, const int offset)
+{
+	long thoff = skb->data - skb->head + offset;
+
+	if (unlikely(thoff != (typeof(skb->transport_header))thoff))
+		return false;
+
+	if (unlikely(thoff == (typeof(skb->transport_header))~0U))
+		return false;
+
+	skb->transport_header = thoff;
+	return true;
+}
+
 static inline unsigned char *skb_network_header(const struct sk_buff *skb)
 {
 	return skb->head + skb->network_header;
net/batman-adv/tvlv.c +4 -1
index 1c9fb21985f6..c384db16bcac 100644
@@ -433,8 +433,11 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
 			return NET_RX_SUCCESS;
 
 		tvlv_offset = (unsigned char *)tvlv_value - skb->data;
+		if (!skb_set_transport_header_careful(skb,
+						      tvlv_offset + tvlv_value_len))
+			return -EINVAL;
+
 		skb_set_network_header(skb, tvlv_offset);
-		skb_set_transport_header(skb, tvlv_offset + tvlv_value_len);
 
 		return tvlv_handler->mcast_handler(bat_priv, skb);
 	}
Signature · 1 line
2.53.0
maintainer Acked-by on v3 1/1
#1 lore raw
Quoted 10 lines

The network and transport header fields in struct sk_buff are 16-bit offsets from skb->head, and U16_MAX is reserved as the unset transport header value. batadv_tvlv_call_handler() sets both fields from a received multicast TVLV without checking whether the TVLV end is representable.

If the end offset exceeds the field's range, skb_set_transport_header() truncates it so that the transport header precedes the network header. The negative difference is then returned by skb_network_header_len() as a large u32. batadv_mcast_forw_packet() consequently accepts an oversized multicast tracker and accesses memory beyond the skb data.

[...]

Quoted 5 lines

---
Changes in v3:
- Rename transport_offset to thoff so the comparisons fit on one line.

v2: https://lore.kernel.org/netdev/[email protected]/

Looks to me like you've also dropped the

b4 diff -v2 [email protected]

Signature · 1 line
Sven Eckelmann <[email protected]>

A message that is not in the archive (sent off-list or not yet received): CAC_etQHifs5Qo-syD4pF+OPkKZFwHoNJFsLdjQsQcm3=_PyydQ@mail.gmail.com

maintainer
#3 lore raw
Quoted 7 lines · On Monday, 17 August 2026 11:41:53 CEST David Lee wrote:

Hi Sven,

Looks to me like you've also dropped the

This was by mistake, would you want v4 to be sent out with the Suggested-by line?

No, not needed. Just wanted to document what I found.

Regards,
Sven

#4 lore raw
In reply to David Lee
This patch was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:

Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:

Quoted 8 lines · On Mon, 17 Aug 2026 08:49:54 +0000 you wrote:

From: Kyle Zeng <[email protected]>

The network and transport header fields in struct sk_buff are 16-bit offsets from skb->head, and U16_MAX is reserved as the unset transport header value. batadv_tvlv_call_handler() sets both fields from a received multicast TVLV without checking whether the TVLV end is representable.

[...]

Here is the summary with links:
- [net,v3] batman-adv: reject unrepresentable multicast TVLV offsets

    https://git.kernel.org/netdev/net/c/f12c2de4f542

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html