IB/core: Fix use after free of ifa
When using ifup/ifdown while executing enum_netdev_ipv4_ips,
ifa could become invalid and cause use after free error.
Fixing it by protecting with RCU lock.
Fixes: 03db3a2d81
('IB/core: Add RoCE GID table management')
Signed-off-by: Matan Barak <matanb@mellanox.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
parent
17b38fb890
commit
3909642034
@ -250,25 +250,44 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev,
|
|||||||
u8 port, struct net_device *ndev)
|
u8 port, struct net_device *ndev)
|
||||||
{
|
{
|
||||||
struct in_device *in_dev;
|
struct in_device *in_dev;
|
||||||
|
struct sin_list {
|
||||||
|
struct list_head list;
|
||||||
|
struct sockaddr_in ip;
|
||||||
|
};
|
||||||
|
struct sin_list *sin_iter;
|
||||||
|
struct sin_list *sin_temp;
|
||||||
|
|
||||||
|
LIST_HEAD(sin_list);
|
||||||
if (ndev->reg_state >= NETREG_UNREGISTERING)
|
if (ndev->reg_state >= NETREG_UNREGISTERING)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
in_dev = in_dev_get(ndev);
|
rcu_read_lock();
|
||||||
if (!in_dev)
|
in_dev = __in_dev_get_rcu(ndev);
|
||||||
|
if (!in_dev) {
|
||||||
|
rcu_read_unlock();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for_ifa(in_dev) {
|
for_ifa(in_dev) {
|
||||||
struct sockaddr_in ip;
|
struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
|
|
||||||
ip.sin_family = AF_INET;
|
if (!entry) {
|
||||||
ip.sin_addr.s_addr = ifa->ifa_address;
|
pr_warn("roce_gid_mgmt: couldn't allocate entry for IPv4 update\n");
|
||||||
update_gid_ip(GID_ADD, ib_dev, port, ndev,
|
continue;
|
||||||
(struct sockaddr *)&ip);
|
}
|
||||||
|
entry->ip.sin_family = AF_INET;
|
||||||
|
entry->ip.sin_addr.s_addr = ifa->ifa_address;
|
||||||
|
list_add_tail(&entry->list, &sin_list);
|
||||||
}
|
}
|
||||||
endfor_ifa(in_dev);
|
endfor_ifa(in_dev);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
in_dev_put(in_dev);
|
list_for_each_entry_safe(sin_iter, sin_temp, &sin_list, list) {
|
||||||
|
update_gid_ip(GID_ADD, ib_dev, port, ndev,
|
||||||
|
(struct sockaddr *)&sin_iter->ip);
|
||||||
|
list_del(&sin_iter->list);
|
||||||
|
kfree(sin_iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enum_netdev_ipv6_ips(struct ib_device *ib_dev,
|
static void enum_netdev_ipv6_ips(struct ib_device *ib_dev,
|
||||||
|
Loading…
Reference in New Issue
Block a user