This is a use-after-free in PPPoE header construction triggered by an unusual composition: the team device with GRE, when a port is added while a PPPoE packet is being transmitted. The bug isn't in the PPPoE code's logic itself — it's in a silent memory-lifetime violation that only emerges when specific subsystems are composed together.
The failure chain: PPPoE saves a pointer to the skb's PPPoE header region, then calls dev_hard_header() to build the Ethernet header. Under normal conditions, this just writes header bytes. But the team device's GRE callback can trigger pskb_expand_head() during this call — specifically when a new port is added while blocking inside copy_from_user(). That reallocation moves the skb's head to a new memory region, invalidating the saved pointer. When PPPoE then writes its six-byte header through the stale pointer, it's writing into freed memory.
The fix is straightforward and follows established kernel discipline: reload the PPPoE header pointer through skb_network_header(skb) after the dev_hard_header() call rather than holding a saved pointer across it. This works because pskb_expand_head() correctly updates the network-header offset.
What makes this notable is the composition hazard. PPPoE was correct code for years — it wasn't buggy when written. It became vulnerable only when composed with a device driver (team + GRE) that evolved to trigger skb head reallocation during header construction. The network-header offset reload pattern exists in newer network code precisely because those subsystems already dealt with this reallocation hazard. PPPoE never entered that composition, so the discipline never got applied there. Dormant networking code that hasn't been composed with newer device-layer callbacks may carry similar latent exposure.
If you're defending systems: the exploit requires unprivileged user access to compose team devices with GRE and trigger port addition during transmission — not a typical attack vector, but reachable in containerized or shared-host environments. Patch promptly. More broadly, audit any code path that holds skb pointers across dev_hard_header() or similar callback-dispatch functions; the kernel's device layer can reallocate the buffer underneath you without any explicit warning.