| Message ID | 20260815181204.2321-6-dragonliu2018@gmail.com |
|---|---|
| State | New |
| Headers | show |
| Series | i2c: fix device_node refcount leaks in 5 bus drivers | expand |
On 8/15/26 21:12, Liu Zhenlong wrote: > i2c_pnx_probe() calls of_node_get() to take an extra reference on the > platform device's of_node when assigning it to the adapter device, but > none of the probe error paths nor i2c_pnx_remove() drops it. > > device_release() does not call of_node_put() and i2c_adapter_dev_release() > only completes a struct, so the extra reference is never released, leaking > the device_node on every probe failure and every adapter removal. > > Add the matching of_node_put() to all probe error paths (the three early > return paths before clk_prepare_enable(), and the out_clock label which > covers the wait_reset(), platform_get_irq(), devm_request_irq() and > i2c_add_numbered_adapter() failure paths; dev is devm_kzalloc'ed so > adapter.dev.of_node is NULL and of_node_put() is a no-op when CONFIG_OF > is not set) and to i2c_pnx_remove(). > > In i2c_pnx_remove(), i2c_del_adapter() clears adap->dev with memset() > at the end (commit bd4bc3dbded9 ("i2c: Clear i2c_adapter.dev on adapter > removal")), which zeroes adap->dev.of_node before of_node_put() runs. > Cache the pointer before calling i2c_del_adapter(), the same approach > used in i2c-mux (i2c_mux_del_adapters) and mtd (commit 56570bdad5e3 > ("mtd: core: Fix refcount error in del_mtd_device()")). > > Compile-tested with gcc on arm64 defconfig using COMPILE_TEST; no > hardware available for runtime testing. > > Fixes: b41a216dafe4 ("i2c: Add device tree support to i2c-pnx.c") > Cc: stable@vger.kernel.org > Assisted-by: Claude:claude-opus-5 > Signed-off-by: Liu Zhenlong <dragonliu2018@gmail.com> Reviewed-by: Vladimir Zapolskiy <vz@kernel.org>
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index 8daa0008bd05..0b6c144d9a21 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -644,20 +644,26 @@ static int i2c_pnx_probe(struct platform_device *pdev) } #endif alg_data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(alg_data->clk)) + if (IS_ERR(alg_data->clk)) { + of_node_put(alg_data->adapter.dev.of_node); return PTR_ERR(alg_data->clk); + } snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name), "%s", pdev->name); /* Register I/O resource */ alg_data->ioaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(alg_data->ioaddr)) + if (IS_ERR(alg_data->ioaddr)) { + of_node_put(alg_data->adapter.dev.of_node); return PTR_ERR(alg_data->ioaddr); + } ret = clk_prepare_enable(alg_data->clk); - if (ret) + if (ret) { + of_node_put(alg_data->adapter.dev.of_node); return ret; + } freq = clk_get_rate(alg_data->clk); @@ -707,14 +713,17 @@ static int i2c_pnx_probe(struct platform_device *pdev) out_clock: clk_disable_unprepare(alg_data->clk); + of_node_put(alg_data->adapter.dev.of_node); return ret; } static void i2c_pnx_remove(struct platform_device *pdev) { struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); + struct device_node *node = alg_data->adapter.dev.of_node; i2c_del_adapter(&alg_data->adapter); + of_node_put(node); clk_disable_unprepare(alg_data->clk); }
i2c_pnx_probe() calls of_node_get() to take an extra reference on the platform device's of_node when assigning it to the adapter device, but none of the probe error paths nor i2c_pnx_remove() drops it. device_release() does not call of_node_put() and i2c_adapter_dev_release() only completes a struct, so the extra reference is never released, leaking the device_node on every probe failure and every adapter removal. Add the matching of_node_put() to all probe error paths (the three early return paths before clk_prepare_enable(), and the out_clock label which covers the wait_reset(), platform_get_irq(), devm_request_irq() and i2c_add_numbered_adapter() failure paths; dev is devm_kzalloc'ed so adapter.dev.of_node is NULL and of_node_put() is a no-op when CONFIG_OF is not set) and to i2c_pnx_remove(). In i2c_pnx_remove(), i2c_del_adapter() clears adap->dev with memset() at the end (commit bd4bc3dbded9 ("i2c: Clear i2c_adapter.dev on adapter removal")), which zeroes adap->dev.of_node before of_node_put() runs. Cache the pointer before calling i2c_del_adapter(), the same approach used in i2c-mux (i2c_mux_del_adapters) and mtd (commit 56570bdad5e3 ("mtd: core: Fix refcount error in del_mtd_device()")). Compile-tested with gcc on arm64 defconfig using COMPILE_TEST; no hardware available for runtime testing. Fixes: b41a216dafe4 ("i2c: Add device tree support to i2c-pnx.c") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Liu Zhenlong <dragonliu2018@gmail.com> --- drivers/i2c/busses/i2c-pnx.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)