[PATCH] rxrpc: Fix slab-out-of-bounds write in rxrpc_send_ACK() during PMTUD probing

From: Gowtham S Bhushan

Date: Wed Nov 11 2026 - 09:53:14 EST


When rxrpc initializes Path MTU Discovery (PMTUD) parameters for a peer,
it computes the usable payload size as peer->if_mtu - peer->hdrsize. If
an interface has an MTU smaller than the transport header size (such as a
virtual interface configured with MTU 0), this unsigned subtraction
underflows to a value near UINT_MAX.

As a result, peer->pmtud_bad and peer->pmtud_trial become excessively
large. When rxrpc_send_ACK() builds a padded RXRPC_ACK_PING packet for
PMTUD probing, it appends PAGE_SIZE zero-page entries to call->local->kvec
inside a loop until len reaches probe_mtu. Because nr_kv is not checked
against the fixed 47-element capacity of call->local->kvec, nr_kv advances
past index 46 and triggers a KASAN slab-out-of-bounds write past the
1584-byte struct rxrpc_local object in the kmalloc-2k cache.

Fix this by guarding the subtraction against unsigned underflow when
peer->if_mtu <= peer->hdrsize, clamping pmtud_good, pmtud_bad, and
pmtud_trial to the usable payload limit, and only enabling pmtud_pending
when the link supports more than the minimum floor.
Additionally, ensure the padding loop in rxrpc_send_ACK() checks
nr_kv < ARRAY_SIZE(call->local->kvec) before writing to the array.

Reported-by: syzbot+0cd6f4783442ac67b56d@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=0cd6f4783442ac67b56d
Fixes: eeaedc5449d9 ("rxrpc: Implement path-MTU probing using padded PING ACKs (RFC8899)")

Signed-off-by: Gowtham S Bhushan <gowtham398gs@xxxxxxxxx>
---
net/rxrpc/output.c | 12 +++++++++++-
net/rxrpc/peer_object.c | 20 +++++++++++++++-----
2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 88cad087f13b..a3dfba0a3bbc 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -325,9 +325,19 @@ void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason,
if (why == rxrpc_propose_ack_ping_for_mtu_probe) {
size_t probe_mtu = call->peer->pmtud_trial + sizeof(struct rxrpc_wire_header);

+ /*
+ * skip padding if the base ACK packets already meets/exceeds probe_mut
+ */
+
if (len > probe_mtu)
goto skip;
- while (len < probe_mtu) {
+ /*
+ * pad the probe packet with zero pages up to probe_mut,
+ * ensuring we never write past the fixed capacity of
+ * local->kvec.
+ */
+
+ while (len < probe_mtu && nr_kv < ARRAY_SIZE(call->local->kvec)) {
size_t part = umin(probe_mtu - len, PAGE_SIZE);

kv[nr_kv].iov_base = page_address(ZERO_PAGE(0));
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c
index fa9a406e1168..a2f87137bb7e 100644
--- a/net/rxrpc/peer_object.c
+++ b/net/rxrpc/peer_object.c
@@ -207,11 +207,21 @@ void rxrpc_assess_MTU_size(struct rxrpc_local *local, struct rxrpc_peer *peer)
peer->tx_seg_max = dst->dev->gso_max_segs;
dst_release(dst);

- peer->max_data = umin(RXRPC_JUMBO(1), peer->if_mtu - peer->hdrsize);
- peer->pmtud_good = 500;
- peer->pmtud_bad = peer->if_mtu - peer->hdrsize + 1;
- peer->pmtud_trial = umin(peer->max_data, peer->pmtud_bad - 1);
- peer->pmtud_pending = true;
+ /*
+ * Avoid unsigned underflow if the interface MTU is smaller than the
+ * transport header size (e.g., on virtual interfaces with MTU 0).
+ */
+ unsigned int max_mtu = peer->if_mtu > peer->hdrsize ?
+ peer->if_mtu - peer->hdrsize : 0;
+
+ /* Clamp payload ceiling and PMTUD binary search bounds to link limits */
+ peer->max_data = umin(RXRPC_JUMBO(1), max_mtu);
+ peer->pmtud_good = umin(500, peer->max_data);
+ peer->pmtud_bad = peer->max_data + 1;
+ peer->pmtud_trial = peer->max_data;
+
+ /* Only schedule PMTUD probing if the link supports more than the floor */
+ peer->pmtud_pending = peer->max_data > peer->pmtud_good;

_leave(" [if_mtu %u]", peer->if_mtu);
}
--
2.56.0