mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Linkui Xiao <xiaolinkui@126.com>
To: davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
	pabeni@redhat.com, horms@kernel.org, gnault@redhat.com,
	kuniyu@google.com, michael.bommarito@gmail.com,
	alice.kernel@fastmail.im
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Linkui Xiao <xiaolinkui@kylinos.cn>,
	stable@vger.kernel.org
Subject: [PATCH net] l2tp: fix use-after-free in l2tp_udp_encap_recv()
Date: Tue, 27 Oct 2026 14:36:57 +0800	[thread overview]
Message-ID: <20261027063657.1359170-1-xiaolinkui@126.com> (raw)

From: Linkui Xiao <xiaolinkui@kylinos.cn>

l2tp_udp_encap_recv() derives the tunnel pointer from the session it has
just looked up, so the reference taken by l2tp_v2_session_get() or
l2tp_v3_session_get() is the only thing that keeps the tunnel alive:
l2tp_session_free() drops the tunnel reference, and l2tp_tunnel_free()
hands the tunnel to kfree_rcu().  Both error paths of that function drop
the reference before jumping to the invalid label, which still uses the
tunnel:

	tunnel = session->tunnel;

	if (version != tunnel->version) {
		l2tp_session_put(session);
		goto invalid;
	}
	...
invalid:
	atomic_long_inc(&tunnel->stats.rx_invalid);

If the session is torn down concurrently, such a put can be the last
one, and the statistics update then writes into a freed tunnel.

The frees are RCU deferred, which hides the bug on the regular path
because __udp4_lib_rcv() holds rcu_read_lock().  It does not hide it on
the backlog path: __release_sock() drops sk_lock before invoking
sk_backlog_rcv(), takes no RCU read lock and may cond_resched() between
packets, so a grace period can complete before the statistics update.

Account the packet first and move the put to the invalid label, which
can only be reached while the reference is still held, so the put is
not applied twice.

The put on the version mismatch path came later, with commit
4d10edfd1475 ("l2tp: Fix memleak in l2tp_udp_encap_recv()."), which
plugged a session leak without accounting for the tunnel going away
with the session.

Found by inspection; no reproducer.  Hitting the window needs a session
teardown racing a packet on the backlog path, and the RCU deferred frees
mean KASAN is only likely to report it with CONFIG_RCU_STRICT_GRACE_PERIOD.

Fixes: ff6a2ac23cb0 ("l2tp: refactor udp recv to lookup to not use sk_user_data")
Cc: stable@vger.kernel.org
Signed-off-by: Linkui Xiao <xiaolinkui@kylinos.cn>
---
 net/l2tp/l2tp_core.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 7e042a3e648c..36804743c761 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1083,16 +1083,12 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	tunnel = session->tunnel;
 
 	/* Check protocol version */
-	if (version != tunnel->version) {
-		l2tp_session_put(session);
+	if (version != tunnel->version)
 		goto invalid;
-	}
 
 	if (version == L2TP_HDR_VER_3 &&
-	    l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) {
-		l2tp_session_put(session);
+	    l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr))
 		goto invalid;
-	}
 
 	l2tp_recv_common(session, skb, ptr, optr, hdrflags, length);
 	l2tp_session_put(session);
@@ -1100,7 +1096,11 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	return 0;
 
 invalid:
+	/* The session reference is the only thing keeping the tunnel alive
+	 * here, so account the invalid packet before dropping it.
+	 */
 	atomic_long_inc(&tunnel->stats.rx_invalid);
+	l2tp_session_put(session);
 
 pass:
 	/* Put UDP header back */
-- 
2.25.1


                 reply	other threads:[~2026-10-27  6:38 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20261027063657.1359170-1-xiaolinkui@126.com \
    --to=xiaolinkui@126.com \
    --cc=alice.kernel@fastmail.im \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=gnault@redhat.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=michael.bommarito@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=stable@vger.kernel.org \
    --cc=xiaolinkui@kylinos.cn \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®