Commit 78c5b2c6 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

Antonio Quartulli says:

====================
pull request [net]: batman-adv 20160117

here you have a bunch of patches intended for net.

This patchset is provided by Sven Eckelmann and it is basically
fixing 2 major issues that exist in several parts of the code -
that is why we have 8 patches.

The first bugfix (patch 1 and 2) is preventing call_rcu from
being invoked recursively. This would deceive any user waiting
on rcu_barrier() because the latter won't be able to wait for
the nested invocation thus triggering any sort of undefined
behaviours.

The second bugfix (patches from 3 to 8) prevents the code from
freeing rcu protected objects without waiting for the proper grace
period. This issue can potentially lead to wrong memory access
and thus kernel crashes.

Unfortunately this bogus code pattern was copy/pasted
all around the place when developing new features, therefore
Sven diligently created several patches to address each component
independently.

Given that such bugs were introduced quite some time ago, all
the patches except patch 5 should be considered for submission
to stable.

Included changes:
- avoid recursive invocations of call_rcu() which would fool users waiting on
  rcu_barrier()
- prevent immediate kfree of objects used in rcu protected contexts
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6c3f5aef 42eff6a6
...@@ -127,21 +127,17 @@ batadv_backbone_gw_free_ref(struct batadv_bla_backbone_gw *backbone_gw) ...@@ -127,21 +127,17 @@ batadv_backbone_gw_free_ref(struct batadv_bla_backbone_gw *backbone_gw)
} }
/* finally deinitialize the claim */ /* finally deinitialize the claim */
static void batadv_claim_free_rcu(struct rcu_head *rcu) static void batadv_claim_release(struct batadv_bla_claim *claim)
{ {
struct batadv_bla_claim *claim;
claim = container_of(rcu, struct batadv_bla_claim, rcu);
batadv_backbone_gw_free_ref(claim->backbone_gw); batadv_backbone_gw_free_ref(claim->backbone_gw);
kfree(claim); kfree_rcu(claim, rcu);
} }
/* free a claim, call claim_free_rcu if its the last reference */ /* free a claim, call claim_free_rcu if its the last reference */
static void batadv_claim_free_ref(struct batadv_bla_claim *claim) static void batadv_claim_free_ref(struct batadv_bla_claim *claim)
{ {
if (atomic_dec_and_test(&claim->refcount)) if (atomic_dec_and_test(&claim->refcount))
call_rcu(&claim->rcu, batadv_claim_free_rcu); batadv_claim_release(claim);
} }
/** /**
......
...@@ -75,18 +75,6 @@ batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface) ...@@ -75,18 +75,6 @@ batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface)
call_rcu(&hard_iface->rcu, batadv_hardif_free_rcu); call_rcu(&hard_iface->rcu, batadv_hardif_free_rcu);
} }
/**
* batadv_hardif_free_ref_now - decrement the hard interface refcounter and
* possibly free it (without rcu callback)
* @hard_iface: the hard interface to free
*/
static inline void
batadv_hardif_free_ref_now(struct batadv_hard_iface *hard_iface)
{
if (atomic_dec_and_test(&hard_iface->refcount))
batadv_hardif_free_rcu(&hard_iface->rcu);
}
static inline struct batadv_hard_iface * static inline struct batadv_hard_iface *
batadv_primary_if_get_selected(struct batadv_priv *bat_priv) batadv_primary_if_get_selected(struct batadv_priv *bat_priv)
{ {
......
...@@ -203,28 +203,25 @@ void batadv_nc_init_orig(struct batadv_orig_node *orig_node) ...@@ -203,28 +203,25 @@ void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
} }
/** /**
* batadv_nc_node_free_rcu - rcu callback to free an nc node and remove * batadv_nc_node_release - release nc_node from lists and queue for free after
* its refcount on the orig_node * rcu grace period
* @rcu: rcu pointer of the nc node * @nc_node: the nc node to free
*/ */
static void batadv_nc_node_free_rcu(struct rcu_head *rcu) static void batadv_nc_node_release(struct batadv_nc_node *nc_node)
{ {
struct batadv_nc_node *nc_node;
nc_node = container_of(rcu, struct batadv_nc_node, rcu);
batadv_orig_node_free_ref(nc_node->orig_node); batadv_orig_node_free_ref(nc_node->orig_node);
kfree(nc_node); kfree_rcu(nc_node, rcu);
} }
/** /**
* batadv_nc_node_free_ref - decrements the nc node refcounter and possibly * batadv_nc_node_free_ref - decrement the nc node refcounter and possibly
* frees it * release it
* @nc_node: the nc node to free * @nc_node: the nc node to free
*/ */
static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node) static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node)
{ {
if (atomic_dec_and_test(&nc_node->refcount)) if (atomic_dec_and_test(&nc_node->refcount))
call_rcu(&nc_node->rcu, batadv_nc_node_free_rcu); batadv_nc_node_release(nc_node);
} }
/** /**
......
This diff is collapsed.
...@@ -38,7 +38,6 @@ int batadv_originator_init(struct batadv_priv *bat_priv); ...@@ -38,7 +38,6 @@ int batadv_originator_init(struct batadv_priv *bat_priv);
void batadv_originator_free(struct batadv_priv *bat_priv); void batadv_originator_free(struct batadv_priv *bat_priv);
void batadv_purge_orig_ref(struct batadv_priv *bat_priv); void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node); void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
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 u8 *addr); const u8 *addr);
struct batadv_hardif_neigh_node * struct batadv_hardif_neigh_node *
......
...@@ -240,20 +240,6 @@ int batadv_tt_global_hash_count(struct batadv_priv *bat_priv, ...@@ -240,20 +240,6 @@ int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
return count; return count;
} }
static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
{
struct batadv_tt_orig_list_entry *orig_entry;
orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu);
/* We are in an rcu callback here, therefore we cannot use
* batadv_orig_node_free_ref() and its call_rcu():
* An rcu_barrier() wouldn't wait for that to finish
*/
batadv_orig_node_free_ref_now(orig_entry->orig_node);
kfree(orig_entry);
}
/** /**
* batadv_tt_local_size_mod - change the size by v of the local table identified * batadv_tt_local_size_mod - change the size by v of the local table identified
* by vid * by vid
...@@ -349,13 +335,25 @@ static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node, ...@@ -349,13 +335,25 @@ static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
batadv_tt_global_size_mod(orig_node, vid, -1); batadv_tt_global_size_mod(orig_node, vid, -1);
} }
/**
* batadv_tt_orig_list_entry_release - release tt orig entry from lists and
* queue for free after rcu grace period
* @orig_entry: tt orig entry to be free'd
*/
static void
batadv_tt_orig_list_entry_release(struct batadv_tt_orig_list_entry *orig_entry)
{
batadv_orig_node_free_ref(orig_entry->orig_node);
kfree_rcu(orig_entry, rcu);
}
static void static void
batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry) batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry)
{ {
if (!atomic_dec_and_test(&orig_entry->refcount)) if (!atomic_dec_and_test(&orig_entry->refcount))
return; return;
call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu); batadv_tt_orig_list_entry_release(orig_entry);
} }
/** /**
......
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