[PATCH 1/2] pinctrl: single: don't leak group/function tables on consumer re-probe

From: A. Sverdlin

Date: Thu Oct 01 2026 - 05:04:51 EST


From: Alexander Sverdlin <alexander.sverdlin@xxxxxxxxxxx>

For every mapping pcs_parse_{one,bits}_in_pinctrl_entry() builds a
pcs_function, a value table, a pin array and a pingroup-name array and
registers them via pcs_add_function()/pinctrl_generic_add_group(). The
core deduplicates groups and functions by name and keeps the first
instance for the controller's lifetime, so the tables built for any later
mapping of the same node - e.g. each consumer re-probe - are dropped by
the core and leaked.

Free the redundant copies once the function turns out to be a duplicate;
the group is deduplicated together with it. Don't remove the deduplicated
instance from the core: its selector is a plain counter (num_groups /
num_functions), so removing any but the last entry corrupts the selector
space.

kmemleak stays silent because the leaked tables remain reachable from the
controller's devres list.

Fixes: 8b8b091bf07f ("pinctrl: Add one-register-per-pin type device tree based pinctrl driver")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@xxxxxxxxxxx>
---
drivers/pinctrl/pinctrl-single.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index e0eb7240a9859..e75e32723100d 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -1097,6 +1097,19 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
} else {
*num_maps = 1;
}
+
+ /*
+ * The core deduplicates groups/functions by name and keeps the first
+ * for the controller's lifetime; drop our now-redundant copies when the
+ * node is mapped again (consumer re-probe). The group is deduplicated
+ * together with the function, so testing the latter is enough.
+ */
+ if (pinmux_generic_get_function(pcs->pctl, fsel)->data != function) {
+ devm_kfree(pcs->dev, pins);
+ devm_kfree(pcs->dev, vals);
+ devm_kfree(pcs->dev, function);
+ devm_kfree(pcs->dev, pgnames);
+ }
mutex_unlock(&pcs->mutex);

return 0;
@@ -1235,6 +1248,14 @@ static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs,
(*map)->data.mux.function = np->name;

*num_maps = 1;
+
+ /* See pcs_parse_one_pinctrl_entry() for the deduplication rationale. */
+ if (pinmux_generic_get_function(pcs->pctl, fsel)->data != function) {
+ devm_kfree(pcs->dev, pins);
+ devm_kfree(pcs->dev, vals);
+ devm_kfree(pcs->dev, function);
+ devm_kfree(pcs->dev, pgnames);
+ }
mutex_unlock(&pcs->mutex);

return 0;
--
2.55.0