diff options
| author | Sven Eckelmann <sven@narfation.org> | 2026-06-26 18:11:33 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-04 13:42:24 +0200 |
| commit | 31dec4dc86cf68e426b94f7ca9011d66faf5e3f2 (patch) | |
| tree | 1cc7df53e89f116933f3ec6c216c5ebde88d5c8c | |
| parent | be3af0c705a139bdedc03fefeeb300ffcf488d60 (diff) | |
| download | linux-31dec4dc86cf68e426b94f7ca9011d66faf5e3f2.tar.gz linux-31dec4dc86cf68e426b94f7ca9011d66faf5e3f2.zip | |
batman-adv: tp_meter: prevent parallel modifications of last_recv
commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream.
When last_recv is updated to store the last receive sequence number, it is
assuming that nothing is modifying in parallel while:
* check for outdated packets is done
* out of order check is performed (and packets are stored in out-of-order
queue)
* the out-of-order queue was searched for closed gaps
* sequence number for next ack is calculated
Nothing of that was actually protected. It could therefore happen that the
last_recv was updated multiple times in parallel and the final sequence
number was calculated with deltas which had no connection to the sequence
number they were added to.
Lock this whole region with the same lock which was already used to protect
the unacked (out-of-order) list.
Cc: stable@kernel.org
Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
[ Switch to pre-splitted tp_vars structure names ]
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 | 22 | ||||
| -rw-r--r-- | net/batman-adv/types.h | 2 |
2 files changed, 14 insertions, 10 deletions
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index b882919a868c..f745bc09a049 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1294,6 +1294,7 @@ out: */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, const struct sk_buff *skb) + __must_hold(&tp_vars->unacked_lock) { const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_unacked *un, *new; @@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, payload_len = skb->len - sizeof(struct batadv_unicast_packet); new->len = payload_len; - spin_lock_bh(&tp_vars->unacked_lock); /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->unacked_list)) { list_add(&new->list, &tp_vars->unacked_list); tp_vars->unacked_count++; - goto out; + return true; } /* otherwise loop over the list and either drop the packet because this @@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, tp_vars->unacked_count--; } -out: - spin_unlock_bh(&tp_vars->unacked_lock); - return true; } @@ -1376,6 +1373,7 @@ out: * @tp_vars: the private data of the current TP meter session */ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + __must_hold(&tp_vars->unacked_lock) { struct batadv_tp_unacked *un, *safe; u32 to_ack; @@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) /* go through the unacked packet list and possibly ACK them as * well */ - spin_lock_bh(&tp_vars->unacked_lock); list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon * there is a gap between the last acked seqno and the seqno of @@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) kfree(un); tp_vars->unacked_count--; } - spin_unlock_bh(&tp_vars->unacked_lock); } /** @@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, const struct batadv_icmp_tp_packet *icmp; struct batadv_tp_vars *tp_vars; size_t packet_size; + u32 to_ack; u32 seqno; icmp = (struct batadv_icmp_tp_packet *)skb->data; @@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, WRITE_ONCE(tp_vars->last_recv_time, jiffies); } + spin_lock_bh(&tp_vars->unacked_lock); + /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK */ @@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /* exit immediately (and do not send any ACK) if the packet has * not been enqueued correctly */ - if (!batadv_tp_handle_out_of_order(tp_vars, skb)) + if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { + spin_unlock_bh(&tp_vars->unacked_lock); goto out; + } /* send a duplicate ACK */ goto send_ack; @@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, batadv_tp_ack_unordered(tp_vars); send_ack: + to_ack = tp_vars->last_recv; + spin_unlock_bh(&tp_vars->unacked_lock); + /* send the ACK. If the received packet was out of order, the ACK that * is going to be sent is a duplicate (the sender will count them and * possibly enter Fast Retransmit as soon as it has reached 3) */ - batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, + batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, icmp->timestamp, icmp->session, icmp->uid); out: batadv_tp_vars_put(tp_vars); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 7c32947414cd..883da8acae73 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1491,7 +1491,7 @@ struct batadv_tp_vars { /** @unacked_list: list of unacked packets (meta-info only) */ struct list_head unacked_list; - /** @unacked_lock: protect unacked_list */ + /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ spinlock_t unacked_lock; /** @unacked_count: number of unacked entries */ |
