[Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

Subsystems: networking [general], networking [ipv4/ipv6], the rest

STALE3464d

7 messages, 3 authors, 2017-02-28 · open the first message on its own page

[Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: Cong Wang <hidden>
Date: 2017-02-27 19:16:21

Andrey reported a NULL pointer deref bug in ipv6_route_ioctl()
-> ip6_route_del() -> __ip6_del_rt_siblings() code path. This is
because ip6_null_entry is returned in this path since ip6_null_entry
is kinda default for a ipv6 route table root node. Quote from
David Ahern:

 ip6_null_entry is the root of all ipv6 fib tables making it integrated
 into the table ...

We should ignore any attempt of trying to delete it, like we do in
__ip6_del_rt() path and several others.

Reported-by: Andrey Konovalov <redacted>
Signed-off-by: Cong Wang <redacted>
---
 net/ipv6/route.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f54f426..9da77e9 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2169,10 +2169,13 @@ int ip6_del_rt(struct rt6_info *rt)
 static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 {
 	struct nl_info *info = &cfg->fc_nlinfo;
+	struct net *net = info->nl_net;
 	struct sk_buff *skb = NULL;
 	struct fib6_table *table;
 	int err;
 
+	if (rt == net->ipv6.ip6_null_entry)
+		return -ENOENT;
 	table = rt->rt6i_table;
 	write_lock_bh(&table->tb6_lock);
 
@@ -2184,7 +2187,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 		if (skb) {
 			u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
 
-			if (rt6_fill_node(info->nl_net, skb, rt,
+			if (rt6_fill_node(net, skb, rt,
 					  NULL, NULL, 0, RTM_DELROUTE,
 					  info->portid, seq, 0) < 0) {
 				kfree_skb(skb);
@@ -2208,7 +2211,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 	ip6_rt_put(rt);
 
 	if (skb) {
-		rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV6_ROUTE,
+		rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
 			    info->nlh, gfp_any());
 	}
 	return err;
-- 
2.5.5

Re: [Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: Eric Dumazet <hidden>
Date: 2017-02-27 20:42:03

On Mon, 2017-02-27 at 11:07 -0800, Cong Wang wrote:
quoted hunk
Andrey reported a NULL pointer deref bug in ipv6_route_ioctl()
-> ip6_route_del() -> __ip6_del_rt_siblings() code path. This is
because ip6_null_entry is returned in this path since ip6_null_entry
is kinda default for a ipv6 route table root node. Quote from
David Ahern:

 ip6_null_entry is the root of all ipv6 fib tables making it integrated
 into the table ...

We should ignore any attempt of trying to delete it, like we do in
__ip6_del_rt() path and several others.

Reported-by: Andrey Konovalov <redacted>
Signed-off-by: Cong Wang <redacted>
---
 net/ipv6/route.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f54f426..9da77e9 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2169,10 +2169,13 @@ int ip6_del_rt(struct rt6_info *rt)
 static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 {
 	struct nl_info *info = &cfg->fc_nlinfo;
+	struct net *net = info->nl_net;
 	struct sk_buff *skb = NULL;
 	struct fib6_table *table;
 	int err;
 
+	if (rt == net->ipv6.ip6_null_entry)
+		return -ENOENT;
It looks the caller did a dst_hold(&rt->dst);

So this new error path would leave a refcount leak.

Note that I was not able to trigger the crash on old kernels, so it
would be nice to get a precise idea of bug origin.

Thanks.

Re: [Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: Cong Wang <hidden>
Date: 2017-02-27 20:57:00

On Mon, Feb 27, 2017 at 12:34 PM, Eric Dumazet [off-list ref] wrote:
On Mon, 2017-02-27 at 11:07 -0800, Cong Wang wrote:
quoted
Andrey reported a NULL pointer deref bug in ipv6_route_ioctl()
-> ip6_route_del() -> __ip6_del_rt_siblings() code path. This is
because ip6_null_entry is returned in this path since ip6_null_entry
is kinda default for a ipv6 route table root node. Quote from
David Ahern:

 ip6_null_entry is the root of all ipv6 fib tables making it integrated
 into the table ...

We should ignore any attempt of trying to delete it, like we do in
__ip6_del_rt() path and several others.

Reported-by: Andrey Konovalov <redacted>
Signed-off-by: Cong Wang <redacted>
---
 net/ipv6/route.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f54f426..9da77e9 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2169,10 +2169,13 @@ int ip6_del_rt(struct rt6_info *rt)
 static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 {
      struct nl_info *info = &cfg->fc_nlinfo;
+     struct net *net = info->nl_net;
      struct sk_buff *skb = NULL;
      struct fib6_table *table;
      int err;

+     if (rt == net->ipv6.ip6_null_entry)
+             return -ENOENT;
It looks the caller did a dst_hold(&rt->dst);

So this new error path would leave a refcount leak.
Interesting, this error path is not new for __ip6_del_rt_siblings()
so the leak was already there before mine, but you are probably
right we have a leak here.

I will send a separate patch to address this leak.
Note that I was not able to trigger the crash on old kernels, so it
would be nice to get a precise idea of bug origin.
Right, I miss:
Fixes: 0ae8133586ad ("net: ipv6: Allow shorthand delete of all
nexthops in multipath route")

Thanks!

Re: [Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: David Ahern <hidden>
Date: 2017-02-27 21:01:07

On 2/27/17 12:34 PM, Eric Dumazet wrote:
On Mon, 2017-02-27 at 11:07 -0800, Cong Wang wrote:
quoted
Andrey reported a NULL pointer deref bug in ipv6_route_ioctl()
-> ip6_route_del() -> __ip6_del_rt_siblings() code path. This is
because ip6_null_entry is returned in this path since ip6_null_entry
is kinda default for a ipv6 route table root node. Quote from
David Ahern:

 ip6_null_entry is the root of all ipv6 fib tables making it integrated
 into the table ...

We should ignore any attempt of trying to delete it, like we do in
__ip6_del_rt() path and several others.

Reported-by: Andrey Konovalov <redacted>
Signed-off-by: Cong Wang <redacted>
---
 net/ipv6/route.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f54f426..9da77e9 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2169,10 +2169,13 @@ int ip6_del_rt(struct rt6_info *rt)
 static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 {
 	struct nl_info *info = &cfg->fc_nlinfo;
+	struct net *net = info->nl_net;
 	struct sk_buff *skb = NULL;
 	struct fib6_table *table;
 	int err;
 
+	if (rt == net->ipv6.ip6_null_entry)
+		return -ENOENT;
It looks the caller did a dst_hold(&rt->dst);

So this new error path would leave a refcount leak.

Note that I was not able to trigger the crash on old kernels, so it
would be nice to get a precise idea of bug origin.
Cong: do you want to send a v2 catching the null entry in ip6_route_del
before the refcnt?

                for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+                       /* do not allow deletion of the null route */
+                       if (rt == net->ipv6.ip6_null_entry)
+                               continue;

Fixes: 0ae8133586ad net: ipv6: Allow shorthand delete of all nexthops in
multipath route

Re: [Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: Cong Wang <hidden>
Date: 2017-02-27 21:34:55

On Mon, Feb 27, 2017 at 1:00 PM, David Ahern [off-list ref] wrote:
On 2/27/17 12:34 PM, Eric Dumazet wrote:
quoted
On Mon, 2017-02-27 at 11:07 -0800, Cong Wang wrote:
quoted
Andrey reported a NULL pointer deref bug in ipv6_route_ioctl()
-> ip6_route_del() -> __ip6_del_rt_siblings() code path. This is
because ip6_null_entry is returned in this path since ip6_null_entry
is kinda default for a ipv6 route table root node. Quote from
David Ahern:

 ip6_null_entry is the root of all ipv6 fib tables making it integrated
 into the table ...

We should ignore any attempt of trying to delete it, like we do in
__ip6_del_rt() path and several others.

Reported-by: Andrey Konovalov <redacted>
Signed-off-by: Cong Wang <redacted>
---
 net/ipv6/route.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index f54f426..9da77e9 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2169,10 +2169,13 @@ int ip6_del_rt(struct rt6_info *rt)
 static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
 {
     struct nl_info *info = &cfg->fc_nlinfo;
+    struct net *net = info->nl_net;
     struct sk_buff *skb = NULL;
     struct fib6_table *table;
     int err;

+    if (rt == net->ipv6.ip6_null_entry)
+            return -ENOENT;
It looks the caller did a dst_hold(&rt->dst);

So this new error path would leave a refcount leak.

Note that I was not able to trigger the crash on old kernels, so it
would be nice to get a precise idea of bug origin.
Cong: do you want to send a v2 catching the null entry in ip6_route_del
before the refcnt?
Yeah, actually it is introduced by my patch because there is already
an ip6_rt_put() in __ip6_del_rt_siblings(). So v2 is coming...
                for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+                       /* do not allow deletion of the null route */
+                       if (rt == net->ipv6.ip6_null_entry)
+                               continue;

Fixes: 0ae8133586ad net: ipv6: Allow shorthand delete of all nexthops in
multipath route
Note, I moved the check into __ip6_del_rt_siblings() because __ip6_del_rt()
has a same check.

Re: [Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: David Ahern <hidden>
Date: 2017-02-27 22:10:18

On 2/27/17 1:04 PM, Cong Wang wrote:
quoted
                for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+                       /* do not allow deletion of the null route */
+                       if (rt == net->ipv6.ip6_null_entry)
+                               continue;

Fixes: 0ae8133586ad net: ipv6: Allow shorthand delete of all nexthops in
multipath route
Note, I moved the check into __ip6_del_rt_siblings() because __ip6_del_rt()
has a same check.
that's b/c __ip6_del_rt has a second call path. __ip6_del_rt_siblings is
new and is not expecting to see the null entry. Catching it before the
dst_hold would be better.

Re: [Patch net] ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()

From: Cong Wang <hidden>
Date: 2017-02-28 01:31:37

On Mon, Feb 27, 2017 at 1:06 PM, David Ahern [off-list ref] wrote:
On 2/27/17 1:04 PM, Cong Wang wrote:
quoted
quoted
                for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+                       /* do not allow deletion of the null route */
+                       if (rt == net->ipv6.ip6_null_entry)
+                               continue;

Fixes: 0ae8133586ad net: ipv6: Allow shorthand delete of all nexthops in
multipath route
Note, I moved the check into __ip6_del_rt_siblings() because __ip6_del_rt()
has a same check.
that's b/c __ip6_del_rt has a second call path. __ip6_del_rt_siblings is
new and is not expecting to see the null entry. Catching it before the
dst_hold would be better.
Yeah, but it also depends on if we want to continue after the null entry,
at least __ip6_del_rt () returns an error for null entry, which looks more
correct than continuing to proceed after it.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help