summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQingfang Deng <qingfang.deng@linux.dev>2026-08-11 11:53:10 +0800
committerJakub Kicinski <kuba@kernel.org>2026-08-17 14:00:30 -0700
commit92c1bf630abf0af646562398eaa36f80b5ff677d (patch)
treed8d714d827b0cf074ec785fc505af7baf52f285f
parent4f1d06cf8aaa9d2cb18e5ee8835aff6177256bc6 (diff)
downloadlinux-next-92c1bf630abf0af646562398eaa36f80b5ff677d.tar.gz
linux-next-92c1bf630abf0af646562398eaa36f80b5ff677d.zip
pppox: drain queued packets on channel handoff
PPPIOCGCHAN both returns the channel index and marks a PPPOX socket as bound to generic PPP, despite its getter semantic. Packets received before that transition are queued on sk_receive_queue, but a bound socket is no longer readable. Such packets therefore remain queued until the socket is destroyed. After marking a socket bound, wait for receive paths that observed the old state to finish queueing packets, and then drain the queue into generic PPP. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev> Link: https://patch.msgid.link/20260811035314.302878-1-qingfang.deng@linux.dev Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/ppp/pppox.c17
1 files changed, 17 insertions, 0 deletions
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c
index 5861a2f6ce3e..a6f72c813bef 100644
--- a/drivers/net/ppp/pppox.c
+++ b/drivers/net/ppp/pppox.c
@@ -74,7 +74,9 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
switch (cmd) {
case PPPIOCGCHAN: {
+ struct sk_buff *skb;
int index;
+
rc = -ENOTCONN;
if (!(sk->sk_state & PPPOX_CONNECTED))
break;
@@ -85,7 +87,22 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break;
rc = 0;
+ /* PPPIOCGCHAN historically marks the userspace handoff to
+ * generic PPP; pppd then attaches the returned channel to
+ * /dev/ppp.
+ */
sk->sk_state |= PPPOX_BOUND;
+ /* Let lockless receive paths finish queueing against the old
+ * state.
+ */
+ synchronize_net();
+ /* Drain packets queued before the handoff because a bound
+ * socket is no longer readable.
+ */
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ skb_orphan(skb);
+ ppp_input(&po->chan, skb);
+ }
break;
}
default: