Commit 7ffb0d31 authored by David S. Miller's avatar David S. Miller

Merge tag 'batman-adv-fix-for-davem' of git://git.open-mesh.org/linux-merge

Included changes:
- fix soft-interface MTU computation
- fix bogus pointer mangling when parsing the TT-TVLV
  container. This bug led to a wrong memory access.
- fix memory leak by properly releasing the VLAN object
  after CRC check
- properly check pskb_may_pull() return value
- avoid potential race condition while adding new neighbour
- fix potential memory leak by removing all the references
  to the orig_node object in case of initialization failure
- fix the TT CRC computation by ensuring that every node uses
  the same byte order when hosts with different endianess are
  part of the same network
- fix severe memory leak by freeing skb after a successful
  TVLV parsing
- avoid potential double free when orig_node initialization
  fails
- fix potential kernel paging error caused by the usage of
  the old value of skb->data after skb reallocation
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a6254864 70b271a7
...@@ -241,19 +241,19 @@ batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr) ...@@ -241,19 +241,19 @@ batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr)
size = bat_priv->num_ifaces * sizeof(uint8_t); size = bat_priv->num_ifaces * sizeof(uint8_t);
orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC); orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC);
if (!orig_node->bat_iv.bcast_own_sum) if (!orig_node->bat_iv.bcast_own_sum)
goto free_bcast_own; goto free_orig_node;
hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig, hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
batadv_choose_orig, orig_node, batadv_choose_orig, orig_node,
&orig_node->hash_entry); &orig_node->hash_entry);
if (hash_added != 0) if (hash_added != 0)
goto free_bcast_own; goto free_orig_node;
return orig_node; return orig_node;
free_bcast_own:
kfree(orig_node->bat_iv.bcast_own);
free_orig_node: free_orig_node:
/* free twice, as batadv_orig_node_new sets refcount to 2 */
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_free_ref(orig_node); batadv_orig_node_free_ref(orig_node);
return NULL; return NULL;
...@@ -266,7 +266,7 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, ...@@ -266,7 +266,7 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
struct batadv_orig_node *orig_neigh) struct batadv_orig_node *orig_neigh)
{ {
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct batadv_neigh_node *neigh_node; struct batadv_neigh_node *neigh_node, *tmp_neigh_node;
neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node); neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node);
if (!neigh_node) if (!neigh_node)
...@@ -281,14 +281,24 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, ...@@ -281,14 +281,24 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
neigh_node->orig_node = orig_neigh; neigh_node->orig_node = orig_neigh;
neigh_node->if_incoming = hard_iface; neigh_node->if_incoming = hard_iface;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Creating new neighbor %pM for orig_node %pM on interface %s\n",
neigh_addr, orig_node->orig, hard_iface->net_dev->name);
spin_lock_bh(&orig_node->neigh_list_lock); spin_lock_bh(&orig_node->neigh_list_lock);
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); tmp_neigh_node = batadv_neigh_node_get(orig_node, hard_iface,
neigh_addr);
if (!tmp_neigh_node) {
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
} else {
kfree(neigh_node);
batadv_hardif_free_ref(hard_iface);
neigh_node = tmp_neigh_node;
}
spin_unlock_bh(&orig_node->neigh_list_lock); spin_unlock_bh(&orig_node->neigh_list_lock);
if (!tmp_neigh_node)
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Creating new neighbor %pM for orig_node %pM on interface %s\n",
neigh_addr, orig_node->orig,
hard_iface->net_dev->name);
out: out:
return neigh_node; return neigh_node;
} }
......
...@@ -241,7 +241,7 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) ...@@ -241,7 +241,7 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface)
{ {
struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_priv *bat_priv = netdev_priv(soft_iface);
const struct batadv_hard_iface *hard_iface; const struct batadv_hard_iface *hard_iface;
int min_mtu = ETH_DATA_LEN; int min_mtu = INT_MAX;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
...@@ -256,8 +256,6 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) ...@@ -256,8 +256,6 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface)
} }
rcu_read_unlock(); rcu_read_unlock();
atomic_set(&bat_priv->packet_size_max, min_mtu);
if (atomic_read(&bat_priv->fragmentation) == 0) if (atomic_read(&bat_priv->fragmentation) == 0)
goto out; goto out;
...@@ -268,13 +266,21 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) ...@@ -268,13 +266,21 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface)
min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE); min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
min_mtu -= sizeof(struct batadv_frag_packet); min_mtu -= sizeof(struct batadv_frag_packet);
min_mtu *= BATADV_FRAG_MAX_FRAGMENTS; min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
atomic_set(&bat_priv->packet_size_max, min_mtu);
/* with fragmentation enabled we can fragment external packets easily */
min_mtu = min_t(int, min_mtu, ETH_DATA_LEN);
out: out:
return min_mtu - batadv_max_header_len(); /* report to the other components the maximum amount of bytes that
* batman-adv can send over the wire (without considering the payload
* overhead). For example, this value is used by TT to compute the
* maximum local table table size
*/
atomic_set(&bat_priv->packet_size_max, min_mtu);
/* the real soft-interface MTU is computed by removing the payload
* overhead from the maximum amount of bytes that was just computed.
*
* However batman-adv does not support MTUs bigger than ETH_DATA_LEN
*/
return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN);
} }
/* adjusts the MTU if a new interface with a smaller MTU appeared. */ /* adjusts the MTU if a new interface with a smaller MTU appeared. */
......
...@@ -457,6 +457,42 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, ...@@ -457,6 +457,42 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
return neigh_node; return neigh_node;
} }
/**
* batadv_neigh_node_get - retrieve a neighbour from the list
* @orig_node: originator which the neighbour belongs to
* @hard_iface: the interface where this neighbour is connected to
* @addr: the address of the neighbour
*
* Looks for and possibly returns a neighbour belonging to this originator list
* which is connected through the provided hard interface.
* Returns NULL if the neighbour is not found.
*/
struct batadv_neigh_node *
batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
const struct batadv_hard_iface *hard_iface,
const uint8_t *addr)
{
struct batadv_neigh_node *tmp_neigh_node, *res = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(tmp_neigh_node, &orig_node->neigh_list, list) {
if (!batadv_compare_eth(tmp_neigh_node->addr, addr))
continue;
if (tmp_neigh_node->if_incoming != hard_iface)
continue;
if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
continue;
res = tmp_neigh_node;
break;
}
rcu_read_unlock();
return res;
}
/** /**
* batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object * batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object
* @rcu: rcu pointer of the orig_ifinfo object * @rcu: rcu pointer of the orig_ifinfo object
......
...@@ -29,6 +29,10 @@ void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node); ...@@ -29,6 +29,10 @@ void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const uint8_t *addr); const uint8_t *addr);
struct batadv_neigh_node * struct batadv_neigh_node *
batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
const struct batadv_hard_iface *hard_iface,
const uint8_t *addr);
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
const uint8_t *neigh_addr, const uint8_t *neigh_addr,
struct batadv_orig_node *orig_node); struct batadv_orig_node *orig_node);
......
...@@ -688,7 +688,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, ...@@ -688,7 +688,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
int is_old_ttvn; int is_old_ttvn;
/* check if there is enough data before accessing it */ /* check if there is enough data before accessing it */
if (pskb_may_pull(skb, hdr_len + ETH_HLEN) < 0) if (!pskb_may_pull(skb, hdr_len + ETH_HLEN))
return 0; return 0;
/* create a copy of the skb (in case of for re-routing) to modify it. */ /* create a copy of the skb (in case of for re-routing) to modify it. */
...@@ -918,6 +918,8 @@ int batadv_recv_unicast_tvlv(struct sk_buff *skb, ...@@ -918,6 +918,8 @@ int batadv_recv_unicast_tvlv(struct sk_buff *skb,
if (ret != NET_RX_SUCCESS) if (ret != NET_RX_SUCCESS)
ret = batadv_route_unicast_packet(skb, recv_if); ret = batadv_route_unicast_packet(skb, recv_if);
else
consume_skb(skb);
return ret; return ret;
} }
......
...@@ -254,9 +254,9 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv, ...@@ -254,9 +254,9 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node, struct batadv_orig_node *orig_node,
unsigned short vid) unsigned short vid)
{ {
struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct ethhdr *ethhdr;
struct batadv_unicast_packet *unicast_packet; struct batadv_unicast_packet *unicast_packet;
int ret = NET_XMIT_DROP; int ret = NET_XMIT_DROP, hdr_size;
if (!orig_node) if (!orig_node)
goto out; goto out;
...@@ -265,12 +265,16 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv, ...@@ -265,12 +265,16 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
case BATADV_UNICAST: case BATADV_UNICAST:
if (!batadv_send_skb_prepare_unicast(skb, orig_node)) if (!batadv_send_skb_prepare_unicast(skb, orig_node))
goto out; goto out;
hdr_size = sizeof(*unicast_packet);
break; break;
case BATADV_UNICAST_4ADDR: case BATADV_UNICAST_4ADDR:
if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb, if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb,
orig_node, orig_node,
packet_subtype)) packet_subtype))
goto out; goto out;
hdr_size = sizeof(struct batadv_unicast_4addr_packet);
break; break;
default: default:
/* this function supports UNICAST and UNICAST_4ADDR only. It /* this function supports UNICAST and UNICAST_4ADDR only. It
...@@ -279,6 +283,7 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv, ...@@ -279,6 +283,7 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
goto out; goto out;
} }
ethhdr = (struct ethhdr *)(skb->data + hdr_size);
unicast_packet = (struct batadv_unicast_packet *)skb->data; unicast_packet = (struct batadv_unicast_packet *)skb->data;
/* inform the destination node that we are still missing a correct route /* inform the destination node that we are still missing a correct route
......
...@@ -1975,6 +1975,7 @@ static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv, ...@@ -1975,6 +1975,7 @@ static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
struct hlist_head *head; struct hlist_head *head;
uint32_t i, crc_tmp, crc = 0; uint32_t i, crc_tmp, crc = 0;
uint8_t flags; uint8_t flags;
__be16 tmp_vid;
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
...@@ -2011,8 +2012,11 @@ static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv, ...@@ -2011,8 +2012,11 @@ static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
orig_node)) orig_node))
continue; continue;
crc_tmp = crc32c(0, &tt_common->vid, /* use network order to read the VID: this ensures that
sizeof(tt_common->vid)); * every node reads the bytes in the same order.
*/
tmp_vid = htons(tt_common->vid);
crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid));
/* compute the CRC on flags that have to be kept in sync /* compute the CRC on flags that have to be kept in sync
* among nodes * among nodes
...@@ -2046,6 +2050,7 @@ static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv, ...@@ -2046,6 +2050,7 @@ static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv,
struct hlist_head *head; struct hlist_head *head;
uint32_t i, crc_tmp, crc = 0; uint32_t i, crc_tmp, crc = 0;
uint8_t flags; uint8_t flags;
__be16 tmp_vid;
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
...@@ -2064,8 +2069,11 @@ static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv, ...@@ -2064,8 +2069,11 @@ static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv,
if (tt_common->flags & BATADV_TT_CLIENT_NEW) if (tt_common->flags & BATADV_TT_CLIENT_NEW)
continue; continue;
crc_tmp = crc32c(0, &tt_common->vid, /* use network order to read the VID: this ensures that
sizeof(tt_common->vid)); * every node reads the bytes in the same order.
*/
tmp_vid = htons(tt_common->vid);
crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid));
/* compute the CRC on flags that have to be kept in sync /* compute the CRC on flags that have to be kept in sync
* among nodes * among nodes
...@@ -2262,6 +2270,7 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node, ...@@ -2262,6 +2270,7 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
{ {
struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp; struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
struct batadv_orig_node_vlan *vlan; struct batadv_orig_node_vlan *vlan;
uint32_t crc;
int i; int i;
/* check if each received CRC matches the locally stored one */ /* check if each received CRC matches the locally stored one */
...@@ -2281,7 +2290,10 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node, ...@@ -2281,7 +2290,10 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
if (!vlan) if (!vlan)
return false; return false;
if (vlan->tt.crc != ntohl(tt_vlan_tmp->crc)) crc = vlan->tt.crc;
batadv_orig_node_vlan_free_ref(vlan);
if (crc != ntohl(tt_vlan_tmp->crc))
return false; return false;
} }
...@@ -3218,7 +3230,6 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv, ...@@ -3218,7 +3230,6 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
spin_lock_bh(&orig_node->tt_lock); spin_lock_bh(&orig_node->tt_lock);
tt_change = (struct batadv_tvlv_tt_change *)tt_buff;
batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes, batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes,
ttvn, tt_change); ttvn, tt_change);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment