CVE-2026-98158: ppp_async: drop the errored frame instead of resetting its headroom
In the Linux kernel, the following vulnerability has been resolved:
pppasync: drop the errored frame instead of resetting its headroom
pppreceivenonmpframe() prepends a two-byte direction tag before running the pass/active BPF filters:
(be16 )skbpush(skb, 2) = htons(PPPFILTERINBOUNDTAG);
Nothing on the receive path guarantees those two bytes of headroom. The frame-error path in pppasync's processinputpacket() resets a reused skb's headroom to zero while claiming to restore it to a freshly allocated state - but a fresh skb from devallocskb() carries NETSKBPAD:
err: if (skb) { / make skb appear as freshly allocated / skbtrim(skb, 0); skbreserve(skb, - skbheadroom(skb)); }
ap->rpkt still points at that skb, so the next frame is reassembled into it with no headroom at all. A peer that sends a bad-FCS frame followed by one beginning ff 03 then leaves a single byte of headroom by the time the filter tag is pushed, which lands one byte below skb->head:
skbuff: skbunderpanic: len:49 put:2 head:ffff888003c10000 data:ffff888003c0ffff tail:0x30 end:0x640 dev:<NULL> kernel BUG at net/core/skbuff.c:214! RIP: 0010:skbpanic+0x13e/0x230 Call Trace: skbpush+0xbd/0x100 pppreceivenonmpframe+0x48a/0x1d10 pppinput+0x4e9/0x2f80 pppasyncprocess+0x2a/0xe0 taskletactioncommon+0x20f/0x8a0 handlesoftirqs+0x18e/0x590 Kernel panic - not syncing: Fatal exception in interrupt
Zeroing the headroom violates the NETSKBPAD guarantee that devallocskb() gives the rest of the receive path. Besides the filter panic above, when CCP compression is enabled pppdecompressframe() hands skb->data - 2 to ->decompress()/->incomp(), which then reads out of bounds before skb->head for the same reason.
Rather than restore the headroom, drop the errored frame - as pppsynctty already does on its error path - and clear ap->rpkt so the next frame is reassembled into a fresh skb with proper headroom. This is simpler and fixes both the filter under-panic and the CCP out-of-bounds read.
The original V1 of this patch made room in pppreceivenonmpframe() with skbcowhead(); Eric pointed out that fixing the root cause in the transport is the right approach.
Found by fuzzing the PPP receive path with a mutating peer on a pty; it is an interesting (remote) DoS: root configures PPP, the peer supplies two crashing frames. The reproducer (repro-ppp-skb.c, unchanged from v1) panics in about a second, and returns cleanly with this applied.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Compensating control
In ppp_async's frame-error path (process_input_packet), drop the errored frame instead of resetting the reused skb's headroom, and clear ap->rpkt so the next frame is reassembled into a fresh skb with proper headroom.
Event History
Frequently Asked Questions
What does an attacker need to send to trigger this issue?
A peer must send a bad-FCS frame and then a subsequent frame beginning with the bytes ff 03. This sequence can cause the reused receive buffer to have insufficient headroom when the PPP inbound filter tag is prepended.
What is the likely observable impact on an affected system?
The kernel can hit an skbuff underflow and BUG in skb_push(), producing an skb_under_panic message and kernel panic. The described failure occurs on the PPP asynchronous receive path.
How can I identify a possible occurrence?
Check kernel logs or crash reports for skb_under_panic output referencing skb_push and a call trace through ppp_receive_nonmp_frame or the PPP asynchronous input path. The example reports data positioned below skb->head and a kernel BUG in net/core/skbuff.c.