Netdev List
 help / color / mirror / Atom feed
* [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
@ 2026-07-01 12:14 Norbert Szetei
  2026-07-01 13:15 ` Breno Leitao
  2026-07-01 13:25 ` Sebastian Andrzej Siewior
  0 siblings, 2 replies; 5+ messages in thread
From: Norbert Szetei @ 2026-07-01 12:14 UTC (permalink / raw)
  To: netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Qingfang Deng, Taegu Ha, Yue Haibing,
	Sebastian Andrzej Siewior, Kees Cook, linux-ppp, linux-kernel

pppol2tp_recv() runs in the L2TP UDP-encap softirq RX path:

 l2tp_udp_encap_recv() -> l2tp_recv_common() -> pppol2tp_recv()
   -> ppp_input(&po->chan)

It runs under rcu_read_lock() holding only an l2tp_session reference and
takes NO reference on the internal PPP channel (struct channel,
chan->ppp) that ppp_input() dereferences.

The pppox socket is SOCK_RCU_FREE, so 'po' and the embedded ppp_channel
are RCU-safe.  But the internal struct channel is a separate allocation
that ppp_release_channel() frees with a plain kfree():

 close(data socket) -> pppol2tp_release() -> pppox_unbind_sock()
   -> ppp_unregister_channel() -> ppp_release_channel() -> kfree(pch)

For a channel that is bound (PPPIOCGCHAN) but not attached to a ppp unit
(no PPPIOCCONNECT, pch->ppp == NULL) and not bridged, teardown skips
both ppp_disconnect_channel()'s synchronize_net() and
ppp_unbridge_channels()'s synchronize_rcu(), so the kfree() has no grace
period.  rcu_read_lock() in pppol2tp_recv() does not protect against a
plain kfree(), so an in-flight ppp_input() on one CPU can dereference
the channel just freed by close() on another CPU.

The bug is reachable by an unprivileged user.

Free the channel with kfree_rcu() instead of kfree() so the grace period
fences any in-flight ppp_input(). Done in ppp_release_channel(), this
covers all callers in one place.

Fixes: ee40fb2e1eb5 ("l2tp: protect sock pointer of struct pppol2tp_session with RCU")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Norbert Szetei <norbert@doyensec.com>
---
 drivers/net/ppp/ppp_generic.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 57c68efa5ff8..cb8fe37170d3 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -184,6 +184,7 @@ struct channel {
 	struct list_head clist;		/* link in list of channels per unit */
 	spinlock_t	upl;		/* protects `ppp' and 'bridge' */
 	struct channel __rcu *bridge;	/* "bridged" ppp channel */
+	struct rcu_head	rcu;		/* for RCU-deferred free of the channel */
 #ifdef CONFIG_PPP_MULTILINK
 	u8		avail;		/* flag used in multilink stuff */
 	u8		had_frag;	/* >= 1 fragments have been sent */
@@ -3583,7 +3584,7 @@ static void ppp_release_channel(struct channel *pch)
 	}
 	skb_queue_purge(&pch->file.xq);
 	skb_queue_purge(&pch->file.rq);
-	kfree(pch);
+	kfree_rcu(pch, rcu);
 }
 
 static void __exit ppp_cleanup(void)
-- 
2.54.0

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

* Re: [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
  2026-07-01 12:14 [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF Norbert Szetei
@ 2026-07-01 13:15 ` Breno Leitao
  2026-07-01 18:00   ` Norbert Szetei
  2026-07-01 13:25 ` Sebastian Andrzej Siewior
  1 sibling, 1 reply; 5+ messages in thread
From: Breno Leitao @ 2026-07-01 13:15 UTC (permalink / raw)
  To: Norbert Szetei
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Qingfang Deng, Taegu Ha, Yue Haibing,
	Sebastian Andrzej Siewior, Kees Cook, linux-ppp, linux-kernel

On Wed, Jul 01, 2026 at 02:14:39PM +0200, Norbert Szetei wrote:
> diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
> index 57c68efa5ff8..cb8fe37170d3 100644
> --- a/drivers/net/ppp/ppp_generic.c
> +++ b/drivers/net/ppp/ppp_generic.c
> @@ -184,6 +184,7 @@ struct channel {
>  	struct list_head clist;		/* link in list of channels per unit */
>  	spinlock_t	upl;		/* protects `ppp' and 'bridge' */
>  	struct channel __rcu *bridge;	/* "bridged" ppp channel */
> +	struct rcu_head	rcu;		/* for RCU-deferred free of the channel */
>  #ifdef CONFIG_PPP_MULTILINK
>  	u8		avail;		/* flag used in multilink stuff */
>  	u8		had_frag;	/* >= 1 fragments have been sent */
> @@ -3583,7 +3584,7 @@ static void ppp_release_channel(struct channel *pch)
>  	}
>  	skb_queue_purge(&pch->file.xq);
>  	skb_queue_purge(&pch->file.rq);
> -	kfree(pch);
> +	kfree_rcu(pch, rcu);

Why not use kfree_rcu_mightsleep() instead? That would eliminate the need
for the additional `struct rcu_head rcu;` field.

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

* Re: [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
  2026-07-01 12:14 [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF Norbert Szetei
  2026-07-01 13:15 ` Breno Leitao
@ 2026-07-01 13:25 ` Sebastian Andrzej Siewior
  2026-07-01 18:03   ` Norbert Szetei
  1 sibling, 1 reply; 5+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-07-01 13:25 UTC (permalink / raw)
  To: Norbert Szetei
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Qingfang Deng, Taegu Ha, Yue Haibing,
	Kees Cook, linux-ppp, linux-kernel

On 2026-07-01 14:14:39 [+0200], Norbert Szetei wrote:
> --- a/drivers/net/ppp/ppp_generic.c
> +++ b/drivers/net/ppp/ppp_generic.c
> @@ -184,6 +184,7 @@ struct channel {
>  	struct list_head clist;		/* link in list of channels per unit */
>  	spinlock_t	upl;		/* protects `ppp' and 'bridge' */
>  	struct channel __rcu *bridge;	/* "bridged" ppp channel */
> +	struct rcu_head	rcu;		/* for RCU-deferred free of the channel */
>  #ifdef CONFIG_PPP_MULTILINK
>  	u8		avail;		/* flag used in multilink stuff */
>  	u8		had_frag;	/* >= 1 fragments have been sent */
> @@ -3583,7 +3584,7 @@ static void ppp_release_channel(struct channel *pch)
>  	}
>  	skb_queue_purge(&pch->file.xq);
>  	skb_queue_purge(&pch->file.rq);
> -	kfree(pch);
> +	kfree_rcu(pch, rcu);

From looking at ppp_input(), what ensures that the skb in-flight is not
added skb_queue which is purged above?

>  }

Sebastian

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

* Re: [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
  2026-07-01 13:15 ` Breno Leitao
@ 2026-07-01 18:00   ` Norbert Szetei
  0 siblings, 0 replies; 5+ messages in thread
From: Norbert Szetei @ 2026-07-01 18:00 UTC (permalink / raw)
  To: Breno Leitao
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Qingfang Deng, Taegu Ha, Yue Haibing,
	Sebastian Andrzej Siewior, Kees Cook, linux-ppp, linux-kernel

On Jul 1, 2026, at 15:15, Breno Leitao <leitao@debian.org> wrote:
> 
> On Wed, Jul 01, 2026 at 02:14:39PM +0200, Norbert Szetei wrote:
>> diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
>> index 57c68efa5ff8..cb8fe37170d3 100644
>> --- a/drivers/net/ppp/ppp_generic.c
>> +++ b/drivers/net/ppp/ppp_generic.c
>> @@ -184,6 +184,7 @@ struct channel {
>> struct list_head clist; /* link in list of channels per unit */
>> spinlock_t upl; /* protects `ppp' and 'bridge' */
>> struct channel __rcu *bridge; /* "bridged" ppp channel */
>> + struct rcu_head rcu; /* for RCU-deferred free of the channel */
>> #ifdef CONFIG_PPP_MULTILINK
>> u8 avail; /* flag used in multilink stuff */
>> u8 had_frag; /* >= 1 fragments have been sent */
>> @@ -3583,7 +3584,7 @@ static void ppp_release_channel(struct channel *pch)
>> }
>> skb_queue_purge(&pch->file.xq);
>> skb_queue_purge(&pch->file.rq);
>> - kfree(pch);
>> + kfree_rcu(pch, rcu);
> 
> Why not use kfree_rcu_mightsleep() instead? That would eliminate the need
> for the additional `struct rcu_head rcu;` field.

You are right, kfree_rcu_mightsleep() would be simpler, but it's
free-only. No callback to run the deferred purge, it can't handle the
in-flight skb. So, I'll keep the rcu_head and use call_rcu() in v2.

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

* Re: [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
  2026-07-01 13:25 ` Sebastian Andrzej Siewior
@ 2026-07-01 18:03   ` Norbert Szetei
  0 siblings, 0 replies; 5+ messages in thread
From: Norbert Szetei @ 2026-07-01 18:03 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Qingfang Deng, Taegu Ha, Yue Haibing,
	Kees Cook, linux-ppp, linux-kernel

On Jul 1, 2026, at 15:25, Sebastian Andrzej Siewior <bigeasy@linutronix.de> wrote:
> 
> On 2026-07-01 14:14:39 [+0200], Norbert Szetei wrote:
>> --- a/drivers/net/ppp/ppp_generic.c
>> +++ b/drivers/net/ppp/ppp_generic.c
>> @@ -184,6 +184,7 @@ struct channel {
>> struct list_head clist; /* link in list of channels per unit */
>> spinlock_t upl; /* protects `ppp' and 'bridge' */
>> struct channel __rcu *bridge; /* "bridged" ppp channel */
>> + struct rcu_head rcu; /* for RCU-deferred free of the channel */
>> #ifdef CONFIG_PPP_MULTILINK
>> u8 avail; /* flag used in multilink stuff */
>> u8 had_frag; /* >= 1 fragments have been sent */
>> @@ -3583,7 +3584,7 @@ static void ppp_release_channel(struct channel *pch)
>> }
>> skb_queue_purge(&pch->file.xq);
>> skb_queue_purge(&pch->file.rq);
>> - kfree(pch);
>> + kfree_rcu(pch, rcu);
> 
> From looking at ppp_input(), what ensures that the skb in-flight is not
> added skb_queue which is purged above?

Good catch, purging before the free races an in-flight ppp_input() and
leaks the skb, confirmed with kmemleak. In v2 I moved the purge into the
call_rcu() callback so it runs after the grace period.

N.

> 
>> }
> 
> Sebastian


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

end of thread, other threads:[~2026-07-01 18:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 12:14 [PATCH net] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF Norbert Szetei
2026-07-01 13:15 ` Breno Leitao
2026-07-01 18:00   ` Norbert Szetei
2026-07-01 13:25 ` Sebastian Andrzej Siewior
2026-07-01 18:03   ` Norbert Szetei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox