Thread (22 messages) 22 messages, 3 authors, 6d ago

[PATCH net-next 12/15] netfilter: add nf_skb_sk helper and use it

flat view
COOLING6d

From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: 2026-10-11 22:00:22
Also in: netfilter-devel
Subsystem: netfilter, networking [general], the rest · Maintainers: Pablo Neira Ayuso, Florian Westphal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Revision v1 of 38 in this series.

Revisions (38)
  1. v1
  2. v1
  3. v1
  4. v1
  5. v1
  6. v1
  7. v1
  8. v1
  9. v1
  10. v1
  11. v1
  12. v1
  13. v1
  14. v1
  15. v1
  16. v1
  17. v1
  18. v1
  19. v1
  20. v1
  21. v1
  22. v1
  23. v1
  24. v1
  25. v1
  26. v1
  27. v1
  28. v1
  29. v1
  30. v1
  31. v1
  32. v1
  33. v1
  34. v1
  35. v1
  36. v1
  37. v1
  38. v1 current
From: Florian Westphal <fw@strlen.de>

nft and xt socket matching pass skb->sk to inet_sk_transparent().
On the LOCAL_OUT path an IP tunnel transmits while preserving the
original skb owner, so a non-INET socket (e.g. PF_PACKET) can reach
these code paths.  This is reachable for nft_socket.c which does allow
LOCAL_OUT. xt_socket only permits PREROUTING/LOCAL_IN.

nf_nat does this too, but there the inet_sk_transparent() call
is explicitly restricted to the INPUT hook.

For a packet socket the INET-specific offset lands inside the
packet statistics area.

Introduce nf_skb_sk() in nf_socket.h which returns skb->sk only when
it belongs to the given net namespace and passes sk_is_inet().

While at it, replace the `sk != skb->sk` refcount test with an
explicit refcounted flag that is set when the socket is obtained
via the lookup helper (the path that takes a reference).

LLM finding, no reproducer, no out-of-bounds memory access.

Assisted-by: LLM
Reported-by: Vega <redacted>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/net/netfilter/nf_socket.h | 20 ++++++++++++++++++++
 net/netfilter/nft_socket.c        | 16 +++++++++-------
 net/netfilter/xt_socket.c         | 26 ++++++++++++++------------
 3 files changed, 43 insertions(+), 19 deletions(-)
diff --git a/include/net/netfilter/nf_socket.h b/include/net/netfilter/nf_socket.h
index f9d7bee9bd4e..39047c73b642 100644
--- a/include/net/netfilter/nf_socket.h
+++ b/include/net/netfilter/nf_socket.h
@@ -10,4 +10,24 @@ struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb,
 struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb,
 				  const struct net_device *indev);
 
+/**
+ * nf_skb_sk - Return the inet socket associated with an skb
+ * @skb: The skb to fetch sk from.
+ * @net: The network namespace the socket must belong to.
+ *
+ * On the output path, skb->sk may be non-INET when the skb is passing
+ * through an IP tunnel.
+ *
+ * Return:
+ * *sk* if it is an INET socket that belongs to @net, %NULL otherwise.
+ */
+static inline struct sock *nf_skb_sk(const struct sk_buff *skb, const struct net *net)
+{
+	struct sock *sk = skb->sk;
+
+	if (sk && net_eq(net, sock_net(sk)) && sk_is_inet(sk))
+		return sk;
+
+	return NULL;
+}
 #endif
diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c
index 52d892e04261..ea6c2f3dc3ff 100644
--- a/net/netfilter/nft_socket.c
+++ b/net/netfilter/nft_socket.c
@@ -111,15 +111,17 @@ static void nft_socket_eval(const struct nft_expr *expr,
 			    const struct nft_pktinfo *pkt)
 {
 	const struct nft_socket *priv = nft_expr_priv(expr);
-	struct sk_buff *skb = pkt->skb;
-	struct sock *sk = skb->sk;
 	u32 *dest = &regs->data[priv->dreg];
+	struct sk_buff *skb = pkt->skb;
+	bool refcounted = false;
+	struct sock *sk;
 
-	if (sk && !net_eq(nft_net(pkt), sock_net(sk)))
-		sk = NULL;
-
-	if (!sk)
+	sk = nf_skb_sk(skb, nft_net(pkt));
+	if (!sk) {
 		sk = nft_socket_do_lookup(pkt);
+		if (sk)
+			refcounted = true;
+	}
 
 	if (!sk) {
 		regs->verdict.code = NFT_BREAK;
@@ -159,7 +161,7 @@ static void nft_socket_eval(const struct nft_expr *expr,
 	}
 
 out_put_sk:
-	if (sk != skb->sk)
+	if (refcounted)
 		sock_gen_put(sk);
 }
 
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index 811e53bee408..cd3fc333ccf1 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -49,14 +49,15 @@ static bool
 socket_match(const struct sk_buff *skb, struct xt_action_param *par,
 	     const struct xt_socket_mtinfo1 *info)
 {
+	struct sock *sk = nf_skb_sk(skb, xt_net(par));
 	struct sk_buff *pskb = (struct sk_buff *)skb;
-	struct sock *sk = skb->sk;
+	bool refcounted = false;
 
-	if (sk && !net_eq(xt_net(par), sock_net(sk)))
-		sk = NULL;
-
-	if (!sk)
+	if (!sk) {
 		sk = nf_sk_lookup_slow_v4(xt_net(par), skb, xt_in(par));
+		if (sk)
+			refcounted = true;
+	}
 
 	if (sk) {
 		bool wildcard;
@@ -79,7 +80,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
 		    transparent && sk_fullsock(sk))
 			pskb->mark = READ_ONCE(sk->sk_mark);
 
-		if (sk != skb->sk)
+		if (refcounted)
 			sock_gen_put(sk);
 
 		if (wildcard || !transparent)
@@ -110,14 +111,15 @@ static bool
 socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par)
 {
 	const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+	struct sock *sk = nf_skb_sk(skb, xt_net(par));
 	struct sk_buff *pskb = (struct sk_buff *)skb;
-	struct sock *sk = skb->sk;
+	bool refcounted = false;
 
-	if (sk && !net_eq(xt_net(par), sock_net(sk)))
-		sk = NULL;
-
-	if (!sk)
+	if (!sk) {
 		sk = nf_sk_lookup_slow_v6(xt_net(par), skb, xt_in(par));
+		if (sk)
+			refcounted = true;
+	}
 
 	if (sk) {
 		bool wildcard;
@@ -140,7 +142,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par)
 		    transparent && sk_fullsock(sk))
 			pskb->mark = READ_ONCE(sk->sk_mark);
 
-		if (sk != skb->sk)
+		if (refcounted)
 			sock_gen_put(sk);
 
 		if (wildcard || !transparent)
-- 
2.47.3
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help