Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

pppoe: drop PFC frames

RFC 2516 Section 7 states that Protocol Field Compression (PFC) is NOT
RECOMMENDED for PPPoE. In practice, pppd does not support negotiating
PFC for PPPoE sessions, and the current PPPoE driver assumes an
uncompressed (2-byte) protocol field. However, the generic PPP layer
function ppp_input() is not aware of the negotiation result, and still
accepts PFC frames.

If a peer with a broken implementation or an attacker sends a frame with
a compressed (1-byte) protocol field, the subsequent PPP payload is
shifted by one byte. This causes the network header to be 4-byte
misaligned, which may trigger unaligned access exceptions on some
architectures.

To reduce the attack surface, drop PPPoE PFC frames. Introduce
ppp_skb_is_compressed_proto() helper function to be used in both
ppp_generic.c and pppoe.c to avoid open-coding.

Fixes: 7fb1b8ca8fa1 ("ppp: Move PFC decompression to PPP generic layer")
Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260415022456.141758-2-qingfang.deng@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Qingfang Deng and committed by
Jakub Kicinski
cc1ff87b d6c19b31

+24 -2
+1 -1
drivers/net/ppp/ppp_generic.c
··· 2245 2245 */ 2246 2246 static void __ppp_decompress_proto(struct sk_buff *skb) 2247 2247 { 2248 - if (skb->data[0] & 0x01) 2248 + if (ppp_skb_is_compressed_proto(skb)) 2249 2249 *(u8 *)skb_push(skb, 1) = 0x00; 2250 2250 } 2251 2251
+7 -1
drivers/net/ppp/pppoe.c
··· 393 393 if (skb_mac_header_len(skb) < ETH_HLEN) 394 394 goto drop; 395 395 396 - if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr))) 396 + if (!pskb_may_pull(skb, PPPOE_SES_HLEN)) 397 397 goto drop; 398 398 399 399 ph = pppoe_hdr(skb); ··· 401 401 402 402 skb_pull_rcsum(skb, sizeof(*ph)); 403 403 if (skb->len < len) 404 + goto drop; 405 + 406 + /* skb->data points to the PPP protocol header after skb_pull_rcsum. 407 + * Drop PFC frames. 408 + */ 409 + if (ppp_skb_is_compressed_proto(skb)) 404 410 goto drop; 405 411 406 412 if (pskb_trim_rcsum(skb, len))
+16
include/linux/ppp_defs.h
··· 8 8 #define _PPP_DEFS_H_ 9 9 10 10 #include <linux/crc-ccitt.h> 11 + #include <linux/skbuff.h> 11 12 #include <uapi/linux/ppp_defs.h> 12 13 13 14 #define PPP_FCS(fcs, c) crc_ccitt_byte(fcs, c) ··· 24 23 static inline bool ppp_proto_is_valid(u16 proto) 25 24 { 26 25 return !!((proto & 0x0101) == 0x0001); 26 + } 27 + 28 + /** 29 + * ppp_skb_is_compressed_proto - checks if PPP protocol in a skb is compressed 30 + * @skb: skb to check 31 + * 32 + * Check if the PPP protocol field is compressed (the least significant 33 + * bit of the most significant octet is 1). skb->data must point to the PPP 34 + * protocol header. 35 + * 36 + * Return: Whether the PPP protocol field is compressed. 37 + */ 38 + static inline bool ppp_skb_is_compressed_proto(const struct sk_buff *skb) 39 + { 40 + return unlikely(skb->data[0] & 0x01); 27 41 } 28 42 29 43 #endif /* _PPP_DEFS_H_ */