diff options
author | Robert Shearman <rshearma@brocade.com> | 2015-06-05 18:51:54 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-06-08 12:12:45 -0700 |
commit | 27e41fcfa6b326ad44eee7e0b1930d080b270895 (patch) | |
tree | 58b71aa33019e31628c0bcdbf557634e1603cb04 /net | |
parent | 1489bdeeae1a47171926e255956c9fc251db13a0 (diff) | |
download | op-kernel-dev-27e41fcfa6b326ad44eee7e0b1930d080b270895.zip op-kernel-dev-27e41fcfa6b326ad44eee7e0b1930d080b270895.tar.gz |
ipv6: fix possible use after free of dev stats
The memory pointed to by idev->stats.icmpv6msgdev,
idev->stats.icmpv6dev and idev->stats.ipv6 can each be used in an RCU
read context without taking a reference on idev. For example, through
IP6_*_STATS_* calls in ip6_rcv. These memory blocks are freed without
waiting for an RCU grace period to elapse. This could lead to the
memory being written to after it has been freed.
Fix this by using call_rcu to free the memory used for stats, as well
as idev after an RCU grace period has elapsed.
Signed-off-by: Robert Shearman <rshearma@brocade.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/addrconf_core.c | 11 |
1 files changed, 9 insertions, 2 deletions
diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index d873cee..ca09bf4 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -133,6 +133,14 @@ static void snmp6_free_dev(struct inet6_dev *idev) free_percpu(idev->stats.ipv6); } +static void in6_dev_finish_destroy_rcu(struct rcu_head *head) +{ + struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu); + + snmp6_free_dev(idev); + kfree(idev); +} + /* Nobody refers to this device, we may destroy it. */ void in6_dev_finish_destroy(struct inet6_dev *idev) @@ -151,7 +159,6 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) pr_warn("Freeing alive inet6 device %p\n", idev); return; } - snmp6_free_dev(idev); - kfree_rcu(idev, rcu); + call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu); } EXPORT_SYMBOL(in6_dev_finish_destroy); |