From: Sasha Levin Date: Sun, 28 Jun 2026 03:32:56 +0000 (-0400) Subject: Fixes for all trees X-Git-Tag: v5.10.260~67 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aaf6f1c288b250ba85849d70797950b0626e0582;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-5.10/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..d96c90ea45 --- /dev/null +++ b/queue-5.10/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From 836f1b3edeffe28bc8547cde960bed26f4936979 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:38 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 00185ed4940f23..5593ac0aae378a 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -517,7 +517,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig, + 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); +@@ -585,7 +585,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, + if (unlikely(!backbone_gw)) + return; + +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + batadv_backbone_gw_put(backbone_gw); + } + +@@ -719,7 +719,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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); +@@ -741,7 +741,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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; +@@ -774,7 +774,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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); +@@ -863,7 +863,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + 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, +@@ -1258,7 +1258,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) + 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; + +@@ -1339,7 +1339,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, + 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; + +@@ -1499,7 +1499,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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); + +@@ -1520,7 +1520,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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) +@@ -1907,7 +1907,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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; + +@@ -2023,7 +2023,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + + if (own_claim) { + /* ... allow it in any case */ +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + goto allow; + } + +@@ -2127,7 +2127,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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. +@@ -2513,7 +2513,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-5.10/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..b01961def6 --- /dev/null +++ b/queue-5.10/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From d4205c11455b8ac4b18ee868a6d8dacf11d0dc13 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:50 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 9d27d7d7b2b4b4..592b61a2e7911e 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -216,10 +216,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -346,6 +349,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-5.10/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..f394809232 --- /dev/null +++ b/queue-5.10/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From 6a5ed9c966eba4121f22365627df5eb721c4825e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:42 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 95c88bbdbcbee0..43dfe86f07a9b1 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -421,6 +421,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-5.10/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..d01f741464 --- /dev/null +++ b/queue-5.10/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,107 @@ +From 5b751cd0ff9e9ba5ef494a22562e732cf0e28e10 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:41 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 724e06e3e799f4..95c88bbdbcbee0 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -386,6 +386,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -395,7 +397,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_dst; +@@ -418,12 +421,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index 881ef328b6cd7e..aba6eecb7d0e76 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 9e5c71e406ffc6..f6df66a534c41e 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1143,10 +1143,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-5.10/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..c803a002fa --- /dev/null +++ b/queue-5.10/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From 19eb8cbdb04997e62a78049f820257384df1ab95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:39 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index 0b5cb03859b25b..e5a0b2c97162d2 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -939,9 +939,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-5.10/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..f7eea35d49 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From 3fa68ff67e2280c13d117673f9723013154072b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:37 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index a4df826e4ece71..a015ff73812f5e 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1106,21 +1106,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + 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 + */ +@@ -1438,10 +1438,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_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); + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-5.10/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..eb510ef616 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From b6e51ec44a0c883b75f7e67cbe4d9601b984d298 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:45 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c7de8dfe9b65bd..91392e48514d64 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1193,7 +1193,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1433,7 +1433,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1463,7 +1463,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1514,7 +1514,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-5.10/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..58c4389432 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From 089534cc33b4073c018a8ad5c383cd365a8ce1ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:34 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 10953096996ab1..118cb5dd285c2d 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -153,9 +153,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-5.10/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..bbd56ddc7b --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From 9f8607e45084aa12a17ab12b562ba613bc0dda79 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:33 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index d8ad58ccef2608..10953096996ab1 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -825,10 +825,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-5.10/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..6a8c193c00 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From 7e3e1a906a157788ca4d4dd490fdc3ad82f70a09 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:35 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 118cb5dd285c2d..cd196842478686 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -738,7 +738,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-5.10/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..4e106eabe1 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From a80b9f4c4ce2e7969b2e23ca2f4724152cda9226 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:47 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c01ff6e72b5da5..3a1561f5791987 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1293,7 +1293,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1302,22 +1303,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1484,7 +1480,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1519,15 +1515,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1537,8 +1535,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-5.10/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..5a2d5fac78 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From b25b3f5c58ef759f60de8fbd9e1debdd98a2fe82 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:36 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index cd196842478686..a4df826e4ece71 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -738,7 +738,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-5.10/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..560c16667e --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From cdb8f527e0043cdd1f8257b7c98126e11d6bc309 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:32 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 1699fb8f8c82d4..d8ad58ccef2608 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1065,6 +1065,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + * 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 + */ +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-5.10/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..6045f9dd81 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From 3033c1b305408031acd97f766e20f0fbde27474a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:31 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c837a91879ac2f..1699fb8f8c82d4 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1055,6 +1055,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-5.10/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..cb6d9494fc --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From e126b797fc3919ca9c90015d9ea74e6193dea658 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:40 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index a015ff73812f5e..ea5640242ddc2e 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1412,8 +1412,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_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, +@@ -1440,6 +1442,8 @@ batadv_tp_init_recv(struct batadv_priv *bat_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); + +@@ -1488,9 +1492,9 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-5.10/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..51a65e963e --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From 2ec7e4f20d84a902f39fe2057c31c2d6942853d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:30 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index fc2f0b49e5a06e..c837a91879ac2f 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1334,7 +1334,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-5.10/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..21a11b2b21 --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From e28be785410fc412794c83b8096704f83ead84e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:46 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 91392e48514d64..c01ff6e72b5da5 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1303,6 +1303,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1319,12 +1320,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1373,9 +1373,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1385,6 +1382,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1392,7 +1390,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1410,7 +1407,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1489,6 +1485,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1517,6 +1514,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1528,8 +1527,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1543,11 +1544,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + if (likely(tp_vars)) +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index b12b0851df6e7e..437d651a1cffe3 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1505,7 +1505,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-5.10/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..fc7ebde2bc --- /dev/null +++ b/queue-5.10/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From 5ea688ad68a89be8b5ccbdc69c8f7c9751c4843d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:44 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index ea5640242ddc2e..c7de8dfe9b65bd 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -86,6 +86,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1205,6 +1210,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1317,6 +1323,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1347,12 +1354,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1389,6 +1408,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1438,6 +1458,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 28f239421f74a3..b12b0851df6e7e 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1508,6 +1508,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-5.10/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..6ce7f98d26 --- /dev/null +++ b/queue-5.10/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,46 @@ +From e25b407d71aa2945e9d8d63eba360cad2f7a40d2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:48 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 79da90b9cf0659..c323660897d43d 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -486,6 +486,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + /* DEL+ADD in the same orig interval have no effect and can be + * removed to avoid silly behaviour on the receiver side. The + * other way around (ADD+DEL) can happen in case of roaming of +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tt-track-roam-count-per-vid.patch b/queue-5.10/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..085499ab81 --- /dev/null +++ b/queue-5.10/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,89 @@ +From bf37b2bb7138f9d53e9df7185103e5d620b76d4b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:49 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index c323660897d43d..4b3f39d6ddaf5b 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3753,6 +3753,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the soft interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3760,7 +3761,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3773,6 +3774,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3794,6 +3798,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3830,7 +3835,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 437d651a1cffe3..88831374e81747 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2059,6 +2059,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-5.10/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..4758636027 --- /dev/null +++ b/queue-5.10/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,155 @@ +From 45e167d455f760a3395459b8f99b3bfbf459f436 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:52 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 84f0fb175b3313..2c224951d0195c 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -394,7 +394,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + } else { + if (!src) + return NET_RX_SUCCESS; +@@ -413,6 +412,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + u8 *src, u8 *dst, + void *tvlv_value, u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -474,12 +517,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler, + &bat_priv->tvlv.handler_list, list) { +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 88831374e81747..79ffd25fd4eba2 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2492,13 +2492,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + /** +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-5.10/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..dcd18c7515 --- /dev/null +++ b/queue-5.10/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,106 @@ +From 617eab49d1ecb5560d3742c791fc337ffcf694df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:51 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/tvlv.c | 6 ++++++ + 3 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 93c2c5f6facc0d..cc4fbfd0b75530 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -312,14 +312,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 12de9b44064dd4..2a62ab13bf87bb 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -864,14 +864,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 8da8184a2ebdfc..84f0fb175b3313 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -448,6 +448,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-5.10/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-5.10/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..da62e25d75 --- /dev/null +++ b/queue-5.10/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From cefdcf236b82687578ad4d6ed69410651ac18441 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:09:43 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index 6dc39fc0350e6a..7b9bc8a4bd48f5 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -1084,6 +1084,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 939aa4b303ad98..12de9b44064dd4 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -255,11 +255,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -422,6 +429,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -437,6 +448,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 238d9824c2d62f..28f239421f74a3 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -131,6 +131,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index e48b2b93a9..70b846084f 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -31,3 +31,26 @@ crypto-qat-replace-kzalloc-copy_from_user-with-memdu.patch crypto-qat-return-pointer-directly-in-adf_ctl_alloc_.patch crypto-qat-remove-unused-character-device-and-ioctls.patch net-sched-act_pedit-fix-action-bind-logic.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch diff --git a/queue-5.15/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-5.15/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..de052d0cc2 --- /dev/null +++ b/queue-5.15/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From 2906041178585aacd76f532e70461277492b14b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:48 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 452e78fd70c039..0b5222ac4f59a1 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -511,7 +511,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig, + 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); +@@ -579,7 +579,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, + if (unlikely(!backbone_gw)) + return; + +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + batadv_backbone_gw_put(backbone_gw); + } + +@@ -713,7 +713,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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); +@@ -735,7 +735,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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; +@@ -768,7 +768,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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); +@@ -857,7 +857,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + 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, +@@ -1252,7 +1252,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) + 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; + +@@ -1333,7 +1333,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, + 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; + +@@ -1493,7 +1493,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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); + +@@ -1514,7 +1514,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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) +@@ -1899,7 +1899,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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; + +@@ -2015,7 +2015,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + + if (own_claim) { + /* ... allow it in any case */ +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + goto allow; + } + +@@ -2117,7 +2117,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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. +@@ -2371,7 +2371,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-5.15/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..be40708487 --- /dev/null +++ b/queue-5.15/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From eb233b47819142a2b5c2d28e31bf8fd28ac14c6f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:02 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 9f561ea95f730d..b8d471dae2888c 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-5.15/batman-adv-ensure-bcast-is-writable-before-modifying.patch new file mode 100644 index 0000000000..cac8771865 --- /dev/null +++ b/queue-5.15/batman-adv-ensure-bcast-is-writable-before-modifying.patch @@ -0,0 +1,50 @@ +From a417ea7a6294239dbbcf64e4eca9d2d22dae1b7c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:51 +0200 +Subject: batman-adv: ensure bcast is writable before modifying TTL + +From: Sven Eckelmann + +commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or used skb_cow() to ensure that the data part is not +shared. + +The old implementation used a shared queue and created copies before +attempting to write to it. But with the new implementation, the broadcast +packet is already modified when it gets received. Potentially writing to +shared buffers in this process. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to rebroadcast the +packet. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 970d0d7ccc981a..503c8c9381ebe9 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1198,6 +1198,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto free_skb; + ++ /* create a copy of the skb, if needed, to modify it. */ ++ if (skb_cow(skb, ETH_HLEN) < 0) ++ goto free_skb; ++ ++ bcast_packet = (struct batadv_bcast_packet *)skb->data; ++ + if (bcast_packet->ttl-- < 2) + goto free_skb; + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-5.15/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch new file mode 100644 index 0000000000..4831926288 --- /dev/null +++ b/queue-5.15/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch @@ -0,0 +1,118 @@ +From 82593888262b84a5910c07b4546692e42980d3e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:52 +0200 +Subject: batman-adv: fix (m|b)cast csum after decrementing TTL + +From: Sven Eckelmann + +commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. + +The broadcast and multicast packets can be received at the same time by the +local system and forwarded to other nodes. Both are simply decrementing the +TTL at the beginning of the receive path - independent of chosen paths +(receive/forward). But such a modification of the data conflicts with the +hw csum. This is not a problem when the packet is directly forwarded but +can cause errors in the local receive path. + +Such a problem can then trigger a "hw csum failure". The receiver path must +therefore ensure that the csum is fixed for each modification of the +payload before batadv_interface_rx() is reached. + +Since all batman-adv packet types with a ttl have it as u8 at offset 2, a +helper can be used for all of them. But it is only used at the moment for +batadv_bcast_packet and batadv_mcast_packet because they are the only ones +which deliver the packet locally but unconditionally modify the TTL. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") +[ Context, Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 55 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 503c8c9381ebe9..0fb37993fd4604 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -205,6 +206,58 @@ bool batadv_check_management_packet(struct sk_buff *skb, + return true; + } + ++/** ++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe ++ * @skb: the received packet with @skb->data pointing to the batman-adv header ++ * ++ * Supports the following packet types, all of which carry the TTL at offset 2: ++ * ++ * - batadv_ogm_packet ++ * - batadv_ogm2_packet ++ * - batadv_icmp_header ++ * - batadv_icmp_packet ++ * - batadv_icmp_tp_packet ++ * - batadv_icmp_packet_rr ++ * - batadv_unicast_packet ++ * - batadv_frag_packet ++ * - batadv_bcast_packet ++ * - batadv_mcast_packet ++ * - batadv_coded_packet ++ * - batadv_unicast_tvlv_packet ++ * ++ * Return: true if the packet may be forwarded (ttl decremented), ++ * false if it must be dropped (ttl would expire) ++ */ ++static bool batadv_skb_decrement_ttl(struct sk_buff *skb) ++{ ++ static const size_t ttl_offset = 2; ++ u8 *ttl_pos; ++ ++ BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); ++ ++ ttl_pos = skb->data + ttl_offset; ++ ++ /* would expire on this hop -> drop, leave header + csum untouched */ ++ if (*ttl_pos < 2) ++ return false; ++ ++ skb_postpull_rcsum(skb, ttl_pos, 1); ++ (*ttl_pos)--; ++ skb_postpush_rcsum(skb, ttl_pos, 1); ++ ++ return true; ++} ++ + /** + * batadv_recv_my_icmp_packet() - receive an icmp packet locally + * @bat_priv: the bat priv with all the soft interface information +@@ -1204,7 +1257,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + +- if (bcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-5.15/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..7fadc37cc1 --- /dev/null +++ b/queue-5.15/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From f54f596f8a17b4218aa235f494ef561c23fdbb44 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:54 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index fbf030c57ac04d..0138b0953e10b5 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -420,6 +420,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-5.15/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..de322ffc76 --- /dev/null +++ b/queue-5.15/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,107 @@ +From ce3d7d051bd7a1bec2e392eb92fca7b5cb7a7938 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:53 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 4c193194034155..fbf030c57ac04d 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -385,6 +385,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -394,7 +396,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_dst; +@@ -417,12 +420,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index dbf0871f870303..51e281027ab630 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 0fb37993fd4604..596e4cc47046a2 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1174,10 +1174,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-5.15/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..60ca806ee1 --- /dev/null +++ b/queue-5.15/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From d1211e333781f3bc9b976568f3edeaf888a94325 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:49 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index bbd6ecf1678c97..cb50b46cb8d30d 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -941,9 +941,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-5.15/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..c72cf0f2f4 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From ee45ff9e66206c819dbe2d926138fdae02a0a9b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:47 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 7385cd02abf9f0..257f8d042d158e 100644 +--- 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 *bat_priv, const u8 *dst, + 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 *bat_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); + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-5.15/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..5d2d473e72 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From 584a72aa30797ddbe2e61b73e0d668c7012222c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:57 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 105cda5b0b2cda..503819e9839c62 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-5.15/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..320cb41312 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From ca58056f2de5dcf4ab6996cdf7d6860e34aac5da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:44 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 3fd46999f36745..f961beac12282e 100644 +--- 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 batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-5.15/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..ba4fb6d7ef --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From 049569def25e4a9f5821e48e92e2a69812e37942 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:43 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f7b0ce3f8e2aef..3fd46999f36745 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-5.15/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..fd5a7e6603 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From 1e20d87ae2704e3b2d4bf5892f1ee18421ca42d4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:45 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f961beac12282e..1fc8d6e6f99b30 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-5.15/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..411a279c73 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From 18d62c85f98b28e7de10db80f1857980c7d159f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:59 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 83749d7b4fd112..5514fef54b0a9a 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-5.15/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..079f48d470 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From 5fdc6dd0ccb9afc5fe090618e841aba968c13545 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:46 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 1fc8d6e6f99b30..7385cd02abf9f0 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-5.15/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..8ed5559e78 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From 0d496b785dde951ccba9bef8260da9ffa0c1ddfe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:42 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 2b68eacd7bf51d..f7b0ce3f8e2aef 100644 +--- 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 *bat_priv, const u8 *dst, + * 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 + */ +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-5.15/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..48df58d8b1 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From 26d78a9abbff74958875494c37408f37f111b6b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:41 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index e8792e1e4eedfa..2b68eacd7bf51d 100644 +--- 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 *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-5.15/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..bc624c722e --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From 2f594743543b43d26878981f25380c6758124313 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:50 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 257f8d042d158e..9e31d86c4b87f5 100644 +--- 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 *bat_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 *bat_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 batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-5.15/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..e5a1d3c0a5 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From bbdabf1821e04892e99790640f6eacf39c8c8ff5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:40 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c7b48d51e2aed2..e8792e1e4eedfa 100644 +--- 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_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-5.15/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..158c2591c2 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From 8284d148758d64bf934574dd56ee614a1fdf2198 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:58 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 503819e9839c62..83749d7b4fd112 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + batadv_tp_vars_put(tp_vars); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index d7fb3046d04412..e1ca1359415bc7 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1491,7 +1491,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-5.15/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..35b5b1b2f7 --- /dev/null +++ b/queue-5.15/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From df7081f69f2061e11ca745b2452ca6c98c96f3f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:56 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 9e31d86c4b87f5..105cda5b0b2cda 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -87,6 +87,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index d298a3983fab97..d7fb3046d04412 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1494,6 +1494,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-5.15/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..a227db4092 --- /dev/null +++ b/queue-5.15/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,46 @@ +From a17c210562764a37e97b52b5137dbf30b08c25e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:00 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 8ab1257bade048..4bc73aceeb89f6 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + /* DEL+ADD in the same orig interval have no effect and can be + * removed to avoid silly behaviour on the receiver side. The + * other way around (ADD+DEL) can happen in case of roaming of +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tt-track-roam-count-per-vid.patch b/queue-5.15/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..7c0d003e28 --- /dev/null +++ b/queue-5.15/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,89 @@ +From 674b41f57012ff41b1bd6b64ae87a79ea87fa6f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:01 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 4bc73aceeb89f6..53ac49ca2c2f1d 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the soft interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index e1ca1359415bc7..f0d5701265ef48 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2034,6 +2034,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-5.15/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..7a6bfb1fe4 --- /dev/null +++ b/queue-5.15/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,155 @@ +From 792d8b5b518a080dc39a800ee7ce47f591dfcf58 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:04 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 66c2043dc1df91..6eb95bd7a4bc36 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -394,7 +394,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + } else { + if (!src) + return NET_RX_SUCCESS; +@@ -413,6 +412,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + u8 *src, u8 *dst, + void *tvlv_value, u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -473,12 +516,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler, + &bat_priv->tvlv.handler_list, list) { +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index f0d5701265ef48..586679ad574f72 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2447,13 +2447,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-5.15/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..18a9d9c1b5 --- /dev/null +++ b/queue-5.15/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,106 @@ +From 289176ea3b5875d64153e062f8e313f3b7317877 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:03 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/tvlv.c | 6 ++++++ + 3 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index 669f77eed073a0..a424c5b7a2462c 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index c7d15887c47c8e..641f3dbde1bde2 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -854,14 +854,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index de0c139426839c..66c2043dc1df91 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -448,6 +448,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-5.15/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-5.15/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..4654b9ac27 --- /dev/null +++ b/queue-5.15/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From 6fcda727f2744048d3e2bcb36b04d68ae6c7e5cc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:55 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index 651e01b86141e3..34874942ae8d03 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -838,6 +838,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 63337e02cf2f11..c7d15887c47c8e 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -255,11 +255,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -422,6 +429,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -437,6 +448,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index f1a835edd115c7..d298a3983fab97 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 0f0db55aee..0a9cc78a3f 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -32,3 +32,28 @@ kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch-23529 kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-che.patch kselftest-arm64-signal-skip-sve-signal-test-if-not-e.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-ensure-bcast-is-writable-before-modifying.patch +batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch diff --git a/queue-6.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..e923424442 --- /dev/null +++ b/queue-6.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From 5a52ae127a87d497c511fc702390e9affad4a1bf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:07 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index cfb1eb25c6ac4d..ac12c06f11b89e 100644 +--- 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_priv *bat_priv, const u8 *orig, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, u8 *an_addr, + 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(struct batadv_priv *bat_priv, int now) + 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(struct batadv_priv *bat_priv, + 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(struct work_struct *work) + 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(struct work_struct *work) + 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 batadv_priv *bat_priv, struct sk_buff *skb, + /* 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 *bat_priv, struct sk_buff *skb, + + 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 *bat_priv, struct sk_buff *skb, + /* 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_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..13985e8141 --- /dev/null +++ b/queue-6.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From 27049ec18ed615ee2fbd658140d74246d7a81057 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:21 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index e8d706d86aa82a..36462d9288c7ee 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch new file mode 100644 index 0000000000..e5526f3b73 --- /dev/null +++ b/queue-6.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch @@ -0,0 +1,50 @@ +From 6632a50cba14b1852f5517c0df65ee5faddbcd25 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:10 +0200 +Subject: batman-adv: ensure bcast is writable before modifying TTL + +From: Sven Eckelmann + +commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or used skb_cow() to ensure that the data part is not +shared. + +The old implementation used a shared queue and created copies before +attempting to write to it. But with the new implementation, the broadcast +packet is already modified when it gets received. Potentially writing to +shared buffers in this process. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to rebroadcast the +packet. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 83f31494ea4d92..0709f42c54b496 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1199,6 +1199,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto free_skb; + ++ /* create a copy of the skb, if needed, to modify it. */ ++ if (skb_cow(skb, ETH_HLEN) < 0) ++ goto free_skb; ++ ++ bcast_packet = (struct batadv_bcast_packet *)skb->data; ++ + if (bcast_packet->ttl-- < 2) + goto free_skb; + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch new file mode 100644 index 0000000000..3ec54515d0 --- /dev/null +++ b/queue-6.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch @@ -0,0 +1,118 @@ +From 97713caef9bd3e682405f26528a5983714af0dad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:11 +0200 +Subject: batman-adv: fix (m|b)cast csum after decrementing TTL + +From: Sven Eckelmann + +commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. + +The broadcast and multicast packets can be received at the same time by the +local system and forwarded to other nodes. Both are simply decrementing the +TTL at the beginning of the receive path - independent of chosen paths +(receive/forward). But such a modification of the data conflicts with the +hw csum. This is not a problem when the packet is directly forwarded but +can cause errors in the local receive path. + +Such a problem can then trigger a "hw csum failure". The receiver path must +therefore ensure that the csum is fixed for each modification of the +payload before batadv_interface_rx() is reached. + +Since all batman-adv packet types with a ttl have it as u8 at offset 2, a +helper can be used for all of them. But it is only used at the moment for +batadv_bcast_packet and batadv_mcast_packet because they are the only ones +which deliver the packet locally but unconditionally modify the TTL. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") +[ Context, Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 55 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 0709f42c54b496..e9347b25d2087a 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -205,6 +206,58 @@ bool batadv_check_management_packet(struct sk_buff *skb, + return true; + } + ++/** ++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe ++ * @skb: the received packet with @skb->data pointing to the batman-adv header ++ * ++ * Supports the following packet types, all of which carry the TTL at offset 2: ++ * ++ * - batadv_ogm_packet ++ * - batadv_ogm2_packet ++ * - batadv_icmp_header ++ * - batadv_icmp_packet ++ * - batadv_icmp_tp_packet ++ * - batadv_icmp_packet_rr ++ * - batadv_unicast_packet ++ * - batadv_frag_packet ++ * - batadv_bcast_packet ++ * - batadv_mcast_packet ++ * - batadv_coded_packet ++ * - batadv_unicast_tvlv_packet ++ * ++ * Return: true if the packet may be forwarded (ttl decremented), ++ * false if it must be dropped (ttl would expire) ++ */ ++static bool batadv_skb_decrement_ttl(struct sk_buff *skb) ++{ ++ static const size_t ttl_offset = 2; ++ u8 *ttl_pos; ++ ++ BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); ++ ++ ttl_pos = skb->data + ttl_offset; ++ ++ /* would expire on this hop -> drop, leave header + csum untouched */ ++ if (*ttl_pos < 2) ++ return false; ++ ++ skb_postpull_rcsum(skb, ttl_pos, 1); ++ (*ttl_pos)--; ++ skb_postpush_rcsum(skb, ttl_pos, 1); ++ ++ return true; ++} ++ + /** + * batadv_recv_my_icmp_packet() - receive an icmp packet locally + * @bat_priv: the bat priv with all the soft interface information +@@ -1205,7 +1258,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + +- if (bcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.1/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..fa86c7bc25 --- /dev/null +++ b/queue-6.1/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From 314a46c28a91be49dc3f83322f498ba03c6906df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:13 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index fbf030c57ac04d..0138b0953e10b5 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -420,6 +420,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..1b6a65a0ae --- /dev/null +++ b/queue-6.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,107 @@ +From b22fa2d5eb7111575d51e5b4d3cf58b60d2542ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:12 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 4c193194034155..fbf030c57ac04d 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -385,6 +385,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -394,7 +396,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_dst; +@@ -417,12 +420,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index dbf0871f870303..51e281027ab630 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index e9347b25d2087a..49cce0cbdff8b6 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1175,10 +1175,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.1/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..6494becc9e --- /dev/null +++ b/queue-6.1/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From f5f9cf61085ff7967aaacbf7bc7824f342b2ffca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:08 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index 86e0664e0511bb..7ff48c968a13fe 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -941,9 +941,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..e61dc45605 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From 48b7c9045ff6b24ec88ce85aee844255527a7244 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:06 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 089ac7cc9fbebc..2bba53fc6da5c0 100644 +--- 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 *bat_priv, const u8 *dst, + 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 *bat_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); + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..7af7868415 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From ede0391be39ad21e7d159985fd99dbc756db8db3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:16 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 638dd438a6c052..b882919a868ca0 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..5f80b9ac31 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From 7749dcd68bed6dc3142962efd15fae37974c5bfd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:03 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0fdcafca3aa02b..4ff80e4214ff0a 100644 +--- 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 batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.1/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..770717b7ec --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From 4aca6456212d50fc31e6ff514ef1c61b8a003c8e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:02 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f6ccb639744a2a..0fdcafca3aa02b 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..02a7c43eb9 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From dd6c5523b0be5cdaceda49f5f13c067fa275fde6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:04 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 4ff80e4214ff0a..c79352cfddc4ae 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.1/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..7703babf05 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From ba1e89c0adeb1b5a17ae3c71e1bf020738400886 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:18 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f745bc09a04963..50b83e0438a07b 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..33bfff13e6 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From 38fc401a35eb38b318195cb72df11b66a029ba45 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:05 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c79352cfddc4ae..089ac7cc9fbebc 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..72a9f7412b --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From e873fb927f6012a780059788943b6b6a2ca0509f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:01 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 750e5e9d3dc9d6..f6ccb639744a2a 100644 +--- 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 *bat_priv, const u8 *dst, + * 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 + */ +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..759ff62c10 --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From 32e6439ebb546a129578459ed5168feddaed52d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:00 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f350a60e6c76b9..750e5e9d3dc9d6 100644 +--- 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 *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..3cdd7748ed --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From 44d8f1adc3b995e0aab69fd66ca19f4eebe6cf47 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:09 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 2bba53fc6da5c0..133eed2fa9507f 100644 +--- 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 *bat_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 *bat_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 batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..33f60b2dac --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From cc1388bbbf10cbfce38716bebd2fec199c425152 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:10:59 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index dfc3374549921f..f350a60e6c76b9 100644 +--- 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_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..6a05f49f7d --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From e5656e4d6f83c88da8e02e42e93e0538668507f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:17 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index b882919a868ca0..f745bc09a04963 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + batadv_tp_vars_put(tp_vars); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index d66726d0ea8112..0a6d0303a616c5 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1491,7 +1491,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..3c8d0ce30b --- /dev/null +++ b/queue-6.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From ab5601dbe335fdfb45f756d336418edb2f780630 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:15 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 133eed2fa9507f..638dd438a6c052 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -87,6 +87,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index a61e319e4db0e9..d66726d0ea8112 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1494,6 +1494,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..147ba9f126 --- /dev/null +++ b/queue-6.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,46 @@ +From 22a90a94c2949165086caa3a6ad1dff0285b06ba Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:19 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 53d2aadcafa154..c3d62df9e2b87d 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + /* DEL+ADD in the same orig interval have no effect and can be + * removed to avoid silly behaviour on the receiver side. The + * other way around (ADD+DEL) can happen in case of roaming of +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.1/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..ee0d8b3a82 --- /dev/null +++ b/queue-6.1/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,89 @@ +From f2e2e77009d59b556ec40f518a2b48e021027b17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:20 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index c3d62df9e2b87d..5161ddf1c788e0 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the soft interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 0a6d0303a616c5..673b04dd71b043 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1995,6 +1995,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..32f461ad9d --- /dev/null +++ b/queue-6.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,155 @@ +From c052a0b7183276a112af8373678e639cf36a50fb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:23 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index e25430da5afb1b..61a1e5e5f0d913 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -394,7 +394,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + } else { + if (!src) + return NET_RX_SUCCESS; +@@ -413,6 +412,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + u8 *src, u8 *dst, + void *tvlv_value, u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -473,12 +516,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler, + &bat_priv->tvlv.handler_list, list) { +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 673b04dd71b043..f009b2a381a468 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2408,13 +2408,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.1/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..98146efde8 --- /dev/null +++ b/queue-6.1/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,106 @@ +From 70e7b2220fad294bafebc8640a223ac08d825f01 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:22 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/tvlv.c | 6 ++++++ + 3 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index c52e3b82889868..7fd692b642e392 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index f87c2b80e4291e..e8ad4817d20018 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -854,14 +854,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 99e5e8518dcc9a..e25430da5afb1b 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -448,6 +448,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-6.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..a5ce23c1e3 --- /dev/null +++ b/queue-6.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From f349fccf6e64eb281a526e38664a966944e11974 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:14 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index 651e01b86141e3..34874942ae8d03 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -838,6 +838,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 57d0c6862f449c..f87c2b80e4291e 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -255,11 +255,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -422,6 +429,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -437,6 +448,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index ca1c258faa095f..a61e319e4db0e9 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index c7c870b56f..8b6c0b06e8 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -47,3 +47,28 @@ kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-che.patch revert-ptp-add-testptp-mask-test.patch mm-mglru-skip-special-vmas-in-lru_gen_look_around.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-ensure-bcast-is-writable-before-modifying.patch +batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch diff --git a/queue-6.12/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.12/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..83c9ee7c08 --- /dev/null +++ b/queue-6.12/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From 8f7ad6fa32ea7f5f6fa95ab7c016d02973aba88d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:38 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 15aeb07285e61f..cc6c14cac06282 100644 +--- 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_priv *bat_priv, const u8 *orig, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, u8 *an_addr, + 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(struct batadv_priv *bat_priv, int now) + 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(struct batadv_priv *bat_priv, + 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(struct work_struct *work) + 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(struct work_struct *work) + 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 batadv_priv *bat_priv, struct sk_buff *skb, + /* 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 *bat_priv, struct sk_buff *skb, + + 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 *bat_priv, struct sk_buff *skb, + /* 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_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.12/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..f92a19d47a --- /dev/null +++ b/queue-6.12/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From ccbf0785c5e840d366ff4c4978fea86733a0ae66 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:52 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 4d53e7c3ea5463..bd5477263ecd7c 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.12/batman-adv-ensure-bcast-is-writable-before-modifying.patch new file mode 100644 index 0000000000..13b6f9d3bc --- /dev/null +++ b/queue-6.12/batman-adv-ensure-bcast-is-writable-before-modifying.patch @@ -0,0 +1,50 @@ +From 9459d407a92bb0848d0407197ecd0a5361c558c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:41 +0200 +Subject: batman-adv: ensure bcast is writable before modifying TTL + +From: Sven Eckelmann + +commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or used skb_cow() to ensure that the data part is not +shared. + +The old implementation used a shared queue and created copies before +attempting to write to it. But with the new implementation, the broadcast +packet is already modified when it gets received. Potentially writing to +shared buffers in this process. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to rebroadcast the +packet. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index f1061985149fc5..32a40c1c115c6a 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1198,6 +1198,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto free_skb; + ++ /* create a copy of the skb, if needed, to modify it. */ ++ if (skb_cow(skb, ETH_HLEN) < 0) ++ goto free_skb; ++ ++ bcast_packet = (struct batadv_bcast_packet *)skb->data; ++ + if (bcast_packet->ttl-- < 2) + goto free_skb; + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.12/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch new file mode 100644 index 0000000000..c4e33bd45f --- /dev/null +++ b/queue-6.12/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch @@ -0,0 +1,128 @@ +From 7a4ead9dab9c3e66c277a9f85fdcdedb553e4517 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:42 +0200 +Subject: batman-adv: fix (m|b)cast csum after decrementing TTL + +From: Sven Eckelmann + +commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. + +The broadcast and multicast packets can be received at the same time by the +local system and forwarded to other nodes. Both are simply decrementing the +TTL at the beginning of the receive path - independent of chosen paths +(receive/forward). But such a modification of the data conflicts with the +hw csum. This is not a problem when the packet is directly forwarded but +can cause errors in the local receive path. + +Such a problem can then trigger a "hw csum failure". The receiver path must +therefore ensure that the csum is fixed for each modification of the +payload before batadv_interface_rx() is reached. + +Since all batman-adv packet types with a ttl have it as u8 at offset 2, a +helper can be used for all of them. But it is only used at the moment for +batadv_bcast_packet and batadv_mcast_packet because they are the only ones +which deliver the packet locally but unconditionally modify the TTL. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 56 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 32a40c1c115c6a..85bc2d284a7798 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -205,6 +206,59 @@ bool batadv_check_management_packet(struct sk_buff *skb, + return true; + } + ++/** ++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe ++ * @skb: the received packet with @skb->data pointing to the batman-adv header ++ * ++ * Supports the following packet types, all of which carry the TTL at offset 2: ++ * ++ * - batadv_ogm_packet ++ * - batadv_ogm2_packet ++ * - batadv_icmp_header ++ * - batadv_icmp_packet ++ * - batadv_icmp_tp_packet ++ * - batadv_icmp_packet_rr ++ * - batadv_unicast_packet ++ * - batadv_frag_packet ++ * - batadv_bcast_packet ++ * - batadv_mcast_packet ++ * - batadv_coded_packet ++ * - batadv_unicast_tvlv_packet ++ * ++ * Return: true if the packet may be forwarded (ttl decremented), ++ * false if it must be dropped (ttl would expire) ++ */ ++static bool batadv_skb_decrement_ttl(struct sk_buff *skb) ++{ ++ static const size_t ttl_offset = 2; ++ u8 *ttl_pos; ++ ++ BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); ++ ++ ttl_pos = skb->data + ttl_offset; ++ ++ /* would expire on this hop -> drop, leave header + csum untouched */ ++ if (*ttl_pos < 2) ++ return false; ++ ++ skb_postpull_rcsum(skb, ttl_pos, 1); ++ (*ttl_pos)--; ++ skb_postpush_rcsum(skb, ttl_pos, 1); ++ ++ return true; ++} ++ + /** + * batadv_recv_my_icmp_packet() - receive an icmp packet locally + * @bat_priv: the bat priv with all the soft interface information +@@ -1204,7 +1258,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + +- if (bcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); +@@ -1311,7 +1365,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, + goto free_skb; + + mcast_packet = (struct batadv_mcast_packet *)skb->data; +- if (mcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + tvlv_buff = (unsigned char *)(skb->data + hdr_size); +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.12/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..75ceccc1b3 --- /dev/null +++ b/queue-6.12/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From a621528e2c42e781f48ebba1caedf6528469e82e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:44 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index f6714405b0f948..375c9499121346 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.12/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..f472e79472 --- /dev/null +++ b/queue-6.12/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,107 @@ +From 17b0bd7b43267127ce0725a2cd18fb7e51cac111 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:43 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index fd7cb789ae9a6a..f6714405b0f948 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_neigh_node *neigh_node = NULL; +@@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index dbf0871f870303..51e281027ab630 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 85bc2d284a7798..6fd18b69e5f6be 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1175,10 +1175,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.12/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..ecf0c7b20c --- /dev/null +++ b/queue-6.12/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From ea963a22dabe874b6f0347ab10ad82c46b268c99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:39 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index 9362cd9d6f3d33..a7522015224a5b 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -936,9 +936,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.12/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..76293d6ae9 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From 9f57d0cb63c0c1c960650d4a1538c34f8a3fc452 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:37 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 089ac7cc9fbebc..2bba53fc6da5c0 100644 +--- 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 *bat_priv, const u8 *dst, + 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 *bat_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); + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.12/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..6ffc386b21 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From ed1e61b7338d93653bfc166aeabde99ef7d2fe7d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:47 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 638dd438a6c052..b882919a868ca0 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.12/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..8d0c09bfb7 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From bb014b18d3d2438fd59d930951ce55b123498369 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:34 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0fdcafca3aa02b..4ff80e4214ff0a 100644 +--- 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 batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.12/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..711e71a7c5 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From 995911928b4f6cd2b8f7d8c67f8cba8857aaa9c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:33 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f6ccb639744a2a..0fdcafca3aa02b 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.12/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..c019bb4007 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From 8bfc320374cf39bb95d3791382e1a563ffe41994 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:35 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 4ff80e4214ff0a..c79352cfddc4ae 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.12/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..3c468ce670 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From 7fb49577e3a6185990d92896981361aafe9a8093 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:49 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f745bc09a04963..50b83e0438a07b 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.12/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..209c75953b --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From d8d12e03670e6d4f0faebea9b5e67a5b0f156489 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:36 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c79352cfddc4ae..089ac7cc9fbebc 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.12/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..f736342163 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From 0c673441894cc4d6eb4e8abefd5f3d8e967fe8f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:32 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 750e5e9d3dc9d6..f6ccb639744a2a 100644 +--- 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 *bat_priv, const u8 *dst, + * 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 + */ +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.12/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..7badcbd13b --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From 81f357d34f1f1b6bfa9fc4890a9e6810c16253d6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:31 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f350a60e6c76b9..750e5e9d3dc9d6 100644 +--- 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 *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.12/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..04c54aaa6a --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From 90d4994e77eb12756cf63db7a0e1f36a55b6b4b8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:40 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 2bba53fc6da5c0..133eed2fa9507f 100644 +--- 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 *bat_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 *bat_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 batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.12/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..fc0462bb28 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From edebcce561dfc2f9b4a9612e33aad29c5357c0c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:30 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index dfc3374549921f..f350a60e6c76b9 100644 +--- 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_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.12/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..e3f3af3e71 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From 3c8832b3656c8a73dce73dfab0e0efca46919922 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:48 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index b882919a868ca0..f745bc09a04963 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + batadv_tp_vars_put(tp_vars); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index e8679a4cd0413b..01c8fef027b419 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1561,7 +1561,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.12/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..251ae89e73 --- /dev/null +++ b/queue-6.12/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From 6e797b12224f160f8c7c12efdbd3496da814d51a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:46 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 133eed2fa9507f..638dd438a6c052 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -87,6 +87,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 3b2a170b1cf18b..e8679a4cd0413b 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1564,6 +1564,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.12/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..51976b03c8 --- /dev/null +++ b/queue-6.12/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,46 @@ +From 2519bd1f1bfc3eaabb26731f985da62aa288fbd6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:50 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 7041cd69e20070..69a42bc3fa02e6 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + /* DEL+ADD in the same orig interval have no effect and can be + * removed to avoid silly behaviour on the receiver side. The + * other way around (ADD+DEL) can happen in case of roaming of +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.12/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..1686c6854f --- /dev/null +++ b/queue-6.12/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,89 @@ +From 7035db422b280d1700bcc15770570cfab1299765 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:51 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 69a42bc3fa02e6..2a7caab5c197ea 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the soft interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 01c8fef027b419..9bde0469e748ce 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2065,6 +2065,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.12/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..e36c527507 --- /dev/null +++ b/queue-6.12/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,154 @@ +From 4c23e693dd8c390dad2284158f8665baf46e51a1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:54 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index e1cd27b99bd119..cc66e231ccb8a0 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + break; + case BATADV_UNICAST_TVLV: + if (!skb) +@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + struct sk_buff *skb, void *tvlv_value, + u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (!tvlv_handler->ogm_handler) + continue; + +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 9bde0469e748ce..0022bee14574df 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2483,13 +2483,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.12/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..1ceeed0244 --- /dev/null +++ b/queue-6.12/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,123 @@ +From 9ec598250ee85db73f5438bf7996e4a66a21f2ee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:53 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/routing.c | 6 ++++++ + net/batman-adv/tvlv.c | 6 ++++++ + 4 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index b37c9fb178ae50..5dd3e1f281bab2 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -310,14 +310,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 48a67705eba85c..c5c4d33cb19838 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -853,14 +853,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 6fd18b69e5f6be..f828d8143f7526 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1373,6 +1373,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, + if (tvlv_buff_len > skb->len - hdr_size) + goto free_skb; + ++ /* the fields of an multicast payload are accessed assuming (at least) ++ * 2-byte alignment, so a following packet must start at an even offset. ++ */ ++ if (tvlv_buff_len & 1) ++ goto free_skb; ++ + ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb, + tvlv_buff, tvlv_buff_len); + if (ret >= 0) { +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 8d6b017c433cc9..e1cd27b99bd119 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-6.12/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.12/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..b79f82afd9 --- /dev/null +++ b/queue-6.12/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From 372ba31c4696617a1a6084e4468b7ef253996048 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:45 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index d35479c465e2c4..e13b39121f2db8 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -819,6 +819,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 8cfc3944dcfd52..48a67705eba85c 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -254,11 +254,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -421,6 +428,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -436,6 +447,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index f703d266780d74..3b2a170b1cf18b 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 9200140766..be988bef51 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -96,3 +96,28 @@ revert-pci-qcom-advertise-hotplug-slot-capability-wi.patch kvm-sev-ignore-mmio-requests-of-length-0.patch kvm-sev-reject-mmio-requests-larger-than-8-bytes-wit.patch kvm-sev-ignore-port-i-o-requests-of-length-0.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-ensure-bcast-is-writable-before-modifying.patch +batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch diff --git a/queue-6.18/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.18/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..e478b6c59b --- /dev/null +++ b/queue-6.18/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From 202878d4663336141898937177592bfccab146f2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:53 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 3072f94275ac60..2c6e2b0d1ded48 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -513,7 +513,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, + 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); +@@ -581,7 +581,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, + if (unlikely(!backbone_gw)) + return; + +- backbone_gw->lasttime = jiffies; ++ WRITE_ONCE(backbone_gw->lasttime, jiffies); + batadv_backbone_gw_put(backbone_gw); + } + +@@ -715,7 +715,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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); +@@ -737,7 +737,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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; +@@ -770,7 +770,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + 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); +@@ -859,7 +859,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, + 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, +@@ -1254,7 +1254,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) + 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; + +@@ -1335,7 +1335,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, + 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; + +@@ -1495,7 +1495,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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); + +@@ -1516,7 +1516,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) + 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) +@@ -1934,7 +1934,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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; + +@@ -2049,7 +2049,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + + if (own_claim) { + /* ... allow it in any case */ +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + goto allow; + } + +@@ -2151,7 +2151,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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. +@@ -2396,7 +2396,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.18/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..a6606b406d --- /dev/null +++ b/queue-6.18/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From bca97555cfa96c3eb4e1f9c40fb57634257a8b88 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:08 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 031c295fff1b1d..860db505e8697f 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -214,10 +214,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -344,6 +347,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.18/batman-adv-ensure-bcast-is-writable-before-modifying.patch new file mode 100644 index 0000000000..0f33ac2d30 --- /dev/null +++ b/queue-6.18/batman-adv-ensure-bcast-is-writable-before-modifying.patch @@ -0,0 +1,50 @@ +From 7f2f2b859d335d0d7c7a07956096ef652b86a5e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:57 +0200 +Subject: batman-adv: ensure bcast is writable before modifying TTL + +From: Sven Eckelmann + +commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or used skb_cow() to ensure that the data part is not +shared. + +The old implementation used a shared queue and created copies before +attempting to write to it. But with the new implementation, the broadcast +packet is already modified when it gets received. Potentially writing to +shared buffers in this process. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to rebroadcast the +packet. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 12c16f81cc51df..0672dc30bed3b1 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto free_skb; + ++ /* create a copy of the skb, if needed, to modify it. */ ++ if (skb_cow(skb, ETH_HLEN) < 0) ++ goto free_skb; ++ ++ bcast_packet = (struct batadv_bcast_packet *)skb->data; ++ + if (bcast_packet->ttl-- < 2) + goto free_skb; + +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.18/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch new file mode 100644 index 0000000000..cf2de0d359 --- /dev/null +++ b/queue-6.18/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch @@ -0,0 +1,127 @@ +From e9436a3c43d56b7427bcaa3b323e71fabd4587ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:58 +0200 +Subject: batman-adv: fix (m|b)cast csum after decrementing TTL + +From: Sven Eckelmann + +commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. + +The broadcast and multicast packets can be received at the same time by the +local system and forwarded to other nodes. Both are simply decrementing the +TTL at the beginning of the receive path - independent of chosen paths +(receive/forward). But such a modification of the data conflicts with the +hw csum. This is not a problem when the packet is directly forwarded but +can cause errors in the local receive path. + +Such a problem can then trigger a "hw csum failure". The receiver path must +therefore ensure that the csum is fixed for each modification of the +payload before batadv_interface_rx() is reached. + +Since all batman-adv packet types with a ttl have it as u8 at offset 2, a +helper can be used for all of them. But it is only used at the moment for +batadv_bcast_packet and batadv_mcast_packet because they are the only ones +which deliver the packet locally but unconditionally modify the TTL. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 56 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 0672dc30bed3b1..cdcea90db61235 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb, + return true; + } + ++/** ++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe ++ * @skb: the received packet with @skb->data pointing to the batman-adv header ++ * ++ * Supports the following packet types, all of which carry the TTL at offset 2: ++ * ++ * - batadv_ogm_packet ++ * - batadv_ogm2_packet ++ * - batadv_icmp_header ++ * - batadv_icmp_packet ++ * - batadv_icmp_tp_packet ++ * - batadv_icmp_packet_rr ++ * - batadv_unicast_packet ++ * - batadv_frag_packet ++ * - batadv_bcast_packet ++ * - batadv_mcast_packet ++ * - batadv_coded_packet ++ * - batadv_unicast_tvlv_packet ++ * ++ * Return: true if the packet may be forwarded (ttl decremented), ++ * false if it must be dropped (ttl would expire) ++ */ ++static bool batadv_skb_decrement_ttl(struct sk_buff *skb) ++{ ++ static const size_t ttl_offset = 2; ++ u8 *ttl_pos; ++ ++ BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); ++ ++ ttl_pos = skb->data + ttl_offset; ++ ++ /* would expire on this hop -> drop, leave header + csum untouched */ ++ if (*ttl_pos < 2) ++ return false; ++ ++ skb_postpull_rcsum(skb, ttl_pos, 1); ++ (*ttl_pos)--; ++ skb_postpush_rcsum(skb, ttl_pos, 1); ++ ++ return true; ++} ++ + /** + * batadv_recv_my_icmp_packet() - receive an icmp packet locally + * @bat_priv: the bat priv with all the mesh interface information +@@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + +- if (bcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); +@@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, + goto free_skb; + + mcast_packet = (struct batadv_mcast_packet *)skb->data; +- if (mcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + tvlv_buff = (unsigned char *)(skb->data + hdr_size); +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.18/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..527e9a3c7b --- /dev/null +++ b/queue-6.18/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From 357b073fd79be41a341e5a0e9195e0339daa8812 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:00 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index ffa5d4b0740959..4779741e7273ea 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.18/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..488088f8a8 --- /dev/null +++ b/queue-6.18/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,106 @@ +From 948a175efc11b0e227f9b94a96d3759a83ab3699 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:59 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 31395281692cb5..ffa5d4b0740959 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface); + struct batadv_neigh_node *neigh_node = NULL; +@@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index dbf0871f870303..51e281027ab630 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index cdcea90db61235..4483f8d9c75831 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch b/queue-6.18/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch new file mode 100644 index 0000000000..b60852f2c3 --- /dev/null +++ b/queue-6.18/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch @@ -0,0 +1,84 @@ +From 292e9ada11423a5949113ba88c6c117ba8949d9f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:56 +0200 +Subject: batman-adv: gw: don't deselect gateway with active hardif + +From: Sven Eckelmann + +commit df97a7107b16375a10a36d7a63e9b4291a8ac680 upstream. + +The batadv_hardif_cnt() was previously checking if there is an +batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since +batadv_hardif_disable_interface() was resetting the +batadv_hard_iface->mesh_iface after this check, it had to verify whether +*1* interface was still part of the mesh_iface before it started the +gateway deselection. + +But after batadv_hardif_cnt() is now checking the lower interfaces of +mesh_iface and batadv_hardif_disable_interface() already removed the +interface via netdev_upper_dev_unlink() earlier in this function, the check +must now make sure that *0* interfaces can be found by batadv_hardif_cnt() +before selected gateway must be deselected. Otherwise the deselection would +already happen one batadv_hard_iface too early. + +Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty +list, it is possible to replace the counting with a simple list_empty(). + +Cc: stable@kernel.org +Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data") +Reviewed-by: Nora Schiffer +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/hard-interface.c | 28 ++-------------------------- + 1 file changed, 2 insertions(+), 26 deletions(-) + +diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c +index 1c488049d55463..39b1ed813497d2 100644 +--- a/net/batman-adv/hard-interface.c ++++ b/net/batman-adv/hard-interface.c +@@ -786,30 +786,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, + return ret; + } + +-/** +- * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface +- * @mesh_iface: mesh interface to check +- * +- * This function is only using RCU for locking - the result can therefore be +- * off when another function is modifying the list at the same time. The +- * caller can use the rtnl_lock to make sure that the count is accurate. +- * +- * Return: number of connected/enslaved hard interfaces +- */ +-static size_t batadv_hardif_cnt(struct net_device *mesh_iface) +-{ +- struct batadv_hard_iface *hard_iface; +- struct list_head *iter; +- size_t count = 0; +- +- rcu_read_lock(); +- netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) +- count++; +- rcu_read_unlock(); +- +- return count; +-} +- + /** + * batadv_hardif_disable_interface() - Remove hard interface from mesh interface + * @hard_iface: hard interface to be removed +@@ -850,8 +826,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) + netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface); + batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface); + +- /* nobody uses this interface anymore */ +- if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1) ++ /* nobody uses this mesh interface anymore */ ++ if (list_empty(&hard_iface->mesh_iface->adj_list.lower)) + batadv_gw_check_client_stop(bat_priv); + + hard_iface->mesh_iface = NULL; +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.18/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..98415e1a6a --- /dev/null +++ b/queue-6.18/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From ee52bb15fff196bf2d65dcfcf7f5f58fe70217ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:54 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index 78c651f634cd39..1d144d8cc0928e 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -917,9 +917,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.18/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..56b40790aa --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From 437963dd64218a45c7608812098b45ef81dd4b32 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:52 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0ebfc4462b8f17..0444aa46b95c29 100644 +--- 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 *bat_priv, const u8 *dst, + 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 *bat_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); + +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.18/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..df23e90d0b --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From 4c40ba3c684901303ac8081252f572173bc24043 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:03 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index e5387e8f33244a..e69bf10e66ac33 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.18/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..223ab1584b --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From 9a7182a7e003167b4f09f93a62e903a1c9ed45e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:49 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 71a4352cd78c77..00d8bb01611f99 100644 +--- 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 batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.18/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..542b824a71 --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From c141c9d1393829739398c5eeca89c56f4bb621eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:48 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 473641d32dc683..71a4352cd78c77 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.18/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..4776860c1c --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From c2da02b138d748873fd5d24a5f764545b33a60d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:50 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 00d8bb01611f99..a85622267ba655 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.18/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..7292c98b7c --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From b909cd0adc450ea331944be0afd87f94abf29fa1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:05 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 629831ea9a58e5..02af19aaaff291 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.18/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..fc28acbb53 --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From 1bb42504094a679da4bcfa7936ae212e44c159cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:51 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index a85622267ba655..0ebfc4462b8f17 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.18/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..674bdeac8c --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From bc6dfc14d9d5b3521dbdf89915c970897a7015ba Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:47 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index fe9a447643074a..473641d32dc683 100644 +--- 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 *bat_priv, const u8 *dst, + * mesh_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 + */ +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.18/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..05ab68a39a --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From b6fe66028fae037882ea6e13809d1299ce87a4ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:46 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f222c5093b647e..fe9a447643074a 100644 +--- 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 *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.18/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..962d21ca4e --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From 057e1f7d30c1fa98382c0be169cd55bca7481a0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:55 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0444aa46b95c29..10b8daca3a6154 100644 +--- 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 *bat_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 *bat_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 batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.18/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..0a6a2ce175 --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From e8c4b955ee14124851fc8b809868476971e75eef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:45 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index b1629e0ac82683..f222c5093b647e 100644 +--- 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_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.18/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..3069bc1839 --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From beae79a5118a251f694c211d72e334a05ac7f8df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:04 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index e69bf10e66ac33..629831ea9a58e5 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + batadv_tp_vars_put(tp_vars); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 8b180d8245b2a6..84de2570eac3ed 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1423,7 +1423,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.18/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..fc704bc9b8 --- /dev/null +++ b/queue-6.18/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From 072ff1e2dee7e78ab8b308f299947774c2b38653 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:02 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 10b8daca3a6154..e5387e8f33244a 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -87,6 +87,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 417d653021c7af..8b180d8245b2a6 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1426,6 +1426,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.18/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..f8e7812ae7 --- /dev/null +++ b/queue-6.18/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,45 @@ +From fde94418383277688ef60900ba712901678eefbe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:06 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 9f6e67771ffa80..acd8af4446671f 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -446,6 +446,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL; + if (del_op_requested != del_op_entry) { + /* DEL+ADD in the same orig interval have no effect and +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.18/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..2e1b020228 --- /dev/null +++ b/queue-6.18/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,88 @@ +From 6f20158d28ae2ff9127aed59c0d5211b4455b63a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:07 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index acd8af4446671f..83dfd804a143ab 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3442,6 +3442,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the mesh interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3449,7 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3462,6 +3463,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3483,6 +3487,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3519,7 +3524,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 84de2570eac3ed..ef712ba4fff2cb 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1912,6 +1912,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.18/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..165a30eafb --- /dev/null +++ b/queue-6.18/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,154 @@ +From 79d578f38da153522da846e36f128a4a786c7524 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:10 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 63fb54024d1543..a91f1891747c0a 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + break; + case BATADV_UNICAST_TVLV: + if (!skb) +@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + struct sk_buff *skb, void *tvlv_value, + u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (!tvlv_handler->ogm_handler) + continue; + +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index ef712ba4fff2cb..ac4494f1b8e2a8 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2245,13 +2245,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.18/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..2cb49ef900 --- /dev/null +++ b/queue-6.18/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,123 @@ +From f2d6ca20d75da2c0d50989e4163b2522aac39a3e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:09 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/routing.c | 6 ++++++ + net/batman-adv/tvlv.c | 6 ++++++ + 4 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index b8b1b997960a96..6e79f69c2fedec 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 6852bf5da8c558..1f9b2d2b4831ce 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 4483f8d9c75831..41951c7a1c50b2 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, + if (tvlv_buff_len > skb->len - hdr_size) + goto free_skb; + ++ /* the fields of an multicast payload are accessed assuming (at least) ++ * 2-byte alignment, so a following packet must start at an even offset. ++ */ ++ if (tvlv_buff_len & 1) ++ goto free_skb; ++ + ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb, + tvlv_buff, tvlv_buff_len); + if (ret >= 0) { +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index cde798c82dcf13..63fb54024d1543 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-6.18/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.18/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..bd3e401d07 --- /dev/null +++ b/queue-6.18/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From edc061f6f6c8712aa9256d46d7c0555f58b96e89 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:01 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index de94447142642e..17d2a1ccdce67a 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index d66ca77b1aaa3c..6852bf5da8c558 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -252,11 +252,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -417,6 +424,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index c9bd49d23547ef..417d653021c7af 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index ad2d2ee538..996c90fee3 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -3,3 +3,29 @@ lsm-add-backing_file-lsm-hooks.patch selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch revert-pci-qcom-advertise-hotplug-slot-capability-wi.patch lockd-fix-test-handling-when-not-all-permissions-are.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-gw-don-t-deselect-gateway-with-active-har.patch +batman-adv-ensure-bcast-is-writable-before-modifying.patch +batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch diff --git a/queue-6.6/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-6.6/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..14e985318c --- /dev/null +++ b/queue-6.6/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From cb5246a7ab87db30dafc9e71218e1f3315d6f916 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:23 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index cfb1eb25c6ac4d..ac12c06f11b89e 100644 +--- 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_priv *bat_priv, const u8 *orig, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, u8 *an_addr, + 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(struct batadv_priv *bat_priv, int now) + 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(struct batadv_priv *bat_priv, + 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(struct work_struct *work) + 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(struct work_struct *work) + 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 batadv_priv *bat_priv, struct sk_buff *skb, + /* 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 *bat_priv, struct sk_buff *skb, + + 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 *bat_priv, struct sk_buff *skb, + /* 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_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-6.6/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..a450481ec0 --- /dev/null +++ b/queue-6.6/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From 576b43c844616c3e0a9ec5ae51ea4237bd071bb9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:37 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index c76bddbf816ada..92d90e352d1f41 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-6.6/batman-adv-ensure-bcast-is-writable-before-modifying.patch new file mode 100644 index 0000000000..9b02b64a6b --- /dev/null +++ b/queue-6.6/batman-adv-ensure-bcast-is-writable-before-modifying.patch @@ -0,0 +1,50 @@ +From 22d7710b0c5f12f47914a4a5df86a87adb7ed249 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:26 +0200 +Subject: batman-adv: ensure bcast is writable before modifying TTL + +From: Sven Eckelmann + +commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or used skb_cow() to ensure that the data part is not +shared. + +The old implementation used a shared queue and created copies before +attempting to write to it. But with the new implementation, the broadcast +packet is already modified when it gets received. Potentially writing to +shared buffers in this process. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to rebroadcast the +packet. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 163cd43c4821b6..747c3ff1c253b0 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1198,6 +1198,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto free_skb; + ++ /* create a copy of the skb, if needed, to modify it. */ ++ if (skb_cow(skb, ETH_HLEN) < 0) ++ goto free_skb; ++ ++ bcast_packet = (struct batadv_bcast_packet *)skb->data; ++ + if (bcast_packet->ttl-- < 2) + goto free_skb; + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-6.6/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch new file mode 100644 index 0000000000..8039690db4 --- /dev/null +++ b/queue-6.6/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch @@ -0,0 +1,118 @@ +From 90abf691e4f1bb5b3ac05a61baaf6e3f35b945c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:27 +0200 +Subject: batman-adv: fix (m|b)cast csum after decrementing TTL + +From: Sven Eckelmann + +commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. + +The broadcast and multicast packets can be received at the same time by the +local system and forwarded to other nodes. Both are simply decrementing the +TTL at the beginning of the receive path - independent of chosen paths +(receive/forward). But such a modification of the data conflicts with the +hw csum. This is not a problem when the packet is directly forwarded but +can cause errors in the local receive path. + +Such a problem can then trigger a "hw csum failure". The receiver path must +therefore ensure that the csum is fixed for each modification of the +payload before batadv_interface_rx() is reached. + +Since all batman-adv packet types with a ttl have it as u8 at offset 2, a +helper can be used for all of them. But it is only used at the moment for +batadv_bcast_packet and batadv_mcast_packet because they are the only ones +which deliver the packet locally but unconditionally modify the TTL. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") +[ Context, Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 55 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 747c3ff1c253b0..36e9df0cca0b2f 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -205,6 +206,58 @@ bool batadv_check_management_packet(struct sk_buff *skb, + return true; + } + ++/** ++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe ++ * @skb: the received packet with @skb->data pointing to the batman-adv header ++ * ++ * Supports the following packet types, all of which carry the TTL at offset 2: ++ * ++ * - batadv_ogm_packet ++ * - batadv_ogm2_packet ++ * - batadv_icmp_header ++ * - batadv_icmp_packet ++ * - batadv_icmp_tp_packet ++ * - batadv_icmp_packet_rr ++ * - batadv_unicast_packet ++ * - batadv_frag_packet ++ * - batadv_bcast_packet ++ * - batadv_mcast_packet ++ * - batadv_coded_packet ++ * - batadv_unicast_tvlv_packet ++ * ++ * Return: true if the packet may be forwarded (ttl decremented), ++ * false if it must be dropped (ttl would expire) ++ */ ++static bool batadv_skb_decrement_ttl(struct sk_buff *skb) ++{ ++ static const size_t ttl_offset = 2; ++ u8 *ttl_pos; ++ ++ BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); ++ ++ ttl_pos = skb->data + ttl_offset; ++ ++ /* would expire on this hop -> drop, leave header + csum untouched */ ++ if (*ttl_pos < 2) ++ return false; ++ ++ skb_postpull_rcsum(skb, ttl_pos, 1); ++ (*ttl_pos)--; ++ skb_postpush_rcsum(skb, ttl_pos, 1); ++ ++ return true; ++} ++ + /** + * batadv_recv_my_icmp_packet() - receive an icmp packet locally + * @bat_priv: the bat priv with all the soft interface information +@@ -1204,7 +1257,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + +- if (bcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-6.6/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..a5648a90bf --- /dev/null +++ b/queue-6.6/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From 99ddece5a90d685eedd734c45dfeaa6c8d1d4b99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:29 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index fbf030c57ac04d..0138b0953e10b5 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -420,6 +420,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-6.6/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..24d2fe30dc --- /dev/null +++ b/queue-6.6/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,107 @@ +From d88f6965e30135818c4dc92c84718761628e9a1a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:28 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 4c193194034155..fbf030c57ac04d 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -385,6 +385,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -394,7 +396,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_dst; +@@ -417,12 +420,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index dbf0871f870303..51e281027ab630 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 36e9df0cca0b2f..ec278f73805e95 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1174,10 +1174,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-6.6/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..7deeba6a6a --- /dev/null +++ b/queue-6.6/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From 3b0f53c4fa6464b2ddd81bf83f61e7d974ab161b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:24 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index 0c64d81a776174..03ba6282af91a3 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -938,9 +938,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-6.6/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..3ae21bc172 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From ce99a933cd31ed38056bbb10c07429de3097d142 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:22 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 089ac7cc9fbebc..2bba53fc6da5c0 100644 +--- 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 *bat_priv, const u8 *dst, + 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 *bat_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); + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-6.6/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..e8a4160cf1 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From 9df041c16713653867e3f5fd61f2c8343fbbbec7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:32 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 638dd438a6c052..b882919a868ca0 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-6.6/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..3f521ad261 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From dc86aea78d0950a0672e883560e39a401039ba94 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:19 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0fdcafca3aa02b..4ff80e4214ff0a 100644 +--- 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 batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-6.6/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..4a4007fd84 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From 2da25fca733480ae48254e672697397abd827735 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:18 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f6ccb639744a2a..0fdcafca3aa02b 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-6.6/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..03a41cd950 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From 6509380ba93875ee13c52d53add68c9d4ee8d87d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:20 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 4ff80e4214ff0a..c79352cfddc4ae 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-6.6/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..faf993ca54 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From 8d52b3bc303833fc0c728d4bc76a15608b93babd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:34 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f745bc09a04963..50b83e0438a07b 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-6.6/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..56fc187d18 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From e9b6e7ba8f3dbd6c4e1d47d35900a8eead4cabe2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:21 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index c79352cfddc4ae..089ac7cc9fbebc 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-6.6/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..ef1d76c4a3 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From 6145606fe38b320c24ea6660a8a00b929abfe0c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:17 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 750e5e9d3dc9d6..f6ccb639744a2a 100644 +--- 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 *bat_priv, const u8 *dst, + * 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 + */ +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-6.6/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..cebe24be4c --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From 3be63c0302467f403a076aaa413d5835c2333034 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:16 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index f350a60e6c76b9..750e5e9d3dc9d6 100644 +--- 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 *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-6.6/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..a29729f358 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From 1a12b4e88d4defb3b9b531da9d319d123ffc9c46 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:25 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 2bba53fc6da5c0..133eed2fa9507f 100644 +--- 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 *bat_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 *bat_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 batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-6.6/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..54fccfcc55 --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From 8ecfc8c90700f52bb6d5c91a50e24cd6be319a55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:15 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index dfc3374549921f..f350a60e6c76b9 100644 +--- 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_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-6.6/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..026f7caf8e --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From b1d63ded013db25261513ec8b576d7d72b490f3f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:33 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index b882919a868ca0..f745bc09a04963 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + batadv_tp_vars_put(tp_vars); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 7c32947414cd80..883da8acae7381 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1491,7 +1491,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-6.6/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..e619c8f30c --- /dev/null +++ b/queue-6.6/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From 642ada629b7c53545d4af2a4bc52a2e1fd84d0c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:31 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 133eed2fa9507f..638dd438a6c052 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -87,6 +87,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index d56886951177d9..7c32947414cd80 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1494,6 +1494,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-6.6/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..d7e48cb7fe --- /dev/null +++ b/queue-6.6/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,46 @@ +From 36b4b364317e4ff265b360a55140487feff9e64f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:35 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 7041cd69e20070..69a42bc3fa02e6 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -485,6 +485,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + /* DEL+ADD in the same orig interval have no effect and can be + * removed to avoid silly behaviour on the receiver side. The + * other way around (ADD+DEL) can happen in case of roaming of +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tt-track-roam-count-per-vid.patch b/queue-6.6/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..9eb7bb882d --- /dev/null +++ b/queue-6.6/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,89 @@ +From 1684d7c52136f57f14fae426b995cf59286509ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:36 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 69a42bc3fa02e6..2a7caab5c197ea 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3517,6 +3517,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the soft interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3524,7 +3525,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3537,6 +3538,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3558,6 +3562,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3594,7 +3599,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 883da8acae7381..0368cb93865f39 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1995,6 +1995,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-6.6/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..5a1649e370 --- /dev/null +++ b/queue-6.6/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,154 @@ +From c4bfecb070ea143bda1e2db4896c482dcf803d35 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:39 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index e1cd27b99bd119..cc66e231ccb8a0 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + break; + case BATADV_UNICAST_TVLV: + if (!skb) +@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + struct sk_buff *skb, void *tvlv_value, + u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (!tvlv_handler->ogm_handler) + continue; + +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 0368cb93865f39..4856cc98fc3c51 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2413,13 +2413,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-6.6/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..b41d3f1fd2 --- /dev/null +++ b/queue-6.6/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,106 @@ +From b9884b623fa18c2def868eac1a8509453235f6c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:38 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +[ Drop change for non-existing mcast handling ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/tvlv.c | 6 ++++++ + 3 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index b37c9fb178ae50..5dd3e1f281bab2 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -310,14 +310,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 48a67705eba85c..c5c4d33cb19838 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -853,14 +853,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index 8d6b017c433cc9..e1cd27b99bd119 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-6.6/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-6.6/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..97e6c5aa8b --- /dev/null +++ b/queue-6.6/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From 55980242fb7e3c6e754d68b05c6794e5b5b8c3d8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:11:30 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index d35479c465e2c4..e13b39121f2db8 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -819,6 +819,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 8cfc3944dcfd52..48a67705eba85c 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -254,11 +254,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -421,6 +428,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -436,6 +447,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 2dfdc78d4ded99..d56886951177d9 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-6.6/fs-prepare-for-adding-lsm-blob-to-backing_file.patch b/queue-6.6/fs-prepare-for-adding-lsm-blob-to-backing_file.patch new file mode 100644 index 0000000000..42b4f23b0a --- /dev/null +++ b/queue-6.6/fs-prepare-for-adding-lsm-blob-to-backing_file.patch @@ -0,0 +1,89 @@ +From aa14e051d0d097c168a672517c3cf44173b3a8cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 27 Jun 2026 14:57:18 +0800 +Subject: fs: prepare for adding LSM blob to backing_file + +From: Amir Goldstein + +[ Upstream commit 880bd496ec72a6dcb00cb70c430ef752ba242ae7 ] + +In preparation to adding LSM blob to backing_file struct, factor out +helpers init_backing_file() and backing_file_free(). + +Cc: stable@vger.kernel.org +Cc: linux-fsdevel@vger.kernel.org +Cc: linux-unionfs@vger.kernel.org +Cc: linux-erofs@lists.ozlabs.org +Signed-off-by: Amir Goldstein +Reviewed-by: Serge Hallyn +[PM: use the term "LSM blob", fix comment style to match file] +Signed-off-by: Paul Moore +[1. The commit def3ae83da02 +("fs: store real path instead of fake path in backing file f_path") +is not merged, The 6.6 LTS version accordingly operates on +&ff->real_path instead of &ff->user_path. + +2. Mainline's file_free() does both the backing_file cleanup and the +kmem_cache_free() synchronously. Linux 6.6.y defers the actual kfree() +to file_free_rcu() via call_rcu(), so only path_put() is done +synchronously in file_free().] +Signed-off-by: Cai Xinchen + +Signed-off-by: Sasha Levin +--- + fs/file_table.c | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/fs/file_table.c b/fs/file_table.c +index 234284ef72a9a5..b4c208a771539d 100644 +--- a/fs/file_table.c ++++ b/fs/file_table.c +@@ -72,11 +72,16 @@ static void file_free_rcu(struct rcu_head *head) + kmem_cache_free(filp_cachep, f); + } + ++static inline void backing_file_free(struct backing_file *ff) ++{ ++ path_put(&ff->real_path); ++} ++ + static inline void file_free(struct file *f) + { + security_file_free(f); + if (unlikely(f->f_mode & FMODE_BACKING)) +- path_put(backing_file_real_path(f)); ++ backing_file_free(backing_file(f)); + if (likely(!(f->f_mode & FMODE_NOACCOUNT))) + percpu_counter_dec(&nr_files); + call_rcu(&f->f_rcuhead, file_free_rcu); +@@ -252,6 +257,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) + return f; + } + ++static int init_backing_file(struct backing_file *ff) ++{ ++ memset(&ff->real_path, 0, sizeof(ff->real_path)); ++ return 0; ++} ++ + /* + * Variant of alloc_empty_file() that allocates a backing_file container + * and doesn't check and modify nr_files. +@@ -274,7 +285,14 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) + return ERR_PTR(error); + } + ++ /* The f_mode flags must be set before fput(). */ + ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; ++ error = init_backing_file(ff); ++ if (unlikely(error)) { ++ fput(&ff->file); ++ return ERR_PTR(error); ++ } ++ + return &ff->file; + } + +-- +2.53.0 + diff --git a/queue-6.6/lsm-add-backing_file-lsm-hooks.patch b/queue-6.6/lsm-add-backing_file-lsm-hooks.patch new file mode 100644 index 0000000000..ce76bb3d60 --- /dev/null +++ b/queue-6.6/lsm-add-backing_file-lsm-hooks.patch @@ -0,0 +1,527 @@ +From f8d61919aeb3768608afa3cc2158c74dda3ee5a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 27 Jun 2026 14:57:19 +0800 +Subject: lsm: add backing_file LSM hooks + +From: Paul Moore + +[ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ] + +Stacked filesystems such as overlayfs do not currently provide the +necessary mechanisms for LSMs to properly enforce access controls on the +mmap() and mprotect() operations. In order to resolve this gap, a LSM +security blob is being added to the backing_file struct and the following +new LSM hooks are being created: + + security_backing_file_alloc() + security_backing_file_free() + security_mmap_backing_file() + +The first two hooks are to manage the lifecycle of the LSM security blob +in the backing_file struct, while the third provides a new mmap() access +control point for the underlying backing file. It is also expected that +LSMs will likely want to update their security_file_mprotect() callback +to address issues with their mprotect() controls, but that does not +require a change to the security_file_mprotect() LSM hook. + +There are a three other small changes to support these new LSM hooks: +* Pass the user file associated with a backing file down to +alloc_empty_backing_file() so it can be included in the +security_backing_file_alloc() hook. +* Add getter and setter functions for the backing_file struct LSM blob +as the backing_file struct remains private to fs/file_table.c. +* Constify the file struct field in the LSM common_audit_data struct to +better support LSMs that need to pass a const file struct pointer into +the common LSM audit code. + +Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL() +and supplying a fixup. + +Cc: stable@vger.kernel.org +Cc: linux-fsdevel@vger.kernel.org +Cc: linux-unionfs@vger.kernel.org +Cc: linux-erofs@lists.ozlabs.org +Reviewed-by: Amir Goldstein +Reviewed-by: Serge Hallyn +Reviewed-by: Christian Brauner +Signed-off-by: Paul Moore +[1. Mainline uses call_int_hook(FUNC, ...) with the default IRC baked +into the macro. Linux 6.6.y uses call_int_hook(FUNC, IRC, ...) requiring +an explicit default return value. + +2. fs/backing-file.c does not exist in LTS +Linux 6.6.y places backing_file_open() in fs/open.c and lacks a +dedicated fs/backing-file.c. The backing_file_mmap() function and +scoped_with_creds() do not exist in 6.6.y. Therefore the LTS patch calls +security_mmap_backing_file() directly in ovl_mmap() in +fs/overlayfs/file.c rather than modifying backing_file_mmap(). + +3. Missing filesystems/modules +Linux 6.6.y does not have backing_tmpfile_open(), fs/fuse/passthrough.c, +or the erofs ishare mmap path that the mainline patch touches. These hunks +are dropped in the 6.6 LTS backport. + +4. Use macro backing_file to replace inline function to eliminate the +const warning.] +Signed-off-by: Cai Xinchen +Signed-off-by: Sasha Levin +--- + fs/file_table.c | 32 +++++++--- + fs/internal.h | 3 +- + fs/open.c | 7 ++- + fs/overlayfs/file.c | 8 ++- + include/linux/fs.h | 15 ++++- + include/linux/lsm_audit.h | 2 +- + include/linux/lsm_hook_defs.h | 5 ++ + include/linux/lsm_hooks.h | 1 + + include/linux/security.h | 22 +++++++ + security/security.c | 110 ++++++++++++++++++++++++++++++++++ + 10 files changed, 190 insertions(+), 15 deletions(-) + +diff --git a/fs/file_table.c b/fs/file_table.c +index b4c208a771539d..fa4d4c54f790c8 100644 +--- a/fs/file_table.c ++++ b/fs/file_table.c +@@ -48,12 +48,12 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp; + struct backing_file { + struct file file; + struct path real_path; ++#ifdef CONFIG_SECURITY ++ void *security; ++#endif + }; + +-static inline struct backing_file *backing_file(struct file *f) +-{ +- return container_of(f, struct backing_file, file); +-} ++#define backing_file(f) container_of(f, struct backing_file, file) + + struct path *backing_file_real_path(struct file *f) + { +@@ -72,8 +72,21 @@ static void file_free_rcu(struct rcu_head *head) + kmem_cache_free(filp_cachep, f); + } + ++#ifdef CONFIG_SECURITY ++void *backing_file_security(const struct file *f) ++{ ++ return backing_file(f)->security; ++} ++ ++void backing_file_set_security(struct file *f, void *security) ++{ ++ backing_file(f)->security = security; ++} ++#endif /* CONFIG_SECURITY */ ++ + static inline void backing_file_free(struct backing_file *ff) + { ++ security_backing_file_free(&ff->file); + path_put(&ff->real_path); + } + +@@ -257,10 +270,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) + return f; + } + +-static int init_backing_file(struct backing_file *ff) ++static int init_backing_file(struct backing_file *ff, ++ const struct file *user_file) + { + memset(&ff->real_path, 0, sizeof(ff->real_path)); +- return 0; ++ backing_file_set_security(&ff->file, NULL); ++ return security_backing_file_alloc(&ff->file, user_file); + } + + /* +@@ -270,7 +285,8 @@ static int init_backing_file(struct backing_file *ff) + * This is only for kernel internal use, and the allocate file must not be + * installed into file tables or such. + */ +-struct file *alloc_empty_backing_file(int flags, const struct cred *cred) ++struct file *alloc_empty_backing_file(int flags, const struct cred *cred, ++ const struct file *user_file) + { + struct backing_file *ff; + int error; +@@ -287,7 +303,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) + + /* The f_mode flags must be set before fput(). */ + ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; +- error = init_backing_file(ff); ++ error = init_backing_file(ff, user_file); + if (unlikely(error)) { + fput(&ff->file); + return ERR_PTR(error); +diff --git a/fs/internal.h b/fs/internal.h +index d64ae03998cce9..576026e1ce3aab 100644 +--- a/fs/internal.h ++++ b/fs/internal.h +@@ -93,7 +93,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *); + */ + struct file *alloc_empty_file(int flags, const struct cred *cred); + struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); +-struct file *alloc_empty_backing_file(int flags, const struct cred *cred); ++struct file *alloc_empty_backing_file(int flags, const struct cred *cred, ++ const struct file *user_file); + + static inline void put_file_access(struct file *file) + { +diff --git a/fs/open.c b/fs/open.c +index b5ea1dcbfb2242..5b164481d80b34 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -1175,18 +1175,19 @@ EXPORT_SYMBOL_GPL(kernel_file_open); + * the backing inode on the underlying filesystem, which can be + * retrieved using backing_file_real_path(). + */ +-struct file *backing_file_open(const struct path *path, int flags, ++struct file *backing_file_open(const struct file *user_file, int flags, + const struct path *real_path, + const struct cred *cred) + { ++ const struct path *user_path = &user_file->f_path; + struct file *f; + int error; + +- f = alloc_empty_backing_file(flags, cred); ++ f = alloc_empty_backing_file(flags, cred, user_file); + if (IS_ERR(f)) + return f; + +- f->f_path = *path; ++ f->f_path = *user_path; + path_get(real_path); + *backing_file_real_path(f) = *real_path; + error = do_dentry_open(f, d_inode(real_path->dentry), NULL); +diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c +index 8be4dc050d1ed2..07f00cf91977ee 100644 +--- a/fs/overlayfs/file.c ++++ b/fs/overlayfs/file.c +@@ -60,7 +60,7 @@ static struct file *ovl_open_realfile(const struct file *file, + if (!inode_owner_or_capable(real_idmap, realinode)) + flags &= ~O_NOATIME; + +- realfile = backing_file_open(&file->f_path, flags, realpath, ++ realfile = backing_file_open(file, flags, realpath, + current_cred()); + } + revert_creds(old_cred); +@@ -527,6 +527,7 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) + + static int ovl_mmap(struct file *file, struct vm_area_struct *vma) + { ++ struct file *user_file = vma->vm_file; + struct file *realfile = file->private_data; + const struct cred *old_cred; + int ret; +@@ -540,6 +541,11 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma) + vma_set_file(vma, realfile); + + old_cred = ovl_override_creds(file_inode(file)->i_sb); ++ ret = security_mmap_backing_file(vma, realfile, user_file); ++ if (ret) { ++ revert_creds(old_cred); ++ return ret; ++ } + ret = call_mmap(vma->vm_file, vma); + revert_creds(old_cred); + ovl_file_accessed(file); +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 4cdeeaedaa404f..5a897ee50f29cf 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -2515,11 +2515,24 @@ struct file *dentry_open(const struct path *path, int flags, + const struct cred *creds); + struct file *dentry_create(const struct path *path, int flags, umode_t mode, + const struct cred *cred); +-struct file *backing_file_open(const struct path *path, int flags, ++struct file *backing_file_open(const struct file *user_file, int flags, + const struct path *real_path, + const struct cred *cred); + struct path *backing_file_real_path(struct file *f); + ++#ifdef CONFIG_SECURITY ++void *backing_file_security(const struct file *f); ++void backing_file_set_security(struct file *f, void *security); ++#else ++static inline void *backing_file_security(const struct file *f) ++{ ++ return NULL; ++} ++static inline void backing_file_set_security(struct file *f, void *security) ++{ ++} ++#endif /* CONFIG_SECURITY */ ++ + /* + * file_real_path - get the path corresponding to f_inode + * +diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h +index 97a8b21eb03339..c0a2839253fa1f 100644 +--- a/include/linux/lsm_audit.h ++++ b/include/linux/lsm_audit.h +@@ -93,7 +93,7 @@ struct common_audit_data { + #endif + char *kmod_name; + struct lsm_ioctlop_audit *op; +- struct file *file; ++ const struct file *file; + struct lsm_ibpkey_audit *ibpkey; + struct lsm_ibendport_audit *ibendport; + int reason; +diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h +index 2923754c13bce6..567d7f16609c8b 100644 +--- a/include/linux/lsm_hook_defs.h ++++ b/include/linux/lsm_hook_defs.h +@@ -169,6 +169,9 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir, + LSM_HOOK(int, 0, file_permission, struct file *file, int mask) + LSM_HOOK(int, 0, file_alloc_security, struct file *file) + LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) ++LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file, ++ const struct file *user_file) ++LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file) + LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, + unsigned long arg) + LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, +@@ -176,6 +179,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, + LSM_HOOK(int, 0, mmap_addr, unsigned long addr) + LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) ++LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma, ++ struct file *backing_file, struct file *user_file) + LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot) + LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) +diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h +index dcb5e5b5eb1352..3b56b60195ceb9 100644 +--- a/include/linux/lsm_hooks.h ++++ b/include/linux/lsm_hooks.h +@@ -59,6 +59,7 @@ struct security_hook_list { + struct lsm_blob_sizes { + int lbs_cred; + int lbs_file; ++ int lbs_backing_file; + int lbs_inode; + int lbs_superblock; + int lbs_ipc; +diff --git a/include/linux/security.h b/include/linux/security.h +index 937840870d86c3..4866ffdb4e6c1d 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -389,11 +389,17 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir, + int security_file_permission(struct file *file, int mask); + int security_file_alloc(struct file *file); + void security_file_free(struct file *file); ++int security_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file); ++void security_backing_file_free(struct file *backing_file); + int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); + int security_file_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg); + int security_mmap_file(struct file *file, unsigned long prot, + unsigned long flags); ++int security_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file); + int security_mmap_addr(unsigned long addr); + int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, + unsigned long prot); +@@ -984,6 +990,15 @@ static inline int security_file_alloc(struct file *file) + static inline void security_file_free(struct file *file) + { } + ++static inline int security_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file) ++{ ++ return 0; ++} ++ ++static inline void security_backing_file_free(struct file *backing_file) ++{ } ++ + static inline int security_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) + { +@@ -1003,6 +1018,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot, + return 0; + } + ++static inline int security_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file) ++{ ++ return 0; ++} ++ + static inline int security_mmap_addr(unsigned long addr) + { + return cap_mmap_addr(addr); +diff --git a/security/security.c b/security/security.c +index 1794860fd614ea..4b61766c3d27bd 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -78,6 +78,7 @@ struct security_hook_heads security_hook_heads __ro_after_init; + static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); + + static struct kmem_cache *lsm_file_cache; ++static struct kmem_cache *lsm_backing_file_cache; + static struct kmem_cache *lsm_inode_cache; + + char *lsm_names; +@@ -200,6 +201,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) + + lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); + lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); ++ lsm_set_blob_size(&needed->lbs_backing_file, ++ &blob_sizes.lbs_backing_file); + /* + * The inode blob gets an rcu_head in addition to + * what the modules might need. +@@ -374,6 +377,7 @@ static void __init ordered_lsm_init(void) + + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); + init_debug("file blob size = %d\n", blob_sizes.lbs_file); ++ init_debug("backing_file blob size = %d\n", blob_sizes.lbs_backing_file); + init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); + init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); + init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); +@@ -388,6 +392,11 @@ static void __init ordered_lsm_init(void) + lsm_file_cache = kmem_cache_create("lsm_file_cache", + blob_sizes.lbs_file, 0, + SLAB_PANIC, NULL); ++ if (blob_sizes.lbs_backing_file) ++ lsm_backing_file_cache = kmem_cache_create( ++ "lsm_backing_file_cache", ++ blob_sizes.lbs_backing_file, ++ 0, SLAB_PANIC, NULL); + if (blob_sizes.lbs_inode) + lsm_inode_cache = kmem_cache_create("lsm_inode_cache", + blob_sizes.lbs_inode, 0, +@@ -616,6 +625,30 @@ static int lsm_file_alloc(struct file *file) + return 0; + } + ++/** ++ * lsm_backing_file_alloc - allocate a composite backing file blob ++ * @backing_file: the backing file ++ * ++ * Allocate the backing file blob for all the modules. ++ * ++ * Returns 0, or -ENOMEM if memory can't be allocated. ++ */ ++static int lsm_backing_file_alloc(struct file *backing_file) ++{ ++ void *blob; ++ ++ if (!lsm_backing_file_cache) { ++ backing_file_set_security(backing_file, NULL); ++ return 0; ++ } ++ ++ blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL); ++ backing_file_set_security(backing_file, blob); ++ if (!blob) ++ return -ENOMEM; ++ return 0; ++} ++ + /** + * lsm_inode_alloc - allocate a composite inode blob + * @inode: the inode that needs a blob +@@ -2630,6 +2663,57 @@ void security_file_free(struct file *file) + } + } + ++/** ++ * security_backing_file_alloc() - Allocate and setup a backing file blob ++ * @backing_file: the backing file ++ * @user_file: the associated user visible file ++ * ++ * Allocate a backing file LSM blob and perform any necessary initialization of ++ * the LSM blob. There will be some operations where the LSM will not have ++ * access to @user_file after this point, so any important state associated ++ * with @user_file that is important to the LSM should be captured in the ++ * backing file's LSM blob. ++ * ++ * LSM's should avoid taking a reference to @user_file in this hook as it will ++ * result in problems later when the system attempts to drop/put the file ++ * references due to a circular dependency. ++ * ++ * Return: Return 0 if the hook is successful, negative values otherwise. ++ */ ++int security_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file) ++{ ++ int rc; ++ ++ rc = lsm_backing_file_alloc(backing_file); ++ if (rc) ++ return rc; ++ rc = call_int_hook(backing_file_alloc, 0, backing_file, user_file); ++ if (unlikely(rc)) ++ security_backing_file_free(backing_file); ++ ++ return rc; ++} ++ ++/** ++ * security_backing_file_free() - Free a backing file blob ++ * @backing_file: the backing file ++ * ++ * Free any LSM state associate with a backing file's LSM blob, including the ++ * blob itself. ++ */ ++void security_backing_file_free(struct file *backing_file) ++{ ++ void *blob = backing_file_security(backing_file); ++ ++ call_void_hook(backing_file_free, backing_file); ++ ++ if (blob) { ++ backing_file_set_security(backing_file, NULL); ++ kmem_cache_free(lsm_backing_file_cache, blob); ++ } ++} ++ + /** + * security_file_ioctl() - Check if an ioctl is allowed + * @file: associated file +@@ -2723,6 +2807,32 @@ int security_mmap_file(struct file *file, unsigned long prot, + return ima_file_mmap(file, prot, prot_adj, flags); + } + ++/** ++ * security_mmap_backing_file - Check if mmap'ing a backing file is allowed ++ * @vma: the vm_area_struct for the mmap'd region ++ * @backing_file: the backing file being mmap'd ++ * @user_file: the user file being mmap'd ++ * ++ * Check permissions for a mmap operation on a stacked filesystem. This hook ++ * is called after the security_mmap_file() and is responsible for authorizing ++ * the mmap on @backing_file. It is important to note that the mmap operation ++ * on @user_file has already been authorized and the @vma->vm_file has been ++ * set to @backing_file. ++ * ++ * Return: Returns 0 if permission is granted. ++ */ ++int security_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file) ++{ ++ /* recommended by the stackable filesystem devs */ ++ if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING))) ++ return -EIO; ++ ++ return call_int_hook(mmap_backing_file, 0, vma, backing_file, user_file); ++} ++EXPORT_SYMBOL_GPL(security_mmap_backing_file); ++ + /** + * security_mmap_addr() - Check if mmap'ing an address is allowed + * @addr: address +-- +2.53.0 + diff --git a/queue-6.6/selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch b/queue-6.6/selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch new file mode 100644 index 0000000000..f4978dce33 --- /dev/null +++ b/queue-6.6/selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch @@ -0,0 +1,429 @@ +From bb1643bbb774b8de439075556cf7364333729fbd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 27 Jun 2026 14:57:20 +0800 +Subject: selinux: fix overlayfs mmap() and mprotect() access checks + +From: Paul Moore + +[ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ] + +The existing SELinux security model for overlayfs is to allow access if +the current task is able to access the top level file (the "user" file) +and the mounter's credentials are sufficient to access the lower +level file (the "backing" file). Unfortunately, the current code does +not properly enforce these access controls for both mmap() and mprotect() +operations on overlayfs filesystems. + +This patch makes use of the newly created security_mmap_backing_file() +LSM hook to provide the missing backing file enforcement for mmap() +operations, and leverages the backing file API and new LSM blob to +provide the necessary information to properly enforce the mprotect() +access controls. + +Cc: stable@vger.kernel.org +Acked-by: Amir Goldstein +Signed-off-by: Paul Moore +[backing_file_user_path() not available +Mainline uses backing_file_user_path(file) to obtain the user-visible path +from a backing file. The 6.6.y version uses &file->f_path directly] +Signed-off-by: Cai Xinchen +Signed-off-by: Sasha Levin +--- + security/selinux/hooks.c | 242 ++++++++++++++++++++++-------- + security/selinux/include/objsec.h | 11 ++ + 2 files changed, 189 insertions(+), 64 deletions(-) + +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index 60092d0b013ce0..3f11c5ae8fbf0e 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -1717,49 +1717,72 @@ static inline int file_path_has_perm(const struct cred *cred, + static int bpf_fd_pass(const struct file *file, u32 sid); + #endif + +-/* Check whether a task can use an open file descriptor to +- access an inode in a given way. Check access to the +- descriptor itself, and then use dentry_has_perm to +- check a particular permission to the file. +- Access to the descriptor is implicitly granted if it +- has the same SID as the process. If av is zero, then +- access to the file is not checked, e.g. for cases +- where only the descriptor is affected like seek. */ +-static int file_has_perm(const struct cred *cred, +- struct file *file, +- u32 av) ++static int __file_has_perm(const struct cred *cred, const struct file *file, ++ u32 av, bool bf_user_file) ++ + { +- struct file_security_struct *fsec = selinux_file(file); +- struct inode *inode = file_inode(file); + struct common_audit_data ad; +- u32 sid = cred_sid(cred); ++ struct inode *inode; ++ u32 ssid = cred_sid(cred); ++ u32 tsid_fd; + int rc; + +- ad.type = LSM_AUDIT_DATA_FILE; +- ad.u.file = file; ++ if (bf_user_file) { ++ struct backing_file_security_struct *bfsec; ++ const struct path *path; + +- if (sid != fsec->sid) { +- rc = avc_has_perm(sid, fsec->sid, +- SECCLASS_FD, +- FD__USE, +- &ad); ++ if (WARN_ON(!(file->f_mode & FMODE_BACKING))) ++ return -EIO; ++ ++ bfsec = selinux_backing_file(file); ++ path = &file->f_path; ++ tsid_fd = bfsec->uf_sid; ++ inode = d_inode(path->dentry); ++ ++ ad.type = LSM_AUDIT_DATA_PATH; ++ ad.u.path = *path; ++ } else { ++ struct file_security_struct *fsec = selinux_file(file); ++ ++ tsid_fd = fsec->sid; ++ inode = file_inode(file); ++ ++ ad.type = LSM_AUDIT_DATA_FILE; ++ ad.u.file = file; ++ } ++ ++ if (ssid != tsid_fd) { ++ rc = avc_has_perm(ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad); + if (rc) +- goto out; ++ return rc; + } + + #ifdef CONFIG_BPF_SYSCALL +- rc = bpf_fd_pass(file, cred_sid(cred)); ++ /* regardless of backing vs user file, use the underlying file here */ ++ rc = bpf_fd_pass(file, ssid); + if (rc) + return rc; + #endif + + /* av is zero if only checking access to the descriptor. */ +- rc = 0; + if (av) +- rc = inode_has_perm(cred, inode, av, &ad); ++ return inode_has_perm(cred, inode, av, &ad); + +-out: +- return rc; ++ return 0; ++} ++ ++/* Check whether a task can use an open file descriptor to ++ access an inode in a given way. Check access to the ++ descriptor itself, and then use dentry_has_perm to ++ check a particular permission to the file. ++ Access to the descriptor is implicitly granted if it ++ has the same SID as the process. If av is zero, then ++ access to the file is not checked, e.g. for cases ++ where only the descriptor is affected like seek. */ ++static inline int file_has_perm(const struct cred *cred, ++ const struct file *file, u32 av) ++{ ++ return __file_has_perm(cred, file, av, false); + } + + /* +@@ -3638,6 +3661,17 @@ static int selinux_file_alloc_security(struct file *file) + return 0; + } + ++static int selinux_backing_file_alloc(struct file *backing_file, ++ const struct file *user_file) ++{ ++ struct backing_file_security_struct *bfsec; ++ ++ bfsec = selinux_backing_file(backing_file); ++ bfsec->uf_sid = selinux_file(user_file)->sid; ++ ++ return 0; ++} ++ + /* + * Check whether a task has the ioctl permission and cmd + * operation to an inode. +@@ -3755,42 +3789,55 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, + + static int default_noexec __ro_after_init; + +-static int file_map_prot_check(struct file *file, unsigned long prot, int shared) ++static int __file_map_prot_check(const struct cred *cred, ++ const struct file *file, unsigned long prot, ++ bool shared, bool bf_user_file) + { +- const struct cred *cred = current_cred(); +- u32 sid = cred_sid(cred); +- int rc = 0; ++ struct inode *inode = NULL; ++ bool prot_exec = prot & PROT_EXEC; ++ bool prot_write = prot & PROT_WRITE; ++ ++ if (file) { ++ if (bf_user_file) ++ inode = d_inode(file->f_path.dentry); ++ else ++ inode = file_inode(file); ++ } ++ ++ if (default_noexec && prot_exec && ++ (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { ++ int rc; ++ u32 sid = cred_sid(cred); + +- if (default_noexec && +- (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) || +- (!shared && (prot & PROT_WRITE)))) { + /* +- * We are making executable an anonymous mapping or a +- * private file mapping that will also be writable. +- * This has an additional check. ++ * We are making executable an anonymous mapping or a private ++ * file mapping that will also be writable. + */ +- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, +- PROCESS__EXECMEM, NULL); ++ rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, ++ NULL); + if (rc) +- goto error; ++ return rc; + } + + if (file) { +- /* read access is always possible with a mapping */ ++ /* "read" always possible, "write" only if shared */ + u32 av = FILE__READ; +- +- /* write access only matters if the mapping is shared */ +- if (shared && (prot & PROT_WRITE)) ++ if (shared && prot_write) + av |= FILE__WRITE; +- +- if (prot & PROT_EXEC) ++ if (prot_exec) + av |= FILE__EXECUTE; + +- return file_has_perm(cred, file, av); ++ return __file_has_perm(cred, file, av, bf_user_file); + } + +-error: +- return rc; ++ return 0; ++} ++ ++static inline int file_map_prot_check(const struct cred *cred, ++ const struct file *file, ++ unsigned long prot, bool shared) ++{ ++ return __file_map_prot_check(cred, file, prot, shared, false); + } + + static int selinux_mmap_addr(unsigned long addr) +@@ -3806,36 +3853,80 @@ static int selinux_mmap_addr(unsigned long addr) + return rc; + } + +-static int selinux_mmap_file(struct file *file, +- unsigned long reqprot __always_unused, +- unsigned long prot, unsigned long flags) ++static int selinux_mmap_file_common(const struct cred *cred, struct file *file, ++ unsigned long prot, bool shared) + { +- struct common_audit_data ad; +- int rc; +- + if (file) { ++ int rc; ++ struct common_audit_data ad; ++ + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; +- rc = inode_has_perm(current_cred(), file_inode(file), +- FILE__MAP, &ad); ++ rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad); + if (rc) + return rc; + } + +- return file_map_prot_check(file, prot, +- (flags & MAP_TYPE) == MAP_SHARED); ++ return file_map_prot_check(cred, file, prot, shared); ++} ++ ++static int selinux_mmap_file(struct file *file, ++ unsigned long reqprot __always_unused, ++ unsigned long prot, unsigned long flags) ++{ ++ return selinux_mmap_file_common(current_cred(), file, prot, ++ (flags & MAP_TYPE) == MAP_SHARED); ++} ++ ++/** ++ * selinux_mmap_backing_file - Check mmap permissions on a backing file ++ * @vma: memory region ++ * @backing_file: stacked filesystem backing file ++ * @user_file: user visible file ++ * ++ * This is called after selinux_mmap_file() on stacked filesystems, and it ++ * is this function's responsibility to verify access to @backing_file and ++ * setup the SELinux state for possible later use in the mprotect() code path. ++ * ++ * By the time this function is called, mmap() access to @user_file has already ++ * been authorized and @vma->vm_file has been set to point to @backing_file. ++ * ++ * Return zero on success, negative values otherwise. ++ */ ++static int selinux_mmap_backing_file(struct vm_area_struct *vma, ++ struct file *backing_file, ++ struct file *user_file __always_unused) ++{ ++ unsigned long prot = 0; ++ ++ /* translate vma->vm_flags perms into PROT perms */ ++ if (vma->vm_flags & VM_READ) ++ prot |= PROT_READ; ++ if (vma->vm_flags & VM_WRITE) ++ prot |= PROT_WRITE; ++ if (vma->vm_flags & VM_EXEC) ++ prot |= PROT_EXEC; ++ ++ return selinux_mmap_file_common(backing_file->f_cred, backing_file, ++ prot, vma->vm_flags & VM_SHARED); + } + + static int selinux_file_mprotect(struct vm_area_struct *vma, + unsigned long reqprot __always_unused, + unsigned long prot) + { ++ int rc; + const struct cred *cred = current_cred(); + u32 sid = cred_sid(cred); ++ const struct file *file = vma->vm_file; ++ bool backing_file; ++ bool shared = vma->vm_flags & VM_SHARED; ++ ++ /* check if we need to trigger the "backing files are awful" mode */ ++ backing_file = file && (file->f_mode & FMODE_BACKING); + + if (default_noexec && + (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { +- int rc = 0; + /* + * We don't use the vma_is_initial_heap() helper as it has + * a history of problems and is currently broken on systems +@@ -3849,11 +3940,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, + vma->vm_end <= vma->vm_mm->brk) { + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECHEAP, NULL); +- } else if (!vma->vm_file && (vma_is_initial_stack(vma) || ++ if (rc) ++ return rc; ++ } else if (!file && (vma_is_initial_stack(vma) || + vma_is_stack_for_current(vma))) { + rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, + PROCESS__EXECSTACK, NULL); +- } else if (vma->vm_file && vma->anon_vma) { ++ if (rc) ++ return rc; ++ } else if (file && vma->anon_vma) { + /* + * We are making executable a file mapping that has + * had some COW done. Since pages might have been +@@ -3861,13 +3956,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, + * modified content. This typically should only + * occur for text relocations. + */ +- rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD); ++ rc = __file_has_perm(cred, file, FILE__EXECMOD, ++ backing_file); ++ if (rc) ++ return rc; ++ if (backing_file) { ++ rc = file_has_perm(file->f_cred, file, ++ FILE__EXECMOD); ++ if (rc) ++ return rc; ++ } + } ++ } ++ ++ rc = __file_map_prot_check(cred, file, prot, shared, backing_file); ++ if (rc) ++ return rc; ++ if (backing_file) { ++ rc = file_map_prot_check(file->f_cred, file, prot, shared); + if (rc) + return rc; + } + +- return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); ++ return 0; + } + + static int selinux_file_lock(struct file *file, unsigned int cmd) +@@ -6870,6 +6981,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) + struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = { + .lbs_cred = sizeof(struct task_security_struct), + .lbs_file = sizeof(struct file_security_struct), ++ .lbs_backing_file = sizeof(struct backing_file_security_struct), + .lbs_inode = sizeof(struct inode_security_struct), + .lbs_ipc = sizeof(struct ipc_security_struct), + .lbs_msg_msg = sizeof(struct msg_security_struct), +@@ -7074,9 +7186,11 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { + + LSM_HOOK_INIT(file_permission, selinux_file_permission), + LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), ++ LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc), + LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), + LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), + LSM_HOOK_INIT(mmap_file, selinux_mmap_file), ++ LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file), + LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), + LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect), + LSM_HOOK_INIT(file_lock, selinux_file_lock), +diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h +index 8159fd53c3de2e..541933dd295c07 100644 +--- a/security/selinux/include/objsec.h ++++ b/security/selinux/include/objsec.h +@@ -60,6 +60,10 @@ struct file_security_struct { + u32 pseqno; /* Policy seqno at the time of file open */ + }; + ++struct backing_file_security_struct { ++ u32 uf_sid; /* associated user file fsec->sid */ ++}; ++ + struct superblock_security_struct { + u32 sid; /* SID of file system superblock */ + u32 def_sid; /* default SID for labeling */ +@@ -158,6 +162,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file) + return file->f_security + selinux_blob_sizes.lbs_file; + } + ++static inline struct backing_file_security_struct * ++selinux_backing_file(const struct file *backing_file) ++{ ++ void *blob = backing_file_security(backing_file); ++ return blob + selinux_blob_sizes.lbs_backing_file; ++} ++ + static inline struct inode_security_struct *selinux_inode( + const struct inode *inode) + { +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index bf52f3ae0a..f4821efb0a 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -85,3 +85,31 @@ kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-che.patch revert-ptp-add-testptp-mask-test.patch bluetooth-btmtk-validate-wmt-event-skb-length-before.patch bluetooth-btmtk-accept-too-short-wmt-func_ctrl-event.patch +fs-prepare-for-adding-lsm-blob-to-backing_file.patch +lsm-add-backing_file-lsm-hooks.patch +selinux-fix-overlayfs-mmap-and-mprotect-access-check.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-ensure-bcast-is-writable-before-modifying.patch +batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch diff --git a/queue-7.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch b/queue-7.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch new file mode 100644 index 0000000000..e11d3e7f4e --- /dev/null +++ b/queue-7.1/batman-adv-bla-annotate-lasttime-access-with-read-wr.patch @@ -0,0 +1,156 @@ +From 96e7f68e827d16e65c8378d4d5ae404b7758082e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:24 +0200 +Subject: batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE + +From: Sven Eckelmann + +commit 98b0fb191c878a64cbaebfe231d96d57576acf8c upstream. + +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. + +Cc: stable@kernel.org +Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index ffe854018bd3a5..7f643de42579df 100644 +--- 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_priv *bat_priv, const u8 *orig, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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 batadv_priv *bat_priv, + 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(struct batadv_priv *bat_priv, u8 *an_addr, + 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(struct batadv_priv *bat_priv, int now) + 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(struct batadv_priv *bat_priv, + 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(struct work_struct *work) + 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(struct work_struct *work) + 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) +@@ -1899,7 +1899,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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; + +@@ -2014,7 +2014,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + + if (own_claim) { + /* ... allow it in any case */ +- claim->lasttime = jiffies; ++ WRITE_ONCE(claim->lasttime, jiffies); + goto allow; + } + +@@ -2116,7 +2116,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + /* 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. +@@ -2361,7 +2361,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, + 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)) { +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch b/queue-7.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch new file mode 100644 index 0000000000..67ecae827a --- /dev/null +++ b/queue-7.1/batman-adv-dat-prevent-false-sharing-between-vlans.patch @@ -0,0 +1,57 @@ +From d31ab8c648c9cf65c6f0b843643f6ddfab6eafdf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:39 +0200 +Subject: batman-adv: dat: prevent false sharing between VLANs + +From: Sven Eckelmann + +commit 20d7658b74169f86d4ac01b9185b3eadddf71f28 upstream. + +The local hash of DAT entries is supposed to be VLAN (VID) aware. But +the adding to the hash and the search in the hash were not checking the VID +information of the hash entries. The entries would therefore only be +correctly separated when batadv_hash_dat() didn't select the same buckets +for different VIDs. + +Cc: stable@kernel.org +Fixes: be1db4f6615b ("batman-adv: make the Distributed ARP Table vlan aware") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/distributed-arp-table.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 0a8bd95e2f99e8..86fb5de5022aca 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -214,10 +214,13 @@ static void batadv_dat_purge(struct work_struct *work) + */ + static bool batadv_compare_dat(const struct hlist_node *node, const void *data2) + { +- const void *data1 = container_of(node, struct batadv_dat_entry, +- hash_entry); ++ const struct batadv_dat_entry *entry1; ++ const struct batadv_dat_entry *entry2; + +- return memcmp(data1, data2, sizeof(__be32)) == 0; ++ entry1 = container_of(node, struct batadv_dat_entry, hash_entry); ++ entry2 = data2; ++ ++ return entry1->ip == entry2->ip && entry1->vid == entry2->vid; + } + + /** +@@ -344,6 +347,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + if (dat_entry->ip != ip) + continue; + ++ if (dat_entry->vid != vid) ++ continue; ++ + if (!kref_get_unless_zero(&dat_entry->refcount)) + continue; + +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch b/queue-7.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch new file mode 100644 index 0000000000..a2392bd7af --- /dev/null +++ b/queue-7.1/batman-adv-ensure-bcast-is-writable-before-modifying.patch @@ -0,0 +1,50 @@ +From dc8162d9a2fb72a3646b361fd3d9fe049049171b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:28 +0200 +Subject: batman-adv: ensure bcast is writable before modifying TTL + +From: Sven Eckelmann + +commit 4cd6d3a4b96a8576f1fed8f9f9f17c2dc2978e0c upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or used skb_cow() to ensure that the data part is not +shared. + +The old implementation used a shared queue and created copies before +attempting to write to it. But with the new implementation, the broadcast +packet is already modified when it gets received. Potentially writing to +shared buffers in this process. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to rebroadcast the +packet. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 12c16f81cc51df..0672dc30bed3b1 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1191,6 +1191,12 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto free_skb; + ++ /* create a copy of the skb, if needed, to modify it. */ ++ if (skb_cow(skb, ETH_HLEN) < 0) ++ goto free_skb; ++ ++ bcast_packet = (struct batadv_bcast_packet *)skb->data; ++ + if (bcast_packet->ttl-- < 2) + goto free_skb; + +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch b/queue-7.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch new file mode 100644 index 0000000000..f4c287b75e --- /dev/null +++ b/queue-7.1/batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch @@ -0,0 +1,127 @@ +From 271603aa8370798e690548246db1a265c5245906 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:29 +0200 +Subject: batman-adv: fix (m|b)cast csum after decrementing TTL + +From: Sven Eckelmann + +commit e728bbdf32660c8f32b8f5e8d09427a2c131ad60 upstream. + +The broadcast and multicast packets can be received at the same time by the +local system and forwarded to other nodes. Both are simply decrementing the +TTL at the beginning of the receive path - independent of chosen paths +(receive/forward). But such a modification of the data conflicts with the +hw csum. This is not a problem when the packet is directly forwarded but +can cause errors in the local receive path. + +Such a problem can then trigger a "hw csum failure". The receiver path must +therefore ensure that the csum is fixed for each modification of the +payload before batadv_interface_rx() is reached. + +Since all batman-adv packet types with a ttl have it as u8 at offset 2, a +helper can be used for all of them. But it is only used at the moment for +batadv_bcast_packet and batadv_mcast_packet because they are the only ones +which deliver the packet locally but unconditionally modify the TTL. + +Cc: stable@kernel.org +Fixes: 3f69339068f9 ("batman-adv: bcast: queue per interface, if needed") +Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/routing.c | 58 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 56 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 0672dc30bed3b1..cdcea90db61235 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -8,6 +8,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb, + return true; + } + ++/** ++ * batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe ++ * @skb: the received packet with @skb->data pointing to the batman-adv header ++ * ++ * Supports the following packet types, all of which carry the TTL at offset 2: ++ * ++ * - batadv_ogm_packet ++ * - batadv_ogm2_packet ++ * - batadv_icmp_header ++ * - batadv_icmp_packet ++ * - batadv_icmp_tp_packet ++ * - batadv_icmp_packet_rr ++ * - batadv_unicast_packet ++ * - batadv_frag_packet ++ * - batadv_bcast_packet ++ * - batadv_mcast_packet ++ * - batadv_coded_packet ++ * - batadv_unicast_tvlv_packet ++ * ++ * Return: true if the packet may be forwarded (ttl decremented), ++ * false if it must be dropped (ttl would expire) ++ */ ++static bool batadv_skb_decrement_ttl(struct sk_buff *skb) ++{ ++ static const size_t ttl_offset = 2; ++ u8 *ttl_pos; ++ ++ BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset); ++ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset); ++ ++ ttl_pos = skb->data + ttl_offset; ++ ++ /* would expire on this hop -> drop, leave header + csum untouched */ ++ if (*ttl_pos < 2) ++ return false; ++ ++ skb_postpull_rcsum(skb, ttl_pos, 1); ++ (*ttl_pos)--; ++ skb_postpush_rcsum(skb, ttl_pos, 1); ++ ++ return true; ++} ++ + /** + * batadv_recv_my_icmp_packet() - receive an icmp packet locally + * @bat_priv: the bat priv with all the mesh interface information +@@ -1197,7 +1251,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, + + bcast_packet = (struct batadv_bcast_packet *)skb->data; + +- if (bcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); +@@ -1304,7 +1358,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, + goto free_skb; + + mcast_packet = (struct batadv_mcast_packet *)skb->data; +- if (mcast_packet->ttl-- < 2) ++ if (!batadv_skb_decrement_ttl(skb)) + goto free_skb; + + tvlv_buff = (unsigned char *)(skb->data + hdr_size); +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-frag-avoid-underflow-of-ttl.patch b/queue-7.1/batman-adv-frag-avoid-underflow-of-ttl.patch new file mode 100644 index 0000000000..e0cc4c11f9 --- /dev/null +++ b/queue-7.1/batman-adv-frag-avoid-underflow-of-ttl.patch @@ -0,0 +1,45 @@ +From e821ae76c6a01d79effefa9559c830143de0539c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:31 +0200 +Subject: batman-adv: frag: avoid underflow of TTL + +From: Sven Eckelmann + +commit 493d9d2528e1a09b090e4b37f0f553def7bd5ce9 upstream. + +Packets with a TTL are using it to limit the amount of time this packet can +be forwarded. But for batadv_frag_packet, the TTL was always only reduced +but it was never evaluated. It could even underflow without any effect. + +Check the TTL in batadv_frag_skb_fwd() before attempting to prepare it for +forwarding. This keeps it in sync with the not fragmented unicast packet. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index 5c2a7629e4e09d..9a5927ecc4746f 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -415,6 +415,13 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (packet->ttl < 2) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ + if (skb_cow(skb, ETH_HLEN) < 0) { + kfree_skb(skb); + *rx_result = NET_RX_DROP; +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch b/queue-7.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch new file mode 100644 index 0000000000..e67cbb85df --- /dev/null +++ b/queue-7.1/batman-adv-frag-ensure-fragment-is-writable-before-m.patch @@ -0,0 +1,106 @@ +From 88dbf2a8c2ce086ac680e2d454702c79fb3a8b09 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:30 +0200 +Subject: batman-adv: frag: ensure fragment is writable before modifying TTL + +From: Sven Eckelmann + +commit b7293c6e8c15b2db77809b25cf8389e35331b27a upstream. + +Before batman-adv is allowed to write to an skb, it either has to have its +own copy of the skb or use skb_cow() to ensure that the data part is not +shared. But batadv_frag_skb_fwd() modifies the TTL even when it is shared. + +Adding a skb_cow() right before this operation avoids this and can at the +same time prepare it for the modifications required to forward the +fragment. + +Cc: stable@kernel.org +Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/fragmentation.c | 15 ++++++++++++++- + net/batman-adv/fragmentation.h | 3 ++- + net/batman-adv/routing.c | 3 +-- + 3 files changed, 17 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c +index e9553db423491a..5c2a7629e4e09d 100644 +--- a/net/batman-adv/fragmentation.c ++++ b/net/batman-adv/fragmentation.c +@@ -384,6 +384,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from ++ * @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and ++ * NET_RX_DROP when it was dropped; only valid when true is returned + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded +@@ -393,7 +395,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, + */ + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src) ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result) + { + struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface); + struct batadv_neigh_node *neigh_node = NULL; +@@ -412,12 +415,22 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { ++ if (skb_cow(skb, ETH_HLEN) < 0) { ++ kfree_skb(skb); ++ *rx_result = NET_RX_DROP; ++ ret = true; ++ goto out; ++ } ++ ++ packet = (struct batadv_frag_packet *)skb->data; ++ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->ttl--; + batadv_send_unicast_skb(skb, neigh_node); ++ *rx_result = NET_RX_SUCCESS; + ret = true; + } + +diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h +index dbf0871f870303..51e281027ab630 100644 +--- a/net/batman-adv/fragmentation.h ++++ b/net/batman-adv/fragmentation.h +@@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); + bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, +- struct batadv_orig_node *orig_node_src); ++ struct batadv_orig_node *orig_node_src, ++ int *rx_result); + bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); + int batadv_frag_send_packet(struct sk_buff *skb, +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index cdcea90db61235..4483f8d9c75831 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1168,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb, + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && +- batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { ++ batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) { + /* skb was consumed */ + skb = NULL; +- ret = NET_RX_SUCCESS; + goto put_orig_node; + } + +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch b/queue-7.1/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch new file mode 100644 index 0000000000..9967e568e2 --- /dev/null +++ b/queue-7.1/batman-adv-gw-don-t-deselect-gateway-with-active-har.patch @@ -0,0 +1,84 @@ +From 194cbd473d76a79408f0c1b4dd250a56bc9cf038 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:27 +0200 +Subject: batman-adv: gw: don't deselect gateway with active hardif + +From: Sven Eckelmann + +commit df97a7107b16375a10a36d7a63e9b4291a8ac680 upstream. + +The batadv_hardif_cnt() was previously checking if there is an +batadv_hard_iface->mesh_iface which is has the same mesh_iface. And since +batadv_hardif_disable_interface() was resetting the +batadv_hard_iface->mesh_iface after this check, it had to verify whether +*1* interface was still part of the mesh_iface before it started the +gateway deselection. + +But after batadv_hardif_cnt() is now checking the lower interfaces of +mesh_iface and batadv_hardif_disable_interface() already removed the +interface via netdev_upper_dev_unlink() earlier in this function, the check +must now make sure that *0* interfaces can be found by batadv_hardif_cnt() +before selected gateway must be deselected. Otherwise the deselection would +already happen one batadv_hard_iface too early. + +Because a 0 hardif count from batadv_hardif_cnt() is equal to an empty +list, it is possible to replace the counting with a simple list_empty(). + +Cc: stable@kernel.org +Fixes: 7dc284702bcd ("batman-adv: store hard_iface as iflink private data") +Reviewed-by: Nora Schiffer +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/hard-interface.c | 28 ++-------------------------- + 1 file changed, 2 insertions(+), 26 deletions(-) + +diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c +index d6732c34aeafc8..c7dbd24ea19985 100644 +--- a/net/batman-adv/hard-interface.c ++++ b/net/batman-adv/hard-interface.c +@@ -786,30 +786,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, + return ret; + } + +-/** +- * batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface +- * @mesh_iface: mesh interface to check +- * +- * This function is only using RCU for locking - the result can therefore be +- * off when another function is modifying the list at the same time. The +- * caller can use the rtnl_lock to make sure that the count is accurate. +- * +- * Return: number of connected/enslaved hard interfaces +- */ +-static size_t batadv_hardif_cnt(struct net_device *mesh_iface) +-{ +- struct batadv_hard_iface *hard_iface; +- struct list_head *iter; +- size_t count = 0; +- +- rcu_read_lock(); +- netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) +- count++; +- rcu_read_unlock(); +- +- return count; +-} +- + /** + * batadv_hardif_disable_interface() - Remove hard interface from mesh interface + * @hard_iface: hard interface to be removed +@@ -850,8 +826,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) + netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface); + batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface); + +- /* nobody uses this interface anymore */ +- if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1) ++ /* nobody uses this mesh interface anymore */ ++ if (list_empty(&hard_iface->mesh_iface->adj_list.lower)) + batadv_gw_check_client_stop(bat_priv); + + hard_iface->mesh_iface = NULL; +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-prevent-elp-transmission-interval-underfl.patch b/queue-7.1/batman-adv-prevent-elp-transmission-interval-underfl.patch new file mode 100644 index 0000000000..5d31813e7a --- /dev/null +++ b/queue-7.1/batman-adv-prevent-elp-transmission-interval-underfl.patch @@ -0,0 +1,52 @@ +From e6dfffb8565c4514bb23a60d4e965684785bd1e7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:25 +0200 +Subject: batman-adv: prevent ELP transmission interval underflow + +From: Sven Eckelmann + +commit 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca upstream. + +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. + +Cc: stable@kernel.org +Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c +index 78c651f634cd39..1d144d8cc0928e 100644 +--- a/net/batman-adv/netlink.c ++++ b/net/batman-adv/netlink.c +@@ -917,9 +917,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, + #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]) { +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch b/queue-7.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch new file mode 100644 index 0000000000..08624725c2 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch @@ -0,0 +1,74 @@ +From ad368ca6b053b93eb4848ac734e0593d5d3c7a97 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:23 +0200 +Subject: batman-adv: tp_meter: add only finished tp_vars to lists + +From: Sven Eckelmann + +commit 15ccbf685222274f5add1387af58c2a41a95f81e upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0801dcf93540ac..7bfad65c862e9a 100644 +--- 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 *bat_priv, const u8 *dst, + 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 *bat_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); + +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch b/queue-7.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch new file mode 100644 index 0000000000..eda6e69595 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-annotate-last_recv_time-access-w.patch @@ -0,0 +1,66 @@ +From b10da62fe1bf22c0d4f160d236d23da6f962a8cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:34 +0200 +Subject: batman-adv: tp_meter: annotate last_recv_time access with + READ/WRITE_ONCE + +From: Sven Eckelmann + +commit d67c728f07fca2ee6ffdc6dd4421cf2e8691f4d1 upstream. + +The last_recv_time field for batadv_tp_receiver 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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 4f8af909893ab9..e23b75846ebe1a 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1183,7 +1183,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + bat_priv = tp_vars->bat_priv; + + /* if there is recent activity rearm the timer */ +- if (!batadv_has_timed_out(tp_vars->last_recv_time, ++ if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time), + BATADV_TP_RECV_TIMEOUT)) { + /* reset the receiver shutdown timer */ + batadv_tp_reset_receiver_timer(tp_vars); +@@ -1424,7 +1424,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, + icmp->session, BATADV_TP_RECEIVER); + if (tp_vars) { +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + goto out_unlock; + } + +@@ -1455,7 +1455,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); +@@ -1506,7 +1506,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + goto out; + } + +- tp_vars->last_recv_time = jiffies; ++ WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + + /* if the packet is a duplicate, it may be the case that an ACK has been +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch b/queue-7.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch new file mode 100644 index 0000000000..97373aaefa --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch @@ -0,0 +1,64 @@ +From 50472f5e86ad53e0eb270d2686fb05c3bfe4fd12 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:20 +0200 +Subject: batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd + +From: Sven Eckelmann + +commit 33ccd52f3cc9ed46ce395199f89aa3234dc83314 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index d3b8f02ca08b14..e4f76c141af3e2 100644 +--- 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 batadv_tp_vars *tp_vars, u32 mss) + 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; +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-avoid-window-underflow.patch b/queue-7.1/batman-adv-tp_meter-avoid-window-underflow.patch new file mode 100644 index 0000000000..dc9d68e839 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-avoid-window-underflow.patch @@ -0,0 +1,56 @@ +From 1e14937db4cc5ff86a2c73915c4afa6354b9e044 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:19 +0200 +Subject: batman-adv: tp_meter: avoid window underflow + +From: Sven Eckelmann + +commit 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 2ff7aa5ed19fb3..d3b8f02ca08b14 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, + 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; + } +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch b/queue-7.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch new file mode 100644 index 0000000000..96d4958335 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-fix-fast-recovery-precondition.patch @@ -0,0 +1,48 @@ +From 0c428129fc44a0745c0dda017f0504b2c9938d95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:21 +0200 +Subject: batman-adv: tp_meter: fix fast recovery precondition + +From: Sven Eckelmann + +commit 2b0d08f08ed3b2174f05c43089ec65f3543a025b upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index e4f76c141af3e2..77bc69573a5624 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-handle-overlapping-packets.patch b/queue-7.1/batman-adv-tp_meter-handle-overlapping-packets.patch new file mode 100644 index 0000000000..9f63e5fd36 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-handle-overlapping-packets.patch @@ -0,0 +1,109 @@ +From 889335ce69ea5dbae87aa12923b8dd7e51e60ab1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:36 +0200 +Subject: batman-adv: tp_meter: handle overlapping packets + +From: Sven Eckelmann + +commit cbde75c38b21f022891525078622587ad557b7c1 upstream. + +If the size of the packets would change during the transmission, it could +happen that some retries of packets are overlapping. In this case, precise +comparisons of sequence numbers by the receiver would be wrong. It is then +necessary to check if the start sequence number to the end sequence number +("seqno + length") would contain a new range. + +If this is the case then this is enough to accept this packet. In all other +cases, the packet still has to be dropped (and not acked). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 6a23050f03d89e..dbaae33db0f1f8 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1284,7 +1284,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + /** + * batadv_tp_handle_out_of_order() - store an out of order packet + * @tp_vars: the private data of the current TP meter session +- * @skb: the buffer containing the received packet ++ * @seqno: sequence number of new received packet ++ * @payload_len: length of the received packet + * + * Store the out of order packet in the unacked list for late processing. This + * packets are kept in this list so that they can be ACKed at once as soon as +@@ -1293,22 +1294,17 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + * Return: true if the packed has been successfully processed, false otherwise + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, +- const struct sk_buff *skb) ++ u32 seqno, u32 payload_len) + __must_hold(&tp_vars->unacked_lock) + { +- const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +- u32 payload_len; + bool added = false; + + new = kmalloc_obj(*new, GFP_ATOMIC); + if (unlikely(!new)) + return false; + +- icmp = (struct batadv_icmp_tp_packet *)skb->data; +- +- new->seqno = ntohl(icmp->seqno); +- payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ new->seqno = seqno; + new->len = payload_len; + + /* if the list is empty immediately attach this new object */ +@@ -1476,7 +1472,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; +- size_t packet_size; ++ u32 payload_len; + u32 to_ack; + u32 seqno; + +@@ -1511,15 +1507,17 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +- if (batadv_seq_before(seqno, tp_vars->last_recv)) ++ payload_len = skb->len - sizeof(struct batadv_unicast_packet); ++ to_ack = seqno + payload_len; ++ if (batadv_seq_before(to_ack, tp_vars->last_recv)) + goto send_ack; + + /* if the packet is out of order enqueue it */ +- if (ntohl(icmp->seqno) != tp_vars->last_recv) { ++ if (batadv_seq_before(tp_vars->last_recv, seqno)) { + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { + spin_unlock_bh(&tp_vars->unacked_lock); + goto out; + } +@@ -1529,8 +1527,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + } + + /* if everything was fine count the ACKed bytes */ +- packet_size = skb->len - sizeof(struct batadv_unicast_packet); +- tp_vars->last_recv += packet_size; ++ tp_vars->last_recv = to_ack; + + /* check if this ordered message filled a gap.... */ + batadv_tp_ack_unordered(tp_vars); +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch b/queue-7.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch new file mode 100644 index 0000000000..09f1de3b0a --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch @@ -0,0 +1,40 @@ +From ba12ba73ab6ec278d8d10821c5609298ea477e4b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:22 +0200 +Subject: batman-adv: tp_meter: handle seqno wrap-around for fast recovery + detection + +From: Sven Eckelmann + +commit f54c85ed42a1b27a516cf2a4728f5a612b799e07 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 77bc69573a5624..0801dcf93540ac 100644 +--- 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 batadv_priv *bat_priv, + 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 */ +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch b/queue-7.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch new file mode 100644 index 0000000000..b9e8a60975 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch @@ -0,0 +1,40 @@ +From d5ff1d0f45192d289c6b78bc7aa2334b7377b8fb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:18 +0200 +Subject: batman-adv: tp_meter: initialize dec_cwnd explicitly + +From: Sven Eckelmann + +commit febfb1b86224489535312296ecfa3d4bf467f339 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0325b951ff8a8e..2ff7aa5ed19fb3 100644 +--- 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 *bat_priv, const u8 *dst, + * mesh_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 + */ +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch b/queue-7.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch new file mode 100644 index 0000000000..b544cfae0f --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-initialize-dup_acks-explicitly.patch @@ -0,0 +1,42 @@ +From c1a29c5c5fefc0bf5899f771c793854209adc01f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:17 +0200 +Subject: batman-adv: tp_meter: initialize dup_acks explicitly + +From: Sven Eckelmann + +commit b2b68b32a715e0328662801576974aa37b942b00 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index e8941f753a9697..0325b951ff8a8e 100644 +--- 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 *bat_priv, const u8 *dst, + 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; +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch b/queue-7.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch new file mode 100644 index 0000000000..42a630c46e --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-initialize-last_recv_time-during.patch @@ -0,0 +1,68 @@ +From f4e0258629da50d0465ea4a44d140cf08fcb7c3e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:26 +0200 +Subject: batman-adv: tp_meter: initialize last_recv_time during init + +From: Sven Eckelmann + +commit 811cb00fa8cdc3f0a7f6eefc000a6888367c8c8f upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 7bfad65c862e9a..397b9456353568 100644 +--- 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 *bat_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 *bat_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 batadv_priv *bat_priv, + 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 +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch b/queue-7.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch new file mode 100644 index 0000000000..de2dc03925 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch @@ -0,0 +1,44 @@ +From 32e3c4739f435b27983632f24739b66c0f08a86f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:16 +0200 +Subject: batman-adv: tp_meter: keep unacked list in ascending ordered + +From: Sven Eckelmann + +commit 5aa8651527ea0b610e7a09fb3b8204c1398b9525 upstream. + +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. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 0fc4ca78e84ebe..e8941f753a9697 100644 +--- 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_order(struct batadv_tp_vars *tp_vars, + * 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; + } +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch b/queue-7.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch new file mode 100644 index 0000000000..5a432843d4 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-prevent-parallel-modifications-o.patch @@ -0,0 +1,157 @@ +From 3f0174568d3a659bba18a1577dcbe5395e5f4273 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:35 +0200 +Subject: batman-adv: tp_meter: prevent parallel modifications of last_recv + +From: Sven Eckelmann + +commit 6dde0cfcb36e4d5b3de35b75696937478441eed4 upstream. + +When last_recv is updated to store the last receive sequence number, it is +assuming that nothing is modifying in parallel while: + +* check for outdated packets is done +* out of order check is performed (and packets are stored in out-of-order + queue) +* the out-of-order queue was searched for closed gaps +* sequence number for next ack is calculated + +Nothing of that was actually protected. It could therefore happen that the +last_recv was updated multiple times in parallel and the final sequence +number was calculated with deltas which had no connection to the sequence +number they were added to. + +Lock this whole region with the same lock which was already used to protect +the unacked (out-of-order) list. + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 22 +++++++++++++--------- + net/batman-adv/types.h | 2 +- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index e23b75846ebe1a..6a23050f03d89e 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -1294,6 +1294,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, + */ + static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + const struct sk_buff *skb) ++ __must_hold(&tp_vars->unacked_lock) + { + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_unacked *un, *new; +@@ -1310,12 +1311,11 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + payload_len = skb->len - sizeof(struct batadv_unicast_packet); + new->len = payload_len; + +- spin_lock_bh(&tp_vars->unacked_lock); + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); + tp_vars->unacked_count++; +- goto out; ++ return true; + } + + /* otherwise loop over the list and either drop the packet because this +@@ -1364,9 +1364,6 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + tp_vars->unacked_count--; + } + +-out: +- spin_unlock_bh(&tp_vars->unacked_lock); +- + return true; + } + +@@ -1376,6 +1373,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + * @tp_vars: the private data of the current TP meter session + */ + static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) ++ __must_hold(&tp_vars->unacked_lock) + { + struct batadv_tp_unacked *un, *safe; + u32 to_ack; +@@ -1383,7 +1381,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + /* go through the unacked packet list and possibly ACK them as + * well + */ +- spin_lock_bh(&tp_vars->unacked_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + /* the list is ordered, therefore it is possible to stop as soon + * there is a gap between the last acked seqno and the seqno of +@@ -1401,7 +1398,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + kfree(un); + tp_vars->unacked_count--; + } +- spin_unlock_bh(&tp_vars->unacked_lock); + } + + /** +@@ -1481,6 +1477,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + const struct batadv_icmp_tp_packet *icmp; + struct batadv_tp_vars *tp_vars; + size_t packet_size; ++ u32 to_ack; + u32 seqno; + + icmp = (struct batadv_icmp_tp_packet *)skb->data; +@@ -1509,6 +1506,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + WRITE_ONCE(tp_vars->last_recv_time, jiffies); + } + ++ spin_lock_bh(&tp_vars->unacked_lock); ++ + /* if the packet is a duplicate, it may be the case that an ACK has been + * lost. Resend the ACK + */ +@@ -1520,8 +1519,10 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + /* exit immediately (and do not send any ACK) if the packet has + * not been enqueued correctly + */ +- if (!batadv_tp_handle_out_of_order(tp_vars, skb)) ++ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) { ++ spin_unlock_bh(&tp_vars->unacked_lock); + goto out; ++ } + + /* send a duplicate ACK */ + goto send_ack; +@@ -1535,11 +1536,14 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + batadv_tp_ack_unordered(tp_vars); + + send_ack: ++ to_ack = tp_vars->last_recv; ++ spin_unlock_bh(&tp_vars->unacked_lock); ++ + /* send the ACK. If the received packet was out of order, the ACK that + * is going to be sent is a duplicate (the sender will count them and + * possibly enter Fast Retransmit as soon as it has reached 3) + */ +- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, ++ batadv_tp_send_ack(bat_priv, icmp->orig, to_ack, + icmp->timestamp, icmp->session, icmp->uid); + out: + batadv_tp_vars_put(tp_vars); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 4f9f1820e3a92a..3f940a201cdf84 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1423,7 +1423,7 @@ struct batadv_tp_vars { + /** @unacked_list: list of unacked packets (meta-info only) */ + struct list_head unacked_list; + +- /** @unacked_lock: protect unacked_list */ ++ /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */ + spinlock_t unacked_lock; + + /** @unacked_count: number of unacked entries */ +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch b/queue-7.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch new file mode 100644 index 0000000000..edeba31596 --- /dev/null +++ b/queue-7.1/batman-adv-tp_meter-restrict-number-of-unacked-list-.patch @@ -0,0 +1,124 @@ +From 523e22bf49a45ae523732a740dcd032b7c5ca624 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:33 +0200 +Subject: batman-adv: tp_meter: restrict number of unacked list entries + +From: Sven Eckelmann + +commit e7c775110e1858e5a7471a23a9c9658c0af9df89 upstream. + +When the unacked_list is unbound, an attacker could send messages with +small lengths and appropriated seqno + gaps to force the receiver to +allocate more and more unacked_list entries. And the end either causing an +out-of-memory situation or increase the management overhead for the (large) +list that significant portions of CPU cycles are wasted in searching +through the list. + +When limiting the list to a specific number, it is important to still +correctly add a new entry to the list. But if the list became larger than +the limit, the last entry of the list (with the highest seqno) must be +dropped to still allow the earlier seqnos to finish and therefore to +continue the process. Otherwise, the process might get stuck with too high +seqnos which are not handled by batadv_tp_ack_unordered(). + +Cc: stable@kernel.org +Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") +[ Switch to pre-splitted tp_vars structure names ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tp_meter.c | 23 ++++++++++++++++++++++- + net/batman-adv/types.h | 3 +++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c +index 397b9456353568..4f8af909893ab9 100644 +--- a/net/batman-adv/tp_meter.c ++++ b/net/batman-adv/tp_meter.c +@@ -87,6 +87,11 @@ + #define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \ + sizeof(struct batadv_unicast_packet)) + ++/** ++ * BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack ++ */ ++#define BATADV_TP_MAX_UNACKED 100 ++ + static u8 batadv_tp_prerandom[4096] __read_mostly; + + /** +@@ -1195,6 +1200,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + +@@ -1308,6 +1314,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + /* if the list is empty immediately attach this new object */ + if (list_empty(&tp_vars->unacked_list)) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; + goto out; + } + +@@ -1338,12 +1345,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars, + */ + list_add(&new->list, &un->list); + added = true; ++ tp_vars->unacked_count++; + break; + } + + /* received packet with smallest seqno out of order; add it to front */ +- if (!added) ++ if (!added) { + list_add(&new->list, &tp_vars->unacked_list); ++ tp_vars->unacked_count++; ++ } ++ ++ /* remove the last (biggest) unacked seqno when list is too large */ ++ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { ++ un = list_last_entry(&tp_vars->unacked_list, ++ struct batadv_tp_unacked, list); ++ list_del(&un->list); ++ kfree(un); ++ tp_vars->unacked_count--; ++ } + + out: + spin_unlock_bh(&tp_vars->unacked_lock); +@@ -1380,6 +1399,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) + + list_del(&un->list); + kfree(un); ++ tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->unacked_lock); + } +@@ -1430,6 +1450,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, + + spin_lock_init(&tp_vars->unacked_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); ++ tp_vars->unacked_count = 0; + + kref_get(&tp_vars->refcount); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 765ba71fe8c69f..4f9f1820e3a92a 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1426,6 +1426,9 @@ struct batadv_tp_vars { + /** @unacked_lock: protect unacked_list */ + spinlock_t unacked_lock; + ++ /** @unacked_count: number of unacked entries */ ++ size_t unacked_count; ++ + /** @last_recv_time: time (jiffies) a msg was received */ + unsigned long last_recv_time; + +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch b/queue-7.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch new file mode 100644 index 0000000000..d808c51b97 --- /dev/null +++ b/queue-7.1/batman-adv-tt-don-t-merge-change-entries-with-differ.patch @@ -0,0 +1,45 @@ +From d6f9bbc2de4900719a81c75f567bc79acb45823a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:37 +0200 +Subject: batman-adv: tt: don't merge change entries with different VIDs + +From: Sven Eckelmann + +commit f08e06c2d5c3e2434e7c773f2213f4a7dce6bc1e upstream. + +batadv_tt_local_event() merges/cancels events for the same client which +would conflict or be duplicates. The matching of the queued events only +compares the MAC address - the VLAN ID stored in each event is ignored. + +If a MAC would now appear on multiple VID, the two ADD change events (for +VID 1 and VID 2) would be merged to a single vid event. The remote can +therefore not calculate the correct TT table and desync. A full translation +table exchange is required to recover from this state. + +A check of VID is therefore necessary to avoid such wrong merges/cancels. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 9f6e67771ffa80..acd8af4446671f 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -446,6 +446,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, + if (!batadv_compare_eth(entry->change.addr, common->addr)) + continue; + ++ if (entry->change.vid != tt_change_node->change.vid) ++ continue; ++ + del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL; + if (del_op_requested != del_op_entry) { + /* DEL+ADD in the same orig interval have no effect and +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tt-track-roam-count-per-vid.patch b/queue-7.1/batman-adv-tt-track-roam-count-per-vid.patch new file mode 100644 index 0000000000..5857c739c5 --- /dev/null +++ b/queue-7.1/batman-adv-tt-track-roam-count-per-vid.patch @@ -0,0 +1,88 @@ +From 0e598bfd4bdb4c923bed647e81511140b39bceb4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:38 +0200 +Subject: batman-adv: tt: track roam count per VID + +From: Sven Eckelmann + +commit 12407d5f61c2653a64f2ff4b22f3c267f8420ef1 upstream. + +batadv_tt_check_roam_count() is supposed to track roaming of a TT entry. +But TT entries are for a MAC + VID. The VID was completely missed and thus +leads to incorrect detection of ROAM counts when a client MAC exists in +multiple VLANs. + +Cc: stable@kernel.org +Fixes: c018ad3de61a ("batman-adv: add the VLAN ID attribute to the TT entry") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/translation-table.c | 9 +++++++-- + net/batman-adv/types.h | 3 +++ + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index acd8af4446671f..83dfd804a143ab 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -3442,6 +3442,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * batadv_tt_check_roam_count() - check if a client has roamed too frequently + * @bat_priv: the bat priv with all the mesh interface information + * @client: mac address of the roaming client ++ * @vid: VLAN identifier + * + * This function checks whether the client already reached the + * maximum number of possible roaming phases. In this case the ROAMING_ADV +@@ -3449,7 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv) + * + * Return: true if the ROAMING_ADV can be sent, false otherwise + */ +-static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) ++static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid) + { + struct batadv_tt_roam_node *tt_roam_node; + bool ret = false; +@@ -3462,6 +3463,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + if (!batadv_compare_eth(tt_roam_node->addr, client)) + continue; + ++ if (tt_roam_node->vid != vid) ++ continue; ++ + if (batadv_has_timed_out(tt_roam_node->first_time, + BATADV_ROAMING_MAX_TIME)) + continue; +@@ -3483,6 +3487,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client) + atomic_set(&tt_roam_node->counter, + BATADV_ROAMING_MAX_COUNT - 1); + ether_addr_copy(tt_roam_node->addr, client); ++ tt_roam_node->vid = vid; + + list_add(&tt_roam_node->list, &bat_priv->tt.roam_list); + ret = true; +@@ -3519,7 +3524,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, + /* before going on we have to check whether the client has + * already roamed to us too many times + */ +- if (!batadv_tt_check_roam_count(bat_priv, client)) ++ if (!batadv_tt_check_roam_count(bat_priv, client, vid)) + goto out; + + batadv_dbg(BATADV_DBG_TT, bat_priv, +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 3f940a201cdf84..4dce996f01ccea 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1912,6 +1912,9 @@ struct batadv_tt_roam_node { + /** @addr: mac address of the client in the roaming phase */ + u8 addr[ETH_ALEN]; + ++ /** @vid: VLAN identifier */ ++ u16 vid; ++ + /** + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch b/queue-7.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch new file mode 100644 index 0000000000..baa3d557bf --- /dev/null +++ b/queue-7.1/batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch @@ -0,0 +1,154 @@ +From 9f855c0e6e12c62a92462b2aef9b08a82442f26a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:41 +0200 +Subject: batman-adv: tvlv: avoid race of cifsnotfound handler state + +From: Sven Eckelmann + +commit edb557b2ba38fea2c5eb710cf366c797e187218c upstream. + +TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to +signal that the OGM handler should be called (with NULL for data) when the +specific TVLV container was not found in the OGM. This is used by: + +* DAT +* GW +* Multicast (OGM + Tracker) + +The state whether the handler was executed was stored in the struct +batadv_tvlv_handler. But the TVLV processing is started without any lock. +Multiple parallel contexts processing TVLVs would therefore overwrite each +others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared +batadv_tvlv_handler. + +Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine, +per TVLV buffer, whether a matching container was present by scanning the +packet's buffer. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/tvlv.c | 63 ++++++++++++++++++++++++++++++++++++++---- + net/batman-adv/types.h | 7 ----- + 2 files changed, 57 insertions(+), 13 deletions(-) + +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index baadf97f98e5f0..afb61a46d7a67c 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -398,7 +398,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); +- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + break; + case BATADV_UNICAST_TVLV: + if (!skb) +@@ -430,6 +429,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + return NET_RX_SUCCESS; + } + ++/** ++ * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container ++ * @tvlv_value: tvlv content ++ * @tvlv_value_len: tvlv content length ++ * @type: tvlv container type to look for ++ * @version: tvlv container version to look for ++ * ++ * Return: true if a container of the given type and version is present in the ++ * tvlv buffer, false otherwise. ++ */ ++static bool batadv_tvlv_containers_contain(void *tvlv_value, ++ u16 tvlv_value_len, u8 type, ++ u8 version) ++{ ++ struct batadv_tvlv_hdr *tvlv_hdr; ++ u16 tvlv_value_cont_len; ++ ++ while (tvlv_value_len >= sizeof(*tvlv_hdr)) { ++ tvlv_hdr = tvlv_value; ++ tvlv_value_cont_len = ntohs(tvlv_hdr->len); ++ tvlv_value = tvlv_hdr + 1; ++ tvlv_value_len -= sizeof(*tvlv_hdr); ++ ++ if (tvlv_value_cont_len > tvlv_value_len) ++ break; ++ ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ ++ if (tvlv_hdr->type == type && tvlv_hdr->version == version) ++ return true; ++ ++ tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len; ++ tvlv_value_len -= tvlv_value_cont_len; ++ } ++ ++ return false; ++} ++ + /** + * batadv_tvlv_containers_process() - parse the given tvlv buffer to call the + * appropriate handlers +@@ -449,7 +490,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + struct sk_buff *skb, void *tvlv_value, + u16 tvlv_value_len) + { ++ u16 tvlv_value_start_len = tvlv_value_len; + struct batadv_tvlv_handler *tvlv_handler; ++ void *tvlv_value_start = tvlv_value; + struct batadv_tvlv_hdr *tvlv_hdr; + u16 tvlv_value_cont_len; + u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; +@@ -493,12 +536,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (!tvlv_handler->ogm_handler) + continue; + +- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && +- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) +- tvlv_handler->ogm_handler(bat_priv, orig_node, +- cifnotfound, NULL, 0); ++ if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)) ++ continue; + +- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; ++ /* if the corresponding container was present then the handler ++ * was already called from the loop above ++ */ ++ if (batadv_tvlv_containers_contain(tvlv_value_start, ++ tvlv_value_start_len, ++ tvlv_handler->type, ++ tvlv_handler->version)) ++ continue; ++ ++ tvlv_handler->ogm_handler(bat_priv, orig_node, ++ cifnotfound, NULL, 0); + } + rcu_read_unlock(); + +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 4dce996f01ccea..30c870cacf4374 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -2245,13 +2245,6 @@ enum batadv_tvlv_handler_flags { + * will call this handler even if its type was not found (with no data) + */ + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), +- +- /** +- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the +- * API marks a handler as being called, so it won't be called if the +- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set +- */ +- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + }; + + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-tvlv-enforce-2-byte-alignment.patch b/queue-7.1/batman-adv-tvlv-enforce-2-byte-alignment.patch new file mode 100644 index 0000000000..8e04ddbdd9 --- /dev/null +++ b/queue-7.1/batman-adv-tvlv-enforce-2-byte-alignment.patch @@ -0,0 +1,123 @@ +From 01e84cf126f63ab51ef800b816bf69a429f182a0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:40 +0200 +Subject: batman-adv: tvlv: enforce 2-byte alignment + +From: Sven Eckelmann + +commit 32a6799255525d6ea4da0f7e9e0e521ad9560a46 upstream. + +The fields of an aggregated OGM(v2) are accessed assuming (at least) 2-byte +alignment, so a following OGM must start at an even offset. As the header +length is even, an odd tvlv_len would misalign it and trigger unaligned +accesses on strict-alignment architectures. + +Such a misaligned TVLV/OGM/OGMv2 is not created by a normal participant in +the mesh. Therefore, reject such malformed packets. + +Cc: stable@kernel.org +Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_iv_ogm.c | 11 ++++++++++- + net/batman-adv/bat_v_ogm.c | 11 ++++++++++- + net/batman-adv/routing.c | 6 ++++++ + net/batman-adv/tvlv.c | 6 ++++++ + 4 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c +index b8b1b997960a96..6e79f69c2fedec 100644 +--- a/net/batman-adv/bat_iv_ogm.c ++++ b/net/batman-adv/bat_iv_ogm.c +@@ -311,14 +311,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm_packet *ogm_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGM are accessed assuming (at least) ++ * 2-byte alignment, so a following OGM must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index 6852bf5da8c558..1f9b2d2b4831ce 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -849,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, + const struct batadv_ogm2_packet *ogm2_packet) + { + int next_buff_pos = 0; ++ u16 tvlv_len; + + /* check if there is enough space for the header */ + next_buff_pos += buff_pos + sizeof(*ogm2_packet); + if (next_buff_pos > packet_len) + return false; + ++ tvlv_len = ntohs(ogm2_packet->tvlv_len); ++ ++ /* the fields of an aggregated OGMv2 are accessed assuming (at least) ++ * 2-byte alignment, so a following OGMv2 must start at an even offset. ++ */ ++ if (tvlv_len & 1) ++ return false; ++ + /* check if there is enough space for the optional TVLV */ +- next_buff_pos += ntohs(ogm2_packet->tvlv_len); ++ next_buff_pos += tvlv_len; + + return next_buff_pos <= packet_len; + } +diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c +index 4483f8d9c75831..41951c7a1c50b2 100644 +--- a/net/batman-adv/routing.c ++++ b/net/batman-adv/routing.c +@@ -1366,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb, + if (tvlv_buff_len > skb->len - hdr_size) + goto free_skb; + ++ /* the fields of an multicast payload are accessed assuming (at least) ++ * 2-byte alignment, so a following packet must start at an even offset. ++ */ ++ if (tvlv_buff_len & 1) ++ goto free_skb; ++ + ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb, + tvlv_buff, tvlv_buff_len); + if (ret >= 0) { +diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c +index cc6ac580c62085..baadf97f98e5f0 100644 +--- a/net/batman-adv/tvlv.c ++++ b/net/batman-adv/tvlv.c +@@ -464,6 +464,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + if (tvlv_value_cont_len > tvlv_value_len) + break; + ++ /* the next tvlv header is accessed assuming (at least) 2-byte ++ * alignment, so it must start at an even offset. ++ */ ++ if (tvlv_value_cont_len & 1) ++ break; ++ + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); +-- +2.53.0 + diff --git a/queue-7.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch b/queue-7.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch new file mode 100644 index 0000000000..7ab993e18c --- /dev/null +++ b/queue-7.1/batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch @@ -0,0 +1,106 @@ +From 748bca1ecf89bc43ed6b143f25c29f48e352a93f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 18:12:32 +0200 +Subject: batman-adv: v: prevent OGM aggregation on disabled hardif + +From: Sven Eckelmann + +commit d11c00b95b2a3b3934007fc003dccc6fdcc061ad upstream. + +When an interface gets disabled, the worker is correctly disabled by +batadv_hardif_disable_interface() -> ... -> batadv_v_ogm_iface_disable(). +In this process, the skb aggr_list is also freed. + +But batadv_v_ogm_send_meshif() can still queue new skbs (via +batadv_v_ogm_queue_on_if()) to the aggr_list. This will only stop after all +cores can no longer find the RCU protected list of hard interfaces. These +queued skbs will never be freed or consumed by batadv_v_ogm_aggr_work. + +The batadv_v_ogm_iface_disable() function must block +batadv_v_ogm_queue_on_if() to avoid leak of skbs. + +Cc: stable@kernel.org +Fixes: f89255a02f1d ("batman-adv: BATMAN_V: introduce per hard-iface OGMv2 queues") +[ Context ] +Signed-off-by: Sven Eckelmann +Signed-off-by: Sasha Levin +--- + net/batman-adv/bat_v.c | 1 + + net/batman-adv/bat_v_ogm.c | 12 ++++++++++++ + net/batman-adv/types.h | 6 ++++++ + 3 files changed, 19 insertions(+) + +diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c +index de94447142642e..17d2a1ccdce67a 100644 +--- a/net/batman-adv/bat_v.c ++++ b/net/batman-adv/bat_v.c +@@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) + + hard_iface->bat_v.aggr_len = 0; + skb_queue_head_init(&hard_iface->bat_v.aggr_list); ++ hard_iface->bat_v.aggr_list_enabled = false; + INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq, + batadv_v_ogm_aggr_work); + } +diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c +index d66ca77b1aaa3c..6852bf5da8c558 100644 +--- a/net/batman-adv/bat_v_ogm.c ++++ b/net/batman-adv/bat_v_ogm.c +@@ -252,11 +252,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + } + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ if (!hard_iface->bat_v.aggr_list_enabled) { ++ kfree_skb(skb); ++ goto unlock; ++ } ++ + if (!batadv_v_ogm_queue_left(skb, 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); ++ ++unlock: + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } + +@@ -417,6 +424,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) + { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + ++ spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = true; ++ spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); ++ + batadv_v_ogm_start_queue_timer(hard_iface); + batadv_v_ogm_start_timer(bat_priv); + +@@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface) + cancel_delayed_work_sync(&hard_iface->bat_v.aggr_wq); + + spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); ++ hard_iface->bat_v.aggr_list_enabled = false; + batadv_v_ogm_aggr_list_free(hard_iface); + spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index a01ee46d97f34f..765ba71fe8c69f 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -130,6 +130,12 @@ struct batadv_hard_iface_bat_v { + /** @aggr_list: queue for to be aggregated OGM packets */ + struct sk_buff_head aggr_list; + ++ /** ++ * @aggr_list_enabled: aggr_list is active and new skbs can be ++ * enqueued. Protected by aggr_list.lock after initialization ++ */ ++ bool aggr_list_enabled:1; ++ + /** @aggr_len: size of the OGM aggregate (excluding ethernet header) */ + unsigned int aggr_len; + +-- +2.53.0 + diff --git a/queue-7.1/series b/queue-7.1/series index a385adea48..9a76cbafd2 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -1 +1,27 @@ kvm-x86-fix-shadow-paging-use-after-free-due-to-unex.patch +batman-adv-tp_meter-keep-unacked-list-in-ascending-o.patch +batman-adv-tp_meter-initialize-dup_acks-explicitly.patch +batman-adv-tp_meter-initialize-dec_cwnd-explicitly.patch +batman-adv-tp_meter-avoid-window-underflow.patch +batman-adv-tp_meter-avoid-divide-by-zero-for-dec_cwn.patch +batman-adv-tp_meter-fix-fast-recovery-precondition.patch +batman-adv-tp_meter-handle-seqno-wrap-around-for-fas.patch +batman-adv-tp_meter-add-only-finished-tp_vars-to-lis.patch +batman-adv-bla-annotate-lasttime-access-with-read-wr.patch +batman-adv-prevent-elp-transmission-interval-underfl.patch +batman-adv-tp_meter-initialize-last_recv_time-during.patch +batman-adv-gw-don-t-deselect-gateway-with-active-har.patch +batman-adv-ensure-bcast-is-writable-before-modifying.patch +batman-adv-fix-m-b-cast-csum-after-decrementing-ttl.patch +batman-adv-frag-ensure-fragment-is-writable-before-m.patch +batman-adv-frag-avoid-underflow-of-ttl.patch +batman-adv-v-prevent-ogm-aggregation-on-disabled-har.patch +batman-adv-tp_meter-restrict-number-of-unacked-list-.patch +batman-adv-tp_meter-annotate-last_recv_time-access-w.patch +batman-adv-tp_meter-prevent-parallel-modifications-o.patch +batman-adv-tp_meter-handle-overlapping-packets.patch +batman-adv-tt-don-t-merge-change-entries-with-differ.patch +batman-adv-tt-track-roam-count-per-vid.patch +batman-adv-dat-prevent-false-sharing-between-vlans.patch +batman-adv-tvlv-enforce-2-byte-alignment.patch +batman-adv-tvlv-avoid-race-of-cifsnotfound-handler-s.patch