mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Guikun Yang <yangguikun19@qq.com>
To: Guillaume Nault <gnault@redhat.com>
Cc: James Chapman <jchapman@katalix.com>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [BUG] l2tp: OOB read in l2tp_recv_common() for L2TPv2 sessions with 8-byte peer cookie
Date: Mon, 26 Oct 2026 01:35:25 +0800	[thread overview]
Message-ID: <tencent_389453EACB37DA0886D3896959D4A4CF5006@qq.com> (raw)

Hi Guillaume,

l2tp_recv_common() (net/l2tp/l2tp_core.c) reads L2TPv2 optional header
fields beyond the linear area guaranteed by the caller, for sessions
configured with an 8-byte peer cookie.

The UDP entry path only guarantees the first L2TP_HDR_SIZE_MAX (14)
bytes:

	if (!pskb_may_pull(skb, L2TP_HDR_SIZE_MAX))
		goto pass;

	l2tp_recv_common(session, skb, ptr, optr, hdrflags, length);

Inside l2tp_recv_common(), for a L2TPv2 session with peer_cookie_len =
8, the cookie check consumes bytes [6, 14):

	if (session->peer_cookie_len > 0) {
		if (memcmp(ptr, &session->peer_cookie[0],
			   session->peer_cookie_len)) { ... discard }
		ptr += session->peer_cookie_len;    /* ptr = optr + 14 */
	}

Then the sequence-number branch for L2TPv2 reads at ptr:

	if (hdrflags & L2TP_HDRFLAG_S) {
		L2TP_SKB_CB(skb)->ns = ntohs(*(__be16 *)ptr);   /* [14, 16) OOB */
		ptr += 2;
		ptr += 2;
	}

With a minimal 14-byte L2TPv2 packet (6-byte fixed header + 8-byte
cookie, no payload, L2TP_HDRFLAG_S set), skb->len is exactly 14 after
__skb_pull(udphdr), so the read at [14, 16) is past the end of the
linear area — a 2-byte slab-out-of-bounds read (KASAN catchable).  The
subsequent `ptr += 2 + offset` handling for L2TP_HDRFLAG_O can read
even further out ([18, 20) with the same setup).

Trigger conditions:
  - an L2TPv2 session with peer_cookie_len == 8 (admin-configured),
  - a UDP packet whose L2TP payload is exactly 14 bytes, with
    L2TP_HDRFLAG_S set and the correct cookie.

The peer address is not verified on the UDP path (l2tp_v2_session_get()
matches only on tunnel_id/session_id), so any host that knows the
cookie — i.e. a peer of the tunnel, or anyone when no cookie is
configured (in which case the OOB read does not trigger, since the
pointer stays at 6 and the seq read lands at [6, 10)) — can send such a
packet.

This is the L2TPv2 counterpart of the L2TPv3 optional-fields OOB fixed
in 2019 by commit 4522a70db7aa ("l2tp: fix reading optional fields of
L2TPv3"), which added l2tp_v3_ensure_opt_in_linear().  The commit
message of that fix explicitly noted that fixing L2TPv2 as well would
require refactoring l2tp_recv_common() and was deferred:

	To fix both L2TPv3 and L2TPv2, we'd better refactor
	l2tp_recv_common. It's complicated to do so.

L2TPv2 has remained unprotected since.  The in-function
pskb_may_pull(skb, offset) at the end of l2tp_recv_common() runs only
after all the header reads have already happened, so it cannot help.

A minimal fix for the v2 path could ensure the header is fully linear
before parsing — e.g. in l2tp_udp_encap_recv(), after determining
version == L2TP_HDR_VER_2 and before calling l2tp_recv_common():

	if (session->tunnel->version == L2TP_HDR_VER_2 &&
	    !pskb_may_pull(skb, 6 + session->peer_cookie_len +
			      (hdrflags & L2TP_HDRFLAG_S ? 4 : 0)))
		goto invalid;

(or a full refactor pulling the cookie/seq handling into a
l2tp_v2_ensure_opt_in_linear() helper mirroring the v3 one).

Checked against 7.3.0-rc7 and linux-next (2026-10-24): neither carries a
fix; l2tp_v3_ensure_opt_in_linear() is still v3-only.  I found no
report of this specific OOB on lore/marc.

Best,
Guikun Yang


             reply	other threads:[~2026-10-25 17:35 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-25 17:35 Guikun Yang [this message]
2026-10-26 11:19 ` Guillaume Nault

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=tencent_389453EACB37DA0886D3896959D4A4CF5006@qq.com \
    --to=yangguikun19@qq.com \
    --cc=edumazet@google.com \
    --cc=gnault@redhat.com \
    --cc=jchapman@katalix.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /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®