Skip to content

Commit 3909642

Browse files
matanb10dledford
authored andcommitted
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: 03db3a2 ('IB/core: Add RoCE GID table management') Signed-off-by: Matan Barak <[email protected]> Signed-off-by: Doug Ledford <[email protected]>
1 parent 17b38fb commit 3909642

File tree

1 file changed

+27
-8
lines changed

1 file changed

+27
-8
lines changed

drivers/infiniband/core/roce_gid_mgmt.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -250,25 +250,44 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev,
250250
u8 port, struct net_device *ndev)
251251
{
252252
struct in_device *in_dev;
253+
struct sin_list {
254+
struct list_head list;
255+
struct sockaddr_in ip;
256+
};
257+
struct sin_list *sin_iter;
258+
struct sin_list *sin_temp;
253259

260+
LIST_HEAD(sin_list);
254261
if (ndev->reg_state >= NETREG_UNREGISTERING)
255262
return;
256263

257-
in_dev = in_dev_get(ndev);
258-
if (!in_dev)
264+
rcu_read_lock();
265+
in_dev = __in_dev_get_rcu(ndev);
266+
if (!in_dev) {
267+
rcu_read_unlock();
259268
return;
269+
}
260270

261271
for_ifa(in_dev) {
262-
struct sockaddr_in ip;
272+
struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
263273

264-
ip.sin_family = AF_INET;
265-
ip.sin_addr.s_addr = ifa->ifa_address;
266-
update_gid_ip(GID_ADD, ib_dev, port, ndev,
267-
(struct sockaddr *)&ip);
274+
if (!entry) {
275+
pr_warn("roce_gid_mgmt: couldn't allocate entry for IPv4 update\n");
276+
continue;
277+
}
278+
entry->ip.sin_family = AF_INET;
279+
entry->ip.sin_addr.s_addr = ifa->ifa_address;
280+
list_add_tail(&entry->list, &sin_list);
268281
}
269282
endfor_ifa(in_dev);
283+
rcu_read_unlock();
270284

271-
in_dev_put(in_dev);
285+
list_for_each_entry_safe(sin_iter, sin_temp, &sin_list, list) {
286+
update_gid_ip(GID_ADD, ib_dev, port, ndev,
287+
(struct sockaddr *)&sin_iter->ip);
288+
list_del(&sin_iter->list);
289+
kfree(sin_iter);
290+
}
272291
}
273292

274293
static void enum_netdev_ipv6_ips(struct ib_device *ib_dev,

0 commit comments

Comments
 (0)