diff --git a/batman-adv/Makefile b/batman-adv/Makefile index 8387654d2..605b5d061 100644 --- a/batman-adv/Makefile +++ b/batman-adv/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=batman-adv PKG_VERSION:=2024.3 -PKG_RELEASE:=10 +PKG_RELEASE:=11 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://downloads.open-mesh.org/batman/releases/batman-adv-$(PKG_VERSION) diff --git a/batman-adv/patches/0016-batman-adv-fix-integer-overflow-on-buff_pos.patch b/batman-adv/patches/0016-batman-adv-fix-integer-overflow-on-buff_pos.patch new file mode 100644 index 000000000..14743d70b --- /dev/null +++ b/batman-adv/patches/0016-batman-adv-fix-integer-overflow-on-buff_pos.patch @@ -0,0 +1,26 @@ +From: Lyes Bourennani +Date: Wed, 22 Apr 2026 00:20:22 +0200 +Subject: batman-adv: fix integer overflow on buff_pos + +Fixing an integer overflow present in batadv_iv_ogm_send_to_if. The size +check is done using the int type in batadv_iv_ogm_aggr_packet whereas the +buff_pos variable uses the s16 type. This could lead to an out-of-bound +read. + +Fixes: b780db96954a ("add packet aggregation add jitter for rebroadcast of packets if aggregation is disabled") +Signed-off-by: Lyes Bourennani +Signed-off-by: Alexis Pinson +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=bacf50d6dab4d833a27b9e2603e0c51d9916665e + +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -334,7 +334,7 @@ static void batadv_iv_ogm_send_to_if(str + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + const char *fwd_str; + u8 packet_num; +- s16 buff_pos; ++ int buff_pos; + struct batadv_ogm_packet *batadv_ogm_packet; + struct sk_buff *skb; + u8 *packet_pos; diff --git a/batman-adv/patches/0017-batman-adv-reject-new-tp_meter-sessions-during-teard.patch b/batman-adv/patches/0017-batman-adv-reject-new-tp_meter-sessions-during-teard.patch new file mode 100644 index 000000000..a5489ee33 --- /dev/null +++ b/batman-adv/patches/0017-batman-adv-reject-new-tp_meter-sessions-during-teard.patch @@ -0,0 +1,68 @@ +From: Jiexun Wang +Date: Mon, 27 Apr 2026 14:43:33 +0800 +Subject: batman-adv: reject new tp_meter sessions during teardown + +Prevent tp_meter from starting new sender or receiver sessions after +mesh_state has left BATADV_MESH_ACTIVE. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Co-developed-by: Luxing Yin +Signed-off-by: Luxing Yin +Signed-off-by: Jiexun Wang +Signed-off-by: Ren Wei +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=856073145b4fc0dfa92dcc70adb1fd7b852484e8 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -947,6 +947,13 @@ void batadv_tp_start(struct batadv_priv + + /* look for an already existing test towards this node */ + spin_lock_bh(&bat_priv->tp_list_lock); ++ if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) { ++ spin_unlock_bh(&bat_priv->tp_list_lock); ++ batadv_tp_batctl_error_notify(BATADV_TP_REASON_DST_UNREACHABLE, ++ dst, bat_priv, session_cookie); ++ return; ++ } ++ + tp_vars = batadv_tp_list_find(bat_priv, dst); + if (tp_vars) { + spin_unlock_bh(&bat_priv->tp_list_lock); +@@ -1329,9 +1336,12 @@ static struct batadv_tp_vars * + batadv_tp_init_recv(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp) + { +- struct batadv_tp_vars *tp_vars; ++ struct batadv_tp_vars *tp_vars = NULL; + + spin_lock_bh(&bat_priv->tp_list_lock); ++ if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) ++ goto out_unlock; ++ + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session); + if (tp_vars) +@@ -1464,6 +1474,9 @@ void batadv_tp_meter_recv(struct batadv_ + { + struct batadv_icmp_tp_packet *icmp; + ++ if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) ++ goto out; ++ + icmp = (struct batadv_icmp_tp_packet *)skb->data; + + switch (icmp->subtype) { +@@ -1478,6 +1491,8 @@ void batadv_tp_meter_recv(struct batadv_ + "Received unknown TP Metric packet type %u\n", + icmp->subtype); + } ++ ++out: + consume_skb(skb); + } + diff --git a/batman-adv/patches/0018-batman-adv-stop-tp_meter-sessions-during-mesh-teardo.patch b/batman-adv/patches/0018-batman-adv-stop-tp_meter-sessions-during-mesh-teardo.patch new file mode 100644 index 000000000..c38483295 --- /dev/null +++ b/batman-adv/patches/0018-batman-adv-stop-tp_meter-sessions-during-mesh-teardo.patch @@ -0,0 +1,213 @@ +From: Jiexun Wang +Date: Mon, 27 Apr 2026 14:43:34 +0800 +Subject: batman-adv: stop tp_meter sessions during mesh teardown + +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: 98d7a766b645 ("batman-adv: throughput meter implementation") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Co-developed-by: Luxing Yin +Signed-off-by: Luxing Yin +Signed-off-by: Jiexun Wang +Signed-off-by: Ren Wei +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=13f49ae8972583ae9142b548a2789ade7dd1411f + +--- a/net/batman-adv/main.c ++++ b/net/batman-adv/main.c +@@ -263,6 +263,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 +@@ -365,23 +365,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 soft 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); +@@ -886,7 +901,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); + +@@ -918,7 +934,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; + } + +@@ -1024,6 +1041,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); +@@ -1126,14 +1144,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) { +@@ -1497,6 +1508,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 (timer_shutdown_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 + #include + #include ++#include + #include + #include + #include +@@ -1466,6 +1467,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/batman-adv/patches/0019-batman-adv-tp_meter-add-missing-completion-header.patch b/batman-adv/patches/0019-batman-adv-tp_meter-add-missing-completion-header.patch new file mode 100644 index 000000000..5f2e67fb6 --- /dev/null +++ b/batman-adv/patches/0019-batman-adv-tp_meter-add-missing-completion-header.patch @@ -0,0 +1,18 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 22:00:20 +0200 +Subject: batman-adv: tp_meter: add missing completion header + +Fixes: 13f49ae89725 ("batman-adv: stop tp_meter sessions during mesh teardown") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=f77931495fadb1b5a1d8f0c863153f9db7e01207 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include diff --git a/batman-adv/patches/0020-batman-adv-stop-caching-unowned-originator-pointers-.patch b/batman-adv/patches/0020-batman-adv-stop-caching-unowned-originator-pointers-.patch new file mode 100644 index 000000000..3421cb67c --- /dev/null +++ b/batman-adv/patches/0020-batman-adv-stop-caching-unowned-originator-pointers-.patch @@ -0,0 +1,129 @@ +From: Jiexun Wang +Date: Sun, 3 May 2026 12:28:58 +0800 +Subject: batman-adv: stop caching unowned originator pointers in BAT IV + +BAT IV keeps the last-hop neighbor address in each neigh_node, but some +paths also cache an originator pointer derived from a temporary lookup. +That pointer is not owned by the neigh_node and may no longer refer to a +live originator entry after purge handling runs. + +Stop storing the auxiliary originator pointer in the BAT IV neighbor +state. When BAT IV needs the neighbor originator data, resolve it from +the stored neighbor address and drop the reference again after use. + +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Jiexun Wang +Signed-off-by: Ren Wei +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=fa15d5b68da1a96c6baf846afd01390e6c217328 + +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -172,19 +172,12 @@ free_orig_node_hash: + static struct batadv_neigh_node * + batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, + const u8 *neigh_addr, +- struct batadv_orig_node *orig_node, +- struct batadv_orig_node *orig_neigh) ++ struct batadv_orig_node *orig_node) + { + struct batadv_neigh_node *neigh_node; + + neigh_node = batadv_neigh_node_get_or_create(orig_node, + hard_iface, neigh_addr); +- if (!neigh_node) +- goto out; +- +- neigh_node->orig_node = orig_neigh; +- +-out: + return neigh_node; + } + +@@ -901,6 +894,31 @@ static u8 batadv_iv_orig_ifinfo_sum(stru + } + + /** ++ * batadv_iv_ogm_neigh_ifinfo_sum() - Get bcast_own sum for a last-hop neighbor ++ * @bat_priv: the bat priv with all the mesh interface information ++ * @neigh_node: last-hop neighbor of an originator ++ * ++ * Return: Number of replied (rebroadcasted) OGMs for the originator currently ++ * announced by the neighbor. Returns 0 if the neighbor's originator entry is ++ * not available anymore. ++ */ ++static u8 batadv_iv_ogm_neigh_ifinfo_sum(struct batadv_priv *bat_priv, ++ const struct batadv_neigh_node *neigh_node) ++{ ++ struct batadv_orig_node *orig_neigh; ++ u8 sum; ++ ++ orig_neigh = batadv_orig_hash_find(bat_priv, neigh_node->addr); ++ if (!orig_neigh) ++ return 0; ++ ++ sum = batadv_iv_orig_ifinfo_sum(orig_neigh, neigh_node->if_incoming); ++ batadv_orig_node_put(orig_neigh); ++ ++ return sum; ++} ++ ++/** + * batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an + * originator + * @bat_priv: the bat priv with all the soft interface information +@@ -969,17 +987,9 @@ batadv_iv_ogm_orig_update(struct batadv_ + } + + if (!neigh_node) { +- struct batadv_orig_node *orig_tmp; +- +- orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source); +- if (!orig_tmp) +- goto unlock; +- + neigh_node = batadv_iv_ogm_neigh_new(if_incoming, + ethhdr->h_source, +- orig_node, orig_tmp); +- +- batadv_orig_node_put(orig_tmp); ++ orig_node); + if (!neigh_node) + goto unlock; + } else { +@@ -1031,10 +1041,9 @@ batadv_iv_ogm_orig_update(struct batadv_ + */ + if (router_ifinfo && + neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg) { +- sum_orig = batadv_iv_orig_ifinfo_sum(router->orig_node, +- router->if_incoming); +- sum_neigh = batadv_iv_orig_ifinfo_sum(neigh_node->orig_node, +- neigh_node->if_incoming); ++ sum_orig = batadv_iv_ogm_neigh_ifinfo_sum(bat_priv, router); ++ sum_neigh = batadv_iv_ogm_neigh_ifinfo_sum(bat_priv, ++ neigh_node); + if (sum_orig >= sum_neigh) + goto out; + } +@@ -1100,7 +1109,6 @@ static bool batadv_iv_ogm_calc_tq(struct + if (!neigh_node) + neigh_node = batadv_iv_ogm_neigh_new(if_incoming, + orig_neigh_node->orig, +- orig_neigh_node, + orig_neigh_node); + + if (!neigh_node) +@@ -1366,8 +1374,8 @@ batadv_iv_ogm_process_per_outif(const st + + router = batadv_orig_router_get(orig_node, if_outgoing); + if (router) { +- router_router = batadv_orig_router_get(router->orig_node, +- if_outgoing); ++ router_router = batadv_orig_to_router(bat_priv, router->addr, ++ if_outgoing); + router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); + } + diff --git a/batman-adv/patches/0021-batman-adv-iv-avoid-bonding-logic-for-outgoing-OGM.patch b/batman-adv/patches/0021-batman-adv-iv-avoid-bonding-logic-for-outgoing-OGM.patch new file mode 100644 index 000000000..f99aefc69 --- /dev/null +++ b/batman-adv/patches/0021-batman-adv-iv-avoid-bonding-logic-for-outgoing-OGM.patch @@ -0,0 +1,64 @@ +From: Sven Eckelmann +Date: Wed, 6 May 2026 22:20:48 +0200 +Subject: batman-adv: iv: avoid bonding logic for outgoing OGM + +The batadv_orig_to_router() function will invoke batadv_find_router() to +get the neighbor node (router) for the specified interface. But +batadv_find_router() also is responsible for the bonding code when the +default (NULL) interface is specified. This is rather unexpected for the +OGM code. + +Instead provide a simpler function which never tries to interact with the +bonding candidates and only returns the router on a specific interface. + +Fixes: fa15d5b68da1 ("batman-adv: stop caching unowned originator pointers in BAT IV") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=b33ac3beccd6864c32d9de7efaf2d1f96a546283 + +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -1305,6 +1305,32 @@ out: + } + + /** ++ * batadv_orig_to_direct_router() - get direct next hop neighbor to an orig address ++ * @bat_priv: the bat priv with all the mesh interface information ++ * @orig_addr: the originator MAC address to search the best next hop router for ++ * @if_outgoing: the interface where the OGM should be sent to ++ * ++ * Return: A neighbor node which is the best router towards the given originator ++ * address. Bonding candidates are ignored. ++ */ ++static struct batadv_neigh_node * ++batadv_orig_to_direct_router(struct batadv_priv *bat_priv, u8 *orig_addr, ++ struct batadv_hard_iface *if_outgoing) ++{ ++ struct batadv_neigh_node *neigh_node; ++ struct batadv_orig_node *orig_node; ++ ++ orig_node = batadv_orig_hash_find(bat_priv, orig_addr); ++ if (!orig_node) ++ return NULL; ++ ++ neigh_node = batadv_orig_router_get(orig_node, if_outgoing); ++ batadv_orig_node_put(orig_node); ++ ++ return neigh_node; ++} ++ ++/** + * batadv_iv_ogm_process_per_outif() - process a batman iv OGM for an outgoing + * interface + * @skb: the skb containing the OGM +@@ -1374,8 +1400,9 @@ batadv_iv_ogm_process_per_outif(const st + + router = batadv_orig_router_get(orig_node, if_outgoing); + if (router) { +- router_router = batadv_orig_to_router(bat_priv, router->addr, +- if_outgoing); ++ router_router = batadv_orig_to_direct_router(bat_priv, ++ router->addr, ++ if_outgoing); + router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); + } + diff --git a/batman-adv/patches/0022-batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failu.patch b/batman-adv/patches/0022-batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failu.patch new file mode 100644 index 000000000..75ad55116 --- /dev/null +++ b/batman-adv/patches/0022-batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failu.patch @@ -0,0 +1,41 @@ +From: Sven Eckelmann +Date: Wed, 6 May 2026 22:20:49 +0200 +Subject: batman-adv: tp_meter: fix tp_num leak on kmalloc failure + +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 + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=25e3d52ef55f63c973ef1d018b6ac03b5f33a9bd + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -994,6 +994,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", +@@ -1366,8 +1367,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/batman-adv/patches/0023-batman-adv-bla-prevent-use-after-free-when-deleting-.patch b/batman-adv/patches/0023-batman-adv-bla-prevent-use-after-free-when-deleting-.patch new file mode 100644 index 000000000..187d1ca5f --- /dev/null +++ b/batman-adv/patches/0023-batman-adv-bla-prevent-use-after-free-when-deleting-.patch @@ -0,0 +1,29 @@ +From: Sven Eckelmann +Date: Wed, 6 May 2026 22:20:50 +0200 +Subject: batman-adv: bla: prevent use-after-free when deleting claims + +When batadv_bla_del_backbone_claims() removes all claims for a backbone, it +does this by dropping the link entry in the hash list. This list entry +itself was one of the references which need to be dropped at the same time +via batadv_claim_put(). + +But the batadv_claim_put() must not be done before the last access to the +claim object in this function. Otherwise the claim might be freed already +by the batadv_claim_release() function before the list entry was dropped. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=d9480770f089d4427ac66a1f675d4c77cce66a53 + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -318,8 +318,8 @@ batadv_bla_del_backbone_claims(struct ba + if (claim->backbone_gw != backbone_gw) + continue; + +- batadv_claim_put(claim); + hlist_del_rcu(&claim->hash_entry); ++ batadv_claim_put(claim); + } + spin_unlock_bh(list_lock); + } diff --git a/batman-adv/patches/0024-batman-adv-bla-only-purge-non-released-claims.patch b/batman-adv/patches/0024-batman-adv-bla-only-purge-non-released-claims.patch new file mode 100644 index 000000000..ebd41a69e --- /dev/null +++ b/batman-adv/patches/0024-batman-adv-bla-only-purge-non-released-claims.patch @@ -0,0 +1,43 @@ +From: Sven Eckelmann +Date: Wed, 6 May 2026 22:20:51 +0200 +Subject: batman-adv: bla: only purge non-released claims + +When batadv_bla_purge_claims() goes through the list of claims, it is only +traversing the hash list with an rcu_read_lock(). Due to a potential +parallel batadv_claim_put(), it can happen that it encounters a claim which +was actually in the process of being released+freed by +batadv_claim_release(). In this case, backbone_gw is set to NULL before the +delayed RCU kfree is started. Calling batadv_bla_claim_get_backbone_gw() is +then no longer allowed because it would cause a NULL-ptr derefence. + +To avoid this, only claims with a valid reference counter must be purged. +All others are already taken care of. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=1526fecbf97673335f25e5d48bd0d3217f60e6ec + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -1288,6 +1288,13 @@ static void batadv_bla_purge_claims(stru + + rcu_read_lock(); + hlist_for_each_entry_rcu(claim, head, hash_entry) { ++ /* only purge claims not currently in the process of being released. ++ * Such claims could otherwise have a NULL-ptr backbone_gw set because ++ * they already went through batadv_claim_release() ++ */ ++ if (!kref_get_unless_zero(&claim->refcount)) ++ continue; ++ + backbone_gw = batadv_bla_claim_get_backbone_gw(claim); + if (now) + goto purge_now; +@@ -1313,6 +1320,7 @@ purge_now: + claim->addr, claim->vid); + skip: + batadv_backbone_gw_put(backbone_gw); ++ batadv_claim_put(claim); + } + rcu_read_unlock(); + } diff --git a/batman-adv/patches/0025-batman-adv-bla-put-backbone-reference-on-failed-clai.patch b/batman-adv/patches/0025-batman-adv-bla-put-backbone-reference-on-failed-clai.patch new file mode 100644 index 000000000..722aa4823 --- /dev/null +++ b/batman-adv/patches/0025-batman-adv-bla-put-backbone-reference-on-failed-clai.patch @@ -0,0 +1,23 @@ +From: Sven Eckelmann +Date: Wed, 6 May 2026 22:20:52 +0200 +Subject: batman-adv: bla: put backbone reference on failed claim hash insert + +When batadv_bla_add_claim() fails to insert a new claim into the hash, it +leaked a reference to the backbone_gw for which the claim was intended. +Call batadv_backbone_gw_put() on the error path to release the reference +and avoid leaking the backbone_gw object. + +Fixes: e401297e3a39 ("batman-adv: Fix non-atomic bla_claim::backbone_gw access") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=05abe1217c5104b3514ef1945dacf511b012b921 + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -723,6 +723,7 @@ static void batadv_bla_add_claim(struct + + if (unlikely(hash_added != 0)) { + /* only local changes happened. */ ++ batadv_backbone_gw_put(backbone_gw); + kfree(claim); + return; + } diff --git a/batman-adv/patches/0026-batman-adv-tt-reject-oversized-local-TVLV-buffers.patch b/batman-adv/patches/0026-batman-adv-tt-reject-oversized-local-TVLV-buffers.patch new file mode 100644 index 000000000..1dae67475 --- /dev/null +++ b/batman-adv/patches/0026-batman-adv-tt-reject-oversized-local-TVLV-buffers.patch @@ -0,0 +1,47 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 19:08:37 +0200 +Subject: batman-adv: tt: reject oversized local TVLV buffers + +The commit 6043a632dd06 ("batman-adv: reject oversized global TT response +buffers") added a check to ensure that a global return buffer size can be +stored in an u16. The same buffer handling also exists for the local data +buffer but was not touched. + +A similar check should be also be in place for the local TVLV buffer. It +doesn't have the similar attack surface because it is only generated from +locally discovered MAC addresses but the dynamic nature could still cause +temporarily to large buffers. + +Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=76fde6c801cc7c6f6edc369503cf32c0c18dfc1e + +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -924,12 +924,12 @@ batadv_tt_prepare_tvlv_local_data(struct + { + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_vlan *vlan; ++ size_t change_offset; + u16 num_vlan = 0; + u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; +- int change_offset; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { +@@ -948,8 +948,10 @@ batadv_tt_prepare_tvlv_local_data(struct + if (*tt_len < 0) + *tt_len = batadv_tt_len(total_entries); + +- tvlv_len = *tt_len; +- tvlv_len += change_offset; ++ if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { ++ tvlv_len = 0; ++ goto out; ++ } + + *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); + if (!*tt_data) { diff --git a/batman-adv/patches/0027-batman-adv-tt-fix-negative-tt_buff_len.patch b/batman-adv/patches/0027-batman-adv-tt-fix-negative-tt_buff_len.patch new file mode 100644 index 000000000..f48281716 --- /dev/null +++ b/batman-adv/patches/0027-batman-adv-tt-fix-negative-tt_buff_len.patch @@ -0,0 +1,32 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 19:53:21 +0200 +Subject: batman-adv: tt: fix negative tt_buff_len + +batadv_orig_node::tt_buff_len was declared as s16, but the field is never +intended to hold a negative value. When a value greater than 32767 is +assigned, it wraps to a negative signed integer. + +In batadv_send_other_tt_response(), tt_buff_len is temporarily widened to +s32. The incorrectly negative s16 value propagates into the s32, causing +batadv_tt_prepare_tvlv_global_data() to allocate a full sized buffer but +populates only a small portion of it with the collected changeset. All +remaining bits are kept uninitialized. + +Using an u16 avoids this type confusion and ensures that no (negative) sign +extension is performed in batadv_send_other_tt_response(). + +Fixes: cea194d90b11 ("batman-adv: improved client announcement mechanism") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=526de44bf330b97a4acf15cefc68938aab04b5d0 + +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -446,7 +446,7 @@ struct batadv_orig_node { + * @tt_buff_len: length of the last tt changeset this node received + * from the orig node + */ +- s16 tt_buff_len; ++ u16 tt_buff_len; + + /** @tt_buff_lock: lock that protects tt_buff and tt_buff_len */ + spinlock_t tt_buff_lock; diff --git a/batman-adv/patches/0028-batman-adv-tt-fix-negative-last_changeset_len.patch b/batman-adv/patches/0028-batman-adv-tt-fix-negative-last_changeset_len.patch new file mode 100644 index 000000000..f0f992251 --- /dev/null +++ b/batman-adv/patches/0028-batman-adv-tt-fix-negative-last_changeset_len.patch @@ -0,0 +1,32 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 19:53:21 +0200 +Subject: batman-adv: tt: fix negative last_changeset_len + +batadv_piv_tt::last_changeset_len len was declared as s16, but the field is +never intended to hold a negative value. When a value greater than 32767 is +assigned, it wraps to a negative signed integer. + +In batadv_send_my_tt_response(), last_changeset_len is temporarily widened +to s32. The incorrectly negative s16 value propagates into the s32, causing +batadv_tt_prepare_tvlv_local_data() to allocate a full sized buffer but +populates only a small portion of it with the collected changeset. All +remaining bits are kept uninitialized. + +Using an u16 avoids this type confusion and ensures that no (negative) sign +extension is performed in batadv_send_my_tt_response(). + +Fixes: cea194d90b11 ("batman-adv: improved client announcement mechanism") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=e818e87e81417a2d73fdbe6cfc70ffb9527662d1 + +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1058,7 +1058,7 @@ struct batadv_priv_tt { + * @last_changeset_len: length of last tt changeset this host has + * generated + */ +- s16 last_changeset_len; ++ u16 last_changeset_len; + + /** + * @last_changeset_lock: lock protecting last_changeset & diff --git a/batman-adv/patches/0029-batman-adv-tt-fix-TOCTOU-race-for-reported-vlans.patch b/batman-adv/patches/0029-batman-adv-tt-fix-TOCTOU-race-for-reported-vlans.patch new file mode 100644 index 000000000..f4e273c45 --- /dev/null +++ b/batman-adv/patches/0029-batman-adv-tt-fix-TOCTOU-race-for-reported-vlans.patch @@ -0,0 +1,63 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 19:47:11 +0200 +Subject: batman-adv: tt: fix TOCTOU race for reported vlans + +The local TT based TVLV is generated by first checking the number of VLANs +which have at least one TT entry. A new buffer with the correct size for +the VLANs is then allocated. Only then, the list of VLANs s used to fill +the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock +is held. But the actual number of TT entries of each VLAN can still +increase during this time - just not the number of VLANs in the list. + +But the prefilter used in the buffer size calculation might still cause an +increase of the number of VLANs which need to be stored. Simply because a +VLAN might now suddenly have at least one entry when it had none in the +pre-alloc check - and then needs to occupy space which was not allocated. + +It is better to overestimate the buffer size at the beginning and then fill +the buffer only with the VLANs which are not empty. + +Fixes: e4687b4be274 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=e7a231fb7392883a00b7a19b923bdcc54e68720f + +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -934,11 +934,8 @@ batadv_tt_prepare_tvlv_local_data(struct + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- if (vlan_entries < 1) +- continue; +- +- num_vlan++; + total_entries += vlan_entries; ++ num_vlan++; + } + + change_offset = sizeof(**tt_data); +@@ -964,6 +961,7 @@ batadv_tt_prepare_tvlv_local_data(struct + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) +@@ -974,8 +972,16 @@ batadv_tt_prepare_tvlv_local_data(struct + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_vlan); ++ tvlv_len = *tt_len + change_offset; ++ ++ (*tt_data)->num_vlan = htons(num_vlan); ++ + tt_change_ptr = (u8 *)*tt_data + change_offset; + *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr; + diff --git a/batman-adv/patches/0030-batman-adv-tt-avoid-empty-VLAN-responses.patch b/batman-adv/patches/0030-batman-adv-tt-avoid-empty-VLAN-responses.patch new file mode 100644 index 000000000..7664d9981 --- /dev/null +++ b/batman-adv/patches/0030-batman-adv-tt-avoid-empty-VLAN-responses.patch @@ -0,0 +1,76 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 20:47:34 +0200 +Subject: batman-adv: tt: avoid empty VLAN responses + +The commit e4687b4be274 ("batman-adv: prevent TT request storms by not +sending inconsistent TT TLVLs") added checks to the local (direct) TT +response code. But the response can also be done indirectly by another node +using the global TT state. To avoid such inconsistency states reported in +the original fix, also avoid sending empty VLANs for replies from the +global TT state. + +Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=491a7535d088c1df81f97da75961942f9e1cb0b0 + +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -843,17 +843,19 @@ batadv_tt_prepare_tvlv_global_data(struc + s32 *tt_len) + { + u16 num_vlan = 0; +- u16 num_entries = 0; + u16 tvlv_len = 0; + unsigned int change_offset; + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_orig_node_vlan *vlan; ++ u16 total_entries = 0; + u8 *tt_change_ptr; ++ int vlan_entries; + + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { ++ vlan_entries = atomic_read(&vlan->tt.num_entries); ++ total_entries += vlan_entries; + num_vlan++; +- num_entries += atomic_read(&vlan->tt.num_entries); + } + + change_offset = sizeof(**tt_data); +@@ -861,7 +863,7 @@ batadv_tt_prepare_tvlv_global_data(struc + + /* if tt_len is negative, allocate the space needed by the full table */ + if (*tt_len < 0) +- *tt_len = batadv_tt_len(num_entries); ++ *tt_len = batadv_tt_len(total_entries); + + if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) { + *tt_len = 0; +@@ -882,14 +884,27 @@ batadv_tt_prepare_tvlv_global_data(struc + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); ++ num_vlan = 0; + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { ++ vlan_entries = atomic_read(&vlan->tt.num_entries); ++ if (vlan_entries < 1) ++ continue; ++ + tt_vlan->vid = htons(vlan->vid); + tt_vlan->crc = htonl(vlan->tt.crc); + tt_vlan->reserved = 0; + + tt_vlan++; ++ num_vlan++; + } + ++ /* recalculate in case number of VLANs reduced */ ++ change_offset = sizeof(**tt_data); ++ change_offset += num_vlan * sizeof(*tt_vlan); ++ tvlv_len = *tt_len + change_offset; ++ ++ (*tt_data)->num_vlan = htons(num_vlan); ++ + tt_change_ptr = (u8 *)*tt_data + change_offset; + *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr; + diff --git a/batman-adv/patches/0031-batman-adv-tt-prevent-TVLV-entry-number-overflow.patch b/batman-adv/patches/0031-batman-adv-tt-prevent-TVLV-entry-number-overflow.patch new file mode 100644 index 000000000..ba7e68e34 --- /dev/null +++ b/batman-adv/patches/0031-batman-adv-tt-prevent-TVLV-entry-number-overflow.patch @@ -0,0 +1,66 @@ +From: Sven Eckelmann +Date: Sat, 2 May 2026 21:25:19 +0200 +Subject: batman-adv: tt: prevent TVLV entry number overflow + +The helpers to prepare the buffers for the local and global TT based +replies are trying to sum up all TT entries which can be found for each +VLAN. In theory, this sum can be too big for an u16 and therefore overflow. +A too small buffer would then be allocated for the TVLV. + +The too small buffer will be handled gracefully by +batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a +truncated reply. But this overflow shouldn't have happened in the first and +the too small buffer should never have been allocated when an overflow was +detected. + +Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=be9801e6f1773f5c68760412aa8a1b6fa18d3294 + +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -850,11 +850,18 @@ batadv_tt_prepare_tvlv_global_data(struc + u16 total_entries = 0; + u8 *tt_change_ptr; + int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ *tt_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + +@@ -941,15 +948,22 @@ batadv_tt_prepare_tvlv_local_data(struct + struct batadv_softif_vlan *vlan; + size_t change_offset; + u16 num_vlan = 0; +- u16 vlan_entries = 0; + u16 total_entries = 0; + u16 tvlv_len; + u8 *tt_change_ptr; ++ int vlan_entries; ++ u16 sum_entries; + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); +- total_entries += vlan_entries; ++ ++ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { ++ tvlv_len = 0; ++ goto out; ++ } ++ ++ total_entries = sum_entries; + num_vlan++; + } + diff --git a/batman-adv/patches/0032-batman-adv-tp_meter-fix-tp_vars-reference-leak-in-re.patch b/batman-adv/patches/0032-batman-adv-tp_meter-fix-tp_vars-reference-leak-in-re.patch new file mode 100644 index 000000000..216dc2f64 --- /dev/null +++ b/batman-adv/patches/0032-batman-adv-tp_meter-fix-tp_vars-reference-leak-in-re.patch @@ -0,0 +1,81 @@ +From: Sven Eckelmann +Date: Sun, 10 May 2026 10:57:29 +0200 +Subject: batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown + +The receiver shutdown timer handler, batadv_tp_receiver_shutdown(), is +responsible for releasing the tp_vars reference it holds. However, the +existing logic for coordinating this release with batadv_tp_stop_all() was +flawed. + +timer_shutdown_sync() guarantees the timer will not fire again after it +returns, but it returns non-zero only when the timer was pending at the +time of the call. If the timer had already expired (and +batadv_tp_stop_all() would unsucessfully try to rearm itself), +batadv_tp_stop_all() skips its batadv_tp_vars_put(), and +batadv_tp_receiver_shutdown() fails to put its own reference as well. + +Fix this by introducing a new atomic variable receiving that is set to 1 +when the receiver is initialized and cleared atomically with atomic_xchg() +by whichever side claims it first. Only the side that observes the +transition from 1 to 0 is responsible for releasing the tp_vars timer +reference, eliminating the uncertainty. + +Fixes: 13f49ae89725 ("batman-adv: stop tp_meter sessions during mesh teardown") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=dae7cff0e6c791627a09c13b3f495fa1fe04bc1d + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -1156,6 +1157,9 @@ static void batadv_tp_receiver_shutdown( + spin_unlock_bh(&tp_vars->unacked_lock); + + /* drop reference of timer */ ++ if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1)) ++ return; ++ + batadv_tp_vars_put(tp_vars); + } + +@@ -1374,6 +1378,7 @@ batadv_tp_init_recv(struct batadv_priv * + + ether_addr_copy(tp_vars->other_end, icmp->orig); + tp_vars->role = BATADV_TP_RECEIVER; ++ atomic_set(&tp_vars->receiving, 1); + memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session)); + tp_vars->last_recv = BATADV_TP_FIRST_SEQ; + tp_vars->bat_priv = bat_priv; +@@ -1546,8 +1551,12 @@ void batadv_tp_stop_all(struct batadv_pr + break; + case BATADV_TP_RECEIVER: + batadv_tp_list_detach(tp_var); +- if (timer_shutdown_sync(&tp_var->timer)) +- batadv_tp_vars_put(tp_var); ++ timer_shutdown_sync(&tp_var->timer); ++ ++ if (atomic_xchg(&tp_var->receiving, 0) != 1) ++ break; ++ ++ batadv_tp_vars_put(tp_var); + break; + } + +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1461,6 +1461,9 @@ struct batadv_tp_vars { + /** @sending: sending binary semaphore: 1 if sending, 0 is not */ + atomic_t sending; + ++ /** @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; + diff --git a/batman-adv/patches/0033-batman-adv-fix-tp_meter-counter-underflow-during-shu.patch b/batman-adv/patches/0033-batman-adv-fix-tp_meter-counter-underflow-during-shu.patch new file mode 100644 index 000000000..55e30d1b2 --- /dev/null +++ b/batman-adv/patches/0033-batman-adv-fix-tp_meter-counter-underflow-during-shu.patch @@ -0,0 +1,48 @@ +From: Luxiao Xu +Date: Mon, 11 May 2026 18:52:09 +0200 +Subject: batman-adv: fix tp_meter counter underflow during shutdown + +batadv_tp_sender_shutdown() unconditionally decrements the "sending" +atomic counter. If multiple paths (e.g. timeout, user cancel, and +normal finish) call this function, the counter can underflow to -1. + +Since the sender logic treats any non-zero value as "still sending", +a negative value causes the sender kthread to loop indefinitely. +This leads to a use-after-free when the interface is removed while +the zombie thread is still active. + +Fix this by using atomic_xchg() to ensure the counter only transitions +from 1 to 0 once. + +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Cc: stable@kernel.org +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Luxiao Xu +Signed-off-by: Ren Wei +[sven: added missing change in batadv_tp_send] +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=57c6af492c1948145db835bb3ea2980472558298 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -452,7 +452,7 @@ 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_dec_and_test(&tp_vars->sending)) ++ if (atomic_xchg(&tp_vars->sending, 0) != 1) + return; + + tp_vars->reason = reason; +@@ -886,7 +886,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_dec_and_test(&tp_vars->sending)) ++ if (atomic_xchg(&tp_vars->sending, 0) == 1) + tp_vars->reason = err; + break; + } diff --git a/batman-adv/patches/0034-batman-adv-fix-fragment-reassembly-length-accounting.patch b/batman-adv/patches/0034-batman-adv-fix-fragment-reassembly-length-accounting.patch new file mode 100644 index 000000000..5f82547f8 --- /dev/null +++ b/batman-adv/patches/0034-batman-adv-fix-fragment-reassembly-length-accounting.patch @@ -0,0 +1,122 @@ +From: Ruide Cao +Date: Wed, 13 May 2026 11:58:15 +0800 +Subject: batman-adv: fix fragment reassembly length accounting + +batman-adv keeps a running payload length for queued fragments and uses it +to validate a fragment chain before reassembly. + +That accounting currently allows the accumulated fragment length to be +truncated during updates. As a result, malformed fragment chains can +bypass the intended validation and drive reassembly with inconsistent +length state, leading to a local denial of service. + +Fix the accounting by storing the accumulated length in a length-typed +field and rejecting update overflows before the existing validation logic +runs. + +The fix was verified against the original reproducer and against valid +fragment reassembly paths. + +Fixes: 9b3eab61754d ("batman-adv: Receive fragmented packets and merge") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Ruide Cao +Tested-by: Ren Wei +Signed-off-by: Ren Wei +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=dfd0d5b673a91a0606b8269b3b861c59552835b3 + +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -80,9 +81,9 @@ void batadv_frag_purge_orig(struct batad + * + * Return: the maximum size of payload that can be fragmented. + */ +-static int batadv_frag_size_limit(void) ++static size_t batadv_frag_size_limit(void) + { +- int limit = BATADV_FRAG_MAX_FRAG_SIZE; ++ size_t limit = BATADV_FRAG_MAX_FRAG_SIZE; + + limit -= sizeof(struct batadv_frag_packet); + limit *= BATADV_FRAG_MAX_FRAGMENTS; +@@ -143,7 +144,9 @@ static bool batadv_frag_insert_packet(st + struct batadv_frag_packet *frag_packet; + u8 bucket; + u16 seqno, hdr_size = sizeof(struct batadv_frag_packet); ++ bool overflow = false; + bool ret = false; ++ size_t data_len; + + /* Linearize packet to avoid linearizing 16 packets in a row when doing + * the later merge. Non-linear merge should be added to remove this +@@ -153,6 +156,7 @@ static bool batadv_frag_insert_packet(st + goto err; + + frag_packet = (struct batadv_frag_packet *)skb->data; ++ data_len = skb->len - hdr_size; + seqno = ntohs(frag_packet->seqno); + bucket = seqno % BATADV_FRAG_BUFFER_COUNT; + +@@ -171,7 +175,7 @@ static bool batadv_frag_insert_packet(st + spin_lock_bh(&chain->lock); + if (batadv_frag_init_chain(chain, seqno)) { + hlist_add_head(&frag_entry_new->list, &chain->fragment_list); +- chain->size = skb->len - hdr_size; ++ chain->size = data_len; + chain->timestamp = jiffies; + chain->total_size = ntohs(frag_packet->total_size); + ret = true; +@@ -188,7 +192,11 @@ static bool batadv_frag_insert_packet(st + if (frag_entry_curr->no < frag_entry_new->no) { + hlist_add_before(&frag_entry_new->list, + &frag_entry_curr->list); +- chain->size += skb->len - hdr_size; ++ ++ if (check_add_overflow(chain->size, data_len, ++ &chain->size)) ++ overflow = true; ++ + chain->timestamp = jiffies; + ret = true; + goto out; +@@ -201,13 +209,16 @@ static bool batadv_frag_insert_packet(st + /* Reached the end of the list, so insert after 'frag_entry_last'. */ + if (likely(frag_entry_last)) { + hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list); +- chain->size += skb->len - hdr_size; ++ ++ if (check_add_overflow(chain->size, data_len, &chain->size)) ++ overflow = true; ++ + chain->timestamp = jiffies; + ret = true; + } + + out: +- if (chain->size > batadv_frag_size_limit() || ++ if (overflow || chain->size > batadv_frag_size_limit() || + chain->total_size != ntohs(frag_packet->total_size) || + chain->total_size > batadv_frag_size_limit()) { + /* Clear chain if total size of either the list or the packet +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -295,7 +295,7 @@ struct batadv_frag_table_entry { + u16 seqno; + + /** @size: accumulated size of packets in list */ +- u16 size; ++ size_t size; + + /** @total_size: expected size of the assembled packet */ + u16 total_size; diff --git a/batman-adv/patches/0035-batman-adv-clear-current-gateway-during-teardown.patch b/batman-adv/patches/0035-batman-adv-clear-current-gateway-during-teardown.patch new file mode 100644 index 000000000..70ccc30c0 --- /dev/null +++ b/batman-adv/patches/0035-batman-adv-clear-current-gateway-during-teardown.patch @@ -0,0 +1,39 @@ +From: Ruijie Li +Date: Thu, 14 May 2026 16:13:25 +0800 +Subject: batman-adv: clear current gateway during teardown + +batadv_gw_node_free() removes the gateway list entries during mesh teardown, +but it does not clear the currently selected gateway. This leaves stale +gateway state behind across cleanup and can break a later mesh recreation. + +Clear bat_priv->gw.curr_gw before walking the gateway list so the selected +gateway reference is dropped as part of teardown. + +Fixes: 6a17ecc4603b ("batman-adv: gateway election code refactoring") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Ruijie Li +Signed-off-by: Zhanpeng Li +Signed-off-by: Ren Wei +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=5e1068c577818529e2f7a7f5ccb9fe4a440198c7 + +--- a/net/batman-adv/gateway_client.c ++++ b/net/batman-adv/gateway_client.c +@@ -479,10 +479,14 @@ void batadv_gw_node_delete(struct batadv + */ + void batadv_gw_node_free(struct batadv_priv *bat_priv) + { ++ struct batadv_gw_node *curr_gw; + struct batadv_gw_node *gw_node; + struct hlist_node *node_tmp; + + spin_lock_bh(&bat_priv->gw.list_lock); ++ curr_gw = rcu_replace_pointer(bat_priv->gw.curr_gw, NULL, true); ++ batadv_gw_node_put(curr_gw); ++ + hlist_for_each_entry_safe(gw_node, node_tmp, + &bat_priv->gw.gateway_list, list) { + hlist_del_init_rcu(&gw_node->list); diff --git a/batman-adv/patches/0036-batman-adv-dat-handle-forward-allocation-error.patch b/batman-adv/patches/0036-batman-adv-dat-handle-forward-allocation-error.patch new file mode 100644 index 000000000..4383fd2e1 --- /dev/null +++ b/batman-adv/patches/0036-batman-adv-dat-handle-forward-allocation-error.patch @@ -0,0 +1,33 @@ +From: Sven Eckelmann +Date: Wed, 13 May 2026 09:01:34 +0200 +Subject: batman-adv: dat: handle forward allocation error + +batadv_dat_forward_data() calls pskb_copy_for_clone() to duplicate an skb +for each DHT candidate, but does not check the return value before passing +it to batadv_send_skb_prepare_unicast_4addr(). That function dereferences +the skb unconditionally, so a failed allocation triggers a NULL pointer +dereference. + +Skip forwarding to the current DHT candidate on allocation failure. + +Fixes: 34b3c3850e7d ("batman-adv: Distributed ARP Table - create DHT helper functions") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=a6f1006bc82749eeb40a4c1c4380a18c61db24f3 + +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -698,6 +698,9 @@ static bool batadv_dat_forward_data(stru + goto free_orig; + + tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC); ++ if (!tmp_skb) ++ goto free_neigh; ++ + if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb, + cand[i].orig_node, + packet_subtype)) { diff --git a/batman-adv/patches/0037-batman-adv-tp_meter-avoid-use-of-uninit-sender-vars.patch b/batman-adv/patches/0037-batman-adv-tp_meter-avoid-use-of-uninit-sender-vars.patch new file mode 100644 index 000000000..6c615253b --- /dev/null +++ b/batman-adv/patches/0037-batman-adv-tp_meter-avoid-use-of-uninit-sender-vars.patch @@ -0,0 +1,56 @@ +From: Sven Eckelmann +Date: Wed, 13 May 2026 09:01:35 +0200 +Subject: batman-adv: tp_meter: avoid use of uninit sender vars + +batadv_tp_recv_ack() and batadv_tp_stop() are only valid for tp_vars in the +BATADV_TP_SENDER role. When called with a BATADV_TP_RECEIVER role, it +proceeds to read sender-only members that were never initialized, leading +to undefined behavior. + +This can be triggered when a node that is currently acting as a receiver in +an ongoing tp_meter session receives a malicious ACK packet. + +Guard against this by checking tp_vars->role immediately after the +lookup and bailing out if it is not BATADV_TP_SENDER, before any of +those members are accessed. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=c5a805c4d536b0875f12555f8772b2ce1210ff43 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -664,6 +664,9 @@ static void batadv_tp_recv_ack(struct ba + if (unlikely(!tp_vars)) + return; + ++ if (unlikely(tp_vars->role != BATADV_TP_SENDER)) ++ goto out; ++ + if (unlikely(atomic_read(&tp_vars->sending) == 0)) + goto out; + +@@ -1101,12 +1104,16 @@ void batadv_tp_stop(struct batadv_priv * + if (!tp_vars) { + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: trying to interrupt an already over connection\n"); +- goto out; ++ 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: ++out_put_orig_node: + batadv_orig_node_put(orig_node); + } + diff --git a/batman-adv/patches/0038-batman-adv-frag-disallow-unicast-fragment-in-fragmen.patch b/batman-adv/patches/0038-batman-adv-frag-disallow-unicast-fragment-in-fragmen.patch new file mode 100644 index 000000000..6315489b8 --- /dev/null +++ b/batman-adv/patches/0038-batman-adv-frag-disallow-unicast-fragment-in-fragmen.patch @@ -0,0 +1,77 @@ +From: Sven Eckelmann +Date: Wed, 13 May 2026 09:01:36 +0200 +Subject: batman-adv: frag: disallow unicast fragment in fragment + +batadv_frag_skb_buffer() is called by batadv_batman_skb_recv() when a +BATADV_UNICAST_FRAG packet is received. Once all fragments are collected +and the packet is reassembled, batadv_recv_frag_packet() calls +batadv_batman_skb_recv() again to process the defragmented payload. + +A malicious sender can craft a BATADV_UNICAST_FRAG packet whose reassembled +payload is itself a BATADV_UNICAST_FRAG packet (matryoshka-style nesting). +Each nesting level recurses through batadv_batman_skb_recv() without bound, +growing the kernel stack until it is exhausted. + +Since refragmentation or fragments in fragments are not actually allowed, +discard all packets which are still BATADV_UNICAST_FRAG packets after the +defragmentation process. + +Fixes: 9b3eab61754d ("batman-adv: Receive fragmented packets and merge") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=b4e35f8c63511e180ea46381ec770f44bc47257b + +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -305,6 +305,31 @@ free: + } + + /** ++ * batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet ++ * @skb: newly merged skb ++ * ++ * Return: if newly skb is of type BATADV_UNICAST_FRAG ++ */ ++static bool batadv_skb_is_frag(struct sk_buff *skb) ++{ ++ struct batadv_ogm_packet *batadv_ogm_packet; ++ ++ /* packet should hold at least type and version */ ++ if (unlikely(!pskb_may_pull(skb, 2))) ++ return false; ++ ++ batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data; ++ ++ if (batadv_ogm_packet->version != BATADV_COMPAT_VERSION) ++ return false; ++ ++ if (batadv_ogm_packet->packet_type != BATADV_UNICAST_FRAG) ++ return false; ++ ++ return true; ++} ++ ++/** + * batadv_frag_skb_buffer() - buffer fragment for later merge + * @skb: skb to buffer + * @orig_node_src: originator that the skb is received from +@@ -337,6 +362,16 @@ bool batadv_frag_skb_buffer(struct sk_bu + if (!skb_out) + goto out_err; + ++ /* fragment in fragment is not allowed. otherwise it is possible ++ * to exhaust the stack when receiving a matryoshka-style ++ * "fragments in a fragment packet" ++ */ ++ if (batadv_skb_is_frag(skb_out)) { ++ kfree_skb(skb_out); ++ skb_out = NULL; ++ goto out_err; ++ } ++ + out: + ret = true; + out_err: diff --git a/batman-adv/patches/0039-batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch b/batman-adv/patches/0039-batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch new file mode 100644 index 000000000..6b180f9cc --- /dev/null +++ b/batman-adv/patches/0039-batman-adv-tp_meter-directly-shut-down-timer-on-clea.patch @@ -0,0 +1,45 @@ +From: Sven Eckelmann +Date: Wed, 13 May 2026 10:43:54 +0200 +Subject: batman-adv: tp_meter: directly shut down timer on cleanup + +batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by +timer_delete() to guard against the timer handler re-arming itself between +the two calls. This double-deletion hack relied on the sending status being +set to 0 to suppress re-arming. + +Replace both calls with a single timer_shutdown_sync(). This function both +waits for any running timer callback to complete (like timer_delete_sync()) +and permanently disarms the timer so it cannot be re-armed afterwards, +making re-arming prevention unconditional and self-documenting. + +The re-arming property is also required because otherwise: + +1. context 0 (batadv_tp_recv_ack()) checks in + batadv_tp_reset_sender_timer() if sending is still 1 -> it is +2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in + this process forces the kthread to stop timer in + batadv_tp_sender_cleanup() +3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the + timer -> but the reference for it is already gone + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=724c381c1dcd0f0682ba36909ca6b843d11d3c2f + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -401,13 +401,7 @@ static void batadv_tp_sender_cleanup(str + batadv_tp_list_detach(tp_vars); + + /* kill the timer and remove its reference */ +- del_timer_sync(&tp_vars->timer); +- /* the worker might have rearmed itself therefore we kill it again. Note +- * that if the worker should run again before invoking the following +- * del_timer(), it would not re-arm itself once again because the status +- * is OFF now +- */ +- del_timer(&tp_vars->timer); ++ timer_shutdown_sync(&tp_vars->timer); + batadv_tp_vars_put(tp_vars); + } + diff --git a/batman-adv/patches/0040-batman-adv-fix-batadv_skb_is_frag-kernel-doc.patch b/batman-adv/patches/0040-batman-adv-fix-batadv_skb_is_frag-kernel-doc.patch new file mode 100644 index 000000000..b2216cd24 --- /dev/null +++ b/batman-adv/patches/0040-batman-adv-fix-batadv_skb_is_frag-kernel-doc.patch @@ -0,0 +1,31 @@ +From: Sven Eckelmann +Date: Sat, 16 May 2026 22:10:08 +0200 +Subject: batman-adv: fix batadv_skb_is_frag() kernel-doc + +The kernel-doc comment for batadv_skb_is_frag() contained two errors: + +* the function description referred to "gain a unicast packet" instead + of "contains unicast fragment". +* the Return section omitted "merged" from "newly skb", leaving the + description grammatically incorrect and inconsistent with the + function description. + +Fixes: b4e35f8c6351 ("batman-adv: frag: disallow unicast fragment in fragment") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=ff182fc0c666754e3f35f7b63c9ccf0dff239e52 + +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -305,10 +305,10 @@ free: + } + + /** +- * batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet ++ * batadv_skb_is_frag() - check if newly merged skb contains unicast fragment + * @skb: newly merged skb + * +- * Return: if newly skb is of type BATADV_UNICAST_FRAG ++ * Return: if newly merged skb is of type BATADV_UNICAST_FRAG + */ + static bool batadv_skb_is_frag(struct sk_buff *skb) + { diff --git a/batman-adv/patches/0041-batman-adv-v-stop-OGMv2-on-disabled-interface.patch b/batman-adv/patches/0041-batman-adv-v-stop-OGMv2-on-disabled-interface.patch new file mode 100644 index 000000000..020d2ab37 --- /dev/null +++ b/batman-adv/patches/0041-batman-adv-v-stop-OGMv2-on-disabled-interface.patch @@ -0,0 +1,134 @@ +From: Sven Eckelmann +Date: Sat, 9 May 2026 22:44:12 +0200 +Subject: batman-adv: v: stop OGMv2 on disabled interface + +When a batadv_hard_iface is disabled, its mesh_iface pointer is set to +NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via +batadv_v_ogm_queue_on_if() for interfaces that have since lost their +mesh_iface association. This results in a NULL pointer dereference when +batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the +now NULL hard_iface->mesh_iface to retrieve the batadv_priv. + +It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that +it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was +called. + +Fixes: 632835348e65 ("batman-adv: OGMv2 - add basic infrastructure") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=3d550f6440fc5b1e699120253d3673eee7d05c0b + +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(str + + /** + * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface ++ * @bat_priv: the bat priv with all the mesh interface information + * @skb: the OGM to send + * @hard_iface: the interface to use to send the OGM + */ +-static void batadv_v_ogm_send_to_if(struct sk_buff *skb, ++static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv, ++ struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) + { +- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); +- + if (hard_iface->if_status != BATADV_IF_ACTIVE) { + kfree_skb(skb); + return; +@@ -189,6 +189,7 @@ static void batadv_v_ogm_aggr_list_free( + + /** + * batadv_v_ogm_aggr_send() - flush & send aggregation queue ++ * @bat_priv: the bat priv with all the mesh interface information + * @hard_iface: the interface with the aggregation queue to flush + * + * Aggregates all OGMv2 packets currently in the aggregation queue into a +@@ -198,7 +199,8 @@ static void batadv_v_ogm_aggr_list_free( + * + * Caller needs to hold the hard_iface->bat_v.aggr_list.lock. + */ +-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) ++static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv, ++ struct batadv_hard_iface *hard_iface) + { + unsigned int aggr_len = hard_iface->bat_v.aggr_len; + struct sk_buff *skb_aggr; +@@ -228,27 +230,32 @@ static void batadv_v_ogm_aggr_send(struc + consume_skb(skb); + } + +- batadv_v_ogm_send_to_if(skb_aggr, hard_iface); ++ batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface); + } + + /** + * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface ++ * @bat_priv: the bat priv with all the mesh interface information + * @skb: the OGM to queue + * @hard_iface: the interface to queue the OGM on + */ +-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb, ++static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, ++ struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) + { +- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); ++ if (hard_iface->soft_iface != bat_priv->soft_iface) { ++ kfree_skb(skb); ++ return; ++ } + + if (!atomic_read(&bat_priv->aggregated_ogms)) { +- batadv_v_ogm_send_to_if(skb, hard_iface); ++ batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface); + return; + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); + if (!batadv_v_ogm_queue_left(skb, hard_iface)) +- batadv_v_ogm_aggr_send(hard_iface); ++ batadv_v_ogm_aggr_send(bat_priv, hard_iface); + + hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb); + __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb); +@@ -347,7 +354,7 @@ static void batadv_v_ogm_send_softif(str + break; + } + +- batadv_v_ogm_queue_on_if(skb_tmp, hard_iface); ++ batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface); + batadv_hardif_put(hard_iface); + } + rcu_read_unlock(); +@@ -387,12 +394,14 @@ void batadv_v_ogm_aggr_work(struct work_ + { + struct batadv_hard_iface_bat_v *batv; + struct batadv_hard_iface *hard_iface; ++ struct batadv_priv *bat_priv; + + batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work); + hard_iface = container_of(batv, struct batadv_hard_iface, bat_v); ++ bat_priv = netdev_priv(hard_iface->soft_iface); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); +- batadv_v_ogm_aggr_send(hard_iface); ++ batadv_v_ogm_aggr_send(bat_priv, hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + + batadv_v_ogm_start_queue_timer(hard_iface); +@@ -582,7 +591,7 @@ static void batadv_v_ogm_forward(struct + if_outgoing->net_dev->name, ntohl(ogm_forward->throughput), + ogm_forward->ttl, if_incoming->net_dev->name); + +- batadv_v_ogm_queue_on_if(skb, if_outgoing); ++ batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing); + + out: + batadv_orig_ifinfo_put(orig_ifinfo); diff --git a/batman-adv/patches/0042-batman-adv-tvlv-abort-OGM-send-on-tvlv-append-failur.patch b/batman-adv/patches/0042-batman-adv-tvlv-abort-OGM-send-on-tvlv-append-failur.patch new file mode 100644 index 000000000..bb1d1673f --- /dev/null +++ b/batman-adv/patches/0042-batman-adv-tvlv-abort-OGM-send-on-tvlv-append-failur.patch @@ -0,0 +1,172 @@ +From: Sven Eckelmann +Date: Thu, 14 May 2026 16:33:12 +0200 +Subject: batman-adv: tvlv: abort OGM send on tvlv append failure + +batadv_tvlv_container_ogm_append() could fail in two ways: a memory +allocation failure when resizing the packet buffer, or the tvlv data +exceeding U16_MAX bytes. In both cases the function previously returned the +old (now stale) tvlv_value_len rather than signalling an error, causing the +OGM/OGM2 send path to transmit a packet whose TVLV length field no longer +matched the actual buffer contents. And because it also didn't fill in the +new TVLV data, sending either uninitialized or corrupted data on the wire. + +All errors in batadv_tvlv_container_ogm_append() must be forwarded to the +caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N. +IV, it is currently not allowed to abort the send. The non-TVLV part of the +OGM must be queued up instead. + +Fixes: 0b6aa0d43767 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=0646fa75c4aa4e7e71a9370d80f6c3ecffb30088 + +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -781,6 +781,7 @@ static void batadv_iv_ogm_schedule_buff( + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); + +@@ -804,9 +805,18 @@ static void batadv_iv_ogm_schedule_buff( + * appended as it may alter the tt tvlv container + */ + batadv_tt_local_commit_changes(bat_priv); +- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, +- ogm_buff_len, +- BATADV_OGM_HLEN); ++ ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, ++ ogm_buff_len, ++ BATADV_OGM_HLEN); ++ if (ret < 0) { ++ /* OGMs must be queued even when the buffer allocation for ++ * TVLVs failed. just fall back to the non-TVLV version ++ */ ++ ret = 0; ++ *ogm_buff_len = BATADV_OGM_HLEN; ++ } ++ ++ tvlv_len = ret; + } + + batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff); +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -271,9 +271,9 @@ static void batadv_v_ogm_send_softif(str + struct batadv_hard_iface *hard_iface; + struct batadv_ogm2_packet *ogm_packet; + struct sk_buff *skb, *skb_tmp; +- unsigned char *ogm_buff; +- int ogm_buff_len; +- u16 tvlv_len = 0; ++ unsigned char **ogm_buff; ++ int *ogm_buff_len; ++ u16 tvlv_len; + int ret; + + lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); +@@ -281,25 +281,27 @@ static void batadv_v_ogm_send_softif(str + if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + goto out; + +- ogm_buff = bat_priv->bat_v.ogm_buff; +- ogm_buff_len = bat_priv->bat_v.ogm_buff_len; ++ ogm_buff = &bat_priv->bat_v.ogm_buff; ++ ogm_buff_len = &bat_priv->bat_v.ogm_buff_len; ++ + /* tt changes have to be committed before the tvlv data is + * appended as it may alter the tt tvlv container + */ + batadv_tt_local_commit_changes(bat_priv); +- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff, +- &ogm_buff_len, +- BATADV_OGM2_HLEN); ++ ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, ++ ogm_buff_len, ++ BATADV_OGM2_HLEN); ++ if (ret < 0) ++ goto reschedule; + +- bat_priv->bat_v.ogm_buff = ogm_buff; +- bat_priv->bat_v.ogm_buff_len = ogm_buff_len; ++ tvlv_len = ret; + +- skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len); ++ skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len); + if (!skb) + goto reschedule; + + skb_reserve(skb, ETH_HLEN); +- skb_put_data(skb, ogm_buff, ogm_buff_len); ++ skb_put_data(skb, *ogm_buff, *ogm_buff_len); + + ogm_packet = (struct batadv_ogm2_packet *)skb->data; + ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno)); +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_b + * The ogm packet might be enlarged or shrunk depending on the current size + * and the size of the to-be-appended tvlv containers. + * +- * Return: size of all appended tvlv containers in bytes. ++ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative ++ * if operation failed + */ +-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, ++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, + unsigned char **packet_buff, + int *packet_buff_len, int packet_min_len) + { +@@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(str + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_len; + void *tvlv_value; ++ int tvlv_len_ret; + bool ret; + + spin_lock_bh(&bat_priv->tvlv.container_list_lock); +@@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(str + + ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, + packet_min_len, tvlv_value_len); +- +- if (!ret) ++ if (!ret) { ++ tvlv_len_ret = -ENOMEM; + goto end; ++ } ++ ++ tvlv_len_ret = tvlv_value_len; + + if (!tvlv_value_len) + goto end; +@@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(str + + end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +- return tvlv_value_len; ++ ++ return tvlv_len_ret; + } + + /** +--- a/net/batman-adv/tvlv.h ++++ b/net/batman-adv/tvlv.h +@@ -16,7 +16,7 @@ + void batadv_tvlv_container_register(struct batadv_priv *bat_priv, + u8 type, u8 version, + void *tvlv_value, u16 tvlv_value_len); +-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, ++int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, + unsigned char **packet_buff, + int *packet_buff_len, int packet_min_len); + void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, diff --git a/batman-adv/patches/0043-batman-adv-tvlv-reject-oversized-TVLV-packets.patch b/batman-adv/patches/0043-batman-adv-tvlv-reject-oversized-TVLV-packets.patch new file mode 100644 index 000000000..426faa815 --- /dev/null +++ b/batman-adv/patches/0043-batman-adv-tvlv-reject-oversized-TVLV-packets.patch @@ -0,0 +1,71 @@ +From: Sven Eckelmann +Date: Sat, 9 May 2026 21:55:29 +0200 +Subject: batman-adv: tvlv: reject oversized TVLV packets + +batadv_tvlv_container_ogm_append() builds a TVLV packet section from +the tvlv.container_list. The total size of this section is computed by +batadv_tvlv_container_list_size(), which sums the sizes of all registered +containers. + +The return type and accumulator in batadv_tvlv_container_list_size() were +u16. If the accumulated size exceeds U16_MAX, the value wraps around, +causing the subsequent allocation in batadv_tvlv_container_ogm_append() +to be undersized. The memcpy-style copy that follows would then write +beyond the end of the allocated buffer, corrupting kernel memory. + +Fix this by widening the return type of batadv_tvlv_container_list_size() +to size_t. In batadv_tvlv_container_ogm_append(), check the computed length +against U16_MAX before proceeding, and bail out as if the allocation had +failed when the limit is exceeded. + +Fixes: 0b6aa0d43767 ("batman-adv: tvlv - basic infrastructure") +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Reviewed-by: Yuan Tan +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=12ef34314d8ec4d71306eacbb8de12aa78bf44f1 + +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_ + * + * Return: size of all currently registered tvlv containers in bytes. + */ +-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv) ++static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv) + { + struct batadv_tvlv_container *tvlv; +- u16 tvlv_len = 0; ++ size_t tvlv_len = 0; + + lockdep_assert_held(&bat_priv->tvlv.container_list_lock); + +@@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(str + { + struct batadv_tvlv_container *tvlv; + struct batadv_tvlv_hdr *tvlv_hdr; +- u16 tvlv_value_len; ++ size_t tvlv_value_len; + void *tvlv_value; + int tvlv_len_ret; + bool ret; + + spin_lock_bh(&bat_priv->tvlv.container_list_lock); + tvlv_value_len = batadv_tvlv_container_list_size(bat_priv); ++ if (tvlv_value_len > U16_MAX) { ++ tvlv_len_ret = -E2BIG; ++ goto end; ++ } + + ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, + packet_min_len, tvlv_value_len); diff --git a/batman-adv/patches/0044-batman-adv-tp_meter-fix-race-condition-in-send-error.patch b/batman-adv/patches/0044-batman-adv-tp_meter-fix-race-condition-in-send-error.patch new file mode 100644 index 000000000..dba0d45f4 --- /dev/null +++ b/batman-adv/patches/0044-batman-adv-tp_meter-fix-race-condition-in-send-error.patch @@ -0,0 +1,172 @@ +From: Sven Eckelmann +Date: Wed, 13 May 2026 23:38:54 +0200 +Subject: batman-adv: tp_meter: fix race condition in send error reporting + +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. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=a7513266d0d981d34e790d7cbf5a77aa57a224aa + +--- 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 +@@ -1458,15 +1458,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/batman-adv/patches/0045-batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch b/batman-adv/patches/0045-batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch new file mode 100644 index 000000000..a96467f03 --- /dev/null +++ b/batman-adv/patches/0045-batman-adv-tp_meter-avoid-role-confusion-in-tp_list.patch @@ -0,0 +1,181 @@ +From: Sven Eckelmann +Date: Sat, 16 May 2026 12:33:41 +0200 +Subject: batman-adv: tp_meter: avoid role confusion in tp_list + +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. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=8342cb92a48b4c710e8e42bbd4be7c6f3406c3a6 + +--- 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/batman-adv/patches/0046-batman-adv-mcast-fix-use-after-free-in-orig_node-RCU.patch b/batman-adv/patches/0046-batman-adv-mcast-fix-use-after-free-in-orig_node-RCU.patch new file mode 100644 index 000000000..bb40005e9 --- /dev/null +++ b/batman-adv/patches/0046-batman-adv-mcast-fix-use-after-free-in-orig_node-RCU.patch @@ -0,0 +1,39 @@ +From: Sven Eckelmann +Date: Thu, 14 May 2026 19:22:02 +0200 +Subject: batman-adv: mcast: fix use-after-free in orig_node RCU release + +batadv_mcast_purge_orig() removes entries from RCU-protected hlists but +does not wait for an RCU grace period before returning. Concurrent RCU +readers may still accesses references to those entries at the point of +removal. RCU-protected readers trying to operate on entries like +orig->mcast_want_all_ipv6_node will then access already freed memory. + +Fix this by moving batadv_mcast_purge_orig() to batadv_orig_node_release(), +just before the call_rcu() invocation. This ensures RCU readers that were +active at purge time have drained before the orig_node memory is reclaimed. + +Fixes: 1c090349e2f6 ("batman-adv: Add IPv4 link-local/IPv6-ll-all-nodes multicast support") +Acked-by: Linus Lüssing +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=4ff638adb7d4fd7d4eb9744d4bd8c5950d05e4e0 + +--- a/net/batman-adv/originator.c ++++ b/net/batman-adv/originator.c +@@ -850,8 +850,6 @@ static void batadv_orig_node_free_rcu(st + + orig_node = container_of(rcu, struct batadv_orig_node, rcu); + +- batadv_mcast_purge_orig(orig_node); +- + batadv_frag_purge_orig(orig_node, NULL); + + kfree(orig_node->tt_buff); +@@ -905,6 +903,8 @@ void batadv_orig_node_release(struct kre + /* Free nc_nodes */ + batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL); + ++ batadv_mcast_purge_orig(orig_node); ++ + call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); + } + diff --git a/batman-adv/patches/0047-batman-adv-iv-recover-OGM-scheduling-after-forward-p.patch b/batman-adv/patches/0047-batman-adv-iv-recover-OGM-scheduling-after-forward-p.patch new file mode 100644 index 000000000..99bb9a4f0 --- /dev/null +++ b/batman-adv/patches/0047-batman-adv-iv-recover-OGM-scheduling-after-forward-p.patch @@ -0,0 +1,210 @@ +From: Sven Eckelmann +Date: Fri, 15 May 2026 22:00:40 +0200 +Subject: batman-adv: iv: recover OGM scheduling after forward packet error + +When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward +packet for OGM transmission, the work item that drives periodic OGM +scheduling is never re-armed. This silently halts transmission of the +node's own OGMs on the affected interface — only OGMs from other peers +continue to be aggregated and forwarded. + +Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively +batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet. +When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing +a dedicated recovery work item (reschedule_work) that fires after one +originator interval and calls batadv_iv_ogm_schedule() again. + +Fixes: d893c5d00f63 ("[batman-adv] Cleanup aggregation.[ch]") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=2ce99b14c3cdf478edf63df70c48c5516d925cfb + +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -223,6 +223,8 @@ static void batadv_iv_ogm_iface_disable( + hard_iface->bat_iv.ogm_buff = NULL; + + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); ++ ++ cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work); + } + + static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface) +@@ -527,8 +529,10 @@ out: + * @if_incoming: interface where the packet was received + * @if_outgoing: interface for which the retransmission should be considered + * @own_packet: true if it is a self-generated ogm ++ * ++ * Return: whether forward packet was scheduled + */ +-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, ++static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, + int packet_len, unsigned long send_time, + bool direct_link, + struct batadv_hard_iface *if_incoming, +@@ -552,13 +556,13 @@ static void batadv_iv_ogm_aggregate_new( + + skb = netdev_alloc_skb_ip_align(NULL, skb_size); + if (!skb) +- return; ++ return false; + + forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, + queue_left, bat_priv, skb); + if (!forw_packet_aggr) { + kfree_skb(skb); +- return; ++ return false; + } + + forw_packet_aggr->skb->priority = TC_PRIO_CONTROL; +@@ -580,6 +584,8 @@ static void batadv_iv_ogm_aggregate_new( + batadv_iv_send_outstanding_bat_ogm_packet); + + batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time); ++ ++ return true; + } + + /* aggregate a new packet into the existing ogm packet */ +@@ -609,8 +615,10 @@ static void batadv_iv_ogm_aggregate(stru + * @if_outgoing: interface for which the retransmission should be considered + * @own_packet: true if it is a self-generated ogm + * @send_time: timestamp (jiffies) when the packet is to be sent ++ * ++ * Return: whether forward packet was scheduled + */ +-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, ++static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, + unsigned char *packet_buff, + int packet_len, + struct batadv_hard_iface *if_incoming, +@@ -662,14 +670,16 @@ static void batadv_iv_ogm_queue_add(stru + if (!own_packet && atomic_read(&bat_priv->aggregated_ogms)) + send_time += max_aggregation_jiffies; + +- batadv_iv_ogm_aggregate_new(packet_buff, packet_len, +- send_time, direct_link, +- if_incoming, if_outgoing, +- own_packet); ++ return batadv_iv_ogm_aggregate_new(packet_buff, packet_len, ++ send_time, direct_link, ++ if_incoming, if_outgoing, ++ own_packet); + } else { + batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff, + packet_len, direct_link); + spin_unlock_bh(&bat_priv->forw_bat_list_lock); ++ ++ return true; + } + } + +@@ -781,6 +791,8 @@ static void batadv_iv_ogm_schedule_buff( + u32 seqno; + u16 tvlv_len = 0; + unsigned long send_time; ++ bool reschedule = false; ++ bool scheduled; + int ret; + + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); +@@ -809,11 +821,8 @@ static void batadv_iv_ogm_schedule_buff( + ogm_buff_len, + BATADV_OGM_HLEN); + if (ret < 0) { +- /* OGMs must be queued even when the buffer allocation for +- * TVLVs failed. just fall back to the non-TVLV version +- */ +- ret = 0; +- *ogm_buff_len = BATADV_OGM_HLEN; ++ reschedule = true; ++ goto out; + } + + tvlv_len = ret; +@@ -835,8 +844,11 @@ static void batadv_iv_ogm_schedule_buff( + /* OGMs from secondary interfaces are only scheduled on their + * respective interfaces. + */ +- batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, +- hard_iface, hard_iface, 1, send_time); ++ scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, ++ hard_iface, hard_iface, 1, send_time); ++ if (!scheduled) ++ reschedule = true; ++ + goto out; + } + +@@ -851,15 +863,28 @@ static void batadv_iv_ogm_schedule_buff( + if (!kref_get_unless_zero(&tmp_hard_iface->refcount)) + continue; + +- batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, +- *ogm_buff_len, hard_iface, +- tmp_hard_iface, 1, send_time); +- ++ scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, ++ *ogm_buff_len, hard_iface, ++ tmp_hard_iface, 1, send_time); + batadv_hardif_put(tmp_hard_iface); ++ ++ if (!scheduled && tmp_hard_iface == hard_iface) ++ reschedule = true; + } + rcu_read_unlock(); + + out: ++ if (reschedule) { ++ /* there was a failure scheduling the own forward packet. ++ * as result, the batadv_iv_send_outstanding_bat_ogm_packet() ++ * work item is no longer scheduled. it is therefore necessary ++ * to reschedule it manually ++ */ ++ queue_delayed_work(batadv_event_workqueue, ++ &hard_iface->bat_iv.reschedule_work, ++ msecs_to_jiffies(atomic_read(&bat_priv->orig_interval))); ++ } ++ + batadv_hardif_put(primary_if); + } + +@@ -874,6 +899,17 @@ static void batadv_iv_ogm_schedule(struc + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); + } + ++static void batadv_iv_ogm_reschedule(struct work_struct *work) ++{ ++ struct delayed_work *delayed_work = to_delayed_work(work); ++ struct batadv_hard_iface *hard_iface; ++ ++ hard_iface = container_of(delayed_work, ++ struct batadv_hard_iface, ++ bat_iv.reschedule_work); ++ batadv_iv_ogm_schedule(hard_iface); ++} ++ + /** + * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface + * @orig_node: originator which reproadcasted the OGMs directly +@@ -2277,6 +2313,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv + + static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface) + { ++ INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule); ++ + /* begin scheduling originator messages on that interface */ + batadv_iv_ogm_schedule(hard_iface); + } +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv { + /** @ogm_seqno: OGM sequence number - used to identify each OGM */ + atomic_t ogm_seqno; + ++ /** @reschedule_work: recover OGM schedule after schedule error */ ++ struct delayed_work reschedule_work; ++ + /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */ + struct mutex ogm_buff_mutex; + }; diff --git a/batman-adv/patches/0048-batman-adv-bla-fix-report_work-leak-on-backbone_gw-p.patch b/batman-adv/patches/0048-batman-adv-bla-fix-report_work-leak-on-backbone_gw-p.patch new file mode 100644 index 000000000..f2b6f4bb0 --- /dev/null +++ b/batman-adv/patches/0048-batman-adv-bla-fix-report_work-leak-on-backbone_gw-p.patch @@ -0,0 +1,106 @@ +From: Sven Eckelmann +Date: Sun, 10 May 2026 11:43:20 +0200 +Subject: batman-adv: bla: fix report_work leak on backbone_gw purge + +batadv_bla_purge_backbone_gw() removes stale backbone gateway entries, +but fails to properly handle their associated report_work: + +- If report_work is running, the purge must wait for it to finish before + freeing the backbone_gw, otherwise the worker may access freed memory + (e.g. bat_priv). +- If report_work is pending, the purge must cancel it and release the + reference held for that pending work item. + +The previous implementation called hlist_for_each_entry_safe() inside a +spin_lock_bh() section, but cancel_work_sync() may sleep and therefore +cannot be called from within a spinlock-protected region. + +Restructure the loop to handle one entry per spinlock critical section: +acquire the lock, find the next entry to purge, remove it from the hash +list, then release the lock before calling cancel_work_sync() and +dropping the hash_entry reference. Repeat until no more entries require +purging. + +Fixes: a998bf5dfbd7 ("batman-adv: add detection for complex bridge loops") +Reviewed-by: Simon Wunderlich +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=86a0d06a9da1f5ef41259949a82b4544995325c1 + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -1224,6 +1224,7 @@ static void batadv_bla_purge_backbone_gw + struct hlist_head *head; + struct batadv_hashtable *hash; + spinlock_t *list_lock; /* protects write access to the hash lists */ ++ bool purged; + int i; + + hash = bat_priv->bla.backbone_hash; +@@ -1234,30 +1235,45 @@ static void batadv_bla_purge_backbone_gw + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + +- spin_lock_bh(list_lock); +- hlist_for_each_entry_safe(backbone_gw, node_tmp, +- head, hash_entry) { +- if (now) +- goto purge_now; +- if (!batadv_has_timed_out(backbone_gw->lasttime, +- BATADV_BLA_BACKBONE_TIMEOUT)) +- continue; +- +- batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, +- "%s(): backbone gw %pM timed out\n", +- __func__, backbone_gw->orig); ++ do { ++ purged = false; ++ ++ spin_lock_bh(list_lock); ++ hlist_for_each_entry_safe(backbone_gw, node_tmp, ++ head, hash_entry) { ++ if (now) ++ goto purge_now; ++ if (!batadv_has_timed_out(backbone_gw->lasttime, ++ BATADV_BLA_BACKBONE_TIMEOUT)) ++ continue; ++ ++ batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, ++ "%s(): backbone gw %pM timed out\n", ++ __func__, backbone_gw->orig); + + purge_now: +- /* don't wait for the pending request anymore */ +- if (atomic_read(&backbone_gw->request_sent)) +- atomic_dec(&bat_priv->bla.num_requests); +- +- batadv_bla_del_backbone_claims(backbone_gw); +- +- hlist_del_rcu(&backbone_gw->hash_entry); +- batadv_backbone_gw_put(backbone_gw); +- } +- spin_unlock_bh(list_lock); ++ purged = true; ++ ++ /* don't wait for the pending request anymore */ ++ if (atomic_read(&backbone_gw->request_sent)) ++ atomic_dec(&bat_priv->bla.num_requests); ++ ++ batadv_bla_del_backbone_claims(backbone_gw); ++ ++ hlist_del_rcu(&backbone_gw->hash_entry); ++ break; ++ } ++ spin_unlock_bh(list_lock); ++ ++ if (purged) { ++ /* reference for pending report_work */ ++ if (cancel_work_sync(&backbone_gw->report_work)) ++ batadv_backbone_gw_put(backbone_gw); ++ ++ /* reference for hash_entry */ ++ batadv_backbone_gw_put(backbone_gw); ++ } ++ } while (purged); + } + } + diff --git a/batman-adv/patches/0049-batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch b/batman-adv/patches/0049-batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch new file mode 100644 index 000000000..e2bd20271 --- /dev/null +++ b/batman-adv/patches/0049-batman-adv-bla-avoid-double-decrement-of-bla.num_req.patch @@ -0,0 +1,212 @@ +From: Sven Eckelmann +Date: Tue, 12 May 2026 09:13:31 +0200 +Subject: batman-adv: bla: avoid double decrement of bla.num_requests + +The bla.num_requests is increased when no request_sent was in progress. And +it is decremented in various places (announcement was received, backbone is +purged, periodic work). But the check if the request_sent is actually set +to a specific state and the atomic_dec/_inc are not safe because they are +not atomic (TOCTOU) and multiple such code portions can run concurrently. + +At the same time, it is necessary to modify request_sent (state) and +bla.num_requests atomically. Otherwise batadv_bla_send_request() might set +request_sent to 1 and is interrupted. batadv_handle_announce() can then +set request_sent back to 0 and decrement num_requests before +batadv_bla_send_request() incremented it. + +The two operations must therefore be locked. And since state (request_sent) +and wait_periods are only accessed inside this lock, they can be converted +to simpler datatypes. And to avoid that the bla.num_requests is touched by +a parallel running context with a valid backbone_gw reference after +batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to +correctly signal that a backbone_gw is in the state of being cleaned up. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=d866aa510616a4efc4274cb2d4e8af5484b0bf37 + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -514,8 +514,8 @@ batadv_bla_get_backbone_gw(struct batadv + entry->crc = BATADV_BLA_CRC_INIT; + entry->bat_priv = bat_priv; + spin_lock_init(&entry->crc_lock); +- atomic_set(&entry->request_sent, 0); +- atomic_set(&entry->wait_periods, 0); ++ entry->state = BATADV_BLA_BACKBONE_GW_SYNCED; ++ entry->wait_periods = 0; + ether_addr_copy(entry->orig, orig); + INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report); + kref_init(&entry->refcount); +@@ -544,9 +544,13 @@ batadv_bla_get_backbone_gw(struct batadv + batadv_bla_send_announce(bat_priv, entry); + + /* this will be decreased in the worker thread */ +- atomic_inc(&entry->request_sent); +- atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS); +- atomic_inc(&bat_priv->bla.num_requests); ++ spin_lock_bh(&bat_priv->bla.num_requests_lock); ++ if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) { ++ entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED; ++ entry->wait_periods = BATADV_BLA_WAIT_PERIODS; ++ atomic_inc(&bat_priv->bla.num_requests); ++ } ++ spin_unlock_bh(&bat_priv->bla.num_requests_lock); + } + + return entry; +@@ -649,10 +653,12 @@ static void batadv_bla_send_request(stru + backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST); + + /* no local broadcasts should be sent or received, for now. */ +- if (!atomic_read(&backbone_gw->request_sent)) { ++ spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock); ++ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) { ++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED; + atomic_inc(&backbone_gw->bat_priv->bla.num_requests); +- atomic_set(&backbone_gw->request_sent, 1); + } ++ spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock); + } + + /** +@@ -873,10 +879,12 @@ static bool batadv_handle_announce(struc + /* if we have sent a request and the crc was OK, + * we can allow traffic again. + */ +- if (atomic_read(&backbone_gw->request_sent)) { ++ spin_lock_bh(&bat_priv->bla.num_requests_lock); ++ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) { ++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED; + atomic_dec(&backbone_gw->bat_priv->bla.num_requests); +- atomic_set(&backbone_gw->request_sent, 0); + } ++ spin_unlock_bh(&bat_priv->bla.num_requests_lock); + } + + batadv_backbone_gw_put(backbone_gw); +@@ -1255,9 +1263,13 @@ purge_now: + purged = true; + + /* don't wait for the pending request anymore */ +- if (atomic_read(&backbone_gw->request_sent)) ++ spin_lock_bh(&bat_priv->bla.num_requests_lock); ++ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) + atomic_dec(&bat_priv->bla.num_requests); + ++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED; ++ spin_unlock_bh(&bat_priv->bla.num_requests_lock); ++ + batadv_bla_del_backbone_claims(backbone_gw); + + hlist_del_rcu(&backbone_gw->hash_entry); +@@ -1508,7 +1520,7 @@ static void batadv_bla_periodic_work(str + batadv_bla_send_loopdetect(bat_priv, + backbone_gw); + +- /* request_sent is only set after creation to avoid ++ /* state is only set to unsynced after creation to avoid + * problems when we are not yet known as backbone gw + * in the backbone. + * +@@ -1517,14 +1529,21 @@ static void batadv_bla_periodic_work(str + * some grace time. + */ + +- if (atomic_read(&backbone_gw->request_sent) == 0) +- continue; ++ spin_lock_bh(&bat_priv->bla.num_requests_lock); ++ if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED) ++ goto unlock_next; + +- if (!atomic_dec_and_test(&backbone_gw->wait_periods)) +- continue; ++ if (backbone_gw->wait_periods > 0) ++ backbone_gw->wait_periods--; ++ ++ if (backbone_gw->wait_periods > 0) ++ goto unlock_next; + ++ backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED; + atomic_dec(&backbone_gw->bat_priv->bla.num_requests); +- atomic_set(&backbone_gw->request_sent, 0); ++ ++unlock_next: ++ spin_unlock_bh(&bat_priv->bla.num_requests_lock); + } + rcu_read_unlock(); + } +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -787,6 +787,7 @@ static int batadv_softif_init_late(struc + atomic_set(&bat_priv->tt.ogm_append_cnt, 0); + #ifdef CONFIG_BATMAN_ADV_BLA + atomic_set(&bat_priv->bla.num_requests, 0); ++ spin_lock_init(&bat_priv->bla.num_requests_lock); + #endif + atomic_set(&bat_priv->tp_num, 0); + +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1092,6 +1092,12 @@ struct batadv_priv_bla { + atomic_t num_requests; + + /** ++ * @num_requests_lock: locks update num_requests + ++ * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update ++ */ ++ spinlock_t num_requests_lock; ++ ++ /** + * @claim_hash: hash table containing mesh nodes this host has claimed + */ + struct batadv_hashtable *claim_hash; +@@ -1825,6 +1831,27 @@ struct batadv_priv { + + #ifdef CONFIG_BATMAN_ADV_BLA + ++enum batadv_bla_backbone_gw_state { ++ /** ++ * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed ++ * and it must not longer work on requests ++ */ ++ BATADV_BLA_BACKBONE_GW_STOPPED, ++ ++ /** ++ * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out ++ * of sync and a request was send. No traffic is forwarded until the ++ * situation is resolved ++ */ ++ BATADV_BLA_BACKBONE_GW_UNSYNCED, ++ ++ /** ++ * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in ++ * sync. traffic can be forwarded ++ */ ++ BATADV_BLA_BACKBONE_GW_SYNCED, ++}; ++ + /** + * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN + */ +@@ -1850,16 +1877,12 @@ struct batadv_bla_backbone_gw { + /** + * @wait_periods: grace time for bridge forward delays and bla group + * forming at bootup phase - no bcast traffic is formwared until it has +- * elapsed ++ * elapsed. Must only be access with num_requests_lock. + */ +- atomic_t wait_periods; ++ u8 wait_periods; + +- /** +- * @request_sent: if this bool is set to true we are out of sync with +- * this backbone gateway - no bcast traffic is formwared until the +- * situation was resolved +- */ +- atomic_t request_sent; ++ /** @state: sync state. Must only be access with num_requests_lock. */ ++ enum batadv_bla_backbone_gw_state state; + + /** @crc: crc16 checksum over all claims */ + u16 crc; diff --git a/batman-adv/patches/0050-batman-adv-bla-avoid-NULL-ptr-deref-for-claim-via-dr.patch b/batman-adv/patches/0050-batman-adv-bla-avoid-NULL-ptr-deref-for-claim-via-dr.patch new file mode 100644 index 000000000..2db499485 --- /dev/null +++ b/batman-adv/patches/0050-batman-adv-bla-avoid-NULL-ptr-deref-for-claim-via-dr.patch @@ -0,0 +1,38 @@ +From: Sven Eckelmann +Date: Tue, 19 May 2026 09:23:49 +0200 +Subject: batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface + +Without rtnl_lock held, a hardif might be retrieved as primary interface of +a meshif, but then (while operating on this interface) getting decoupled +from the mesh interface. In this case, the meshif still exists but the +pointer from the primary hardif to the meshif is set to NULL. + +The mesh_iface must be checked first to be non-NULL before continuing to +send an ARP request using meshif. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Reported-by: Ido Schimmel +Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=ec9f686b42917e9b67725d7245b50de72f59fa9e + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct + sizeof(local_claim_dest)); + local_claim_dest.type = claimtype; + +- soft_iface = primary_if->soft_iface; ++ soft_iface = READ_ONCE(primary_if->soft_iface); ++ if (!soft_iface) ++ goto out; + + skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + /* IP DST: 0.0.0.0 */ + zeroip, +- primary_if->soft_iface, ++ soft_iface, + /* IP SRC: 0.0.0.0 */ + zeroip, + /* Ethernet DST: Broadcast */ diff --git a/batman-adv/patches/0051-batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/batman-adv/patches/0051-batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 000000000..4493abfc4 --- /dev/null +++ b/batman-adv/patches/0051-batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,30 @@ +From: Sven Eckelmann +Date: Thu, 28 May 2026 21:14:39 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +When batadv_tp_handle_out_of_order inserts a new entry in the list of +unacked (out of order) packets, it searches from the entry with the newest +sequence number towards oldest sequence number. If an entry is found which +is older than the newly entry, the new entry has to be added after the +found one to keep the ascending order. + +But for this operation list_add_tail() was used. But this function adds an +entry _before_ another one. As result, the list would contain a lot of +swapped sequence numbers. The consumer of this list +(batadv_tp_ack_unordered()) would then fail to correctly ack packets. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=36c273e70322541cafe0d390a342ac56e01c403a + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_orde + * one is attached _after_ it. In this way the list is kept in + * ascending order + */ +- list_add_tail(&new->list, &un->list); ++ list_add(&new->list, &un->list); + added = true; + break; + } diff --git a/batman-adv/patches/0052-batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/batman-adv/patches/0052-batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 000000000..282b2be2d --- /dev/null +++ b/batman-adv/patches/0052-batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,28 @@ +From: Sven Eckelmann +Date: Thu, 28 May 2026 21:14:39 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +When an ack with a sequence number equal to the last_acked is received, the +dup_acks counter is increased to decide whether fast retransmit should be +performed. Only when the sequence numbers are not equal, the dup_acks is +set to the initial value (0). + +But if the initial packet would have the sequence number +BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would +operate on an undefined starting value. It is therefore required to have it +explicitly initialized during the start of the sender session. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=77e274fb53940599cef5580f93540f0da53f99e5 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv + tp_vars->icmp_uid = icmp_uid; + + tp_vars->last_sent = BATADV_TP_FIRST_SEQ; ++ atomic_set(&tp_vars->dup_acks, 0); + atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ); + tp_vars->fast_recovery = false; + tp_vars->recover = BATADV_TP_FIRST_SEQ; diff --git a/batman-adv/patches/0053-batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/batman-adv/patches/0053-batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 000000000..78f786bb1 --- /dev/null +++ b/batman-adv/patches/0053-batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,26 @@ +From: Sven Eckelmann +Date: Thu, 28 May 2026 21:14:39 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd +is only initialixed (to 0) when a duplicate Ack was received or when cwnd +is below the ss_threshold. + +Just initialize the cwnd during the initialization to avoid any potential +access of uninitialized data. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=bd8fbe31a7ef073676b42133861cc2c7abff6d32 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv + * soft_interface, hence its MTU + */ + tp_vars->cwnd = BATADV_TP_PLEN * 3; ++ tp_vars->dec_cwnd = 0; ++ + /* at the beginning initialise the SS threshold to the biggest possible + * window size, hence the AWND size + */ diff --git a/batman-adv/patches/0054-batman-adv-tp_meter-avoid-window-underflow.patch b/batman-adv/patches/0054-batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 000000000..1879f090d --- /dev/null +++ b/batman-adv/patches/0054-batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,42 @@ +From: Sven Eckelmann +Date: Thu, 28 May 2026 21:14:39 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +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. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=f54aacc76b7f45335b5fe9e19a328be2bbb448fb + +--- 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; + } diff --git a/batman-adv/patches/0055-batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/batman-adv/patches/0055-batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 000000000..a264ddb03 --- /dev/null +++ b/batman-adv/patches/0055-batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,50 @@ +From: Sven Eckelmann +Date: Sun, 31 May 2026 12:38:05 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in +batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics. + + ((mss * 8) ** 2) / (cwnd * 8) + +In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the +left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around +and be 0 - resulting in: + + ((mss * 8) ** 2) / 0 + +This is of course invalid and cannot be calculated. The calculation should +must be simplified to avoid this overflow: + + (mss ** 2) * 8 / cwnd + +It will keep the precision enhancement from the scaling (by 8) but avoid +the overflow in the divisor. + +In theory, there could still be an overflow in the dividend. It is at the +moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an +imminent problem. But allowing it to use the whole u32 bit range, would +mean that it can still use up to 67 bits. To keep this calculation safe for +32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits - +or in other words: must never be larger than 16383. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=71a89fe15f263f2b08861d46c7d0859b7baa8184 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct + return; + } + ++ /* prevent overflow in (mss * mss) << 3 */ ++ mss = min_t(u32, mss, (1U << 14) - 1); ++ + /* increment CWND at least of 1 (section 3.1 of RFC5681) */ + tp_vars->dec_cwnd += max_t(u32, 1U << 3, +- ((mss * mss) << 6) / (tp_vars->cwnd << 3)); ++ ((mss * mss) << 3) / tp_vars->cwnd); + if (tp_vars->dec_cwnd < (mss << 3)) { + spin_unlock_bh(&tp_vars->cwnd_lock); + return; diff --git a/batman-adv/patches/0056-batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/batman-adv/patches/0056-batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 000000000..bd4afed65 --- /dev/null +++ b/batman-adv/patches/0056-batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,34 @@ +From: Sven Eckelmann +Date: Mon, 1 Jun 2026 11:00:23 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +The fast recovery precondition checks if the recover (initialized to +BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is +only updated when this check is successful, it will never enter the fast +recovery mode. + +According to RFC6582 Section 3.2 step 2, the check should actually be +different: + +> When the third duplicate ACK is received, the TCP sender first +> checks the value of recover to see if the Cumulative +> Acknowledgment field covers more than recover + +The precondition must therefore check if recover is smaller than the +received ack - basically swapping the operands of the current check. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=e710bc19ca21be957cac15981e5f52b7c9983679 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct ba + if (atomic_read(&tp_vars->dup_acks) != 3) + goto out; + +- if (recv_ack >= tp_vars->recover) ++ if (tp_vars->recover >= recv_ack) + goto out; + + /* if this is the third duplicate ACK do Fast Retransmit */ diff --git a/batman-adv/patches/0057-batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/batman-adv/patches/0057-batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 000000000..11737299f --- /dev/null +++ b/batman-adv/patches/0057-batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,25 @@ +From: Sven Eckelmann +Date: Mon, 1 Jun 2026 11:47:29 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery detection + +The recover variable and the last_sent sequence number are initialized on +purpose as a really high value which will wrap-around after the first 2000 +bytes. The fast recovery precondition must therefore not use simple integer +comparisons but use helpers which are aware of the sequence number +wrap-arounds. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=f6463f77ba6b8453b5efe994e7c6b532afec16bf + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct ba + if (atomic_read(&tp_vars->dup_acks) != 3) + goto out; + +- if (tp_vars->recover >= recv_ack) ++ if (!batadv_seq_before(tp_vars->recover, recv_ack)) + goto out; + + /* if this is the third duplicate ACK do Fast Retransmit */ diff --git a/batman-adv/patches/0058-batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/batman-adv/patches/0058-batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 000000000..0a8f8a904 --- /dev/null +++ b/batman-adv/patches/0058-batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,60 @@ +From: Sven Eckelmann +Date: Tue, 2 Jun 2026 16:51:42 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +When the receiver variables (aka "session") are initialized, then they are +added to the list of sessions before the timer is set up. A RCU protected +reader could therefore find the entry and run mod_setup before +batadv_tp_init_recv() finished the timer initialization. + +The same is true for batadv_tp_start(), which must first initialize the +finish_work and the test_length to avoid a similar problem. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=c0e15b75ee2cdab1668ef29937691fef87915210 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv + tp_vars->prerandom_offset = 0; + spin_lock_init(&tp_vars->prerandom_lock); + +- kref_get(&tp_vars->refcount); +- hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +- spin_unlock_bh(&bat_priv->tp_list_lock); +- + tp_vars->test_length = test_length; + if (!tp_vars->test_length) + tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH; + ++ /* init work item for finished tp tests */ ++ INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); ++ ++ kref_get(&tp_vars->refcount); ++ hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); ++ spin_unlock_bh(&bat_priv->tp_list_lock); ++ + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: starting throughput meter towards %pM (length=%ums)\n", + dst, test_length); + +- /* init work item for finished tp tests */ +- INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); +- + /* start tp kthread. This way the write() call issued from userspace can + * happily return and avoid to block + */ +@@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv * + INIT_LIST_HEAD(&tp_vars->unacked_list); + + kref_get(&tp_vars->refcount); +- hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); ++ timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + + kref_get(&tp_vars->refcount); +- timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); ++ hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + + batadv_tp_reset_receiver_timer(tp_vars); + diff --git a/batman-adv/patches/0059-batman-adv-bla-annotate-lasttime-access-with-READ-WR.patch b/batman-adv/patches/0059-batman-adv-bla-annotate-lasttime-access-with-READ-WR.patch new file mode 100644 index 000000000..5ec173fcf --- /dev/null +++ b/batman-adv/patches/0059-batman-adv-bla-annotate-lasttime-access-with-READ-WR.patch @@ -0,0 +1,142 @@ +From: Sven Eckelmann +Date: Tue, 26 May 2026 21:50:51 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +The lasttime field for claim, backbone_gw, and loopdetect tracks the +jiffies value of the most recent activity and is used to detect timeouts. +These accesses are not consistently protected by a lock, so +READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler +optimizations. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=1c9d4fb01236b4061515bc791ca8dc7837db3fa3 + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -512,7 +512,7 @@ batadv_bla_get_backbone_gw(struct batadv + return NULL; + + entry->vid = vid; +- entry->lasttime = jiffies; ++ WRITE_ONCE(entry->lasttime, jiffies); + entry->crc = BATADV_BLA_CRC_INIT; + entry->bat_priv = bat_priv; + spin_lock_init(&entry->crc_lock); +@@ -580,7 +580,7 @@ batadv_bla_update_own_backbone_gw(struct + if (unlikely(!backbone_gw)) + return; + +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + batadv_backbone_gw_put(backbone_gw); + } + +@@ -714,7 +714,7 @@ static void batadv_bla_add_claim(struct + ether_addr_copy(claim->addr, mac); + spin_lock_init(&claim->backbone_lock); + claim->vid = vid; +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + kref_get(&backbone_gw->refcount); + claim->backbone_gw = backbone_gw; + kref_init(&claim->refcount); +@@ -736,7 +736,7 @@ static void batadv_bla_add_claim(struct + return; + } + } else { +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + if (claim->backbone_gw == backbone_gw) + /* no need to register a new backbone */ + goto claim_free_ref; +@@ -769,7 +769,7 @@ static void batadv_bla_add_claim(struct + spin_lock_bh(&backbone_gw->crc_lock); + backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); + spin_unlock_bh(&backbone_gw->crc_lock); +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + + claim_free_ref: + batadv_claim_put(claim); +@@ -858,7 +858,7 @@ static bool batadv_handle_announce(struc + return true; + + /* handle as ANNOUNCE frame */ +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + crc = ntohs(*((__force __be16 *)(&an_addr[4]))); + + batadv_dbg(BATADV_DBG_BLA, bat_priv, +@@ -1253,7 +1253,7 @@ static void batadv_bla_purge_backbone_gw + head, hash_entry) { + if (now) + goto purge_now; +- if (!batadv_has_timed_out(backbone_gw->lasttime, ++ if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime), + BATADV_BLA_BACKBONE_TIMEOUT)) + continue; + +@@ -1334,7 +1334,7 @@ static void batadv_bla_purge_claims(stru + primary_if->net_dev->dev_addr)) + goto skip; + +- if (!batadv_has_timed_out(claim->lasttime, ++ if (!batadv_has_timed_out(READ_ONCE(claim->lasttime), + BATADV_BLA_CLAIM_TIMEOUT)) + goto skip; + +@@ -1494,7 +1494,7 @@ static void batadv_bla_periodic_work(str + eth_random_addr(bat_priv->bla.loopdetect_addr); + bat_priv->bla.loopdetect_addr[0] = 0xba; + bat_priv->bla.loopdetect_addr[1] = 0xbe; +- bat_priv->bla.loopdetect_lasttime = jiffies; ++ WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies); + atomic_set(&bat_priv->bla.loopdetect_next, + BATADV_BLA_LOOPDETECT_PERIODS); + +@@ -1515,7 +1515,7 @@ static void batadv_bla_periodic_work(str + primary_if->net_dev->dev_addr)) + continue; + +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + + batadv_bla_send_announce(bat_priv, backbone_gw); + if (send_loopdetect) +@@ -1900,7 +1900,7 @@ batadv_bla_loopdetect_check(struct batad + /* If the packet came too late, don't forward it on the mesh + * but don't consider that as loop. It might be a coincidence. + */ +- if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime, ++ if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime), + BATADV_BLA_LOOPDETECT_TIMEOUT)) + return true; + +@@ -2016,7 +2016,7 @@ bool batadv_bla_rx(struct batadv_priv *b + + if (own_claim) { + /* ... allow it in any case */ +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + goto allow; + } + +@@ -2118,7 +2118,7 @@ bool batadv_bla_tx(struct batadv_priv *b + /* if yes, the client has roamed and we have + * to unclaim it. + */ +- if (batadv_has_timed_out(claim->lasttime, 100)) { ++ if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) { + /* only unclaim if the last claim entry is + * older than 100 ms to make sure we really + * have a roaming client here. +@@ -2372,7 +2372,7 @@ batadv_bla_backbone_dump_entry(struct sk + backbone_crc = backbone_gw->crc; + spin_unlock_bh(&backbone_gw->crc_lock); + +- msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime); ++ msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime)); + + if (is_own) + if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) { diff --git a/batman-adv/patches/0060-batman-adv-prevent-ELP-transmission-interval-underfl.patch b/batman-adv/patches/0060-batman-adv-prevent-ELP-transmission-interval-underfl.patch new file mode 100644 index 000000000..d7181c267 --- /dev/null +++ b/batman-adv/patches/0060-batman-adv-prevent-ELP-transmission-interval-underfl.patch @@ -0,0 +1,37 @@ +From: Sven Eckelmann +Date: Fri, 29 May 2026 23:36:43 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts +is randomly chosen between (elp_interval - BATADV_JITTER) and +(elp_interval + BATADV_JITTER). The configured elp_interval must therefore +be larger or equal to BATADV_JITTER to avoid that it causes an underflow of +the unsigned integer. If this would happen, then a "fast" ELP interval +would turn into a "day long" delay. + +At the same time, it must not be larger than the maximum value the variable +can store. + +Fixes: 914db9648ed5 ("batman-adv: Add elp_interval hardif genl configuration") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=120f830ef0d0befbadf16623b5c1ec62a73e8b43 + +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -936,9 +936,15 @@ static int batadv_netlink_set_hardif(str + #ifdef CONFIG_BATMAN_ADV_BATMAN_V + + if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) { ++ u32 elp_interval; ++ + attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; ++ elp_interval = nla_get_u32(attr); ++ ++ elp_interval = min_t(u32, elp_interval, INT_MAX); ++ elp_interval = max_t(u32, elp_interval, BATADV_JITTER); + +- atomic_set(&hard_iface->bat_v.elp_interval, nla_get_u32(attr)); ++ atomic_set(&hard_iface->bat_v.elp_interval, elp_interval); + } + + if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) { diff --git a/batman-adv/patches/0061-batman-adv-tp_meter-initialize-last_recv_time-during.patch b/batman-adv/patches/0061-batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 000000000..5e25a92a8 --- /dev/null +++ b/batman-adv/patches/0061-batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,53 @@ +From: Sven Eckelmann +Date: Thu, 4 Jun 2026 10:58:51 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +The last_recv_time is the most important indicator for a receiver session +to figure out whether a session timed out or not. But this information was +only initialized after the session was added to the tp_receiver_list and +after the timer was started. + +In the worst case, the timer (function) could have tried to access this +information before the actual initialization was reached. Like rest of the +variables of the tp_meter receiver session, this field has to be filled out +before any other (parallel running) context has the chance to access it. + +Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/?id=44af440c2b658f89fee58565f85016772ce52c82 + +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1403,8 +1403,10 @@ batadv_tp_init_recv(struct batadv_priv * + + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); +- if (tp_vars) ++ if (tp_vars) { ++ tp_vars->last_recv_time = jiffies; + goto out_unlock; ++ } + + if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, +@@ -1432,6 +1434,8 @@ batadv_tp_init_recv(struct batadv_priv * + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + ++ tp_vars->last_recv_time = jiffies; ++ + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + +@@ -1480,9 +1484,9 @@ static void batadv_tp_recv_msg(struct ba + icmp->orig); + goto out; + } +- } + +- tp_vars->last_recv_time = jiffies; ++ tp_vars->last_recv_time = jiffies; ++ } + + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK