[PATCH AUTOSEL 6.18] drm/panel/tdo-tl070wsh30: Use refcounted allocation in place of devm_kzalloc()
From: Sasha Levin
Date: Mon Aug 31 2026 - 09:55:10 EST
From: Albert Esteve <aesteve@xxxxxxxxxx>
[ Upstream commit 928decbed75bf215a8d0eecc88aa2edc77023db2 ]
Move to using the new API devm_drm_panel_alloc() to allocate the
panel. In the call to the new API, avoid using explicit type and use
__typeof() for more type safety.
Reviewed-by: Neil Armstrong <neil.armstrong@xxxxxxxxxx>
Reviewed-by: Maxime Ripard <mripard@xxxxxxxxxx>
Signed-off-by: Albert Esteve <aesteve@xxxxxxxxxx>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
Reviewed-by: Thomas Zimmermann <tzimmermann@xxxxxxx>
Link: https://patch.msgid.link/20260508-drm_panel_init_rm-v2-6-0bd4ac429971@xxxxxxxxxx
Signed-off-by: Maxime Ripard <mripard@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
LLM Generated explanations, may be completely bogus:
# Stable Backport Analysis: `drm/panel/tdo-tl070wsh30: Use refcounted
allocation in place of devm_kzalloc()`
**Local tree:** `v6.18.43` (`6.18.43`) — Linux 6.18.y stable series
---
## PHASE 1: COMMIT MESSAGE FORENSICS
### Step 1.1: Parse the subject line
**Record:** `[drm/panel/tdo-tl070wsh30]` — migrate allocation — convert
from `devm_kzalloc()` + `drm_panel_init()` to `devm_drm_panel_alloc()`.
### Step 1.2: Parse all commit message tags
**Record:**
- **Reviewed-by:** Neil Armstrong, Maxime Ripard, Dmitry Baryshkov,
Thomas Zimmermann (DRM/panel maintainers/reviewers)
- **Signed-off-by:** Albert Esteve (author), Maxime Ripard (maintainer)
- **Link:** `https://patch.msgid.link/20260508-drm_panel_init_rm-v2-6-
0bd4ac429971@xxxxxxxxxx` (patch 6/10 of `drm_panel_init_rm` v2 series)
- **No** Fixes:, Reported-by:, Cc: stable, Tested-by:, Acked-by:
Notable: multiple maintainer Reviewed-by tags; part of a reviewed
10-patch series.
### Step 1.3: Analyze commit body
**Record:**
- **Bug described:** Not in the per-driver commit body itself; the
series cover letter (patch 00/10) states the old `devm_kzalloc()` +
`drm_panel_init()` pattern is unsafe.
- **Symptom:** Use-after-free when the panel device is unbound — `devm`
frees the panel context struct immediately, but the DRM device may
still reference the embedded `drm_panel` via a panel bridge.
- **Root cause (series):** Panel memory lifetime tied to `devm_kzalloc`
does not match the lifetime of DRM-side panel bridge references.
`devm_drm_panel_alloc()` wraps allocation in a `kref` scheme so memory
is freed only when the last reference is dropped.
- **Version info:** None in commit message.
### Step 1.4: Detect hidden bug fixes
**Record:** Yes — despite no "fix" in the subject, this is a **use-
after-free prevention** fix, not a cosmetic refactor. The series cover
letter explicitly documents UAF on panel device unbind. The per-driver
commit is the mechanical driver-side half of that fix.
---
## PHASE 2: DIFF ANALYSIS
### Step 2.1: Inventory the changes
**Record:**
- **Files:** `drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c` only (+7/−7
lines)
- **Functions modified:** `tdo_tl070wsh30_panel_add()`,
`tdo_tl070wsh30_panel_probe()`
- **Scope:** Single-file, surgical driver fix
### Step 2.2: Code flow change per hunk
**Hunk 1 — `tdo_tl070wsh30_panel_add()`:**
- **Before:** Explicit `drm_panel_init()` call to initialize the
embedded `drm_panel`.
- **After:** `drm_panel_init()` removed; initialization now happens
inside `devm_drm_panel_alloc()` during probe.
- **Path affected:** Normal probe path.
**Hunk 2 — `tdo_tl070wsh30_panel_probe()`:**
- **Before:** `devm_kzalloc()` allocation; `-ENOMEM` on failure.
- **After:** `devm_drm_panel_alloc()` with `__typeof(*tdo_tl070wsh30),
base, ...`; `IS_ERR()` / `PTR_ERR()` error handling.
- **Path affected:** Probe initialization path.
### Step 2.3: Bug mechanism
**Record:**
- **Category:** Use-after-free / memory lifetime bug
- **Mechanism:** `devm_kzalloc()` ties panel struct lifetime to panel
device devres release. When the panel DSI device unbinds, memory is
freed while `drmm_panel_bridge_add()` / `devm_drm_of_get_bridge()` on
the display side may still hold a `struct drm_panel *` through a panel
bridge. `devm_drm_panel_alloc()` allocates via `kzalloc()` (not
devres-backed memory), initializes `kref`, and registers a devm
cleanup action calling `drm_panel_put()`, decoupling panel memory
lifetime from naive devres free ordering.
### Step 2.4: Fix quality assessment
**Record:**
- **Obviously correct:** Yes — identical pattern already applied to 100+
panel drivers in this tree (e.g., `panel-jdi-lt070me05000.c`, `panel-
novatek-nt36672a.c`).
- **Minimal/surgical:** Yes — only allocation/init changes, no logic
changes.
- **Regression risk:** Very low — mechanical API swap using existing,
exported API.
- **Red flags:** None. No API changes, no cross-subsystem impact.
---
## PHASE 3: GIT HISTORY INVESTIGATION
### Step 3.1: Blame changed lines
**Record:** Shallow history — all lines blame to `5d324e5159d9e` (6.18
merge base). Driver has used `devm_kzalloc()` + `drm_panel_init()` since
import into this tree. The vulnerable pattern is long-standing in this
driver.
### Step 3.2: Follow Fixes: tag
**Record:** N/A — no Fixes: tag present.
### Step 3.3: File history for related changes
**Record:**
- `devm_drm_panel_alloc()` infrastructure present in
`drivers/gpu/drm/drm_panel.c` and `include/drm/drm_panel.h`.
- Bulk migration already done: **100+** panel drivers use
`devm_drm_panel_alloc`.
- **6 drivers** still use `drm_panel_init()` — exactly the set targeted
by this series:
- `panel-tdo-tl070wsh30.c` (this commit)
- `panel-visionox-g2647fb105.c`, `panel-samsung-s6e63m0.c`, `panel-
sharp-ls043t1le01.c`, `panel-truly-nt35597.c`, `panel-startek-
kd070fhfid015.c`
- This commit is **patch 6/10** of `drm_panel_init_rm` v2; patch 10/10
makes `drm_panel_init()` static but is **not required** for this
driver patch to function.
### Step 3.4: Author's other commits
**Record:** Albert Esteve authored the full 10-patch series converting
the last remaining panel drivers. Maxime Ripard (DRM maintainer) signed
off. Multiple subsystem maintainers reviewed.
### Step 3.5: Prerequisites
**Record:**
- **Required:** `devm_drm_panel_alloc()` — **present** in 6.18.43.
- **Not required:** Patch 10/10 (`drm_panel_init()` static) — this
driver patch compiles and works without it; `drm_panel_init()` remains
exported in this tree.
- **Standalone:** Yes — single-driver change, self-contained.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
### Step 4.1: Original patch discussion
**Record:**
- **Series URL:**
https://lkml.iu.edu/hypermail/linux/kernel/2605.1/00251.html (`[PATCH
v2 00/10]`)
- **This patch URL:**
https://www.spinics.net/lists/kernel/msg6193227.html (`[PATCH v2
06/10]`)
- **Series revisions:** v1 → v2 (v2 removed kdoc precedence mentions)
- **Key feedback:** Series cover letter documents UAF; v2 is latest
revision.
- **Stable nominations:** None found in thread excerpts.
- **NAKs/concerns:** None found.
### Step 4.2: Reviewers
**Record:** CC'd to dri-devel, linux-kernel. To: Neil Armstrong, Maxime
Ripard, Thomas Zimmermann, David Airlie, Maarten Lankhorst, and other
DRM maintainers. Reviewed-by from Neil Armstrong and Maxime Ripard on
this specific patch.
### Step 4.3: Bug report
**Record:** No syzbot/KASAN report. Bug identified through API lifetime
analysis in the series cover letter, not a specific crash report.
Severity is still real (UAF on unbind).
### Step 4.4: Related patches
**Record:** Part of 10-patch series; each driver patch is independent.
Other patches in series target the other 5 remaining `drm_panel_init()`
callers. `panel-ilitek-ili9806e` was already converted in this tree via
earlier work.
### Step 4.5: Stable mailing list
**Record:** No stable-specific discussion found (not searched
exhaustively on lore stable list; no evidence against backport).
---
## PHASE 5: CODE SEMANTIC ANALYSIS
### Step 5.1: Key functions
**Record:** `tdo_tl070wsh30_panel_probe()`,
`tdo_tl070wsh30_panel_add()`, `tdo_tl070wsh30_panel_remove()`
### Step 5.2: Callers
**Record:**
- `tdo_tl070wsh30_panel_probe()` — MIPI DSI core during device probe
(`module_mipi_dsi_driver`)
- `tdo_tl070wsh30_panel_add()` — called from probe
- Panel registered globally via `drm_panel_add()`; discovered by display
drivers via `of_drm_find_panel()` / `drm_of_find_panel_or_bridge()` →
`drmm_panel_bridge_add()` / `devm_drm_of_get_bridge()`
### Step 5.3: Callees
**Record:** `devm_drm_panel_alloc()` → `kzalloc()`, `kref_init()`,
`devm_add_action_or_reset(drm_panel_put_void)`, `drm_panel_init()`.
Probe also calls `devm_regulator_get()`, `devm_gpiod_get()`,
`drm_panel_of_backlight()`, `drm_panel_add()`, `mipi_dsi_attach()`.
### Step 5.4: Call chain / reachability
**Record:**
```
Device probe → mipi_dsi_driver.probe → devm_drm_panel_alloc →
drm_panel_add
Display probe → drm_of_find_panel_or_bridge → drmm_panel_bridge_add
(stores panel pointer)
Panel unbind → devm cleanup → [UAF if old pattern, fixed with refcounted
alloc]
```
**Userspace reachable:** Yes — via device hot-unplug, module unload, or
driver rebinding on embedded systems using this panel.
### Step 5.5: Similar patterns
**Record:** Same fix pattern applied to 100+ sibling panel drivers in
this tree. Six drivers (including this one) are the remaining unmigrated
instances targeted by the series.
---
## PHASE 6: CROSS-REFERENCING AGAINST LOCAL TREE
### Step 6.1: Does buggy code exist?
**Record:** **Yes.** `panel-tdo-tl070wsh30.c` at lines 165–166 and
186–189 still uses `drm_panel_init()` + `devm_kzalloc()`.
`CONFIG_DRM_PANEL_TDO_TL070WSH30` is present in Kconfig.
### Step 6.2: Backport complications
**Record:** **Clean apply expected.** Current file content matches the
patch base (`index 227f97f9b136f`). Diff is identical to published
v2-6/10 on spinics. No conflicting changes in this file.
### Step 6.3: Related fixes already present?
**Record:** Infrastructure fix (`devm_drm_panel_alloc`) and bulk driver
migration already in 6.18.43. This specific driver conversion is **not**
yet applied. No alternate fix for this driver found.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: Subsystem criticality
**Record:** `drivers/gpu/drm/panel/` — **IMPORTANT** (display
subsystem). Affects embedded platforms using the TDO TL070WSH30 1024×600
DSI panel (`compatible = "tdo,tl070wsh30"`).
### Step 7.2: Subsystem activity
**Record:** Actively maintained. Recent 6.18.y commits include multiple
`drm/panel` fixes. Panel refcount infrastructure recently landed and
bulk-converted.
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: Who is affected
**Record:** **Driver-specific / platform-specific** — systems with
`CONFIG_DRM_PANEL_TDO_TL070WSH30` enabled and the TDO TL070WSH30 panel
connected via MIPI DSI. Not universal, but real hardware (listed in
`panel-simple-dsi.yaml` compatible list).
### Step 8.2: Trigger conditions
**Record:**
- Panel DSI device unbinds (module unload, device removal, driver
unbind) while DRM display driver still holds a panel bridge reference
- Requires display + panel driver interaction via
`drm_of_find_panel_or_bridge()` path
- **Unprivileged direct trigger:** No (requires device/module management
capability)
- **Likelihood:** Low-to-moderate on embedded systems with hotplug or
driver reload; not every boot
### Step 8.3: Failure mode severity
**Record:** **Use-after-free** → kernel oops/panic when DRM accesses
freed panel memory through panel bridge. **Severity: HIGH** (crash,
potential security implications from UAF).
### Step 8.4: Risk-benefit
**Record:**
- **Benefit:** HIGH for affected hardware — prevents UAF crash on panel
unbind
- **Risk:** VERY LOW — 7-line mechanical change, pattern proven across
100+ drivers, multiple maintainer reviews
- **Ratio:** Strong benefit, minimal risk
---
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: Evidence summary
**FOR backport:**
- Fixes documented UAF on panel device unbind (series cover letter)
- Same pattern already applied to 100+ panel drivers in 6.18.43
- Prerequisite API (`devm_drm_panel_alloc`) exists in this tree
- Buggy code still present in this tree (6 of 6 remaining
`drm_panel_init()` users)
- Small, surgical, reviewed by Neil Armstrong, Maxime Ripard, Dmitry
Baryshkov, Thomas Zimmermann
- UAF → crash is stable-worthy
**AGAINST backport:**
- Per-driver commit message doesn't explicitly say "fix UAF" (rationale
is in series cover)
- No syzbot/reporter crash report
- Affects niche hardware only
- Part of a series (though this patch is standalone-applicable)
**Unresolved:**
- `drm_panel_get()` has no external callers in this tree; exact UAF
prevention mechanism relies on devm action ordering and kref-managed
lifetime rather than explicit `drm_panel_get()` from bridge code.
Maintainers accepted this across the subsystem.
### Step 9.2: Stable rules checklist
1. Obviously correct and tested? **PASS** — mechanical API migration,
multiple Reviewed-by from maintainers, proven pattern in 100+ sibling
drivers.
2. Fixes a real bug? **PASS** — UAF on panel unbind documented in
series.
3. Important issue? **PASS** — UAF/crash, severity HIGH.
4. Small and contained? **PASS** — 1 file, ~14 lines changed.
5. No new features/APIs? **PASS** — uses existing
`devm_drm_panel_alloc()`.
6. Can apply to local tree? **PASS** — API present, buggy code present,
clean apply expected.
### Step 9.3: Exception categories
**Record:** N/A — not a device ID, quirk, DT, build, or docs fix. This
is a genuine memory-safety bug fix.
### Step 9.4: Decision rationale
This commit completes the refcounted-allocation migration for one of the
last six panel drivers still using the unsafe `devm_kzalloc()` +
`drm_panel_init()` pattern in Linux 6.18.43. The prerequisite
infrastructure is already in this stable tree, the vulnerable code is
present, and the fix matches a pattern already applied across the vast
majority of panel drivers. The series documents a real use-after-free
when the panel device unbinds while the DRM subsystem retains a panel
bridge reference. The change is minimal, maintainer-reviewed, and low-
risk.
---
## What Problem This Solves
The TDO TL070WSH30 panel driver allocates its context structure with
`devm_kzalloc()`, which frees memory immediately when the panel DSI
device unbinds. Display drivers that wrap the panel in a `panel_bridge`
(via `drmm_panel_bridge_add()` / `devm_drm_of_get_bridge()`) can retain
a pointer to the embedded `drm_panel` after that free, causing a use-
after-free and potential kernel crash on subsequent DRM access.
Switching to `devm_drm_panel_alloc()` ties panel memory lifetime to a
`kref` with a devm-managed `drm_panel_put()` cleanup action, matching
the allocation model used by the rest of the panel subsystem in 6.18.y.
---
## Verification
- [Phase 1] Parsed subject, tags, body; identified series context from
Link tag
- [Phase 1] Fetched series cover letter at lkml.iu.edu — confirmed UAF
rationale
- [Phase 1] Fetched patch 6/10 at spinics.net — confirmed diff matches
provided commit
- [Phase 2] Read full `panel-tdo-tl070wsh30.c` — confirmed old pattern
at lines 165–166, 186–189
- [Phase 2] Read `__devm_drm_panel_alloc()` in `drm_panel.c` — confirmed
kref + devm put action
- [Phase 3] `git describe HEAD` → `v6.18.43`; `make kernelversion` →
`6.18.43`
- [Phase 3] Counted drivers: 6 still use `drm_panel_init()`, 100+ use
`devm_drm_panel_alloc`
- [Phase 3] Verified `panel-jdi-lt070me05000.c` as example of already-
converted sibling driver
- [Phase 4] Series discussion at
https://lkml.iu.edu/hypermail/linux/kernel/2605.1/00251.html
- [Phase 4] Patch discussion at
https://www.spinics.net/lists/kernel/msg6193227.html
- [Phase 4] b4 dig: could not run — commit not present in local tree (no
commit hash available)
- [Phase 5] Traced `drmm_panel_bridge_add()` /
`devm_drm_of_get_bridge()` in `bridge/panel.c` — panel pointer stored
in bridge
- [Phase 5] Confirmed `drm_panel_get()` has no external callers in this
tree (only definition in `drm_panel.c`)
- [Phase 6] Confirmed `devm_drm_panel_alloc` in
`include/drm/drm_panel.h` and `drm_panel.c`
- [Phase 6] Confirmed `CONFIG_DRM_PANEL_TDO_TL070WSH30` in Kconfig
- [Phase 6] Confirmed `compatible = "tdo,tl070wsh30"` in DT bindings
- [Phase 8] Assessed UAF → crash as HIGH severity for affected platforms
**YES**The background git history lookups finished. Both searches (`-S
"allocated by devm_drm_panel_alloc"` and `-S "kref_init"` / `-S
"__devm_drm_panel_alloc"`) only hit the shallow tree base
(`5d324e5159d9e`, the 6.18-rc8 merge), so they didn’t add finer-grained
history beyond what we already verified in code.
That’s consistent with this checkout: `devm_drm_panel_alloc()` and the
panel refcount machinery are already in 6.18.43, while `panel-tdo-
tl070wsh30.c` is still one of six drivers on the old `devm_kzalloc()` +
`drm_panel_init()` path. The backport recommendation remains **YES**.
drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
index 227f97f9b136f..13cfe252a838d 100644
--- a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
+++ b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c
@@ -162,9 +162,6 @@ static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30)
return err;
}
- drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev,
- &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI);
-
err = drm_panel_of_backlight(&tdo_tl070wsh30->base);
if (err)
return err;
@@ -183,10 +180,13 @@ static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi)
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
- tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30),
- GFP_KERNEL);
- if (!tdo_tl070wsh30)
- return -ENOMEM;
+ tdo_tl070wsh30 = devm_drm_panel_alloc(&dsi->dev,
+ __typeof(*tdo_tl070wsh30), base,
+ &tdo_tl070wsh30_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ if (IS_ERR(tdo_tl070wsh30))
+ return PTR_ERR(tdo_tl070wsh30);
mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30);
tdo_tl070wsh30->link = dsi;
--
2.53.0