Skip to content

Commit e9777ad

Browse files
ssanchez11dledford
authored andcommitted
IB/{hfi1, rdmavt}: Fix memory leak in hfi1_alloc_devdata() upon failure
When allocating device data, if there's an allocation failure, the already allocated memory won't be freed such as per-cpu counters. Fix memory leaks in exception path by creating a common reentrant clean up function hfi1_clean_devdata() to be used at driver unload time and device data allocation failure. To accomplish this, free_platform_config() and clean_up_i2c() are changed to be reentrant to remove dependencies when they are called in different order. This helps avoid NULL pointer dereferences introduced by this patch if those two functions weren't reentrant. In addition, set dd->int_counter, dd->rcv_limit, dd->send_schedule and dd->tx_opstats to NULL after they're freed in hfi1_clean_devdata(), so that hfi1_clean_devdata() is fully reentrant. Reviewed-by: Mike Marciniszyn <[email protected]> Reviewed-by: Michael J. Ruhl <[email protected]> Signed-off-by: Sebastian Sanchez <[email protected]> Signed-off-by: Dennis Dalessandro <[email protected]> Signed-off-by: Doug Ledford <[email protected]>
1 parent 45d9245 commit e9777ad

File tree

3 files changed

+30
-10
lines changed

3 files changed

+30
-10
lines changed

drivers/infiniband/hw/hfi1/init.c

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,30 +1209,49 @@ static void finalize_asic_data(struct hfi1_devdata *dd,
12091209
kfree(ad);
12101210
}
12111211

1212-
static void __hfi1_free_devdata(struct kobject *kobj)
1212+
/**
1213+
* hfi1_clean_devdata - cleans up per-unit data structure
1214+
* @dd: pointer to a valid devdata structure
1215+
*
1216+
* It cleans up all data structures set up by
1217+
* by hfi1_alloc_devdata().
1218+
*/
1219+
static void hfi1_clean_devdata(struct hfi1_devdata *dd)
12131220
{
1214-
struct hfi1_devdata *dd =
1215-
container_of(kobj, struct hfi1_devdata, kobj);
12161221
struct hfi1_asic_data *ad;
12171222
unsigned long flags;
12181223

12191224
spin_lock_irqsave(&hfi1_devs_lock, flags);
1220-
idr_remove(&hfi1_unit_table, dd->unit);
1221-
list_del(&dd->list);
1225+
if (!list_empty(&dd->list)) {
1226+
idr_remove(&hfi1_unit_table, dd->unit);
1227+
list_del_init(&dd->list);
1228+
}
12221229
ad = release_asic_data(dd);
12231230
spin_unlock_irqrestore(&hfi1_devs_lock, flags);
1224-
if (ad)
1225-
finalize_asic_data(dd, ad);
1231+
1232+
finalize_asic_data(dd, ad);
12261233
free_platform_config(dd);
12271234
rcu_barrier(); /* wait for rcu callbacks to complete */
12281235
free_percpu(dd->int_counter);
12291236
free_percpu(dd->rcv_limit);
12301237
free_percpu(dd->send_schedule);
12311238
free_percpu(dd->tx_opstats);
1239+
dd->int_counter = NULL;
1240+
dd->rcv_limit = NULL;
1241+
dd->send_schedule = NULL;
1242+
dd->tx_opstats = NULL;
12321243
sdma_clean(dd, dd->num_sdma);
12331244
rvt_dealloc_device(&dd->verbs_dev.rdi);
12341245
}
12351246

1247+
static void __hfi1_free_devdata(struct kobject *kobj)
1248+
{
1249+
struct hfi1_devdata *dd =
1250+
container_of(kobj, struct hfi1_devdata, kobj);
1251+
1252+
hfi1_clean_devdata(dd);
1253+
}
1254+
12361255
static struct kobj_type hfi1_devdata_type = {
12371256
.release = __hfi1_free_devdata,
12381257
};
@@ -1333,9 +1352,7 @@ struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, size_t extra)
13331352
return dd;
13341353

13351354
bail:
1336-
if (!list_empty(&dd->list))
1337-
list_del_init(&dd->list);
1338-
rvt_dealloc_device(&dd->verbs_dev.rdi);
1355+
hfi1_clean_devdata(dd);
13391356
return ERR_PTR(ret);
13401357
}
13411358

drivers/infiniband/hw/hfi1/platform.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ void free_platform_config(struct hfi1_devdata *dd)
199199
{
200200
/* Release memory allocated for eprom or fallback file read. */
201201
kfree(dd->platform_config.data);
202+
dd->platform_config.data = NULL;
202203
}
203204

204205
void get_port_type(struct hfi1_pportdata *ppd)

drivers/infiniband/hw/hfi1/qsfp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ static void clean_i2c_bus(struct hfi1_i2c_bus *bus)
204204

205205
void clean_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad)
206206
{
207+
if (!ad)
208+
return;
207209
clean_i2c_bus(ad->i2c_bus0);
208210
ad->i2c_bus0 = NULL;
209211
clean_i2c_bus(ad->i2c_bus1);

0 commit comments

Comments
 (0)