diff options
author | Matan Barak <matanb@mellanox.com> | 2015-10-15 08:01:03 -0400 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2015-10-20 13:10:46 -0400 |
commit | 3909642034ffd7a8906ff3f2b2a71455bf39e7f6 (patch) | |
tree | 6c8a84b1f0d965a5fcd8462e87eb112a4aff6a17 | |
parent | 17b38fb89055bf5df402980c9546a8b046552f2b (diff) |
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: 03db3a2d81e6 ('IB/core: Add RoCE GID table management')
Signed-off-by: Matan Barak <matanb@mellanox.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r-- | drivers/infiniband/core/roce_gid_mgmt.c | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index 6b24cba1e474..178f98482e13 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c | |||
@@ -250,25 +250,44 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev, | |||
250 | u8 port, struct net_device *ndev) | 250 | u8 port, struct net_device *ndev) |
251 | { | 251 | { |
252 | struct in_device *in_dev; | 252 | 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; | ||
253 | 259 | ||
260 | LIST_HEAD(sin_list); | ||
254 | if (ndev->reg_state >= NETREG_UNREGISTERING) | 261 | if (ndev->reg_state >= NETREG_UNREGISTERING) |
255 | return; | 262 | return; |
256 | 263 | ||
257 | in_dev = in_dev_get(ndev); | 264 | rcu_read_lock(); |
258 | if (!in_dev) | 265 | in_dev = __in_dev_get_rcu(ndev); |
266 | if (!in_dev) { | ||
267 | rcu_read_unlock(); | ||
259 | return; | 268 | return; |
269 | } | ||
260 | 270 | ||
261 | for_ifa(in_dev) { | 271 | for_ifa(in_dev) { |
262 | struct sockaddr_in ip; | 272 | struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); |
263 | 273 | ||
264 | ip.sin_family = AF_INET; | 274 | if (!entry) { |
265 | ip.sin_addr.s_addr = ifa->ifa_address; | 275 | pr_warn("roce_gid_mgmt: couldn't allocate entry for IPv4 update\n"); |
266 | update_gid_ip(GID_ADD, ib_dev, port, ndev, | 276 | continue; |
267 | (struct sockaddr *)&ip); | 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); | ||
268 | } | 281 | } |
269 | endfor_ifa(in_dev); | 282 | endfor_ifa(in_dev); |
283 | rcu_read_unlock(); | ||
270 | 284 | ||
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 | } | ||
272 | } | 291 | } |
273 | 292 | ||
274 | static void enum_netdev_ipv6_ips(struct ib_device *ib_dev, | 293 | static void enum_netdev_ipv6_ips(struct ib_device *ib_dev, |