mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Guillaume Nault <gnault@redhat.com>
To: Guikun Yang <yangguikun19@qq.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: Re: [BUG] l2tp: OOB read in l2tp_recv_common() for L2TPv2 sessions with 8-byte peer cookie
Date: Mon, 26 Oct 2026 12:19:13 +0100	[thread overview]
Message-ID: <at83MWsp98Mei-Si@debian> (raw)
In-Reply-To: <tencent_389453EACB37DA0886D3896959D4A4CF5006@qq.com>

On Mon, Oct 26, 2026 at 01:35:25AM +0800, Guikun Yang wrote:
> Hi Guillaume,

Hi Guikun,

> 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.

Cookies are an L2TPv3 feature (RFC 3931). They don't exist in L2TPv2
(RFC 2661) and l2tp_nl_cmd_session_create() ignores L2TP_ATTR_PEER_COOKIE
when creating L2TPv2 sessions. Have you found a way to get a non-null
->peer_cookie_len on an L2TPv2 session?

> 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).

I personally think that a full refactor should first stop mixing L2TPv2
and L2TPv3 in the same code paths. Although these protocols share
similar concepts, they actually don't have much in common from a
technical point of view (different headers, features, transports, etc.).

Starting packet processing with a pskb_may_pull() over actual header
length would make sense but, for L2TPv2, that lenght should be computed
based on L2TPv2 header flags (the Length, Sequence and Offset bits) plus
the Offset Size field if the O bit is set. The ->peer_cookie_len field
should never be considered for L2TPv2.

> 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-26 11:19 UTC|newest]

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

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