]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Jun 2026 14:00:24 +0000 (19:30 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Jun 2026 14:00:24 +0000 (19:30 +0530)
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

queue-5.15/batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch [new file with mode: 0644]
queue-5.15/batman-adv-tp_meter-fix-race-condition-in-send-error-reporting.patch [new file with mode: 0644]
queue-5.15/series

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 (file)
index 0000000..94963fb
--- /dev/null
@@ -0,0 +1,190 @@
+From ff24f2ecfd94c07a2b89bac497433e3b23271cac Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 16 May 2026 12:33:41 +0200
+Subject: batman-adv: tp_meter: avoid role confusion in tp_list
+
+From: Sven Eckelmann <sven@narfation.org>
+
+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 <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..35a80dd
--- /dev/null
@@ -0,0 +1,182 @@
+From 71dce47f0758537fff78fddb5fb0d4632d29b29f Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 13 May 2026 23:38:54 +0200
+Subject: batman-adv: tp_meter: fix race condition in send error reporting
+
+From: Sven Eckelmann <sven@narfation.org>
+
+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 <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
index dc07ae5bc88f9c55fe50ab9f554ed016821b9102..9463561aab1b58e8887486060abd6c717a0884d7 100644 (file)
@@ -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