For WQ_UNBOUND workqueues, alloc_and_link_pwqs() allocates wq->cpu_pwq via alloc_percpu() and then calls apply_workqueue_attrs_locked(). On failure it returns the error directly, bypassing the enomem: label which holds the only free_percpu(wq->cpu_pwq) in this function. The caller's error path kfree()s wq without touching wq->cpu_pwq, leaking one percpu pointer table (nr_cpu_ids * sizeof(void *) bytes) per failed call. If kmemleak is enabled, we can see: unreferenced object (percpu) 0xc0fffa5b121048 (size 8): comm "insmod", pid 776, jiffies 4294682844 backtrace (crc 0): pcpu_alloc_noprof+0x665/0xac0 __alloc_workqueue+0x33f/0xa20 alloc_workqueue_noprof+0x60/0x100 Route the error through the existing enomem: cleanup and any error before this one.
Showing the changes to kernel/workqueue.c (1 of 1 file). Show all
Unified · Split
| @@ -5654,7 +5654,9 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) · 5653 unmodified lines | ||
| 5654 | 5654 | ret = apply_workqueue_attrs_locked(wq, unbound_std_wq_attrs[highpri]); |
| 5655 | 5655 | } |
| 5656 | 5656 | |
| 5657 | − return ret; | |
| 5657 | + if (ret) | |
| 5658 | + goto enomem; | |
| 5659 | + return 0; | |
| 5658 | 5660 | |
| 5659 | 5661 | enomem: |
| 5660 | 5662 | if (wq->cpu_pwq) { |