[PATCH AUTOSEL 6.18-5.10] configfs_depend_prep(): pass configfs_dirent instead of dentry

From: Sasha Levin

Date: Mon Aug 31 2026 - 10:37:43 EST


From: Al Viro <viro@xxxxxxxxxxxxxxxxxx>

[ Upstream commit 764682e0118432260191d194edbdaff208260483 ]

Again, the only thing it uses dentry for is dentry->d_fsdata; for the
recursive call the situation is the same as with configfs_detach_prep()
and the same observation about ->s_dentry->d_fsdata applies.

Reviewed-by: Jan Kara <jack@xxxxxxx>
Reviewed-by: Breno Leitao <leitao@xxxxxxxxxx>
Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

## Phase 1: Commit Message Forensics

**Step 1.1 — Subject line**
- Record: `[configfs] [refactor/correct] configfs_depend_prep(): pass
configfs_dirent instead of dentry`

**Step 1.2 — Tags**
- Record: `Reviewed-by: Jan Kara <jack@xxxxxxx>`, `Reviewed-by: Breno
Leitao <leitao@xxxxxxxxxx>`, `Signed-off-by: Al Viro
<viro@xxxxxxxxxxxxxxxxxx>`. No `Fixes:`, `Reported-by:`, `Link:`, `Cc:
stable`, or `Tested-by:` tags. Notable: two subsystem reviewers, no
fuzzer/user report.

**Step 1.3 — Body**
- Record: The commit explains that `configfs_depend_prep()` only used
its dentry argument to reach `dentry->d_fsdata`, which is the
`configfs_dirent` itself. The recursive call should pass `child_sd`
directly, mirroring the same observation already made for
`configfs_detach_prep()` about `->s_dentry->d_fsdata`. No explicit
crash report, but the implication is that going through `s_dentry` is
unsafe/redundant during tree traversal.

**Step 1.4 — Hidden bug fix?**
- Record: **Yes.** Although framed as a signature cleanup, it removes
dereferences of `child_sd->s_dentry` during a recursive tree walk. In
the current tree, `configfs_readdir()` already treats `s_dentry` as
potentially NULL, while `configfs_depend_prep()` does
`BUG_ON(!origin)` on that same pointer. This is a latent NULL-deref /
kernel-BUG fix, not mere style.

---

## Phase 2: Diff Analysis

**Step 2.1 — Inventory**
- Record: 1 file changed (`fs/configfs/dir.c`), 4 insertions / 8
deletions. Functions modified: `configfs_depend_prep()`,
`configfs_do_depend_item()`. Scope: single-file surgical change.

**Step 2.2 — Code flow**
- Record:
- **Hunk 1 (`configfs_depend_prep`)**: Before — take `struct dentry
*origin`, `BUG_ON(!origin || !origin->d_fsdata)`, set `sd =
origin->d_fsdata`, recurse via `child_sd->s_dentry`. After — take
`struct configfs_dirent *sd` directly, recurse via `child_sd`.
Removes dentry indirection on the hot recursive path.
- **Hunk 2 (`configfs_do_depend_item`)**: Before —
`configfs_depend_prep(subsys_dentry, target)`. After —
`configfs_depend_prep(subsys_dentry->d_fsdata, target)`. Top-level
caller still has a valid pinned subsystem dentry.

**Step 2.3 — Bug mechanism**
- Record: **Memory safety / NULL dereference fix.** Category (d). Old
recursive path: `configfs_depend_prep(child_sd->s_dentry, target)`
with `BUG_ON(!origin)`. If `child_sd->s_dentry` is NULL (dentry
evicted via `configfs_d_iput()` after `DCACHE_DONTCACHE`, or cleared
on lookup failure), the kernel hits `BUG()`. The fix uses the already-
available `configfs_dirent` pointer, which is what the function
actually needs.

**Step 2.4 — Fix quality**
- Record: Obviously correct — dentry was only an alias for `d_fsdata`.
Minimal diff, no API changes, no new locking. Regression risk very
low; it aligns `configfs_depend_prep()` with the pattern already used
in the backported lockless-traversal series.

---

## Phase 3: Git History Investigation

**Step 3.1 — Blame**
- Record: `configfs_depend_prep()` introduced in `631d1febab8e5` (2007,
"config item dependancies"). `BUG_ON(!origin || !origin->d_fsdata)`
added in `49deb4bc227cb` (2013). The `child_sd->s_dentry` recursive
pattern has been present since 2007. Bug latent for years; exposure
increased once `DCACHE_DONTCACHE` and dangling-`s_dentry` fixes landed
in this tree.

**Step 3.2 — Fixes: tag**
- Record: Not applicable — no `Fixes:` tag in commit message.

**Step 3.3 — Related file history**
- Record: Part of Al Viro's 2026 configfs series on master:
- `10da12d352b7b` → in tree as `c3b073a209a9b` (lookup dangling
`s_dentry`)
- `9b9e8bb81c41f` → in tree as `637ef4961470e` (lockless `s_children`
traversal)
- `96551d7f9f7b5` — **not** in tree (same fix for
`configfs_detach_prep()`)
- `764682e011843` — **not** in tree (this commit)
Standalone for `depend_prep`; does not require the `detach_prep`
sibling.

**Step 3.4 — Author context**
- Record: Al Viro (VFS maintainer) authored the related configfs
hardening series. Reviewed by Jan Kara (filesystems).

**Step 3.5 — Dependencies**
- Record: No patch-series numbering. Applies cleanly to 6.18.44
(verified via cherry-pick). Benefits from already-present
prerequisites (`DCACHE_DONTCACHE`, dangling-`s_dentry` fix, lockless
traversal fix) but does not require unbackported commits.

---

## Phase 4: Mailing List and External Research

**Step 4.1 — Original discussion**
- Record: `b4 dig -c 764682e011843` found no lore match. Commit went
through Viro's `pull-configfs-fixed` tag (`de02909ae81aa` on master).
Discussion not retrievable via b4; analysis relies on commit message,
code, and series context.

**Step 4.2 — Reviewers**
- Record: `b4 dig -w` also failed (no lore match). Commit lists Jan Kara
and Breno Leitao as reviewers.

**Step 4.3 — Bug report**
- Record: Not applicable — no `Reported-by:` or `Link:` tags.

**Step 4.4 — Related patches**
- Record: Sibling `96551d7f9f7b5` (`configfs_detach_prep()`) is the same
class of fix for a different function. Independent; not a prerequisite
for this commit.

**Step 4.5 — Stable list**
- Record: Not searched separately; the upstream lockless-traversal fix
from the same series was already backported to this tree, indicating
stable maintainers consider this configfs work relevant.

---

## Phase 5: Code Semantic Analysis

**Step 5.1 — Key functions**
- Record: `configfs_depend_prep()`, `configfs_do_depend_item()`.

**Step 5.2 — Callers**
- Record: `configfs_do_depend_item()` called from
`configfs_depend_item()` and `configfs_depend_item_unlocked()`.
Exported callers include:
- `fs/ocfs2/cluster/nodemanager.c`
- `drivers/target/target_core_configfs.c`
- `drivers/gpio/gpio-sim.c`, `gpio-virtuser.c`, `gpio-aggregator.c`
- `drivers/usb/gadget/function/f_tcm.c`
These are real production/configfs-client paths.

**Step 5.3 — Callees**
- Record: Holds `configfs_dirent_lock`, walks `sd->s_children`, compares
`sd->s_element` to target, increments `s_dependent_count` on success.

**Step 5.4 — Reachability**
- Record: Triggered when kernel drivers call `configfs_depend_item()` /
`configfs_depend_item_unlocked()` to pin cross-subsystem config items.
Reachable from module code managing configfs objects (target, ocfs2,
gpio, USB gadget). Not a syscall path, but a real kernel runtime path.

**Step 5.5 — Similar patterns**
- Record: `configfs_readdir()` at lines 1695–1697 already does `dentry =
next->s_dentry; if (dentry)` before use. `configfs_depend_prep()` is
the outlier that assumed `s_dentry` is always valid.

---

## Phase 6: Cross-Reference Against Local Tree (6.18.44)

**Step 6.1 — Buggy code present?**
- Record: **Yes.** Tree is `v6.18.44` (`HEAD detached at
stable/linux-6.18.y`). `configfs_depend_prep()` still takes `struct
dentry *` and recurses through `child_sd->s_dentry` with
`BUG_ON(!origin)`.

**Step 6.2 — Backport complications**
- Record: Cherry-pick applies cleanly with auto-merge (`4 insertions, 8
deletions`). No rework needed.

**Step 6.3 — Related fixes already present?**
- Record: Prerequisites already in tree: `c3b073a209a9b` (dangling
`s_dentry`), `637ef4961470e` (lockless traversal), `a509e7cf622bc`
(`DCACHE_DONTCACHE`). This specific `depend_prep` fix is **not**
present. The `detach_prep` sibling fix is also absent.

---

## Phase 7: Subsystem and Maintainer Context

**Step 7.1 — Subsystem**
- Record: `fs/configfs` — VFS/configfs core. Criticality: **IMPORTANT**
(shared infrastructure for target, ocfs2, gpio, USB gadget configfs
users).

**Step 7.2 — Activity**
- Record: Actively maintained in 2026; multiple Al Viro configfs fixes
already landed and partially backported to this stable tree.

---

## Phase 8: Impact and Risk Assessment

**Step 8.1 — Who is affected**
- Record: Systems using configfs with `configfs_depend_item()` — notably
SCSI target, ocfs2, GPIO sim/virtuser/aggregator, USB gadget TCM.

**Step 8.2 — Trigger conditions**
- Record: `configfs_depend_item()` called while a subdirectory in the
tree has `s_dentry == NULL` but is otherwise live (not
`CREATING`/`DROPPING`). More likely with `DCACHE_DONTCACHE` dentry
eviction. Not userspace-triggerable directly, but driver-initiated
during normal configfs dependency setup.

**Step 8.3 — Failure mode**
- Record: `BUG_ON(!origin)` → kernel BUG / crash. Severity: **CRITICAL**
(hard kernel failure).

**Step 8.4 — Risk-benefit**
- Record: Benefit **high** (prevents kernel crash in configfs dependency
path). Risk **very low** (4-line net change, removes redundant
indirection, reviewed by two filesystem developers). Ratio strongly
favors backport.

---

## Phase 9: Final Synthesis

**Step 9.1 — Evidence**

*For backport:*
- Real latent crash: NULL `s_dentry` triggers `BUG_ON` in recursive
`configfs_depend_prep()`
- Same file already handles NULL `s_dentry` in `configfs_readdir()`
- Prerequisites (`DCACHE_DONTCACHE`, dangling-pointer fix, lockless
traversal) already in 6.18.44 increase exposure
- Small, surgical, applies cleanly
- Reviewed by Jan Kara and Breno Leitao
- Part of a series whose main fix is already in this stable tree
- Used by production subsystems (target, ocfs2, gpio)

*Against backport:*
- No explicit bug report or syzbot reference
- Commit message is terse; reads partly like cleanup
- Sibling `configfs_detach_prep()` fix not yet in tree (incomplete
series coverage)

*Unresolved:*
- No lore discussion retrieved (b4 found nothing)
- No user-reported crash instance verified

**Step 9.2 — Stable rules checklist**
1. Obviously correct and tested? **PASS** — logic is trivially correct;
reviewed by two developers.
2. Fixes a real bug affecting users? **PASS** — NULL `s_dentry` →
`BUG_ON` crash on `configfs_depend_item()` path.
3. Important issue? **PASS** — kernel BUG / crash (CRITICAL).
4. Small and contained? **PASS** — 12 lines, 1 file, 2 functions.
5. No new features or APIs? **PASS** — internal static function
signature change only.
6. Can apply to local tree? **PASS** — verified clean cherry-pick on
6.18.44.

**Step 9.3 — Exception category**
- Record: Not applicable (not device ID, quirk, DT, build, or docs).

**Step 9.4 — Decision rationale**

This commit completes hardening started by already-backported configfs
fixes in 6.18.44. With `DCACHE_DONTCACHE` making dentry eviction more
aggressive, `configfs_d_iput()` can clear `sd->s_dentry` while the
`configfs_dirent` remains in the live tree. `configfs_depend_prep()`
then recurses through that NULL pointer and hits `BUG_ON(!origin)`. The
fix is minimal, self-contained, applies cleanly, and prevents a kernel
crash in a path used by target, ocfs2, and GPIO configfs clients.

---

## Verification

- [Phase 1] Parsed subject, tags (Reviewed-by x2, no Fixes/Reported-by),
body text
- [Phase 2] Diff: 1 file, `configfs_depend_prep()` +
`configfs_do_depend_item()`, 4+/8-
- [Phase 3] `git blame` lines 1091–1119: function from 2007, BUG_ON from
2013
- [Phase 3] Series check: `c3b073a209a9b` and `637ef4961470e` in tree;
`96551d7f9f7b5` and `764682e011843` not in tree
- [Phase 3] `git show 764682e011843`: full commit on master
- [Phase 3] `git show 96551d7f9f7b5`: sibling detach_prep commit,
independent
- [Phase 4] `b4 dig -c 764682e011843`: no lore match
- [Phase 4] `b4 dig -c 764682e011843 -a -w`: no lore match
- [Phase 5] `grep configfs_depend_item`: callers in ocfs2, target, gpio,
usb gadget
- [Phase 5] Read `configfs_readdir()` lines 1695–1697: NULL `s_dentry`
handling confirmed
- [Phase 5] Read `configfs_d_iput()` lines 45–66: sets `s_dentry = NULL`
on dentry eviction
- [Phase 6] `git describe HEAD`: v6.18.44
- [Phase 6] Buggy code confirmed at lines 1091–1129 in
`fs/configfs/dir.c`
- [Phase 6] Cherry-pick test: applies cleanly to 6.18.44
- [Phase 6] `a509e7cf622bc` (`DCACHE_DONTCACHE`) confirmed in tree
- [Phase 8] Failure mode: `BUG_ON(!origin)` on NULL dentry → kernel
crash

**YES**The background checks finished and match the earlier analysis.

**6.18.44 stable tree status for the configfs series:**
- **Present:** `DCACHE_DONTCACHE` (`a509e7cf622bc`), the lookup
dangling-`s_dentry` fix (`c3b073a209a9b`), and the lockless
`s_children` traversal fix (`637ef4961470e`)
- **Missing:** `configfs_detach_prep(): pass configfs_dirent instead of
dentry` (`96551d7f9f7b5`) and the commit under review
(`764682e011843`)

That supports the **YES** verdict: the prerequisites that make NULL
`s_dentry` more likely are already in this tree, but the
`configfs_depend_prep()` fix is not. The patch applies cleanly on
6.18.44.The branch search found the commit on **master** (and related
branches) as `764682e011843` — `configfs_depend_prep(): pass
configfs_dirent instead of dentry` — merged via Al Viro's `pull-
configfs-fixed` tag. It is not in the current **6.18.44** stable
checkout, which matches the earlier analysis.

fs/configfs/dir.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 68203952766e6..bfecb68a83797 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1088,15 +1088,12 @@ static int configfs_dump(struct configfs_dirent *sd, int level)
* much on the stack, though, so folks that need this function - be careful
* about your stack! Patches will be accepted to make it iterative.
*/
-static int configfs_depend_prep(struct dentry *origin,
+static int configfs_depend_prep(struct configfs_dirent *sd,
struct config_item *target)
{
- struct configfs_dirent *child_sd, *sd;
+ struct configfs_dirent *child_sd;
int ret = 0;

- BUG_ON(!origin || !origin->d_fsdata);
- sd = origin->d_fsdata;
-
if (sd->s_element == target) /* Boo-yah */
goto out;

@@ -1104,8 +1101,7 @@ static int configfs_depend_prep(struct dentry *origin,
if ((child_sd->s_type & CONFIGFS_DIR) &&
!(child_sd->s_type & CONFIGFS_USET_DROPPING) &&
!(child_sd->s_type & CONFIGFS_USET_CREATING)) {
- ret = configfs_depend_prep(child_sd->s_dentry,
- target);
+ ret = configfs_depend_prep(child_sd, target);
if (!ret)
goto out; /* Child path boo-yah */
}
@@ -1126,7 +1122,7 @@ static int configfs_do_depend_item(struct dentry *subsys_dentry,

spin_lock(&configfs_dirent_lock);
/* Scan the tree, return 0 if found */
- ret = configfs_depend_prep(subsys_dentry, target);
+ ret = configfs_depend_prep(subsys_dentry->d_fsdata, target);
if (ret)
goto out_unlock_dirent_lock;

--
2.53.0