[PATCH net-next] pppoe: pass bound packets directly to generic PPP
From: Qingfang Deng <hidden>
Date: 2026-08-04 07:45:38
Also in:
lkml
Subsystem:
networking drivers, ppp over ethernet, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Bound PPPoE sockets pass received frames to the generic PPP layer. They
currently do so through __sk_receive_skb(), which takes the socket BH
lock and serializes calls to ppp_input().
The lock originally prevented ppp_input() from racing with
ppp_unregister_channel(). Commit ec4215683e47 ("ppp: defer channel free
to an RCU grace period to fix pppol2tp RX UAF") now keeps the generic
PPP channel alive until in-flight RCU readers have completed, so bound
packets can be passed directly to ppp_input() from pppoe_rcv().
That lifetime guarantee does not cover reuse of the ppp_channel embedded
in struct pppox_sock. An RX handler can find the old session before it
is unhashed, then resume after disconnect and reconnect have cleared and
re-registered po->chan. It could then race initialization of the new
channel or pass an old-session packet through it.
After unhashing an old session, call synchronize_net() before clearing
and reusing po->chan. This drains every receive path that could have
found the old binding while retaining concurrent delivery for the active
session.
Assisted-by: Codex:GPT-5.6
Signed-off-by: Qingfang Deng <redacted>
---
drivers/net/ppp/pppoe.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 6874a1a8edaf..062411624182 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c@@ -345,10 +345,10 @@ static struct notifier_block pppoe_notifier = { /************************************************************************ * - * Do the real work of receiving a PPPoE Session frame. + * Backlog receive a PPPoE Session frame and deliver to userspace. * ***********************************************************************/ -static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) +static int pppoe_backlog_rcv(struct sock *sk, struct sk_buff *skb) { struct pppox_sock *po = pppox_sk(sk);
@@ -373,7 +373,7 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) /************************************************************************ * - * Receive wrapper called in BH context. + * Receive a PPPoE Session frame. * ***********************************************************************/ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
@@ -420,6 +420,10 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, if (!po) goto drop; + if (likely(po->sk.sk_state & PPPOX_BOUND)) { + ppp_input(&po->chan, skb); + return NET_RX_SUCCESS; + } return __sk_receive_skb(&po->sk, skb, 0, 1, false); drop:
@@ -524,7 +528,7 @@ static int pppoe_create(struct net *net, struct socket *sock, int kern) sock->state = SS_UNCONNECTED; sock->ops = &pppoe_ops; - sk->sk_backlog_rcv = pppoe_rcv_core; + sk->sk_backlog_rcv = pppoe_backlog_rcv; sk->sk_destruct = pppoe_destruct; sk->sk_state = PPPOX_NONE; sk->sk_type = SOCK_STREAM;
@@ -625,6 +629,13 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr pn = pppoe_pernet(sock_net(sk)); delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote, po->pppoe_ifindex); + + /* pppoe_rcv() can call ppp_input() without taking the socket + * lock. Once the socket is unhashed, wait for any receive path + * that found it earlier before clearing and reusing po->chan. + */ + synchronize_net(); + if (po->pppoe_dev) { dev_put(po->pppoe_dev); po->pppoe_dev = NULL;
--
2.43.0