[PATCH AUTOSEL 6.19-6.18] pinctrl: mediatek: make devm allocations safer and clearer in mtk_eint_do_init()

Sasha Levin
2026-02-19T02:04:49Z

From: Liang Jie <liangjie@lixiang.com>

[ Upstream commit 255b721c96046d4c57fa2268e4c72607868ce91f ]

mtk_eint_do_init() allocates several pointer arrays which are then
populated in a per-instance loop and freed on error. The arrays are
currently allocated with devm_kmalloc(), so their entries are left
uninitialised until the per-instance allocations succeed.

On a failure in the middle of the loop, the error path iterates over
the full nbase range and calls devm_kfree() on each element. For
indices which were never initialised, the corresponding array entries
contain stack garbage. If any of those happen to be non-zero,
devm_kfree() will pass them to devres_destroy(), which will WARN
because there is no matching devm_kmalloc() resource for such bogus
pointers.

Improve the robustness and readability by:

- Using devm_kcalloc() for the pointer arrays so that all entries
start as NULL, ensuring that only genuinely initialised elements
may be freed and preventing spurious WARN_ON()s in the error path.
- Switching the allocations to sizeof(*ptr) / sizeof(**ptr) forms,
avoiding hard-coded element types and making the code more resilient
to future type changes.
- Dropping the redundant NULL checks before devm_kfree(), as
devm_kfree() safely handles NULL pointers.

The functional behaviour in the successful initialisation path remains
unchanged, while the error handling becomes simpler and less
error-prone.

Reviewed-by: fanggeng <fanggeng@lixiang.com>
Signed-off-by: Liang Jie <liangjie@lixiang.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

So the buggy code is NOT in 6.12 or older LTS trees (as expected, since
it was added in 6.15). This fix is only relevant for 6.15+ stable trees.

### 7. Verification Summary

**Verification:**
- Read the current file (`mtk-eint.c` lines 520-622) — confirmed the
buggy `devm_kmalloc()` calls at lines 547, 551, 555 and the error path
at lines 598-606 that iterates over uninitialized entries.
- `git log` confirmed `3ef9f710efcb5` ("Add EINT support for multiple
addresses") introduced the multi-base code with `nbase`, the loop, and
the error path — landed in v6.15.
- `git tag --contains 3ef9f710efcb5` confirmed it's in `p-6.15`,
`p-6.16`, `p-6.17`.
- `git log --oneline v6.18.12` confirmed the buggy commit is present in
the 6.18.y stable tree.
- `git log --oneline v6.12` confirmed the buggy code is NOT in 6.12 or
older LTS trees (not applicable for those).
- Verified the error path: loop at line 599 iterates `0..nbase-1`, but
if allocation fails at index `k`, indices `k+1..nbase-1` have
uninitialized pointer entries — confirmed in pre-patch code.
- `devm_kfree(NULL)` is safe (no-op) — confirmed by kernel API
convention.
- The patch is self-contained; no other patches are needed as
dependencies.

### 8. Conclusion

This commit fixes a real bug: **use of uninitialized memory in the error
path** of `mtk_eint_do_init()`. When memory allocation fails partway
through a loop, the cleanup code passes uninitialized (garbage) pointer
values to `devm_kfree()`, which can trigger `WARN_ON()` in
`devres_destroy()` and potentially cause memory corruption.

The fix is small, surgical (3 `devm_kmalloc` → `devm_kcalloc` changes +
removal of now-redundant NULL checks), well-understood, reviewed by a
maintainer, and carries minimal risk. It only affects the error path —
the successful path is functionally unchanged. The buggy code exists in
stable trees 6.15+.

**YES**

drivers/pinctrl/mediatek/mtk-eint.c | 29 +++++++++++++++++------------
1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c
index c8c5097c11c4d..2a3c04eedc5f3 100644
--- a/drivers/pinctrl/mediatek/mtk-eint.c
+++ b/drivers/pinctrl/mediatek/mtk-eint.c
@@ -544,24 +544,32 @@ int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)
}
}

- eint->pin_list = devm_kmalloc(eint->dev, eint->nbase * sizeof(u16 *), GFP_KERNEL);
+ eint->pin_list = devm_kcalloc(eint->dev, eint->nbase,
+ sizeof(*eint->pin_list), GFP_KERNEL);
if (!eint->pin_list)
goto err_pin_list;

- eint->wake_mask = devm_kmalloc(eint->dev, eint->nbase * sizeof(u32 *), GFP_KERNEL);
+ eint->wake_mask = devm_kcalloc(eint->dev, eint->nbase,
+ sizeof(*eint->wake_mask), GFP_KERNEL);
if (!eint->wake_mask)
goto err_wake_mask;

- eint->cur_mask = devm_kmalloc(eint->dev, eint->nbase * sizeof(u32 *), GFP_KERNEL);
+ eint->cur_mask = devm_kcalloc(eint->dev, eint->nbase,
+ sizeof(*eint->cur_mask), GFP_KERNEL);
if (!eint->cur_mask)
goto err_cur_mask;

for (i = 0; i < eint->nbase; i++) {
- eint->pin_list[i] = devm_kzalloc(eint->dev, eint->base_pin_num[i] * sizeof(u16),
+ eint->pin_list[i] = devm_kzalloc(eint->dev,
+ eint->base_pin_num[i] * sizeof(**eint->pin_list),
GFP_KERNEL);
port = DIV_ROUND_UP(eint->base_pin_num[i], 32);
- eint->wake_mask[i] = devm_kzalloc(eint->dev, port * sizeof(u32), GFP_KERNEL);
- eint->cur_mask[i] = devm_kzalloc(eint->dev, port * sizeof(u32), GFP_KERNEL);
+ eint->wake_mask[i] = devm_kzalloc(eint->dev,
+ port * sizeof(**eint->wake_mask),
+ GFP_KERNEL);
+ eint->cur_mask[i] = devm_kzalloc(eint->dev,
+ port * sizeof(**eint->cur_mask),
+ GFP_KERNEL);
if (!eint->pin_list[i] || !eint->wake_mask[i] || !eint->cur_mask[i])
goto err_eint;
}
@@ -597,12 +605,9 @@ int mtk_eint_do_init(struct mtk_eint *eint, struct mtk_eint_pin *eint_pin)

err_eint:
for (i = 0; i < eint->nbase; i++) {
- if (eint->cur_mask[i])
- devm_kfree(eint->dev, eint->cur_mask[i]);
- if (eint->wake_mask[i])
- devm_kfree(eint->dev, eint->wake_mask[i]);
- if (eint->pin_list[i])
- devm_kfree(eint->dev, eint->pin_list[i]);
+ devm_kfree(eint->dev, eint->cur_mask[i]);
+ devm_kfree(eint->dev, eint->wake_mask[i]);
+ devm_kfree(eint->dev, eint->pin_list[i]);
}
devm_kfree(eint->dev, eint->cur_mask);
err_cur_mask:
--
2.51.0