aboutsummaryrefslogtreecommitdiff
path: root/net/xfrm
diff options
context:
space:
mode:
authorFlorian Westphal2019-01-04 14:17:05 +0100
committerSteffen Klassert2019-01-09 13:58:23 +0100
commit12750abad517a991c4568969bc748db302ab52cd (patch)
treed5a896fc8490185aee1e73bc7ab13c58d8059c92 /net/xfrm
parentfcf86f55f2d4a6b58da5feccd45d9584edc17c5a (diff)
xfrm: policy: fix infinite loop when merging src-nodes
With very small change to test script we can trigger softlockup due to bogus assignment of 'p' (policy to be examined) on restart. Previously the two to-be-merged nodes had same address/prefixlength pair, so no erase/reinsert was necessary, we only had to append the list from node a to b. If prefix lengths are different, the node has to be deleted and re-inserted into the tree, with the updated prefix length. This was broken; due to bogus update to 'p' this loops forever. Add a 'restart' label and use that instead. While at it, don't perform the unneeded reinserts of the policies that are already sorted into the 'new' node. A previous patch in this series made xfrm_policy_inexact_list_reinsert() use the relative position indicator to sort policies according to age in case priorities are identical. Fixes: 6ac098b2a9d30 ("xfrm: policy: add 2nd-level saddr trees for inexact policies") Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Diffstat (limited to 'net/xfrm')
-rw-r--r--net/xfrm/xfrm_policy.c15
1 files changed, 7 insertions, 8 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index e691683223ee..8cfd75b62396 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -886,12 +886,13 @@ static void xfrm_policy_inexact_node_reinsert(struct net *net,
struct rb_root *new,
u16 family)
{
- struct rb_node **p, *parent = NULL;
struct xfrm_pol_inexact_node *node;
+ struct rb_node **p, *parent;
/* we should not have another subtree here */
WARN_ON_ONCE(!RB_EMPTY_ROOT(&n->root));
-
+restart:
+ parent = NULL;
p = &new->rb_node;
while (*p) {
u8 prefixlen;
@@ -911,12 +912,11 @@ static void xfrm_policy_inexact_node_reinsert(struct net *net,
} else {
struct xfrm_policy *tmp;
- hlist_for_each_entry(tmp, &node->hhead, bydst)
- tmp->bydst_reinsert = true;
- hlist_for_each_entry(tmp, &n->hhead, bydst)
+ hlist_for_each_entry(tmp, &n->hhead, bydst) {
tmp->bydst_reinsert = true;
+ hlist_del_rcu(&tmp->bydst);
+ }
- INIT_HLIST_HEAD(&node->hhead);
xfrm_policy_inexact_list_reinsert(net, node, family);
if (node->prefixlen == n->prefixlen) {
@@ -928,8 +928,7 @@ static void xfrm_policy_inexact_node_reinsert(struct net *net,
kfree_rcu(n, rcu);
n = node;
n->prefixlen = prefixlen;
- *p = new->rb_node;
- parent = NULL;
+ goto restart;
}
}