diff options
author | David S. Miller <davem@davemloft.net> | 2014-02-18 15:40:50 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-02-18 15:40:50 -0500 |
commit | 7ffb0d317d90eba63c4e7780a3ca62ecd51f2439 (patch) | |
tree | 03d9e6dfb75238d11508402048b5de0abc6adc3f | |
parent | a6254864c08109c66a194612585afc0439005286 (diff) | |
parent | 70b271a78beba787155d6696aacd7c4d4a251c50 (diff) |
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>
-rw-r--r-- | net/batman-adv/bat_iv_ogm.c | 30 | ||||
-rw-r--r-- | net/batman-adv/hard-interface.c | 22 | ||||
-rw-r--r-- | net/batman-adv/originator.c | 36 | ||||
-rw-r--r-- | net/batman-adv/originator.h | 4 | ||||
-rw-r--r-- | net/batman-adv/routing.c | 4 | ||||
-rw-r--r-- | net/batman-adv/send.c | 9 | ||||
-rw-r--r-- | net/batman-adv/translation-table.c | 23 |
7 files changed, 101 insertions, 27 deletions
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 512159bf607f..8323bced8e5b 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c | |||
@@ -241,19 +241,19 @@ batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr) | |||
241 | size = bat_priv->num_ifaces * sizeof(uint8_t); | 241 | size = bat_priv->num_ifaces * sizeof(uint8_t); |
242 | orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC); | 242 | orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC); |
243 | if (!orig_node->bat_iv.bcast_own_sum) | 243 | if (!orig_node->bat_iv.bcast_own_sum) |
244 | goto free_bcast_own; | 244 | goto free_orig_node; |
245 | 245 | ||
246 | hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig, | 246 | hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig, |
247 | batadv_choose_orig, orig_node, | 247 | batadv_choose_orig, orig_node, |
248 | &orig_node->hash_entry); | 248 | &orig_node->hash_entry); |
249 | if (hash_added != 0) | 249 | if (hash_added != 0) |
250 | goto free_bcast_own; | 250 | goto free_orig_node; |
251 | 251 | ||
252 | return orig_node; | 252 | return orig_node; |
253 | 253 | ||
254 | free_bcast_own: | ||
255 | kfree(orig_node->bat_iv.bcast_own); | ||
256 | free_orig_node: | 254 | free_orig_node: |
255 | /* free twice, as batadv_orig_node_new sets refcount to 2 */ | ||
256 | batadv_orig_node_free_ref(orig_node); | ||
257 | batadv_orig_node_free_ref(orig_node); | 257 | batadv_orig_node_free_ref(orig_node); |
258 | 258 | ||
259 | return NULL; | 259 | return NULL; |
@@ -266,7 +266,7 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, | |||
266 | struct batadv_orig_node *orig_neigh) | 266 | struct batadv_orig_node *orig_neigh) |
267 | { | 267 | { |
268 | struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); | 268 | struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); |
269 | struct batadv_neigh_node *neigh_node; | 269 | struct batadv_neigh_node *neigh_node, *tmp_neigh_node; |
270 | 270 | ||
271 | neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node); | 271 | neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node); |
272 | if (!neigh_node) | 272 | if (!neigh_node) |
@@ -281,14 +281,24 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, | |||
281 | neigh_node->orig_node = orig_neigh; | 281 | neigh_node->orig_node = orig_neigh; |
282 | neigh_node->if_incoming = hard_iface; | 282 | neigh_node->if_incoming = hard_iface; |
283 | 283 | ||
284 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | ||
285 | "Creating new neighbor %pM for orig_node %pM on interface %s\n", | ||
286 | neigh_addr, orig_node->orig, hard_iface->net_dev->name); | ||
287 | |||
288 | spin_lock_bh(&orig_node->neigh_list_lock); | 284 | spin_lock_bh(&orig_node->neigh_list_lock); |
289 | hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); | 285 | tmp_neigh_node = batadv_neigh_node_get(orig_node, hard_iface, |
286 | neigh_addr); | ||
287 | if (!tmp_neigh_node) { | ||
288 | hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); | ||
289 | } else { | ||
290 | kfree(neigh_node); | ||
291 | batadv_hardif_free_ref(hard_iface); | ||
292 | neigh_node = tmp_neigh_node; | ||
293 | } | ||
290 | spin_unlock_bh(&orig_node->neigh_list_lock); | 294 | spin_unlock_bh(&orig_node->neigh_list_lock); |
291 | 295 | ||
296 | if (!tmp_neigh_node) | ||
297 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | ||
298 | "Creating new neighbor %pM for orig_node %pM on interface %s\n", | ||
299 | neigh_addr, orig_node->orig, | ||
300 | hard_iface->net_dev->name); | ||
301 | |||
292 | out: | 302 | out: |
293 | return neigh_node; | 303 | return neigh_node; |
294 | } | 304 | } |
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 3d417d3641c6..b851cc580853 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c | |||
@@ -241,7 +241,7 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) | |||
241 | { | 241 | { |
242 | struct batadv_priv *bat_priv = netdev_priv(soft_iface); | 242 | struct batadv_priv *bat_priv = netdev_priv(soft_iface); |
243 | const struct batadv_hard_iface *hard_iface; | 243 | const struct batadv_hard_iface *hard_iface; |
244 | int min_mtu = ETH_DATA_LEN; | 244 | int min_mtu = INT_MAX; |
245 | 245 | ||
246 | rcu_read_lock(); | 246 | rcu_read_lock(); |
247 | list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { | 247 | 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 | } | 256 | } |
257 | rcu_read_unlock(); | 257 | rcu_read_unlock(); |
258 | 258 | ||
259 | atomic_set(&bat_priv->packet_size_max, min_mtu); | ||
260 | |||
261 | if (atomic_read(&bat_priv->fragmentation) == 0) | 259 | if (atomic_read(&bat_priv->fragmentation) == 0) |
262 | goto out; | 260 | goto out; |
263 | 261 | ||
@@ -268,13 +266,21 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) | |||
268 | min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE); | 266 | min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE); |
269 | min_mtu -= sizeof(struct batadv_frag_packet); | 267 | min_mtu -= sizeof(struct batadv_frag_packet); |
270 | min_mtu *= BATADV_FRAG_MAX_FRAGMENTS; | 268 | min_mtu *= BATADV_FRAG_MAX_FRAGMENTS; |
271 | atomic_set(&bat_priv->packet_size_max, min_mtu); | ||
272 | |||
273 | /* with fragmentation enabled we can fragment external packets easily */ | ||
274 | min_mtu = min_t(int, min_mtu, ETH_DATA_LEN); | ||
275 | 269 | ||
276 | out: | 270 | out: |
277 | return min_mtu - batadv_max_header_len(); | 271 | /* report to the other components the maximum amount of bytes that |
272 | * batman-adv can send over the wire (without considering the payload | ||
273 | * overhead). For example, this value is used by TT to compute the | ||
274 | * maximum local table table size | ||
275 | */ | ||
276 | atomic_set(&bat_priv->packet_size_max, min_mtu); | ||
277 | |||
278 | /* the real soft-interface MTU is computed by removing the payload | ||
279 | * overhead from the maximum amount of bytes that was just computed. | ||
280 | * | ||
281 | * However batman-adv does not support MTUs bigger than ETH_DATA_LEN | ||
282 | */ | ||
283 | return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN); | ||
278 | } | 284 | } |
279 | 285 | ||
280 | /* adjusts the MTU if a new interface with a smaller MTU appeared. */ | 286 | /* adjusts the MTU if a new interface with a smaller MTU appeared. */ |
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 6df12a2e3605..853941629dc1 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c | |||
@@ -458,6 +458,42 @@ out: | |||
458 | } | 458 | } |
459 | 459 | ||
460 | /** | 460 | /** |
461 | * batadv_neigh_node_get - retrieve a neighbour from the list | ||
462 | * @orig_node: originator which the neighbour belongs to | ||
463 | * @hard_iface: the interface where this neighbour is connected to | ||
464 | * @addr: the address of the neighbour | ||
465 | * | ||
466 | * Looks for and possibly returns a neighbour belonging to this originator list | ||
467 | * which is connected through the provided hard interface. | ||
468 | * Returns NULL if the neighbour is not found. | ||
469 | */ | ||
470 | struct batadv_neigh_node * | ||
471 | batadv_neigh_node_get(const struct batadv_orig_node *orig_node, | ||
472 | const struct batadv_hard_iface *hard_iface, | ||
473 | const uint8_t *addr) | ||
474 | { | ||
475 | struct batadv_neigh_node *tmp_neigh_node, *res = NULL; | ||
476 | |||
477 | rcu_read_lock(); | ||
478 | hlist_for_each_entry_rcu(tmp_neigh_node, &orig_node->neigh_list, list) { | ||
479 | if (!batadv_compare_eth(tmp_neigh_node->addr, addr)) | ||
480 | continue; | ||
481 | |||
482 | if (tmp_neigh_node->if_incoming != hard_iface) | ||
483 | continue; | ||
484 | |||
485 | if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) | ||
486 | continue; | ||
487 | |||
488 | res = tmp_neigh_node; | ||
489 | break; | ||
490 | } | ||
491 | rcu_read_unlock(); | ||
492 | |||
493 | return res; | ||
494 | } | ||
495 | |||
496 | /** | ||
461 | * batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object | 497 | * batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object |
462 | * @rcu: rcu pointer of the orig_ifinfo object | 498 | * @rcu: rcu pointer of the orig_ifinfo object |
463 | */ | 499 | */ |
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 37be290f63f6..db3a9ed734cb 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h | |||
@@ -29,6 +29,10 @@ void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node); | |||
29 | struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, | 29 | struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, |
30 | const uint8_t *addr); | 30 | const uint8_t *addr); |
31 | struct batadv_neigh_node * | 31 | struct batadv_neigh_node * |
32 | batadv_neigh_node_get(const struct batadv_orig_node *orig_node, | ||
33 | const struct batadv_hard_iface *hard_iface, | ||
34 | const uint8_t *addr); | ||
35 | struct batadv_neigh_node * | ||
32 | batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, | 36 | batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, |
33 | const uint8_t *neigh_addr, | 37 | const uint8_t *neigh_addr, |
34 | struct batadv_orig_node *orig_node); | 38 | struct batadv_orig_node *orig_node); |
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 1ed9f7c9ecea..a953d5b196a3 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c | |||
@@ -688,7 +688,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, | |||
688 | int is_old_ttvn; | 688 | int is_old_ttvn; |
689 | 689 | ||
690 | /* check if there is enough data before accessing it */ | 690 | /* check if there is enough data before accessing it */ |
691 | if (pskb_may_pull(skb, hdr_len + ETH_HLEN) < 0) | 691 | if (!pskb_may_pull(skb, hdr_len + ETH_HLEN)) |
692 | return 0; | 692 | return 0; |
693 | 693 | ||
694 | /* create a copy of the skb (in case of for re-routing) to modify it. */ | 694 | /* 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 | 918 | ||
919 | if (ret != NET_RX_SUCCESS) | 919 | if (ret != NET_RX_SUCCESS) |
920 | ret = batadv_route_unicast_packet(skb, recv_if); | 920 | ret = batadv_route_unicast_packet(skb, recv_if); |
921 | else | ||
922 | consume_skb(skb); | ||
921 | 923 | ||
922 | return ret; | 924 | return ret; |
923 | } | 925 | } |
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 579f5f00a385..843febd1e519 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c | |||
@@ -254,9 +254,9 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv, | |||
254 | struct batadv_orig_node *orig_node, | 254 | struct batadv_orig_node *orig_node, |
255 | unsigned short vid) | 255 | unsigned short vid) |
256 | { | 256 | { |
257 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; | 257 | struct ethhdr *ethhdr; |
258 | struct batadv_unicast_packet *unicast_packet; | 258 | struct batadv_unicast_packet *unicast_packet; |
259 | int ret = NET_XMIT_DROP; | 259 | int ret = NET_XMIT_DROP, hdr_size; |
260 | 260 | ||
261 | if (!orig_node) | 261 | if (!orig_node) |
262 | goto out; | 262 | goto out; |
@@ -265,12 +265,16 @@ static int batadv_send_skb_unicast(struct batadv_priv *bat_priv, | |||
265 | case BATADV_UNICAST: | 265 | case BATADV_UNICAST: |
266 | if (!batadv_send_skb_prepare_unicast(skb, orig_node)) | 266 | if (!batadv_send_skb_prepare_unicast(skb, orig_node)) |
267 | goto out; | 267 | goto out; |
268 | |||
269 | hdr_size = sizeof(*unicast_packet); | ||
268 | break; | 270 | break; |
269 | case BATADV_UNICAST_4ADDR: | 271 | case BATADV_UNICAST_4ADDR: |
270 | if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb, | 272 | if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb, |
271 | orig_node, | 273 | orig_node, |
272 | packet_subtype)) | 274 | packet_subtype)) |
273 | goto out; | 275 | goto out; |
276 | |||
277 | hdr_size = sizeof(struct batadv_unicast_4addr_packet); | ||
274 | break; | 278 | break; |
275 | default: | 279 | default: |
276 | /* this function supports UNICAST and UNICAST_4ADDR only. It | 280 | /* 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 | goto out; | 283 | goto out; |
280 | } | 284 | } |
281 | 285 | ||
286 | ethhdr = (struct ethhdr *)(skb->data + hdr_size); | ||
282 | unicast_packet = (struct batadv_unicast_packet *)skb->data; | 287 | unicast_packet = (struct batadv_unicast_packet *)skb->data; |
283 | 288 | ||
284 | /* inform the destination node that we are still missing a correct route | 289 | /* inform the destination node that we are still missing a correct route |
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index b6071f675a3e..959dde721c46 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c | |||
@@ -1975,6 +1975,7 @@ static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv, | |||
1975 | struct hlist_head *head; | 1975 | struct hlist_head *head; |
1976 | uint32_t i, crc_tmp, crc = 0; | 1976 | uint32_t i, crc_tmp, crc = 0; |
1977 | uint8_t flags; | 1977 | uint8_t flags; |
1978 | __be16 tmp_vid; | ||
1978 | 1979 | ||
1979 | for (i = 0; i < hash->size; i++) { | 1980 | for (i = 0; i < hash->size; i++) { |
1980 | head = &hash->table[i]; | 1981 | head = &hash->table[i]; |
@@ -2011,8 +2012,11 @@ static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv, | |||
2011 | orig_node)) | 2012 | orig_node)) |
2012 | continue; | 2013 | continue; |
2013 | 2014 | ||
2014 | crc_tmp = crc32c(0, &tt_common->vid, | 2015 | /* use network order to read the VID: this ensures that |
2015 | sizeof(tt_common->vid)); | 2016 | * every node reads the bytes in the same order. |
2017 | */ | ||
2018 | tmp_vid = htons(tt_common->vid); | ||
2019 | crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid)); | ||
2016 | 2020 | ||
2017 | /* compute the CRC on flags that have to be kept in sync | 2021 | /* compute the CRC on flags that have to be kept in sync |
2018 | * among nodes | 2022 | * among nodes |
@@ -2046,6 +2050,7 @@ static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv, | |||
2046 | struct hlist_head *head; | 2050 | struct hlist_head *head; |
2047 | uint32_t i, crc_tmp, crc = 0; | 2051 | uint32_t i, crc_tmp, crc = 0; |
2048 | uint8_t flags; | 2052 | uint8_t flags; |
2053 | __be16 tmp_vid; | ||
2049 | 2054 | ||
2050 | for (i = 0; i < hash->size; i++) { | 2055 | for (i = 0; i < hash->size; i++) { |
2051 | head = &hash->table[i]; | 2056 | head = &hash->table[i]; |
@@ -2064,8 +2069,11 @@ static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv, | |||
2064 | if (tt_common->flags & BATADV_TT_CLIENT_NEW) | 2069 | if (tt_common->flags & BATADV_TT_CLIENT_NEW) |
2065 | continue; | 2070 | continue; |
2066 | 2071 | ||
2067 | crc_tmp = crc32c(0, &tt_common->vid, | 2072 | /* use network order to read the VID: this ensures that |
2068 | sizeof(tt_common->vid)); | 2073 | * every node reads the bytes in the same order. |
2074 | */ | ||
2075 | tmp_vid = htons(tt_common->vid); | ||
2076 | crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid)); | ||
2069 | 2077 | ||
2070 | /* compute the CRC on flags that have to be kept in sync | 2078 | /* compute the CRC on flags that have to be kept in sync |
2071 | * among nodes | 2079 | * among nodes |
@@ -2262,6 +2270,7 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node, | |||
2262 | { | 2270 | { |
2263 | struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp; | 2271 | struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp; |
2264 | struct batadv_orig_node_vlan *vlan; | 2272 | struct batadv_orig_node_vlan *vlan; |
2273 | uint32_t crc; | ||
2265 | int i; | 2274 | int i; |
2266 | 2275 | ||
2267 | /* check if each received CRC matches the locally stored one */ | 2276 | /* 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 | if (!vlan) | 2290 | if (!vlan) |
2282 | return false; | 2291 | return false; |
2283 | 2292 | ||
2284 | if (vlan->tt.crc != ntohl(tt_vlan_tmp->crc)) | 2293 | crc = vlan->tt.crc; |
2294 | batadv_orig_node_vlan_free_ref(vlan); | ||
2295 | |||
2296 | if (crc != ntohl(tt_vlan_tmp->crc)) | ||
2285 | return false; | 2297 | return false; |
2286 | } | 2298 | } |
2287 | 2299 | ||
@@ -3218,7 +3230,6 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv, | |||
3218 | 3230 | ||
3219 | spin_lock_bh(&orig_node->tt_lock); | 3231 | spin_lock_bh(&orig_node->tt_lock); |
3220 | 3232 | ||
3221 | tt_change = (struct batadv_tvlv_tt_change *)tt_buff; | ||
3222 | batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes, | 3233 | batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes, |
3223 | ttvn, tt_change); | 3234 | ttvn, tt_change); |
3224 | 3235 | ||