diff options
| author | Sven Eckelmann <sven@narfation.org> | 2026-06-26 18:10:43 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-04 13:39:38 +0200 |
| commit | d435f4ff57c6a86097c6ae0c408e59a3cb1deee1 (patch) | |
| tree | 7770b764cfc178c3145a6b1dc643fe9b37c432a7 | |
| parent | edaadf868d988905a5bc1252ce86b54ffaaa10b6 (diff) | |
| download | linux-stable-d435f4ff57c6a86097c6ae0c408e59a3cb1deee1.tar.gz linux-stable-d435f4ff57c6a86097c6ae0c408e59a3cb1deee1.zip | |
batman-adv: tp_meter: avoid window underflow
commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream.
In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
arithmetic: win_left = win_limit - tp_vars->last_sent;
During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
shrunk win_limit is less than last_sent, the unsigned subtraction will
underflow, wrapping to a massive positive value. Instead of returning that
the window is full (unavailable), it returns that the sender can continue
sending.
To handle this situation, it must be checked whether the windows end
sequence number (win_limit) has to be compared with the last sent sequence
number. If it would be before the last sent sequence number, then more acks
are needed before the transmission can be started again.
Cc: stable@kernel.org
Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
| -rw-r--r-- | net/batman-adv/tp_meter.c | 7 |
1 files changed, 6 insertions, 1 deletions
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index f7b0ce3f8e2a..3fd46999f367 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -817,10 +817,15 @@ out: static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars, size_t payload_len) { + u32 last_sent = READ_ONCE(tp_vars->last_sent); u32 win_left, win_limit; win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd; - win_left = win_limit - tp_vars->last_sent; + + if (batadv_seq_before(last_sent, win_limit)) + win_left = win_limit - last_sent; + else + win_left = 0; return win_left >= payload_len; } |
