Message ID | 20190104131705.9550-8-fw@strlen.de |
---|---|
State | Awaiting Upstream |
Delegated to: | David Miller |
Headers | show |
Series | xfrm: policy: fix various bugs | expand |
On Fri, Jan 4, 2019 at 5:19 AM Florian Westphal <fw@strlen.de> wrote: > > 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> > --- > net/xfrm/xfrm_policy.c | 15 +++++++-------- > tools/testing/selftests/net/xfrm_policy.sh | 4 ++-- > 2 files changed, 9 insertions(+), 10 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) { hlist_for_each_entry_safe()? > 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; > } > } > > diff --git a/tools/testing/selftests/net/xfrm_policy.sh b/tools/testing/selftests/net/xfrm_policy.sh > index 8ce54600d4d1..71d7fdc513c1 100755 > --- a/tools/testing/selftests/net/xfrm_policy.sh > +++ b/tools/testing/selftests/net/xfrm_policy.sh > @@ -78,8 +78,8 @@ do_overlap() > # adds a new node in the 10.0.0.0/24 tree (dst node exists). > ip -net $ns xfrm policy add src 10.2.0.0/24 dst 10.0.0.0/24 dir fwd priority 200 action block > > - # adds a 10.2.0.0/24 node, but for different dst. > - ip -net $ns xfrm policy add src 10.2.0.0/24 dst 10.0.1.0/24 dir fwd priority 200 action block > + # adds a 10.2.0.0/23 node, but for different dst. > + ip -net $ns xfrm policy add src 10.2.0.0/23 dst 10.0.1.0/24 dir fwd priority 200 action block > > # dst now overlaps with the 10.0.1.0/24 ESP policy in fwd. > # kernel must 'promote' existing one (10.0.0.0/24) to 10.0.0.0/23. > -- > 2.19.2 >
Cong Wang <xiyou.wangcong@gmail.com> wrote: > > - 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) { > > > hlist_for_each_entry_safe()? Could be used instead indeed, but its not required. Steffen, just let me know your preference. If you think that hlist_for_each_entry(...) hlist_del_rcu(&tmp->bydst); ... looks unsafe then i can respin this with _safe version.
On Sat, Jan 05, 2019 at 10:59:08AM +0100, Florian Westphal wrote: > Cong Wang <xiyou.wangcong@gmail.com> wrote: > > > - 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) { > > > > > > hlist_for_each_entry_safe()? > > Could be used instead indeed, but its not required. > > Steffen, just let me know your preference. I tend to apply the patchset as is after some testing.
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; } } diff --git a/tools/testing/selftests/net/xfrm_policy.sh b/tools/testing/selftests/net/xfrm_policy.sh index 8ce54600d4d1..71d7fdc513c1 100755 --- a/tools/testing/selftests/net/xfrm_policy.sh +++ b/tools/testing/selftests/net/xfrm_policy.sh @@ -78,8 +78,8 @@ do_overlap() # adds a new node in the 10.0.0.0/24 tree (dst node exists). ip -net $ns xfrm policy add src 10.2.0.0/24 dst 10.0.0.0/24 dir fwd priority 200 action block - # adds a 10.2.0.0/24 node, but for different dst. - ip -net $ns xfrm policy add src 10.2.0.0/24 dst 10.0.1.0/24 dir fwd priority 200 action block + # adds a 10.2.0.0/23 node, but for different dst. + ip -net $ns xfrm policy add src 10.2.0.0/23 dst 10.0.1.0/24 dir fwd priority 200 action block # dst now overlaps with the 10.0.1.0/24 ESP policy in fwd. # kernel must 'promote' existing one (10.0.0.0/24) to 10.0.0.0/23.
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> --- net/xfrm/xfrm_policy.c | 15 +++++++-------- tools/testing/selftests/net/xfrm_policy.sh | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-)