From: Albert Esteve <[email protected]>

[ 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 <[email protected]>
Reviewed-by: Maxime Ripard <[email protected]>
Signed-off-by: Albert Esteve <[email protected]>
Reviewed-by: Dmitry Baryshkov <[email protected]>
Reviewed-by: Thomas Zimmermann <[email protected]>
Link: 
https://patch.msgid.link/[email protected]
Signed-off-by: Maxime Ripard <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
---

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-
  [email protected]` (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

Reply via email to