diff options
author | WANG Cong <xiyou.wangcong@gmail.com> | 2017-02-27 19:07:43 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-03-02 15:43:47 -0500 |
commit | e3330039ea28dc199e3b2da993895ff742a91adf (patch) | |
tree | e2755e80b6742c3f4b8a82464210f52c87b20e65 | |
parent | 48cac18ecf1de82f76259a54402c3adb7839ad01 (diff) |
ipv6: check for ip6_null_entry in __ip6_del_rt_siblings()
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 <andreyknvl@google.com>
Fixes: 0ae8133586ad ("net: ipv6: Allow shorthand delete of all nexthops in multipath route")
Cc: David Ahern <dsa@cumulusnetworks.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Acked-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv6/route.c | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d94f1dfa54c8..43ca90d50ae9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -2169,10 +2169,13 @@ int ip6_del_rt(struct rt6_info *rt) | |||
2169 | static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) | 2169 | static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) |
2170 | { | 2170 | { |
2171 | struct nl_info *info = &cfg->fc_nlinfo; | 2171 | struct nl_info *info = &cfg->fc_nlinfo; |
2172 | struct net *net = info->nl_net; | ||
2172 | struct sk_buff *skb = NULL; | 2173 | struct sk_buff *skb = NULL; |
2173 | struct fib6_table *table; | 2174 | struct fib6_table *table; |
2174 | int err; | 2175 | int err = -ENOENT; |
2175 | 2176 | ||
2177 | if (rt == net->ipv6.ip6_null_entry) | ||
2178 | goto out_put; | ||
2176 | table = rt->rt6i_table; | 2179 | table = rt->rt6i_table; |
2177 | write_lock_bh(&table->tb6_lock); | 2180 | write_lock_bh(&table->tb6_lock); |
2178 | 2181 | ||
@@ -2184,7 +2187,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) | |||
2184 | if (skb) { | 2187 | if (skb) { |
2185 | u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; | 2188 | u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; |
2186 | 2189 | ||
2187 | if (rt6_fill_node(info->nl_net, skb, rt, | 2190 | if (rt6_fill_node(net, skb, rt, |
2188 | NULL, NULL, 0, RTM_DELROUTE, | 2191 | NULL, NULL, 0, RTM_DELROUTE, |
2189 | info->portid, seq, 0) < 0) { | 2192 | info->portid, seq, 0) < 0) { |
2190 | kfree_skb(skb); | 2193 | kfree_skb(skb); |
@@ -2198,17 +2201,18 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) | |||
2198 | rt6i_siblings) { | 2201 | rt6i_siblings) { |
2199 | err = fib6_del(sibling, info); | 2202 | err = fib6_del(sibling, info); |
2200 | if (err) | 2203 | if (err) |
2201 | goto out; | 2204 | goto out_unlock; |
2202 | } | 2205 | } |
2203 | } | 2206 | } |
2204 | 2207 | ||
2205 | err = fib6_del(rt, info); | 2208 | err = fib6_del(rt, info); |
2206 | out: | 2209 | out_unlock: |
2207 | write_unlock_bh(&table->tb6_lock); | 2210 | write_unlock_bh(&table->tb6_lock); |
2211 | out_put: | ||
2208 | ip6_rt_put(rt); | 2212 | ip6_rt_put(rt); |
2209 | 2213 | ||
2210 | if (skb) { | 2214 | if (skb) { |
2211 | rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV6_ROUTE, | 2215 | rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, |
2212 | info->nlh, gfp_any()); | 2216 | info->nlh, gfp_any()); |
2213 | } | 2217 | } |
2214 | return err; | 2218 | return err; |