mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 02:01:18 -04:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user