From: Greg Kroah-Hartman Date: Wed, 17 Jun 2026 14:00:24 +0000 (+0530) Subject: 5.15-stable patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2568b1d3383913efdf332ff132df4279218f1395;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch --- diff --git a/queue-5.15/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/queue-5.15/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch new file mode 100644 index 0000000000..94963fbe83 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch @@ -0,0 +1,190 @@ +From ff24f2ecfd94c07a2b89bac497433e3b23271cac Mon Sep 17 00:00:00 2001 +From: Sven Eckelmann +Date: Sat, 16 May 2026 12:33:41 +0200 +Subject: batman-adv: tp_meter: avoid role confusion in tp_list + +From: Sven Eckelmann + +commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream. + +Session lookups in tp_list matched only on destination address (and +optionally session ID), leaving role validation to the caller. If two +sessions with the same other_end coexisted (one as sender, one as receiver) +a lookup could silently return the wrong one, causing the caller's role to +bail out early, potentially skipping necessary cleanup. + +Move the role check into the lookup functions themselves so the correct +entry is always returned, or none at all. Since batadv_tp_start() +legitimately needs to detect any active session to a destination regardless +of role, introduce a dedicated helper for that case rather than bending the +existing lookup semantics. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Greg Kroah-Hartman +--- + net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++++++------------------ + 1 file changed, 36 insertions(+), 23 deletions(-) + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notif + * batadv_tp_list_find() - find a tp_vars object in the global list + * @bat_priv: the bat priv with all the soft interface information + * @dst: the other endpoint MAC address to look for ++ * @role: role of the session + * + * Look for a tp_vars object matching dst as end_point and return it after + * having increment the refcounter. Return NULL is not found +@@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notif + * Return: matching tp_vars or NULL when no tp_vars with @dst was found + */ + static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, +- const u8 *dst) ++ const u8 *dst, ++ enum batadv_tp_meter_role role) + { + struct batadv_tp_vars *pos, *tp_vars = NULL; + +@@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_ + if (!batadv_compare_eth(pos->other_end, dst)) + continue; + ++ if (pos->role != role) ++ continue; ++ + /* most of the time this function is invoked during the normal + * process..it makes sens to pay more when the session is + * finished and to speed the process up during the measurement +@@ -287,11 +292,32 @@ static struct batadv_tp_vars *batadv_tp_ + } + + /** ++ * batadv_tp_list_active() - check if session from/to destination is ongoing ++ * @bat_priv: the bat priv with all the mesh interface information ++ * @dst: the other endpoint MAC address to look for ++ * ++ * Return: if matching session with @dst was found ++ */ ++static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst) ++ __must_hold(&bat_priv->tp_list_lock) ++{ ++ struct batadv_tp_vars *tp_vars; ++ ++ hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) { ++ if (batadv_compare_eth(tp_vars->other_end, dst)) ++ return true; ++ } ++ ++ return false; ++} ++ ++/** + * batadv_tp_list_find_session() - find tp_vars session object in the global + * list + * @bat_priv: the bat priv with all the soft interface information + * @dst: the other endpoint MAC address to look for + * @session: session identifier ++ * @role: role of the session + * + * Look for a tp_vars object matching dst as end_point, session as tp meter + * session and return it after having increment the refcounter. Return NULL +@@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_ + */ + static struct batadv_tp_vars * + batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, +- const u8 *session) ++ const u8 *session, enum batadv_tp_meter_role role) + { + struct batadv_tp_vars *pos, *tp_vars = NULL; + +@@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batad + if (memcmp(pos->session, session, sizeof(pos->session)) != 0) + continue; + ++ if (pos->role != role) ++ continue; ++ + /* most of the time this function is invoked during the normal + * process..it makes sense to pay more when the session is + * finished and to speed the process up during the measurement +@@ -665,13 +694,10 @@ static void batadv_tp_recv_ack(struct ba + + /* find the tp_vars */ + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, +- icmp->session); ++ icmp->session, BATADV_TP_SENDER); + if (unlikely(!tp_vars)) + return; + +- if (unlikely(tp_vars->role != BATADV_TP_SENDER)) +- goto out; +- + if (unlikely(batadv_tp_sender_stopped(tp_vars))) + goto out; + +@@ -980,10 +1006,8 @@ void batadv_tp_start(struct batadv_priv + return; + } + +- tp_vars = batadv_tp_list_find(bat_priv, dst); +- if (tp_vars) { ++ if (batadv_tp_list_active(bat_priv, dst)) { + spin_unlock_bh(&bat_priv->tp_list_lock); +- batadv_tp_vars_put(tp_vars); + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: test to or from the same node already ongoing, aborting\n"); + batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING, +@@ -1104,18 +1128,14 @@ void batadv_tp_stop(struct batadv_priv * + if (!orig_node) + return; + +- tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig); ++ tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER); + if (!tp_vars) { + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: trying to interrupt an already over connection\n"); + goto out_put_orig_node; + } + +- if (unlikely(tp_vars->role != BATADV_TP_SENDER)) +- goto out_put_tp_vars; +- + batadv_tp_sender_shutdown(tp_vars, return_value); +-out_put_tp_vars: + batadv_tp_vars_put(tp_vars); + out_put_orig_node: + batadv_orig_node_put(orig_node); +@@ -1371,7 +1391,7 @@ batadv_tp_init_recv(struct batadv_priv * + goto out_unlock; + + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, +- icmp->session); ++ icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) + goto out_unlock; + +@@ -1442,7 +1462,7 @@ static void batadv_tp_recv_msg(struct ba + } + } else { + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, +- icmp->session); ++ icmp->session, BATADV_TP_RECEIVER); + if (!tp_vars) { + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Unexpected packet from %pM!\n", +@@ -1451,13 +1471,6 @@ static void batadv_tp_recv_msg(struct ba + } + } + +- if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) { +- batadv_dbg(BATADV_DBG_TP_METER, bat_priv, +- "Meter: dropping packet: not expected (role=%u)\n", +- tp_vars->role); +- goto out; +- } +- + tp_vars->last_recv_time = jiffies; + + /* if the packet is a duplicate, it may be the case that an ACK has been diff --git a/queue-5.15/batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch b/queue-5.15/batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch new file mode 100644 index 0000000000..35a80dd042 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch @@ -0,0 +1,182 @@ +From 71dce47f0758537fff78fddb5fb0d4632d29b29f Mon Sep 17 00:00:00 2001 +From: Sven Eckelmann +Date: Wed, 13 May 2026 23:38:54 +0200 +Subject: batman-adv: tp_meter: fix race condition in send error reporting + +From: Sven Eckelmann + +commit 71dce47f0758537fff78fddb5fb0d4632d29b29f upstream. + +batadv_tp_sender_shutdown() previously used two separate variables to track +session state: sending (an atomic flag indicating whether the session was +active) and reason (a plain enum storing the stop reason). This introduced +a race window between the two writes: after sending was cleared to 0, +batadv_tp_send() could observe the stopped state and call +batadv_tp_sender_end() before reason was written, causing the wrong stop +reason to be reported to the caller. + +Fix this by consolidating both variables into a single atomic send_result, +which holds 0 while the session is running and the stop reason once it +ends. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Greg Kroah-Hartman +--- + net/batman-adv/tp_meter.c | 40 +++++++++++++++++++++++++--------------- + net/batman-adv/types.h | 10 +++++----- + 2 files changed, 30 insertions(+), 20 deletions(-) + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -413,11 +413,14 @@ static void batadv_tp_sender_cleanup(str + static void batadv_tp_sender_end(struct batadv_priv *bat_priv, + struct batadv_tp_vars *tp_vars) + { ++ enum batadv_tp_meter_reason reason; + u32 session_cookie; + ++ reason = atomic_read(&tp_vars->send_result); ++ + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Test towards %pM finished..shutting down (reason=%d)\n", +- tp_vars->other_end, tp_vars->reason); ++ tp_vars->other_end, reason); + + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n", +@@ -430,7 +433,7 @@ static void batadv_tp_sender_end(struct + session_cookie = batadv_tp_session_cookie(tp_vars->session, + tp_vars->icmp_uid); + +- batadv_tp_batctl_notify(tp_vars->reason, ++ batadv_tp_batctl_notify(reason, + tp_vars->other_end, + bat_priv, + tp_vars->start_time, +@@ -446,10 +449,18 @@ static void batadv_tp_sender_end(struct + static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars, + enum batadv_tp_meter_reason reason) + { +- if (atomic_xchg(&tp_vars->sending, 0) != 1) +- return; ++ atomic_cmpxchg(&tp_vars->send_result, 0, reason); ++} + +- tp_vars->reason = reason; ++/** ++ * batadv_tp_sender_stopped() - check if tp session was stopped with reason ++ * @tp_vars: the private data of the current TP meter session ++ * ++ * Return: whether stop reason was found ++ */ ++static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars) ++{ ++ return atomic_read(&tp_vars->send_result) != 0; + } + + /** +@@ -479,7 +490,7 @@ static void batadv_tp_reset_sender_timer + /* most of the time this function is invoked while normal packet + * reception... + */ +- if (unlikely(atomic_read(&tp_vars->sending) == 0)) ++ if (unlikely(batadv_tp_sender_stopped(tp_vars))) + /* timer ref will be dropped in batadv_tp_sender_cleanup */ + return; + +@@ -499,7 +510,7 @@ static void batadv_tp_sender_timeout(str + struct batadv_tp_vars *tp_vars = from_timer(tp_vars, t, timer); + struct batadv_priv *bat_priv = tp_vars->bat_priv; + +- if (atomic_read(&tp_vars->sending) == 0) ++ if (batadv_tp_sender_stopped(tp_vars)) + return; + + /* if the user waited long enough...shutdown the test */ +@@ -661,7 +672,7 @@ static void batadv_tp_recv_ack(struct ba + if (unlikely(tp_vars->role != BATADV_TP_SENDER)) + goto out; + +- if (unlikely(atomic_read(&tp_vars->sending) == 0)) ++ if (unlikely(batadv_tp_sender_stopped(tp_vars))) + goto out; + + /* old ACK? silently drop it.. */ +@@ -827,21 +838,21 @@ static int batadv_tp_send(void *arg) + + if (unlikely(tp_vars->role != BATADV_TP_SENDER)) { + err = BATADV_TP_REASON_DST_UNREACHABLE; +- tp_vars->reason = err; ++ batadv_tp_sender_shutdown(tp_vars, err); + goto out; + } + + orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); + if (unlikely(!orig_node)) { + err = BATADV_TP_REASON_DST_UNREACHABLE; +- tp_vars->reason = err; ++ batadv_tp_sender_shutdown(tp_vars, err); + goto out; + } + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (unlikely(!primary_if)) { + err = BATADV_TP_REASON_DST_UNREACHABLE; +- tp_vars->reason = err; ++ batadv_tp_sender_shutdown(tp_vars, err); + goto out; + } + +@@ -860,7 +871,7 @@ static int batadv_tp_send(void *arg) + queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work, + msecs_to_jiffies(tp_vars->test_length)); + +- while (atomic_read(&tp_vars->sending) != 0) { ++ while (!batadv_tp_sender_stopped(tp_vars)) { + if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) { + batadv_tp_wait_available(tp_vars, payload_len); + continue; +@@ -883,8 +894,7 @@ static int batadv_tp_send(void *arg) + "Meter: %s() cannot send packets (%d)\n", + __func__, err); + /* ensure nobody else tries to stop the thread now */ +- if (atomic_xchg(&tp_vars->sending, 0) == 1) +- tp_vars->reason = err; ++ batadv_tp_sender_shutdown(tp_vars, err); + break; + } + +@@ -1006,7 +1016,7 @@ void batadv_tp_start(struct batadv_priv + ether_addr_copy(tp_vars->other_end, dst); + kref_init(&tp_vars->refcount); + tp_vars->role = BATADV_TP_SENDER; +- atomic_set(&tp_vars->sending, 1); ++ atomic_set(&tp_vars->send_result, 0); + memcpy(tp_vars->session, session_id, sizeof(session_id)); + tp_vars->icmp_uid = icmp_uid; + +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1397,15 +1397,15 @@ struct batadv_tp_vars { + /** @role: receiver/sender modi */ + enum batadv_tp_meter_role role; + +- /** @sending: sending binary semaphore: 1 if sending, 0 is not */ +- atomic_t sending; ++ /** ++ * @send_result: 0 when sending is ongoing and otherwise ++ * enum batadv_tp_meter_reason ++ */ ++ atomic_t send_result; + + /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ + atomic_t receiving; + +- /** @reason: reason for a stopped session */ +- enum batadv_tp_meter_reason reason; +- + /** @finish_work: work item for the finishing procedure */ + struct delayed_work finish_work; + diff --git a/queue-5.15/series b/queue-5.15/series index dc07ae5bc8..9463561aab 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -408,3 +408,5 @@ batman-adv-tp_meter-fix-tp_vars-reference-leak-in-receiver-shutdown.patch media-rc-igorplugusb-fix-control-request-setup-packet.patch bluetooth-mgmt-fix-backward-compatibility-with-userspace.patch ksmbd-oob-read-regression-in-smb_check_perm_dacl-ace-walk-loops.patch +batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch +batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch