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

Configure Feed

Select the types of activity you want to include in your feed.

of/irq: Fix device node refcount leakage in API of_irq_parse_one()

of_irq_parse_one(@int_gen_dev, i, ...) will leak refcount of @i_th_phandle

int_gen_dev {
...
interrupts-extended = ..., <&i_th_phandle ...>, ...;
...
};

Refcount of @i_th_phandle is increased by of_parse_phandle_with_args()
but is not decreased by API of_irq_parse_one() before return, so causes
refcount leakage.

Rework the refcounting to use __free() cleanup and simplify the code to
have a single call to of_irq_parse_raw().

Also add comments about refcount of node @out_irq->np got by the API.

Fixes: 79d9701559a9 ("of/irq: create interrupts-extended property")
Cc: stable@vger.kernel.org
Signed-off-by: Zijun Hu <quic_zijuhu@quicinc.com>
Link: https://lore.kernel.org/r/20250209-of_irq_fix-v2-2-93e3a2659aa7@quicinc.com
[robh: Use __free() to do puts]
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>

authored by

Zijun Hu and committed by
Rob Herring (Arm)
0cb58d6c 65f4be07

+26 -31
+26 -31
drivers/of/irq.c
··· 16 16 17 17 #define pr_fmt(fmt) "OF: " fmt 18 18 19 + #include <linux/cleanup.h> 19 20 #include <linux/device.h> 20 21 #include <linux/errno.h> 21 22 #include <linux/list.h> ··· 340 339 * This function resolves an interrupt for a node by walking the interrupt tree, 341 340 * finding which interrupt controller node it is attached to, and returning the 342 341 * interrupt specifier that can be used to retrieve a Linux IRQ number. 342 + * 343 + * Note: refcount of node @out_irq->np is increased by 1 on success. 343 344 */ 344 345 int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) 345 346 { 346 - struct device_node *p; 347 + struct device_node __free(device_node) *p = NULL; 347 348 const __be32 *addr; 348 349 u32 intsize; 349 350 int i, res, addr_len; ··· 370 367 /* Try the new-style interrupts-extended first */ 371 368 res = of_parse_phandle_with_args(device, "interrupts-extended", 372 369 "#interrupt-cells", index, out_irq); 373 - if (!res) 374 - return of_irq_parse_raw(addr_buf, out_irq); 370 + if (!res) { 371 + p = out_irq->np; 372 + } else { 373 + /* Look for the interrupt parent. */ 374 + p = of_irq_find_parent(device); 375 + /* Get size of interrupt specifier */ 376 + if (!p || of_property_read_u32(p, "#interrupt-cells", &intsize)) 377 + return -EINVAL; 375 378 376 - /* Look for the interrupt parent. */ 377 - p = of_irq_find_parent(device); 378 - if (p == NULL) 379 - return -EINVAL; 379 + pr_debug(" parent=%pOF, intsize=%d\n", p, intsize); 380 380 381 - /* Get size of interrupt specifier */ 382 - if (of_property_read_u32(p, "#interrupt-cells", &intsize)) { 383 - res = -EINVAL; 384 - goto out; 381 + /* Copy intspec into irq structure */ 382 + out_irq->np = p; 383 + out_irq->args_count = intsize; 384 + for (i = 0; i < intsize; i++) { 385 + res = of_property_read_u32_index(device, "interrupts", 386 + (index * intsize) + i, 387 + out_irq->args + i); 388 + if (res) 389 + return res; 390 + } 391 + 392 + pr_debug(" intspec=%d\n", *out_irq->args); 385 393 } 386 - 387 - pr_debug(" parent=%pOF, intsize=%d\n", p, intsize); 388 - 389 - /* Copy intspec into irq structure */ 390 - out_irq->np = p; 391 - out_irq->args_count = intsize; 392 - for (i = 0; i < intsize; i++) { 393 - res = of_property_read_u32_index(device, "interrupts", 394 - (index * intsize) + i, 395 - out_irq->args + i); 396 - if (res) 397 - goto out; 398 - } 399 - 400 - pr_debug(" intspec=%d\n", *out_irq->args); 401 - 402 394 403 395 /* Check if there are any interrupt-map translations to process */ 404 - res = of_irq_parse_raw(addr_buf, out_irq); 405 - out: 406 - of_node_put(p); 407 - return res; 396 + return of_irq_parse_raw(addr_buf, out_irq); 408 397 } 409 398 EXPORT_SYMBOL_GPL(of_irq_parse_one); 410 399