diff mbox series

[ipsec,7/7] xfrm: policy: fix infinite loop when merging src-nodes

Message ID 20190104131705.9550-8-fw@strlen.de
State Awaiting Upstream
Delegated to: David Miller
Headers show
Series xfrm: policy: fix various bugs | expand

Commit Message

Florian Westphal Jan. 4, 2019, 1:17 p.m. UTC
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(-)

Comments

Cong Wang Jan. 5, 2019, 4:49 a.m. UTC | #1
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
>
Florian Westphal Jan. 5, 2019, 9:59 a.m. UTC | #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.
Steffen Klassert Jan. 9, 2019, 1:03 p.m. UTC | #3
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 mbox series

Patch

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.