DORMANTno replies

[PATCH net v2] ppp: enforce minimum MTU/MRU to fix Deflate buffer underflows

From: Yilin Zhang <hidden>
Date: 2026-09-08 08:41:41
Also in: lkml
Subsystem: networking drivers, ppp protocol drivers and compressors, the rest · Maintainers: Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

z_compress() assumes that its output buffer can hold the six-byte
PPP/Deflate header.  A user with CAP_NET_ADMIN can set the MTU to 1,
making pad_compress_skb() allocate a five-byte skb.  z_compress() then
writes the header past the end and makes avail_out wrap when subtracting
the header length, allowing zlib to continue writing past the allocation.

On receive, PPPIOCSMRU accepts any signed int.  An MRU of -1 makes
ppp_decompress_frame() allocate a three-byte skb, after which
z_decompress() writes the PPP header and makes avail_out wrap when
subtracting PPP_HDRLEN, letting inflate overwrite the skb.

Set the PPP netdevice minimum MTU to 128 and reject MRU values below 128
(or large enough to overflow the allocation-size calculation) in
PPPIOCSMRU, matching the minimum that pppd uses when negotiating MRU
values.  This ensures that the output buffer seen by z_compress() and
z_decompress() always covers the PPP/Deflate header, so avail_out
cannot wrap.

Take one MTU snapshot when calculating both the skb allocation and
compressor sizes, so a concurrent MTU change cannot make them
inconsistent.

The two memory-corruption paths produce (Linux 6.1.0, KASAN, decoded):

  BUG: KASAN: slab-out-of-bounds in deflate_slow (lib/zlib_deflate/defutil.h:431 lib/zlib_deflate/deflate.c:1123)
  Write of size 8198 at addr ff110000045edc06 by task exp/78
  CPU: 1 PID: 78 Comm: exp Not tainted 6.1.0 #1
  Call Trace:
    kasan_check_range (mm/kasan/generic.c:190)
    memcpy (mm/kasan/shadow.c:65)
    deflate_slow (lib/zlib_deflate/defutil.h:431 lib/zlib_deflate/deflate.c:1123)
    zlib_deflate (lib/zlib_deflate/deflate.c:412)
    z_compress (drivers/net/ppp/ppp_deflate.c:227)
    __ppp_xmit_process (drivers/net/ppp/ppp_generic.c:1703 drivers/net/ppp/ppp_generic.c:1826 drivers/net/ppp/ppp_generic.c:1646)
    ppp_xmit_process (drivers/net/ppp/ppp_generic.c:1668)
    ppp_write (drivers/net/ppp/ppp_generic.c:520)

  Allocated by task 78:
    __alloc_skb (net/core/skbuff.c:437 net/core/skbuff.c:509)
    __ppp_xmit_process (include/linux/skbuff.h:1267 drivers/net/ppp/ppp_generic.c:1692 drivers/net/ppp/ppp_generic.c:1826 drivers/net/ppp/ppp_generic.c:1646)
    ppp_xmit_process (drivers/net/ppp/ppp_generic.c:1668)
    ppp_write (drivers/net/ppp/ppp_generic.c:520)

  The buggy address belongs to the object at ff110000045edc00
   which belongs to the cache kmalloc-512 of size 512
  The buggy address is located 6 bytes inside of
   512-byte region [ff110000045edc00, ff110000045ede00)

  BUG: KASAN: slab-out-of-bounds in zlib_inflate (lib/zlib_inflate/inflate.c:458)
  Write of size 8191 at addr ff110000021d1044 by task ksoftirqd/0/12
  CPU: 0 PID: 12 Comm: ksoftirqd/0 Not tainted 6.1.0 #1
  Call Trace:
    kasan_check_range (mm/kasan/generic.c:190)
    memcpy (mm/kasan/shadow.c:65)
    zlib_inflate (lib/zlib_inflate/inflate.c:458)
    z_decompress (drivers/net/ppp/ppp_deflate.c:460)
    ppp_receive_nonmp_frame (drivers/net/ppp/ppp_generic.c:2546 drivers/net/ppp/ppp_generic.c:2383)
    ppp_input (drivers/net/ppp/ppp_generic.c:2355 drivers/net/ppp/ppp_generic.c:2195 drivers/net/ppp/ppp_generic.c:2310)
    ppp_async_process (drivers/net/ppp/ppp_async.c:496)

  Allocated by task 12:
    __alloc_skb (net/core/skbuff.c:437 net/core/skbuff.c:509)
    __netdev_alloc_skb (net/core/skbuff.c:575)
    ppp_receive_nonmp_frame (include/linux/skbuff.h:3155 include/linux/skbuff.h:3168 drivers/net/ppp/ppp_generic.c:2539 drivers/net/ppp/ppp_generic.c:2383)
    ppp_input (drivers/net/ppp/ppp_generic.c:2355 drivers/net/ppp/ppp_generic.c:2195 drivers/net/ppp/ppp_generic.c:2310)
    ppp_async_process (drivers/net/ppp/ppp_async.c:496)

  The buggy address belongs to the object at ff110000021d1000
   which belongs to the cache kmalloc-512 of size 512
  The buggy address is located 68 bytes inside of
   512-byte region [ff110000021d1000, ff110000021d1200)

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Suggested-by: Paul Mackerras <redacted>
Reported-by: Kimi Security Team <redacted>
Co-developed-by: Weiming Shi <redacted>
Signed-off-by: Weiming Shi <redacted>
Signed-off-by: Yilin Zhang <redacted>
---
 drivers/net/ppp/ppp_generic.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

Changes in v2:
- Set dev->min_mtu = 128 and reject MRU < 128 in PPPIOCSMRU per Paul's
  suggestion; drop the z_compress()/z_decompress() changes from v1.
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 1a610a1..4022117 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -72,6 +72,8 @@
 
 #define PPP_PROTO_LEN	2
 #define PPP_LCP_HDRLEN	4
+#define PPP_MIN_MTU	128
+#define PPP_MIN_MRU	128
 
 /* The filter instructions generated by libpcap are constructed
  * assuming a four-byte PPP header on each packet, where the last
@@ -808,6 +810,10 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case PPPIOCSMRU:
 		if (get_user(val, p))
 			break;
+		if (val < PPP_MIN_MRU || val > INT_MAX - PPP_HDRLEN - 1) {
+			err = -EINVAL;
+			break;
+		}
 		ppp_recv_lock(ppp);
 		ppp->mru = val;
 		ppp_recv_unlock(ppp);
@@ -1628,6 +1634,7 @@ static void ppp_setup(struct net_device *dev)
 
 	dev->hard_header_len = PPP_HDRLEN;
 	dev->mtu = PPP_MRU;
+	dev->min_mtu = PPP_MIN_MTU;
 	dev->addr_len = 0;
 	dev->tx_queue_len = 3;
 	dev->type = ARPHRD_PPP;
@@ -1719,10 +1726,11 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
 {
 	struct net_device *dev = netdev_from_priv(ppp);
 	struct sk_buff *new_skb;
+	int mtu = READ_ONCE(dev->mtu);
 	int len;
-	int new_skb_size = dev->mtu +
+	int new_skb_size = mtu +
 		ppp->xcomp->comp_extra + dev->hard_header_len;
-	int compressor_skb_size = dev->mtu +
+	int compressor_skb_size = mtu +
 		ppp->xcomp->comp_extra + PPP_HDRLEN;
 
 	if (skb_linearize(skb))
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help