Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: mana: Fix accessing freed irq affinity_hint

After calling irq_set_affinity_and_hint(), the cpumask pointer is
saved in desc->affinity_hint, and will be used later when reading
/proc/irq/<num>/affinity_hint. So the cpumask variable needs to be
persistent. Otherwise, we are accessing freed memory when reading
the affinity_hint file.

Also, need to clear affinity_hint before free_irq(), otherwise there
is a one-time warning and stack trace during module unloading:

[ 243.948687] WARNING: CPU: 10 PID: 1589 at kernel/irq/manage.c:1913 free_irq+0x318/0x360
...
[ 243.948753] Call Trace:
[ 243.948754] <TASK>
[ 243.948760] mana_gd_remove_irqs+0x78/0xc0 [mana]
[ 243.948767] mana_gd_remove+0x3e/0x80 [mana]
[ 243.948773] pci_device_remove+0x3d/0xb0
[ 243.948778] device_remove+0x46/0x70
[ 243.948782] device_release_driver_internal+0x1fe/0x280
[ 243.948785] driver_detach+0x4e/0xa0
[ 243.948787] bus_remove_driver+0x70/0xf0
[ 243.948789] driver_unregister+0x35/0x60
[ 243.948792] pci_unregister_driver+0x44/0x90
[ 243.948794] mana_driver_exit+0x14/0x3fe [mana]
[ 243.948800] __do_sys_delete_module.constprop.0+0x185/0x2f0

To fix the bug, use the persistent mask, cpumask_of(cpu#), and set
affinity_hint to NULL before freeing the IRQ, as required by free_irq().

Cc: stable@vger.kernel.org
Fixes: 71fa6887eeca ("net: mana: Assign interrupts to CPUs based on NUMA nodes")
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: Michael Kelley <mikelley@microsoft.com>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Link: https://lore.kernel.org/r/1675718929-19565-1-git-send-email-haiyangz@microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Haiyang Zhang and committed by
Jakub Kicinski
18a04837 b1f4fbab

+11 -26
+11 -26
drivers/net/ethernet/microsoft/mana/gdma_main.c
··· 1217 1217 unsigned int max_queues_per_port = num_online_cpus(); 1218 1218 struct gdma_context *gc = pci_get_drvdata(pdev); 1219 1219 struct gdma_irq_context *gic; 1220 - unsigned int max_irqs; 1221 - u16 *cpus; 1222 - cpumask_var_t req_mask; 1220 + unsigned int max_irqs, cpu; 1223 1221 int nvec, irq; 1224 1222 int err, i = 0, j; 1225 1223 ··· 1238 1240 goto free_irq_vector; 1239 1241 } 1240 1242 1241 - if (!zalloc_cpumask_var(&req_mask, GFP_KERNEL)) { 1242 - err = -ENOMEM; 1243 - goto free_irq; 1244 - } 1245 - 1246 - cpus = kcalloc(nvec, sizeof(*cpus), GFP_KERNEL); 1247 - if (!cpus) { 1248 - err = -ENOMEM; 1249 - goto free_mask; 1250 - } 1251 - for (i = 0; i < nvec; i++) 1252 - cpus[i] = cpumask_local_spread(i, gc->numa_node); 1253 - 1254 1243 for (i = 0; i < nvec; i++) { 1255 - cpumask_set_cpu(cpus[i], req_mask); 1256 1244 gic = &gc->irq_contexts[i]; 1257 1245 gic->handler = NULL; 1258 1246 gic->arg = NULL; ··· 1253 1269 irq = pci_irq_vector(pdev, i); 1254 1270 if (irq < 0) { 1255 1271 err = irq; 1256 - goto free_mask; 1272 + goto free_irq; 1257 1273 } 1258 1274 1259 1275 err = request_irq(irq, mana_gd_intr, 0, gic->name, gic); 1260 1276 if (err) 1261 - goto free_mask; 1262 - irq_set_affinity_and_hint(irq, req_mask); 1263 - cpumask_clear(req_mask); 1277 + goto free_irq; 1278 + 1279 + cpu = cpumask_local_spread(i, gc->numa_node); 1280 + irq_set_affinity_and_hint(irq, cpumask_of(cpu)); 1264 1281 } 1265 - free_cpumask_var(req_mask); 1266 - kfree(cpus); 1267 1282 1268 1283 err = mana_gd_alloc_res_map(nvec, &gc->msix_resource); 1269 1284 if (err) ··· 1273 1290 1274 1291 return 0; 1275 1292 1276 - free_mask: 1277 - free_cpumask_var(req_mask); 1278 - kfree(cpus); 1279 1293 free_irq: 1280 1294 for (j = i - 1; j >= 0; j--) { 1281 1295 irq = pci_irq_vector(pdev, j); 1282 1296 gic = &gc->irq_contexts[j]; 1297 + 1298 + irq_update_affinity_hint(irq, NULL); 1283 1299 free_irq(irq, gic); 1284 1300 } 1285 1301 ··· 1306 1324 continue; 1307 1325 1308 1326 gic = &gc->irq_contexts[i]; 1327 + 1328 + /* Need to clear the hint before free_irq */ 1329 + irq_update_affinity_hint(irq, NULL); 1309 1330 free_irq(irq, gic); 1310 1331 } 1311 1332