devm_alloc_workqueue() built a va_list and passed it as a single positional argument to the variadic alloc_workqueue() macro: va_start(args, max_active); wq = alloc_workqueue(fmt, flags, max_active, args); va_end(args); C does not allow forwarding a va_list through a ... parameter. alloc_workqueue() expands to alloc_workqueue_noprof(), which runs its own va_start() over its ... params, so the inner vsnprintf(wq->name, sizeof(wq->name), fmt, args) in __alloc_workqueue() received the outer va_list object as the first variadic slot rather than the caller's actual format arguments. Add a new static helper alloc_workqueue_va() that wraps __alloc_workqueue() and runs wq_init_lockdep() on success, and fold both alloc_workqueue_noprof() and devm_alloc_workqueue_noprof() onto it as suggested by Tejun. The wq_init_lockdep() step is required on the devm path too, otherwise __flush_workqueue()'s on-stack COMPLETION_INITIALIZER_ONSTACK_MAP would NULL-deref wq->lockdep_map. No caller changes are required. devm_alloc_ordered_workqueue() is a macro forwarding to devm_alloc_workqueue() and inherits the fix. Two in-tree callers actively trigger the broken path on every probe: drivers/power/supply/mt6370-charger.c:889 drivers/power/supply/max77705_charger.c:649 both of which use devm_alloc_ordered_workqueue(dev, "%s", 0, dev_name(dev)). A standalone reproducer module is available at[1].
Unified · Split
| @@ -534,8 +534,10 @@ alloc_workqueue_noprof(const char *fmt, unsigned int flags, int max_active, ...) · 533 unmodified lines | |||
| 534 | * Pointer to the allocated workqueue on success, %NULL on failure. | 534 | * Pointer to the allocated workqueue on success, %NULL on failure. |
| 535 | */ | 535 | */ |
| 536 | __printf(2, 5) struct workqueue_struct * | 536 | __printf(2, 5) struct workqueue_struct * |
| 537 | devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags, | 537 | devm_alloc_workqueue_noprof(struct device *dev, const char *fmt, |
| 538 | int max_active, ...); | 538 | unsigned int flags, int max_active, ...); |
| 539 | #define devm_alloc_workqueue(...) \ | ||
| 540 | alloc_hooks(devm_alloc_workqueue_noprof(__VA_ARGS__)) | ||
| 539 | 541 | ||
| 540 | #ifdef CONFIG_LOCKDEP | 542 | #ifdef CONFIG_LOCKDEP |
| 541 | /** | 543 | /** |
| @@ -5906,6 +5906,20 @@ err_destroy: · 5905 unmodified lines | |||
| 5906 | return NULL; | 5906 | return NULL; |
| 5907 | } | 5907 | } |
| 5908 | 5908 | ||
| 5909 | static struct workqueue_struct *alloc_workqueue_va(const char *fmt, | ||
| 5910 | unsigned int flags, | ||
| 5911 | int max_active, | ||
| 5912 | va_list args) | ||
| 5913 | { | ||
| 5914 | struct workqueue_struct *wq; | ||
| 5915 | |||
| 5916 | wq = __alloc_workqueue(fmt, flags, max_active, args); | ||
| 5917 | if (wq) | ||
| 5918 | wq_init_lockdep(wq); | ||
| 5919 | |||
| 5920 | return wq; | ||
| 5921 | } | ||
| 5922 | |||
| 5909 | __printf(1, 4) | 5923 | __printf(1, 4) |
| 5910 | struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, | 5924 | struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, |
| 5911 | unsigned int flags, | 5925 | unsigned int flags, |
| @@ -5915,12 +5929,8 @@ struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, · 3 unmodified lines | |||
| 5915 | va_list args; | 5929 | va_list args; |
| 5916 | 5930 | ||
| 5917 | va_start(args, max_active); | 5931 | va_start(args, max_active); |
| 5918 | wq = __alloc_workqueue(fmt, flags, max_active, args); | 5932 | wq = alloc_workqueue_va(fmt, flags, max_active, args); |
| 5919 | va_end(args); | 5933 | va_end(args); |
| 5920 | if (!wq) | ||
| 5921 | return NULL; | ||
| 5922 | |||
| 5923 | wq_init_lockdep(wq); | ||
| 5924 | 5934 | ||
| 5925 | return wq; | 5935 | return wq; |
| 5926 | } | 5936 | } |
| @@ -5932,15 +5942,15 @@ static void devm_workqueue_release(void *res) · 5 unmodified lines | |||
| 5932 | } | 5942 | } |
| 5933 | 5943 | ||
| 5934 | __printf(2, 5) struct workqueue_struct * | 5944 | __printf(2, 5) struct workqueue_struct * |
| 5935 | devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags, | 5945 | devm_alloc_workqueue_noprof(struct device *dev, const char *fmt, |
| 5936 | int max_active, ...) | 5946 | unsigned int flags, int max_active, ...) |
| 5937 | { | 5947 | { |
| 5938 | struct workqueue_struct *wq; | 5948 | struct workqueue_struct *wq; |
| 5939 | va_list args; | 5949 | va_list args; |
| 5940 | int ret; | 5950 | int ret; |
| 5941 | 5951 | ||
| 5942 | va_start(args, max_active); | 5952 | va_start(args, max_active); |
| 5943 | wq = alloc_workqueue(fmt, flags, max_active, args); | 5953 | wq = alloc_workqueue_va(fmt, flags, max_active, args); |
| 5944 | va_end(args); | 5954 | va_end(args); |
| 5945 | if (!wq) | 5955 | if (!wq) |
| 5946 | return NULL; | 5956 | return NULL; |
| @@ -5951,7 +5961,7 @@ devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags, · 4 unmodified lines | |||
| 5951 | 5961 | ||
| 5952 | return wq; | 5962 | return wq; |
| 5953 | } | 5963 | } |
| 5954 | EXPORT_SYMBOL_GPL(devm_alloc_workqueue); | 5964 | EXPORT_SYMBOL_GPL(devm_alloc_workqueue_noprof); |
| 5955 | 5965 | ||
| 5956 | #ifdef CONFIG_LOCKDEP | 5966 | #ifdef CONFIG_LOCKDEP |
| 5957 | __printf(1, 5) | 5967 | __printf(1, 5) |