Merge tag 'batadv-net-pullrequest-20260508' of https://git.open-mesh.org/batadv

Simon Wunderlich says:

====================
Here are some batman-adv bugfixes:

- fix integer overflow on buff_pos, by Lyes Bourennani

- fix invalid tp_meter access during teardown, by Jiexun Wang (2 patches)

- stop caching unowned originator pointers in BAT IV, by Jiexun Wang

- tp_meter: fix tp_num leak on kmalloc failure, by Sven Eckelmann

- fix BLA refcounting issues, by Sven Eckelmann (3 patches)

* tag 'batadv-net-pullrequest-20260508' of https://git.open-mesh.org/batadv:
  batman-adv: bla: put backbone reference on failed claim hash insert
  batman-adv: bla: only purge non-released claims
  batman-adv: bla: prevent use-after-free when deleting claims
  batman-adv: tp_meter: fix tp_num leak on kmalloc failure
  batman-adv: stop caching unowned originator pointers in BAT IV
  batman-adv: stop tp_meter sessions during mesh teardown
  batman-adv: reject new tp_meter sessions during teardown
  batman-adv: fix integer overflow on buff_pos
====================

Link: https://patch.msgid.link/20260508154314.12817-1-sw@simonwunderlich.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-05-10 10:07:37 -07:00
6 changed files with 172 additions and 46 deletions

View File

@@ -173,19 +173,12 @@ batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const u8 *addr)
static struct batadv_neigh_node *
batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr,
struct batadv_orig_node *orig_node,
struct batadv_orig_node *orig_neigh)
struct batadv_orig_node *orig_node)
{
struct batadv_neigh_node *neigh_node;
neigh_node = batadv_neigh_node_get_or_create(orig_node,
hard_iface, neigh_addr);
if (!neigh_node)
goto out;
neigh_node->orig_node = orig_neigh;
out:
return neigh_node;
}
@@ -335,7 +328,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
const char *fwd_str;
u8 packet_num;
s16 buff_pos;
int buff_pos;
struct batadv_ogm_packet *batadv_ogm_packet;
struct sk_buff *skb;
u8 *packet_pos;
@@ -906,6 +899,31 @@ static u8 batadv_iv_orig_ifinfo_sum(struct batadv_orig_node *orig_node,
return sum;
}
/**
* batadv_iv_ogm_neigh_ifinfo_sum() - Get bcast_own sum for a last-hop neighbor
* @bat_priv: the bat priv with all the mesh interface information
* @neigh_node: last-hop neighbor of an originator
*
* Return: Number of replied (rebroadcasted) OGMs for the originator currently
* announced by the neighbor. Returns 0 if the neighbor's originator entry is
* not available anymore.
*/
static u8 batadv_iv_ogm_neigh_ifinfo_sum(struct batadv_priv *bat_priv,
const struct batadv_neigh_node *neigh_node)
{
struct batadv_orig_node *orig_neigh;
u8 sum;
orig_neigh = batadv_orig_hash_find(bat_priv, neigh_node->addr);
if (!orig_neigh)
return 0;
sum = batadv_iv_orig_ifinfo_sum(orig_neigh, neigh_node->if_incoming);
batadv_orig_node_put(orig_neigh);
return sum;
}
/**
* batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an
* originator
@@ -975,17 +993,9 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
}
if (!neigh_node) {
struct batadv_orig_node *orig_tmp;
orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source);
if (!orig_tmp)
goto unlock;
neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
ethhdr->h_source,
orig_node, orig_tmp);
batadv_orig_node_put(orig_tmp);
orig_node);
if (!neigh_node)
goto unlock;
} else {
@@ -1037,10 +1047,9 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
*/
if (router_ifinfo &&
neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg) {
sum_orig = batadv_iv_orig_ifinfo_sum(router->orig_node,
router->if_incoming);
sum_neigh = batadv_iv_orig_ifinfo_sum(neigh_node->orig_node,
neigh_node->if_incoming);
sum_orig = batadv_iv_ogm_neigh_ifinfo_sum(bat_priv, router);
sum_neigh = batadv_iv_ogm_neigh_ifinfo_sum(bat_priv,
neigh_node);
if (sum_orig >= sum_neigh)
goto out;
}
@@ -1106,7 +1115,6 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
if (!neigh_node)
neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
orig_neigh_node->orig,
orig_neigh_node,
orig_neigh_node);
if (!neigh_node)
@@ -1302,6 +1310,32 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
return ret;
}
/**
* batadv_orig_to_direct_router() - get direct next hop neighbor to an orig address
* @bat_priv: the bat priv with all the mesh interface information
* @orig_addr: the originator MAC address to search the best next hop router for
* @if_outgoing: the interface where the OGM should be sent to
*
* Return: A neighbor node which is the best router towards the given originator
* address. Bonding candidates are ignored.
*/
static struct batadv_neigh_node *
batadv_orig_to_direct_router(struct batadv_priv *bat_priv, u8 *orig_addr,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_neigh_node *neigh_node;
struct batadv_orig_node *orig_node;
orig_node = batadv_orig_hash_find(bat_priv, orig_addr);
if (!orig_node)
return NULL;
neigh_node = batadv_orig_router_get(orig_node, if_outgoing);
batadv_orig_node_put(orig_node);
return neigh_node;
}
/**
* batadv_iv_ogm_process_per_outif() - process a batman iv OGM for an outgoing
* interface
@@ -1372,8 +1406,9 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
router = batadv_orig_router_get(orig_node, if_outgoing);
if (router) {
router_router = batadv_orig_router_get(router->orig_node,
if_outgoing);
router_router = batadv_orig_to_direct_router(bat_priv,
router->addr,
if_outgoing);
router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing);
}

View File

@@ -318,8 +318,8 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
if (claim->backbone_gw != backbone_gw)
continue;
batadv_claim_put(claim);
hlist_del_rcu(&claim->hash_entry);
batadv_claim_put(claim);
}
spin_unlock_bh(list_lock);
}
@@ -723,6 +723,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
if (unlikely(hash_added != 0)) {
/* only local changes happened. */
batadv_backbone_gw_put(backbone_gw);
kfree(claim);
return;
}
@@ -1288,6 +1289,13 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
rcu_read_lock();
hlist_for_each_entry_rcu(claim, head, hash_entry) {
/* only purge claims not currently in the process of being released.
* Such claims could otherwise have a NULL-ptr backbone_gw set because
* they already went through batadv_claim_release()
*/
if (!kref_get_unless_zero(&claim->refcount))
continue;
backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
if (now)
goto purge_now;
@@ -1313,6 +1321,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
claim->addr, claim->vid);
skip:
batadv_backbone_gw_put(backbone_gw);
batadv_claim_put(claim);
}
rcu_read_unlock();
}

View File

@@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
batadv_purge_outstanding_packets(bat_priv, NULL);
batadv_tp_stop_all(bat_priv);
batadv_gw_node_free(bat_priv);

View File

@@ -12,6 +12,7 @@
#include <linux/byteorder/generic.h>
#include <linux/cache.h>
#include <linux/compiler.h>
#include <linux/completion.h>
#include <linux/container_of.h>
#include <linux/err.h>
#include <linux/etherdevice.h>
@@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
}
/**
* batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
* @bat_priv: the bat priv with all the mesh interface information
* @tp_vars: the private data of the current TP meter session to cleanup
* batadv_tp_list_detach() - remove tp session from mesh session list once
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
struct batadv_tp_vars *tp_vars)
static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
{
cancel_delayed_work(&tp_vars->finish_work);
bool detached = false;
spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
hlist_del_rcu(&tp_vars->list);
if (!hlist_unhashed(&tp_vars->list)) {
hlist_del_init_rcu(&tp_vars->list);
detached = true;
}
spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
if (!detached)
return;
atomic_dec(&tp_vars->bat_priv->tp_num);
/* drop list reference */
batadv_tp_vars_put(tp_vars);
}
atomic_dec(&tp_vars->bat_priv->tp_num);
/**
* batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
* @tp_vars: the private data of the current TP meter session to cleanup
*/
static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
{
cancel_delayed_work_sync(&tp_vars->finish_work);
batadv_tp_list_detach(tp_vars);
/* kill the timer and remove its reference */
timer_delete_sync(&tp_vars->timer);
@@ -886,7 +902,8 @@ static int batadv_tp_send(void *arg)
batadv_orig_node_put(orig_node);
batadv_tp_sender_end(bat_priv, tp_vars);
batadv_tp_sender_cleanup(bat_priv, tp_vars);
batadv_tp_sender_cleanup(tp_vars);
complete(&tp_vars->finished);
batadv_tp_vars_put(tp_vars);
@@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
batadv_tp_vars_put(tp_vars);
/* cleanup of failed tp meter variables */
batadv_tp_sender_cleanup(bat_priv, tp_vars);
batadv_tp_sender_cleanup(tp_vars);
complete(&tp_vars->finished);
return;
}
@@ -947,6 +965,13 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
/* look for an already existing test towards this node */
spin_lock_bh(&bat_priv->tp_list_lock);
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) {
spin_unlock_bh(&bat_priv->tp_list_lock);
batadv_tp_batctl_error_notify(BATADV_TP_REASON_DST_UNREACHABLE,
dst, bat_priv, session_cookie);
return;
}
tp_vars = batadv_tp_list_find(bat_priv, dst);
if (tp_vars) {
spin_unlock_bh(&bat_priv->tp_list_lock);
@@ -969,6 +994,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
tp_vars = kmalloc_obj(*tp_vars, GFP_ATOMIC);
if (!tp_vars) {
atomic_dec(&bat_priv->tp_num);
spin_unlock_bh(&bat_priv->tp_list_lock);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: %s cannot allocate list elements\n",
@@ -1017,6 +1043,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
tp_vars->start_time = jiffies;
init_waitqueue_head(&tp_vars->more_bytes);
init_completion(&tp_vars->finished);
spin_lock_init(&tp_vars->unacked_lock);
INIT_LIST_HEAD(&tp_vars->unacked_list);
@@ -1119,14 +1146,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
"Shutting down for inactivity (more than %dms) from %pM\n",
BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
hlist_del_rcu(&tp_vars->list);
spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
/* drop list reference */
batadv_tp_vars_put(tp_vars);
atomic_dec(&bat_priv->tp_num);
batadv_tp_list_detach(tp_vars);
spin_lock_bh(&tp_vars->unacked_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
@@ -1329,9 +1349,12 @@ static struct batadv_tp_vars *
batadv_tp_init_recv(struct batadv_priv *bat_priv,
const struct batadv_icmp_tp_packet *icmp)
{
struct batadv_tp_vars *tp_vars;
struct batadv_tp_vars *tp_vars = NULL;
spin_lock_bh(&bat_priv->tp_list_lock);
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto out_unlock;
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session);
if (tp_vars)
@@ -1344,8 +1367,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
}
tp_vars = kmalloc_obj(*tp_vars, GFP_ATOMIC);
if (!tp_vars)
if (!tp_vars) {
atomic_dec(&bat_priv->tp_num);
goto out_unlock;
}
ether_addr_copy(tp_vars->other_end, icmp->orig);
tp_vars->role = BATADV_TP_RECEIVER;
@@ -1464,6 +1489,9 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
{
struct batadv_icmp_tp_packet *icmp;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto out;
icmp = (struct batadv_icmp_tp_packet *)skb->data;
switch (icmp->subtype) {
@@ -1478,9 +1506,57 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
"Received unknown TP Metric packet type %u\n",
icmp->subtype);
}
out:
consume_skb(skb);
}
/**
* batadv_tp_stop_all() - stop all currently running tp meter sessions
* @bat_priv: the bat priv with all the mesh interface information
*/
void batadv_tp_stop_all(struct batadv_priv *bat_priv)
{
struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
struct batadv_tp_vars *tp_var;
size_t count = 0;
size_t i;
spin_lock_bh(&bat_priv->tp_list_lock);
hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
break;
if (!kref_get_unless_zero(&tp_var->refcount))
continue;
tp_vars[count++] = tp_var;
}
spin_unlock_bh(&bat_priv->tp_list_lock);
for (i = 0; i < count; i++) {
tp_var = tp_vars[i];
switch (tp_var->role) {
case BATADV_TP_SENDER:
batadv_tp_sender_shutdown(tp_var,
BATADV_TP_REASON_CANCEL);
wake_up(&tp_var->more_bytes);
wait_for_completion(&tp_var->finished);
break;
case BATADV_TP_RECEIVER:
batadv_tp_list_detach(tp_var);
if (timer_shutdown_sync(&tp_var->timer))
batadv_tp_vars_put(tp_var);
break;
}
batadv_tp_vars_put(tp_var);
}
synchronize_net();
}
/**
* batadv_tp_meter_init() - initialize global tp_meter structures
*/

View File

@@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
u32 test_length, u32 *cookie);
void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
u8 return_value);
void batadv_tp_stop_all(struct batadv_priv *bat_priv);
void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
#endif /* _NET_BATMAN_ADV_TP_METER_H_ */

View File

@@ -14,6 +14,7 @@
#include <linux/average.h>
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/completion.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/kref.h>
@@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
/** @finish_work: work item for the finishing procedure */
struct delayed_work finish_work;
/** @finished: completion signaled when a sender thread exits */
struct completion finished;
/** @test_length: test length in milliseconds */
u32 test_length;