mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [BUG] l2tp: OOB read in l2tp_recv_common() for L2TPv2 sessions with 8-byte peer cookie
@ 2026-10-25 17:35 Guikun Yang
  2026-10-26 11:19 ` Guillaume Nault
  0 siblings, 1 reply; 2+ messages in thread
From: Guikun Yang @ 2026-10-25 17:35 UTC (permalink / raw)
  To: Guillaume Nault
  Cc: James Chapman, Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev,
	linux-kernel

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


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-26 11:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-25 17:35 [BUG] l2tp: OOB read in l2tp_recv_common() for L2TPv2 sessions with 8-byte peer cookie Guikun Yang
2026-10-26 11:19 ` Guillaume Nault

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®