]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 16 Jun 2026 06:56:12 +0000 (12:26 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 16 Jun 2026 06:56:12 +0000 (12:26 +0530)
added patches:
batman-adv-stop-tp_meter-sessions-during-mesh-teardown.patch
batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failure.patch
ipvs-skip-ipv6-extension-headers-for-csum-checks.patch

queue-5.10/batman-adv-stop-tp_meter-sessions-during-mesh-teardown.patch [new file with mode: 0644]
queue-5.10/batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failure.patch [new file with mode: 0644]
queue-5.10/ipvs-skip-ipv6-extension-headers-for-csum-checks.patch [new file with mode: 0644]
queue-5.10/series

diff --git a/queue-5.10/batman-adv-stop-tp_meter-sessions-during-mesh-teardown.patch b/queue-5.10/batman-adv-stop-tp_meter-sessions-during-mesh-teardown.patch
new file mode 100644 (file)
index 0000000..c56fa93
--- /dev/null
@@ -0,0 +1,235 @@
+From 3d3cf6a7314aca4df0a6dde28ce784a2a30d0166 Mon Sep 17 00:00:00 2001
+From: Jiexun Wang <wangjiexun2025@gmail.com>
+Date: Mon, 27 Apr 2026 14:43:34 +0800
+Subject: batman-adv: stop tp_meter sessions during mesh teardown
+
+From: Jiexun Wang <wangjiexun2025@gmail.com>
+
+commit 3d3cf6a7314aca4df0a6dde28ce784a2a30d0166 upstream.
+
+TP meter sessions remain linked on bat_priv->tp_list after the netlink
+request has already finished. When the mesh interface is removed,
+batadv_mesh_free() currently tears down the mesh without first draining
+these sessions.
+
+A running sender thread or a late incoming tp_meter packet can then keep
+processing against a mesh instance which is already shutting down.
+Synchronize tp_meter with the mesh lifetime by stopping all active
+sessions from batadv_mesh_free() and waiting for sender threads to exit
+before teardown continues.
+
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Cc: stable@kernel.org
+Reported-by: Yuan Tan <yuantan098@gmail.com>
+Reported-by: Yifan Wu <yifanwucs@gmail.com>
+Reported-by: Juefei Pu <tomapufckgml@gmail.com>
+Reported-by: Xin Liu <bird@lzu.edu.cn>
+Co-developed-by: Luxing Yin <tr0jan@lzu.edu.cn>
+Signed-off-by: Luxing Yin <tr0jan@lzu.edu.cn>
+Signed-off-by: Jiexun Wang <wangjiexun2025@gmail.com>
+Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+[ Context, use del_timer_sync instead timer_shutdown_sync ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/main.c     |    1 
+ net/batman-adv/tp_meter.c |   94 +++++++++++++++++++++++++++++++++++++---------
+ net/batman-adv/tp_meter.h |    1 
+ net/batman-adv/types.h    |    4 +
+ 4 files changed, 82 insertions(+), 18 deletions(-)
+
+--- a/net/batman-adv/main.c
++++ b/net/batman-adv/main.c
+@@ -269,6 +269,7 @@ void batadv_mesh_free(struct net_device
+       atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
+       batadv_purge_outstanding_packets(bat_priv, NULL);
++      batadv_tp_stop_all(bat_priv);
+       batadv_gw_node_free(bat_priv);
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -12,6 +12,7 @@
+ #include <linux/byteorder/generic.h>
+ #include <linux/cache.h>
+ #include <linux/compiler.h>
++#include <linux/completion.h>
+ #include <linux/err.h>
+ #include <linux/etherdevice.h>
+ #include <linux/gfp.h>
+@@ -394,23 +395,38 @@ static void batadv_tp_vars_put(struct ba
+ }
+ /**
+- * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
+- * @bat_priv: the bat priv with all the soft interface information
+- * @tp_vars: the private data of the current TP meter session to cleanup
++ * batadv_tp_list_detach() - remove tp session from mesh session list once
++ * @tp_vars: the private data of the current TP meter session
+  */
+-static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
+-                                   struct batadv_tp_vars *tp_vars)
++static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
+ {
+-      cancel_delayed_work(&tp_vars->finish_work);
++      bool detached = false;
+       spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
+-      hlist_del_rcu(&tp_vars->list);
++      if (!hlist_unhashed(&tp_vars->list)) {
++              hlist_del_init_rcu(&tp_vars->list);
++              detached = true;
++      }
+       spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
++      if (!detached)
++              return;
++
++      atomic_dec(&tp_vars->bat_priv->tp_num);
++
+       /* drop list reference */
+       batadv_tp_vars_put(tp_vars);
++}
+-      atomic_dec(&tp_vars->bat_priv->tp_num);
++/**
++ * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
++ * @tp_vars: the private data of the current TP meter session to cleanup
++ */
++static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
++{
++      cancel_delayed_work_sync(&tp_vars->finish_work);
++
++      batadv_tp_list_detach(tp_vars);
+       /* kill the timer and remove its reference */
+       del_timer_sync(&tp_vars->timer);
+@@ -930,7 +946,8 @@ out:
+               batadv_orig_node_put(orig_node);
+       batadv_tp_sender_end(bat_priv, tp_vars);
+-      batadv_tp_sender_cleanup(bat_priv, tp_vars);
++      batadv_tp_sender_cleanup(tp_vars);
++      complete(&tp_vars->finished);
+       batadv_tp_vars_put(tp_vars);
+@@ -962,7 +979,8 @@ static void batadv_tp_start_kthread(stru
+               batadv_tp_vars_put(tp_vars);
+               /* cleanup of failed tp meter variables */
+-              batadv_tp_sender_cleanup(bat_priv, tp_vars);
++              batadv_tp_sender_cleanup(tp_vars);
++              complete(&tp_vars->finished);
+               return;
+       }
+@@ -1066,6 +1084,7 @@ void batadv_tp_start(struct batadv_priv
+       tp_vars->start_time = jiffies;
+       init_waitqueue_head(&tp_vars->more_bytes);
++      init_completion(&tp_vars->finished);
+       spin_lock_init(&tp_vars->unacked_lock);
+       INIT_LIST_HEAD(&tp_vars->unacked_list);
+@@ -1168,14 +1187,7 @@ static void batadv_tp_receiver_shutdown(
+                  "Shutting down for inactivity (more than %dms) from %pM\n",
+                  BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
+-      spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
+-      hlist_del_rcu(&tp_vars->list);
+-      spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
+-
+-      /* drop list reference */
+-      batadv_tp_vars_put(tp_vars);
+-
+-      atomic_dec(&bat_priv->tp_num);
++      batadv_tp_list_detach(tp_vars);
+       spin_lock_bh(&tp_vars->unacked_lock);
+       list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+@@ -1535,6 +1547,52 @@ out:
+ }
+ /**
++ * batadv_tp_stop_all() - stop all currently running tp meter sessions
++ * @bat_priv: the bat priv with all the mesh interface information
++ */
++void batadv_tp_stop_all(struct batadv_priv *bat_priv)
++{
++      struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
++      struct batadv_tp_vars *tp_var;
++      size_t count = 0;
++      size_t i;
++
++      spin_lock_bh(&bat_priv->tp_list_lock);
++      hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
++              if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
++                      break;
++
++              if (!kref_get_unless_zero(&tp_var->refcount))
++                      continue;
++
++              tp_vars[count++] = tp_var;
++      }
++      spin_unlock_bh(&bat_priv->tp_list_lock);
++
++      for (i = 0; i < count; i++) {
++              tp_var = tp_vars[i];
++
++              switch (tp_var->role) {
++              case BATADV_TP_SENDER:
++                      batadv_tp_sender_shutdown(tp_var,
++                                                BATADV_TP_REASON_CANCEL);
++                      wake_up(&tp_var->more_bytes);
++                      wait_for_completion(&tp_var->finished);
++                      break;
++              case BATADV_TP_RECEIVER:
++                      batadv_tp_list_detach(tp_var);
++                      if (del_timer_sync(&tp_var->timer))
++                              batadv_tp_vars_put(tp_var);
++                      break;
++              }
++
++              batadv_tp_vars_put(tp_var);
++      }
++
++      synchronize_net();
++}
++
++/**
+  * batadv_tp_meter_init() - initialize global tp_meter structures
+  */
+ void __init batadv_tp_meter_init(void)
+--- a/net/batman-adv/tp_meter.h
++++ b/net/batman-adv/tp_meter.h
+@@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv
+                    u32 test_length, u32 *cookie);
+ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
+                   u8 return_value);
++void batadv_tp_stop_all(struct batadv_priv *bat_priv);
+ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
+ #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
+--- a/net/batman-adv/types.h
++++ b/net/batman-adv/types.h
+@@ -14,6 +14,7 @@
+ #include <linux/average.h>
+ #include <linux/bitops.h>
+ #include <linux/compiler.h>
++#include <linux/completion.h>
+ #include <linux/if.h>
+ #include <linux/if_ether.h>
+ #include <linux/kref.h>
+@@ -1422,6 +1423,9 @@ struct batadv_tp_vars {
+       /** @finish_work: work item for the finishing procedure */
+       struct delayed_work finish_work;
++      /** @finished: completion signaled when a sender thread exits */
++      struct completion finished;
++
+       /** @test_length: test length in milliseconds */
+       u32 test_length;
diff --git a/queue-5.10/batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failure.patch b/queue-5.10/batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failure.patch
new file mode 100644 (file)
index 0000000..95b06dc
--- /dev/null
@@ -0,0 +1,52 @@
+From ce425dd05d0fe7594930a0fb103634f35ac47bb6 Mon Sep 17 00:00:00 2001
+From: Sven Eckelmann <sven@narfation.org>
+Date: Wed, 6 May 2026 22:20:49 +0200
+Subject: batman-adv: tp_meter: fix tp_num leak on kmalloc failure
+
+From: Sven Eckelmann <sven@narfation.org>
+
+commit ce425dd05d0fe7594930a0fb103634f35ac47bb6 upstream.
+
+When batadv_tp_start() or batadv_tp_init_recv() fail to allocate a new
+tp_vars object, the previously incremented bat_priv->tp_num counter is
+never decremented. This causes tp_num to drift upward on each allocation
+failure. Since only BATADV_TP_MAX_NUM sessions can be started and the count
+is never reduced for these failed allocations, it causes to an exhaustion
+of throughput meter sessions. In worst case, no new throughput meter
+session can be started until the mesh interface is removed.
+
+The error handling must decrement tp_num releasing the lock and aborting
+the creation of an throughput meter session
+
+Cc: stable@kernel.org
+Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation")
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+[ Context ]
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/batman-adv/tp_meter.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/net/batman-adv/tp_meter.c
++++ b/net/batman-adv/tp_meter.c
+@@ -1036,6 +1036,7 @@ void batadv_tp_start(struct batadv_priv
+       tp_vars = kmalloc(sizeof(*tp_vars), GFP_ATOMIC);
+       if (!tp_vars) {
++              atomic_dec(&bat_priv->tp_num);
+               spin_unlock_bh(&bat_priv->tp_list_lock);
+               batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+                          "Meter: %s cannot allocate list elements\n",
+@@ -1410,8 +1411,10 @@ batadv_tp_init_recv(struct batadv_priv *
+       }
+       tp_vars = kmalloc(sizeof(*tp_vars), GFP_ATOMIC);
+-      if (!tp_vars)
++      if (!tp_vars) {
++              atomic_dec(&bat_priv->tp_num);
+               goto out_unlock;
++      }
+       ether_addr_copy(tp_vars->other_end, icmp->orig);
+       tp_vars->role = BATADV_TP_RECEIVER;
diff --git a/queue-5.10/ipvs-skip-ipv6-extension-headers-for-csum-checks.patch b/queue-5.10/ipvs-skip-ipv6-extension-headers-for-csum-checks.patch
new file mode 100644 (file)
index 0000000..f82fb0b
--- /dev/null
@@ -0,0 +1,195 @@
+From 05cfe9863ef049d98141dc2969eefde72fb07625 Mon Sep 17 00:00:00 2001
+From: Julian Anastasov <ja@ssi.bg>
+Date: Sat, 14 Feb 2026 16:58:49 +0200
+Subject: ipvs: skip ipv6 extension headers for csum checks
+
+From: Julian Anastasov <ja@ssi.bg>
+
+commit 05cfe9863ef049d98141dc2969eefde72fb07625 upstream.
+
+Protocol checksum validation fails for IPv6 if there are extension
+headers before the protocol header. iph->len already contains its
+offset, so use it to fix the problem.
+
+Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support")
+Fixes: 0bbdd42b7efa ("IPVS: Extend protocol DNAT/SNAT and state handlers")
+Signed-off-by: Julian Anastasov <ja@ssi.bg>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Nazar Kalashnikov <nazarkalashnikov0@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/netfilter/ipvs/ip_vs_proto_sctp.c |   18 ++++++------------
+ net/netfilter/ipvs/ip_vs_proto_tcp.c  |   21 +++++++--------------
+ net/netfilter/ipvs/ip_vs_proto_udp.c  |   20 +++++++-------------
+ 3 files changed, 20 insertions(+), 39 deletions(-)
+
+--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
+@@ -10,7 +10,8 @@
+ #include <net/ip_vs.h>
+ static int
+-sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
++sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
++              unsigned int sctphoff);
+ static int
+ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -108,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, s
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp))
++              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
+                       return 0;
+               /* Call application helper if needed */
+@@ -156,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, s
+               int ret;
+               /* Some checks before mangling */
+-              if (!sctp_csum_check(cp->af, skb, pp))
++              if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
+                       return 0;
+               /* Call application helper if needed */
+@@ -185,19 +186,12 @@ sctp_dnat_handler(struct sk_buff *skb, s
+ }
+ static int
+-sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
++sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
++              unsigned int sctphoff)
+ {
+-      unsigned int sctphoff;
+       struct sctphdr *sh;
+       __le32 cmp, val;
+-#ifdef CONFIG_IP_VS_IPV6
+-      if (af == AF_INET6)
+-              sctphoff = sizeof(struct ipv6hdr);
+-      else
+-#endif
+-              sctphoff = ip_hdrlen(skb);
+-
+       sh = (struct sctphdr *)(skb->data + sctphoff);
+       cmp = sh->checksum;
+       val = sctp_compute_cksum(skb, sctphoff);
+--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
+@@ -29,7 +29,8 @@
+ #include <net/ip_vs.h>
+ static int
+-tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
++tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
++             unsigned int tcphoff);
+ static int
+ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -166,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, st
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp))
++              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
+                       return 0;
+               /* Call application helper if needed */
+@@ -244,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, st
+               int ret;
+               /* Some checks before mangling */
+-              if (!tcp_csum_check(cp->af, skb, pp))
++              if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
+                       return 0;
+               /*
+@@ -301,17 +302,9 @@ tcp_dnat_handler(struct sk_buff *skb, st
+ static int
+-tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
++tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
++             unsigned int tcphoff)
+ {
+-      unsigned int tcphoff;
+-
+-#ifdef CONFIG_IP_VS_IPV6
+-      if (af == AF_INET6)
+-              tcphoff = sizeof(struct ipv6hdr);
+-      else
+-#endif
+-              tcphoff = ip_hdrlen(skb);
+-
+       switch (skb->ip_summed) {
+       case CHECKSUM_NONE:
+               skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
+@@ -322,7 +315,7 @@ tcp_csum_check(int af, struct sk_buff *s
+                       if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+                                           &ipv6_hdr(skb)->daddr,
+                                           skb->len - tcphoff,
+-                                          ipv6_hdr(skb)->nexthdr,
++                                          IPPROTO_TCP,
+                                           skb->csum)) {
+                               IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+                                                "Failed checksum for");
+--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
+@@ -25,7 +25,8 @@
+ #include <net/ip6_checksum.h>
+ static int
+-udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp);
++udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
++             unsigned int udphoff);
+ static int
+ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
+@@ -155,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, st
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp))
++              if (!udp_csum_check(cp->af, skb, pp, udphoff))
+                       return 0;
+               /*
+@@ -238,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, st
+               int ret;
+               /* Some checks before mangling */
+-              if (!udp_csum_check(cp->af, skb, pp))
++              if (!udp_csum_check(cp->af, skb, pp, udphoff))
+                       return 0;
+               /*
+@@ -297,17 +298,10 @@ udp_dnat_handler(struct sk_buff *skb, st
+ static int
+-udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
++udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
++             unsigned int udphoff)
+ {
+       struct udphdr _udph, *uh;
+-      unsigned int udphoff;
+-
+-#ifdef CONFIG_IP_VS_IPV6
+-      if (af == AF_INET6)
+-              udphoff = sizeof(struct ipv6hdr);
+-      else
+-#endif
+-              udphoff = ip_hdrlen(skb);
+       uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
+       if (uh == NULL)
+@@ -325,7 +319,7 @@ udp_csum_check(int af, struct sk_buff *s
+                               if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+                                                   &ipv6_hdr(skb)->daddr,
+                                                   skb->len - udphoff,
+-                                                  ipv6_hdr(skb)->nexthdr,
++                                                  IPPROTO_UDP,
+                                                   skb->csum)) {
+                                       IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
+                                                        "Failed checksum for");
index 94338720698082a8a3ebdcf427e34e29931d67c8..5ec666068ee4a7e9645279c5b32f12da94ccef29 100644 (file)
@@ -212,3 +212,6 @@ drm-amd-display-clamp-hdmi-hdcp2-rx_id_list-read-to-buffer-size.patch
 drm-amd-display-fix-null-deref-and-buffer-over-read-in-sdp-debugfs.patch
 fs-fcntl-fix-softirq-unsafe-lock-order-in-fasync-signaling.patch
 io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch
+ipvs-skip-ipv6-extension-headers-for-csum-checks.patch
+batman-adv-stop-tp_meter-sessions-during-mesh-teardown.patch
+batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failure.patch