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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABCAAGBQJTAj4LAAoJEEKTMo6mOh1VXoQP/2WVjuIrB7rd4mpq5MSXjkWm qCRRmuU9MVSbwBPvKcNAT4sDb9KliodqMu7jtUNKJ118afTK5VIh1EmbFGIm2vA+ QowpfvSOFaDVrd6pB1bKlPlX5Xi9OF+hj82LalfMRWvsdvQUN00fkCMjyrxPivhR zq7ucyff1YTft/mSmD+X0gqNK1L99om2xNcWzPjl+CZ0LOBFe411/sWf8Ujldgl0 F6jTPXckNBToukmYO8wwmtG8PFrIWNBRUEfpY/P+VNp+Cg7GF9KOts4mdym9PviI //PkonRNylfeTvBlztmCdTQB9vHhlT3e/9KTd/lXBQ669Mz/eQ6H1MascDZ8e0Ib 1IeqL6cyOaEDIOh8Bgr2WcRTH/JCx0F0cy+PISJx0DEVYKLWZedm8ECIU9eXWMr6 hnTcBue51IoVbDE5SJ0apoDmQOZZF2euaYBPXtRrziZBzcHubt69rQKOqQ/A5atR m5kuA7E14NR7F/FOTdKsfLyAVqx9j5mw7NQYAhlbXex0Lp+qQQ9YMtHBv4pgzYA3 UYE9pnuMkr3EXOQ9wAt/ldq+hWBkXDFkg5nd3bzY8aKw5QLBPHZdrTgFtOmVO1RP Fa7fJSwt2ImCa50w59u4f22U870QK7AYK7xvHeLHbvIzthTDgA71OKRePcRu9EU3 yN6J5h/+A4X7fGgz0Z/X =6IO8 -----END PGP SIGNATURE----- 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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
7ffb0d317d
|
@ -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,
|
||||||
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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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 @@ out:
|
||||||
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);
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue