* [PATCH net-next] pppoe: pass bound packets directly to generic PPP
@ 2026-08-04 7:44 Qingfang Deng
2026-08-06 1:28 ` Jakub Kicinski
0 siblings, 1 reply; 3+ messages in thread
From: Qingfang Deng @ 2026-08-04 7:44 UTC (permalink / raw)
To: linux-ppp, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Qingfang Deng, Kees Cook,
Eric Woudstra, Asim Viladi Oglu Manizada, Felix Fietkau, netdev,
linux-kernel
Cc: Norbert Szetei, Guillaume Nault
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 <qingfang.deng@linux.dev>
---
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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net-next] pppoe: pass bound packets directly to generic PPP
2026-08-04 7:44 [PATCH net-next] pppoe: pass bound packets directly to generic PPP Qingfang Deng
@ 2026-08-06 1:28 ` Jakub Kicinski
2026-08-06 3:11 ` Qingfang Deng
0 siblings, 1 reply; 3+ messages in thread
From: Jakub Kicinski @ 2026-08-06 1:28 UTC (permalink / raw)
To: qingfang.deng
Cc: Jakub Kicinski, linux-ppp, andrew+netdev, davem, edumazet, pabeni,
kees, ericwouds, manizada, nbd, netdev, linux-kernel, norbert,
gnault
This is (mostly) an AI-generated review of your patch.
One non-AI question - presumably you're doing this to improve performance?
Could you add some numbers to the cover letter?
---
pppoe: pass bound packets directly to generic PPP
This drops __sk_receive_skb() for PPPOX_BOUND PPPoE sockets and hands
session frames straight to ppp_input() from pppoe_rcv(), relying on the
RCU-deferred channel free added by ec4215683e47. It also adds a
synchronize_net() in pppoe_connect() after the old session is unhashed,
before po->chan is cleared and reused.
No Link: tags are present in the commit header.
A few questions below.
> diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
> index 6874a1a8edaf1..0624116241823 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);
[Severity: Low]
Is the new banner comment accurate? The function body still delivers to
the generic PPP layer, not to userspace:
drivers/net/ppp/pppoe.c:pppoe_backlog_rcv() {
if (sk->sk_state & PPPOX_BOUND) {
ppp_input(&po->chan, skb);
} else {
if (sock_queue_rcv_skb(sk, skb))
goto abort_kfree;
}
}
That branch still looks reachable: pppoe_rcv() only takes the new direct
path when it observes PPPOX_BOUND, and a frame observed while !BOUND is put
on the socket backlog by __sk_receive_skb() and only evaluated at
release_sock() time. By then pppox_ioctl(PPPIOCGCHAN) may already have run
sk->sk_state |= PPPOX_BOUND under lock_sock().
Could the comment mention that the bound case still goes to ppp_input(), so
that a later reader does not remove the branch on the strength of the banner
text?
[ ... ]
> @@ -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);
[Severity: Medium]
Does this skip socket filters and the skb->dev clearing that
__sk_receive_skb() used to perform for bound sessions?
net/core/sock.c:__sk_receive_skb() {
reason = sk_filter_trim_cap(sk, skb, trim_cap);
if (reason)
goto discard_and_relse;
skb->dev = NULL;
...
}
Two things in sk_filter_trim_cap() look relevant here. A filter attached
with SO_ATTACH_FILTER/SO_ATTACH_BPF is now silently ignored for all bound
session traffic; do_sock_setsockopt() routes SOL_SOCKET options to
sock_setsockopt() for PF_PPPOX since pppoe does not set SOCK_CUSTOM_SOCKOPT,
so sk->sk_filter can be non-NULL. The pfmemalloc guard is also bypassed:
net/core/filter.c:sk_filter_trim_cap() {
if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_PFMEMALLOCDROP);
return SKB_DROP_REASON_PFMEMALLOC;
}
...
}
The dropped skb->dev = NULL also matters for the frames ppp_input() parks on
pch->file.rq (proto >= 0xc000, PPP_CCPFRAG, or pch->ppp == NULL) -- those can
now sit queued indefinitely holding a non-refcounted net_device pointer. Is
that intentional? The cgroup-BPF and LSM parts of sk_filter_trim_cap() do
appear to be no-ops for PF_PPPOX, so those are not a concern, and the
sk_rcvqueues_full() check is not meaningful for the bound path.
Note the PPPOX_BOUND branch in pppoe_backlog_rcv() still runs under the old
contract, so the same class of frame is handled two different ways depending
on timing. Could the changelog say something about the filtering and
skb->dev behaviour change?
[Severity: Low]
Should this read be annotated? po->sk.sk_state is loaded here from softirq
with no lock held, while every writer stores it with a plain access under
lock_sock():
drivers/net/ppp/pppox.c:pppox_unbind_sock() sk->sk_state = PPPOX_DEAD;
drivers/net/ppp/pppox.c:pppox_ioctl() sk->sk_state |= PPPOX_BOUND;
drivers/net/ppp/pppoe.c:pppoe_connect() sk->sk_state = PPPOX_CONNECTED;
Before the patch the test happened inside bh_lock_sock() in
__sk_receive_skb(), so it was serialized against those writers. Would
KCSAN report this now, and would READ_ONCE()/WRITE_ONCE() be appropriate?
There is also a bare control dependency: pppoe_connect() fills in
po->chan.{hdrlen,mtu,private,ops}, calls ppp_register_net_channel() (which
sets chan->ppp), and only then publishes sk->sk_state, with no release
store to pair with. The reader then loads po->chan.ppp inside ppp_input()
with no acquire. As far as I can tell the worst outcome is ppp_input()
seeing !pch and dropping the frame, so this looks like an annotation
question rather than a correctness one. pppol2tp_recv() has the same
unannotated pattern.
[Severity: Low]
Can frames reach ppp_input() out of arrival order now? Two paths can feed
the same channel:
pppoe_rcv() -> ppp_input() (softirq, no socket lock)
release_sock() -> pppoe_backlog_rcv() -> ppp_input() (process context)
pppox_ioctl(PPPIOCGCHAN) sets sk->sk_state |= PPPOX_BOUND under lock_sock().
A frame that arrived just before that, while the socket was owned by the
ioctl caller, went to the socket backlog. A frame arriving after the BOUND
store but before release_sock() takes the new fast path and is handed to
ppp_input() immediately, ahead of the older backlogged one.
ppp_input() serializes internally, and the window only exists once per
session while LCP/auth frames are in flight, so this may be acceptable --
but is it worth a note?
[Severity: Low]
Does this leave the documented channel contract stale?
include/linux/ppp_channel.h {
* SMP locking notes:
* The channel code must ensure that when it calls ppp_unregister_channel,
* nothing is executing in any of the procedures above, for that
* channel.
}
ppp_input() is one of the listed procedures. After this change pppoe_rcv()
calls it from softirq with no serialization, while every pppoe caller that
reaches ppp_unregister_channel() via pppox_unbind_sock() -- pppoe_release(),
pppoe_flush_dev(), pppoe_unbind_sock_work(), pppoe_connect() -- does so
without draining in-flight ppp_input() callers. In pppoe_connect() the new
synchronize_net() sits after pppox_unbind_sock(), so it fences the later
memset(&po->chan) and re-registration rather than the unregister itself.
The RCU-deferred struct channel free from ec4215683e47 does appear to close
the use-after-free, and pppol2tp_recv() already calls ppp_input() the same
lockless way, so this is about the text rather than a runtime failure.
Should include/linux/ppp_channel.h (and possibly
Documentation/networking/ppp_generic.rst) be updated to describe the
RCU-based rule that now makes this safe?
[ ... ]
> @@ -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;
[Severity: Low]
Is holding the socket lock across the grace period necessary here?
pppoe_connect() takes lock_sock(sk) at the top, so every disconnect/rebind
of a session now blocks other users of that socket for a full,
non-expedited RCU grace period. The drain is only needed to protect the
following memset(&po->chan, 0, sizeof(po->chan)), so could it be arranged
without the socket lock held?
For the record, this does not look like it can stall RTNL:
delete_item() unhashes the socket before synchronize_net(), and
pppoe_flush_dev() only locks sockets it finds by walking pn->hash_table, so
the RTNL-holding notifier cannot find the socket sleeping in the grace
period.
--
pw-bot: cr
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net-next] pppoe: pass bound packets directly to generic PPP
2026-08-06 1:28 ` Jakub Kicinski
@ 2026-08-06 3:11 ` Qingfang Deng
0 siblings, 0 replies; 3+ messages in thread
From: Qingfang Deng @ 2026-08-06 3:11 UTC (permalink / raw)
To: Jakub Kicinski
Cc: linux-ppp, andrew+netdev, davem, edumazet, pabeni, kees,
ericwouds, manizada, nbd, netdev, linux-kernel, norbert, gnault
Hi,
On 2026/8/6 9:28, Jakub Kicinski wrote:
> This is (mostly) an AI-generated review of your patch.
>
> One non-AI question - presumably you're doing this to improve performance?
> Could you add some numbers to the cover letter?
Yes. I may add those numbers to v2. Note that the generic PPP RX also
serializes internally (which I plan to remove in another patch), so this
patch alone won't significantly improve performance.
> ---
> pppoe: pass bound packets directly to generic PPP
>
> This drops __sk_receive_skb() for PPPOX_BOUND PPPoE sockets and hands
> session frames straight to ppp_input() from pppoe_rcv(), relying on the
> RCU-deferred channel free added by ec4215683e47. It also adds a
> synchronize_net() in pppoe_connect() after the old session is unhashed,
> before po->chan is cleared and reused.
>
> No Link: tags are present in the commit header.
>
> A few questions below.
>
>> diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
>> index 6874a1a8edaf1..0624116241823 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);
> [Severity: Low]
> Is the new banner comment accurate? The function body still delivers to
> the generic PPP layer, not to userspace:
>
> drivers/net/ppp/pppoe.c:pppoe_backlog_rcv() {
> if (sk->sk_state & PPPOX_BOUND) {
> ppp_input(&po->chan, skb);
> } else {
> if (sock_queue_rcv_skb(sk, skb))
> goto abort_kfree;
> }
> }
>
> That branch still looks reachable: pppoe_rcv() only takes the new direct
> path when it observes PPPOX_BOUND, and a frame observed while !BOUND is put
> on the socket backlog by __sk_receive_skb() and only evaluated at
> release_sock() time. By then pppox_ioctl(PPPIOCGCHAN) may already have run
> sk->sk_state |= PPPOX_BOUND under lock_sock().
>
> Could the comment mention that the bound case still goes to ppp_input(), so
> that a later reader does not remove the branch on the strength of the banner
> text?
I'll update the banner comment.
>
> [ ... ]
>
>> @@ -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);
> [Severity: Medium]
> Does this skip socket filters and the skb->dev clearing that
> __sk_receive_skb() used to perform for bound sessions?
>
> net/core/sock.c:__sk_receive_skb() {
> reason = sk_filter_trim_cap(sk, skb, trim_cap);
> if (reason)
> goto discard_and_relse;
>
> skb->dev = NULL;
> ...
> }
>
> Two things in sk_filter_trim_cap() look relevant here. A filter attached
> with SO_ATTACH_FILTER/SO_ATTACH_BPF is now silently ignored for all bound
> session traffic; do_sock_setsockopt() routes SOL_SOCKET options to
> sock_setsockopt() for PF_PPPOX since pppoe does not set SOCK_CUSTOM_SOCKOPT,
> so sk->sk_filter can be non-NULL. The pfmemalloc guard is also bypassed:
>
> net/core/filter.c:sk_filter_trim_cap() {
> if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) {
> NET_INC_STATS(sock_net(sk), LINUX_MIB_PFMEMALLOCDROP);
> return SKB_DROP_REASON_PFMEMALLOC;
> }
> ...
> }
>
> The dropped skb->dev = NULL also matters for the frames ppp_input() parks on
> pch->file.rq (proto >= 0xc000, PPP_CCPFRAG, or pch->ppp == NULL) -- those can
> now sit queued indefinitely holding a non-refcounted net_device pointer. Is
> that intentional? The cgroup-BPF and LSM parts of sk_filter_trim_cap() do
> appear to be no-ops for PF_PPPOX, so those are not a concern, and the
> sk_rcvqueues_full() check is not meaningful for the bound path.
>
> Note the PPPOX_BOUND branch in pppoe_backlog_rcv() still runs under the old
> contract, so the same class of frame is handled two different ways depending
> on timing. Could the changelog say something about the filtering and
> skb->dev behaviour change?
The sk filter is an unintended side effect when sk_receive_skb() was
introduced (commit 25995ff57767). pppd has never used that to filter
PPPoE packets, instead it uses the generic PPP filter.
The dropped skb->dev = NULL should not matter, as the generic PPP layer
does not expect it to be initialized by an underlying channel.
>
> [Severity: Low]
> Should this read be annotated? po->sk.sk_state is loaded here from softirq
> with no lock held, while every writer stores it with a plain access under
> lock_sock():
>
> drivers/net/ppp/pppox.c:pppox_unbind_sock() sk->sk_state = PPPOX_DEAD;
> drivers/net/ppp/pppox.c:pppox_ioctl() sk->sk_state |= PPPOX_BOUND;
> drivers/net/ppp/pppoe.c:pppoe_connect() sk->sk_state = PPPOX_CONNECTED;
>
> Before the patch the test happened inside bh_lock_sock() in
> __sk_receive_skb(), so it was serialized against those writers. Would
> KCSAN report this now, and would READ_ONCE()/WRITE_ONCE() be appropriate?
>
> There is also a bare control dependency: pppoe_connect() fills in
> po->chan.{hdrlen,mtu,private,ops}, calls ppp_register_net_channel() (which
> sets chan->ppp), and only then publishes sk->sk_state, with no release
> store to pair with. The reader then loads po->chan.ppp inside ppp_input()
> with no acquire. As far as I can tell the worst outcome is ppp_input()
> seeing !pch and dropping the frame, so this looks like an annotation
> question rather than a correctness one. pppol2tp_recv() has the same
> unannotated pattern.
sk->sk_state is already `volatile` so I don't think it needs an extra
READ/WRITE_ONCE() which does the same thing.
> [Severity: Low]
> Can frames reach ppp_input() out of arrival order now? Two paths can feed
> the same channel:
>
> pppoe_rcv() -> ppp_input() (softirq, no socket lock)
> release_sock() -> pppoe_backlog_rcv() -> ppp_input() (process context)
>
> pppox_ioctl(PPPIOCGCHAN) sets sk->sk_state |= PPPOX_BOUND under lock_sock().
> A frame that arrived just before that, while the socket was owned by the
> ioctl caller, went to the socket backlog. A frame arriving after the BOUND
> store but before release_sock() takes the new fast path and is handed to
> ppp_input() immediately, ahead of the older backlogged one.
>
> ppp_input() serializes internally, and the window only exists once per
> session while LCP/auth frames are in flight, so this may be acceptable --
> but is it worth a note?
I may add a comment into pppoe_backlog_rcv().
>
> [Severity: Low]
> Does this leave the documented channel contract stale?
>
> include/linux/ppp_channel.h {
> * SMP locking notes:
> * The channel code must ensure that when it calls ppp_unregister_channel,
> * nothing is executing in any of the procedures above, for that
> * channel.
> }
>
> ppp_input() is one of the listed procedures. After this change pppoe_rcv()
> calls it from softirq with no serialization, while every pppoe caller that
> reaches ppp_unregister_channel() via pppox_unbind_sock() -- pppoe_release(),
> pppoe_flush_dev(), pppoe_unbind_sock_work(), pppoe_connect() -- does so
> without draining in-flight ppp_input() callers. In pppoe_connect() the new
> synchronize_net() sits after pppox_unbind_sock(), so it fences the later
> memset(&po->chan) and re-registration rather than the unregister itself.
>
> The RCU-deferred struct channel free from ec4215683e47 does appear to close
> the use-after-free, and pppol2tp_recv() already calls ppp_input() the same
> lockless way, so this is about the text rather than a runtime failure.
> Should include/linux/ppp_channel.h (and possibly
> Documentation/networking/ppp_generic.rst) be updated to describe the
> RCU-based rule that now makes this safe?
>
> [ ... ]
True, but it should be done in a separate patch.
>
>> @@ -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;
> [Severity: Low]
> Is holding the socket lock across the grace period necessary here?
> pppoe_connect() takes lock_sock(sk) at the top, so every disconnect/rebind
> of a session now blocks other users of that socket for a full,
> non-expedited RCU grace period. The drain is only needed to protect the
> following memset(&po->chan, 0, sizeof(po->chan)), so could it be arranged
> without the socket lock held?
>
> For the record, this does not look like it can stall RTNL:
> delete_item() unhashes the socket before synchronize_net(), and
> pppoe_flush_dev() only locks sockets it finds by walking pn->hash_table, so
> the RTNL-holding notifier cannot find the socket sleeping in the grace
> period.
pppd is single-threaded, so in practice, "other users of that socket" do
not exist.
If there are multiple users of one socket, the current implementation
needs the lock to keep the entire teardown/rebind transaction serialized.
A plain unlock creates this race:
1. Caller A unbinds and unhashes the old session, then unlocks.
2. Caller B connects a new session on the same socket. PPPOX_DEAD
permits this.
3. B clears and registers po->chan for its session.
4. A reacquires the lock and resumes inside its old teardown branch,
overwriting B's socket.
Best regards,
Qingfang
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-06 3:11 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 7:44 [PATCH net-next] pppoe: pass bound packets directly to generic PPP Qingfang Deng
2026-08-06 1:28 ` Jakub Kicinski
2026-08-06 3:11 ` Qingfang Deng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox