aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatan Barak <matanb@mellanox.com>2015-10-15 08:01:03 -0400
committerDoug Ledford <dledford@redhat.com>2015-10-20 13:10:46 -0400
commit3909642034ffd7a8906ff3f2b2a71455bf39e7f6 (patch)
tree6c8a84b1f0d965a5fcd8462e87eb112a4aff6a17
parent17b38fb89055bf5df402980c9546a8b046552f2b (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.c35
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
274static void enum_netdev_ipv6_ips(struct ib_device *ib_dev, 293static void enum_netdev_ipv6_ips(struct ib_device *ib_dev,