* [PATCH 1/3] rxrpc: fix stack OOB read in TLP soft-ACK processing
2026-08-03 7:33 [PATCH 0/3] rxrpc: fix three bugs found by autokbug fuzzing Henry Martin
@ 2026-08-03 7:33 ` Henry Martin
2026-08-03 7:33 ` [PATCH 2/3] rxrpc: wait for deferred conn destruction before conn_proc_list check Henry Martin
2026-08-03 7:33 ` [PATCH 3/3] rxrpc: recheck RXRPC_CALL_TX_NO_MORE after sleeping in rxrpc_send_data() Henry Martin
2 siblings, 0 replies; 4+ messages in thread
From: Henry Martin @ 2026-08-03 7:33 UTC (permalink / raw)
To: netdev; +Cc: dhowells, marc.dionne, linux-afs, Henry Martin
rxrpc_seq_in_txq() is meant to test whether a sequence number belongs
to a given txqueue segment, but it compares the in-segment slot
number (seq & 63, range 0..63) against the segment's absolute base
sequence (tq->qbase, 0/64/128/...). Two consequences:
- For the first segment (qbase == 0), any seq that is a multiple of
64 is wrongly judged to belong to it;
- For any later segment (qbase >= 64), the test is always false, so
TLP probe handling is silently skipped for them.
In rxrpc_input_soft_ack_tq() the first case leads to
test_bit(call->tlp_seq - tq->qbase, &new_acks)
being evaluated with tlp_seq - qbase == 64*N while new_acks is a
single unsigned long on the stack, i.e. a stack out-of-bounds read
8*N bytes above new_acks (KASAN reports stack-out-of-bounds at
offset 40 for tlp_seq == 64). With a large enough tlp_seq the read
walks off the vmalloc'd kthread stack into the guard page and panics.
Turn the broken slot comparison into a real range check. This bounds
tlp_seq - tq->qbase to [0, RXRPC_NR_TXQUEUE), keeping the test_bit()
inside new_acks, and also fixes TLP probe handling for non-first
segments.
Found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab.
Fixes: 7c482665931b ("rxrpc: Implement RACK/TLP to deal with transmission stalls [RFC8985]")
Signed-off-by: Henry Martin <bsdhenrymartin@gmail.com>
---
net/rxrpc/ar-internal.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 865f05fe37ab9..27992e10d82ad 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -1580,7 +1580,8 @@ static inline u32 latest(u32 seq1, u32 seq2)
static inline bool rxrpc_seq_in_txq(const struct rxrpc_txqueue *tq, rxrpc_seq_t seq)
{
- return (seq & (RXRPC_NR_TXQUEUE - 1)) == tq->qbase;
+ return after_eq(seq, tq->qbase) &&
+ before(seq, tq->qbase + RXRPC_NR_TXQUEUE);
}
static inline void rxrpc_queue_rx_call_packet(struct rxrpc_call *call, struct sk_buff *skb)
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 2/3] rxrpc: wait for deferred conn destruction before conn_proc_list check
2026-08-03 7:33 [PATCH 0/3] rxrpc: fix three bugs found by autokbug fuzzing Henry Martin
2026-08-03 7:33 ` [PATCH 1/3] rxrpc: fix stack OOB read in TLP soft-ACK processing Henry Martin
@ 2026-08-03 7:33 ` Henry Martin
2026-08-03 7:33 ` [PATCH 3/3] rxrpc: recheck RXRPC_CALL_TX_NO_MORE after sleeping in rxrpc_send_data() Henry Martin
2 siblings, 0 replies; 4+ messages in thread
From: Henry Martin @ 2026-08-03 7:33 UTC (permalink / raw)
To: netdev; +Cc: dhowells, marc.dionne, linux-afs, Henry Martin
rxrpc_destroy_all_connections() flushes rxrpc_workqueue and then
immediately asserts that rxnet->conn_proc_list is empty. However,
connection destruction is normally deferred to system_wq: the
final-ACK timer is still pending when the last ref is dropped, so
rxrpc_put_connection() schedules conn->destructor instead of running
it inline. conn->proc_link is only removed by the deferred
destructor (rxrpc_clean_up_connection()), which the
flush_workqueue(rxrpc_workqueue) call does not cover - so the
assertion can fire on a perfectly healthy intermediate state:
rxrpc: Assertion failed
kernel BUG at net/rxrpc/conn_object.c:488!
Workqueue: netns cleanup_net
The BUG kills the cleanup_net kworker mid-teardown, leaving the
netns half-destroyed and potentially wedging later netns operations.
This is easily reachable from an unprivileged userns+netns running
loopback AF_RXRPC traffic.
The existing wait_var_event() on nr_conns is exactly the right
synchronization: nr_conns only reaches zero after every destructor
and RCU free has completed, which implies proc_link has been removed
from every connection. It is, however, placed *after* the assertion
it is meant to make reliable. Move it between the service_conns
leak check (whose outcome is already final once the reaper has been
flushed) and the conn_proc_list assertion.
This also silences the spurious "AF_RXRPC: Leaked peer" messages
seen during netns teardown, which share the same root cause: peer
references are dropped by the same deferred destructors.
Found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab.
Signed-off-by: Henry Martin <bsdhenrymartin@gmail.com>
---
net/rxrpc/conn_object.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 0ece717db0f85..df3f92b1a42e9 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -485,11 +485,13 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
write_unlock(&rxnet->conn_lock);
BUG_ON(leak);
- ASSERT(list_empty(&rxnet->conn_proc_list));
-
- /* We need to wait for the connections to be destroyed by RCU as they
- * pin things that we still need to get rid of.
+ /* Connection destruction is normally deferred to system_wq because
+ * the final-ACK timer is still pending when the last ref is dropped.
+ * Wait for the deferred destructors (and the RCU frees) to complete
+ * before checking conn_proc_list; they remove conns from it.
*/
wait_var_event(&rxnet->nr_conns, !atomic_read(&rxnet->nr_conns));
+
+ ASSERT(list_empty(&rxnet->conn_proc_list));
_leave("");
}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 3/3] rxrpc: recheck RXRPC_CALL_TX_NO_MORE after sleeping in rxrpc_send_data()
2026-08-03 7:33 [PATCH 0/3] rxrpc: fix three bugs found by autokbug fuzzing Henry Martin
2026-08-03 7:33 ` [PATCH 1/3] rxrpc: fix stack OOB read in TLP soft-ACK processing Henry Martin
2026-08-03 7:33 ` [PATCH 2/3] rxrpc: wait for deferred conn destruction before conn_proc_list check Henry Martin
@ 2026-08-03 7:33 ` Henry Martin
2 siblings, 0 replies; 4+ messages in thread
From: Henry Martin @ 2026-08-03 7:33 UTC (permalink / raw)
To: netdev; +Cc: dhowells, marc.dionne, linux-afs, Henry Martin
Commit ae4f89989479 ("rxrpc: Fix ability to add more data to a call
once MSG_MORE deasserted") added the RXRPC_CALL_TX_NO_MORE flag and an
entry check in rxrpc_send_data() to stop late sends on a finalized
call. However, the check runs only once at function entry: when the
transmit window is full, wait_for_space drops call->user_mutex and
sleeps, during which another thread sharing the same user_call_ID can
finalize the call (queue the LAST packet, setting TX_NO_MORE and
clearing send_queue). Upon waking, the reload path only re-verifies
the shutdown flag and call state - and a just-finalized call is still
in RXRPC_CALL_CLIENT_SEND_REQUEST until its data is hard-acked - so
the thread proceeds to rxrpc_alloc_txqueue() with send_queue == NULL
but tx_queue != NULL, triggering:
WARNING: net/rxrpc/sendmsg.c:297 at rxrpc_alloc_txqueue
and returning a spurious -ENOMEM. The call state itself is entirely
legal at that point; the WARN_ON's implied assumption that
send_queue == NULL implies tx_queue == NULL simply does not hold after
finalization.
Re-check RXRPC_CALL_TX_NO_MORE in the reload path after reacquiring
user_mutex, mirroring the entry check, and bail out with -EPROTO.
Found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab.
Fixes: ae4f89989479 ("rxrpc: Fix ability to add more data to a call once MSG_MORE deasserted")
Signed-off-by: Henry Martin <bsdhenrymartin@gmail.com>
---
net/rxrpc/sendmsg.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c
index ed2c9a51005ad..b39af552c6b46 100644
--- a/net/rxrpc/sendmsg.c
+++ b/net/rxrpc/sendmsg.c
@@ -358,6 +358,14 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
if (txb)
rxrpc_see_txbuf(txb, rxrpc_txbuf_see_send_more);
+ ret = -EPROTO;
+ if (test_bit(RXRPC_CALL_TX_NO_MORE, &call->flags)) {
+ trace_rxrpc_abort(call->debug_id, rxrpc_sendmsg_late_send,
+ call->cid, call->call_id, call->rx_consumed,
+ 0, -EPROTO);
+ goto maybe_error;
+ }
+
ret = -EPIPE;
if (sk->sk_shutdown & SEND_SHUTDOWN)
goto maybe_error;
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread