Thread (22 messages) flat view 22 messages, 4 authors, 2h ago
HOTtoday

[PATCH net-next v2 03/14] bpf: Make BPF skb extension survive packet scrubbing

From: Jakub Sitnicki <jakub@cloudflare.com>
Date: 2026-09-10 14:02:49
Also in: bpf
Subsystem: networking [general], the rest, user datagram protocol (udp) · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, Willem de Bruijn

skb_scrub_packet() drops all skb extensions unconditionally via
skb_ext_reset(). It runs on tunnel encap/decap (ip_tunnel_rcv, vxlan_rcv,
etc.) and cross-netns forwarding (dev_forward_skb).

This makes it impossible for a BPF program to pass metadata via bpf_skb_ext
through a tunnel or across a netns boundary. The extension is always lost
at the scrub point.

Introduce skb_ext_scrub(), a selective variant of skb_ext_reset(). It
deletes every extension except SKB_EXT_BPF. Scrubbing is safe when the
extension slab is shared with clones: deleting an extension only clears the
per-skb active_extensions bit, and the shared slab payload is released
lazily by __skb_ext_put() once the last reference goes away.

Replace the skb_ext_reset() call in skb_scrub_packet() with skb_ext_scrub()
and also switch udp_try_make_stateless() to skb_ext_scrub() as well, so the
BPF metadata survives queueing onto a UDP socket receive queue and stays
readable there (e.g. for a sockmap verdict program). Only mark the skb
stateless when no extension survives the scrub. Otherwise skb_consume_udp()
would take the __consume_stateless_skb() fast path, which skips
skb_release_head_state(), and leak the extension slab.

Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
---
 include/linux/skbuff.h |  2 ++
 net/core/skbuff.c      | 24 ++++++++++++++++++++++--
 net/ipv4/udp.c         |  6 ++----
 3 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 0202bcb9338d..509d447179e1 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -5091,6 +5091,7 @@ void *__skb_ext_set(struct sk_buff *skb, enum skb_ext_id id,
 void *skb_ext_add(struct sk_buff *skb, enum skb_ext_id id);
 void __skb_ext_del(struct sk_buff *skb, enum skb_ext_id id);
 void __skb_ext_put(struct skb_ext *ext);
+void skb_ext_scrub(struct sk_buff *skb);
 
 static inline void skb_ext_put(struct sk_buff *skb)
 {
@@ -5167,6 +5168,7 @@ static inline bool skb_ext_shared(const struct sk_buff *skb)
 static inline void __skb_ext_put(struct skb_ext *ext) {}
 static inline void skb_ext_put(struct sk_buff *skb) {}
 static inline void skb_ext_reset(struct sk_buff *skb) {}
+static inline void skb_ext_scrub(struct sk_buff *skb) {}
 static inline void skb_ext_del(struct sk_buff *skb, int unused) {}
 static inline void __skb_ext_copy(struct sk_buff *d, const struct sk_buff *s) {}
 static inline void skb_ext_copy(struct sk_buff *dst, const struct sk_buff *s) {}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 9c03cd7c63af..a479e25de564 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -83,6 +83,7 @@
 #include <net/page_pool/helpers.h>
 #include <net/psp/types.h>
 #include <net/dropreason.h>
+#include <linux/bpf.h>
 #include <net/xdp_sock.h>
 
 #include <linux/uaccess.h>
@@ -6290,7 +6291,8 @@ EXPORT_SYMBOL(skb_try_coalesce);
  * operations.
  * skb_scrub_packet can also be used to clean a skb before injecting it in
  * another namespace (@xnet == true). We have to clear all information in the
- * skb that could impact namespace isolation.
+ * skb that could impact namespace isolation. Note that BPF skb extension is
+ * meant to carry information across namespaces by design.
  */
 void skb_scrub_packet(struct sk_buff *skb, bool xnet)
 {
@@ -6298,7 +6300,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
 	skb->skb_iif = 0;
 	skb->ignore_df = 0;
 	skb_dst_drop(skb);
-	skb_ext_reset(skb);
+	skb_ext_scrub(skb);
 	nf_reset_ct(skb);
 	nf_reset_trace(skb);
 
@@ -7307,6 +7309,24 @@ void __skb_ext_put(struct skb_ext *ext)
 	kmem_cache_free(skbuff_ext_cache, ext);
 }
 EXPORT_SYMBOL(__skb_ext_put);
+
+void skb_ext_scrub(struct sk_buff *skb)
+{
+	unsigned int id;
+
+	if (likely(!skb->active_extensions))
+		return;
+
+	for (id = 0; id < SKB_EXT_NUM; id++) {
+#if IS_ENABLED(CONFIG_BPF_SKB_EXT)
+		if (id == SKB_EXT_BPF)
+			continue;
+#endif
+		skb_ext_del(skb, id);
+	}
+}
+EXPORT_SYMBOL(skb_ext_scrub);
+
 #endif /* CONFIG_SKB_EXTENSIONS */
 
 static void kfree_skb_napi_cache(struct sk_buff *skb)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index b3887c42adfd..cf093b0d66c5 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1540,8 +1540,6 @@ void udp_splice_eof(struct socket *sock)
  *
  * We need to preserve secpath, if present, to eventually process
  * IP_CMSG_PASSSEC at recvmsg() time.
- *
- * Other extensions can be cleared.
  */
 static bool udp_try_make_stateless(struct sk_buff *skb)
 {
@@ -1549,8 +1547,8 @@ static bool udp_try_make_stateless(struct sk_buff *skb)
 		return true;
 
 	if (!secpath_exists(skb)) {
-		skb_ext_reset(skb);
-		return true;
+		skb_ext_scrub(skb);
+		return !skb_has_extensions(skb);
 	}
 
 	return false;
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help