[PATCH AUTOSEL 6.18-5.10] fuse: set ff->flock only on success

From: Sasha Levin

Date: Mon Aug 31 2026 - 10:47:12 EST


From: Zhang Tianci <zhangtianci.1997@xxxxxxxxxxxxx>

[ Upstream commit 71947173cef279be5eed209ec28f8c11f9d73159 ]

If FUSE_SETLK fails (e.g., due to EWOULDBLOCK), we shall not set
FUSE_RELEASE_FLOCK_UNLOCK in fuse_file_release().

Reported-by: Li Yichao <liyichao.1@xxxxxxxxxxxxx>
Signed-off-by: Zhang Tianci <zhangtianci.1997@xxxxxxxxxxxxx>
Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

# Stable Backport Analysis: `fuse: set ff->flock only on success`

**Local tree:** `v6.18.44-1-g2736c32da98b9` (6.18.44 stable, detached
from `stable/linux-6.18.y`)
**Commit under review:** `71947173cef279be5eed209ec28f8c11f9d73159`
(mainline; **not yet in this tree**)

---

## PHASE 1: COMMIT MESSAGE FORENSICS

### Step 1.1: Parse the subject line
**Record:** `[fuse]` `[set]` — set `ff->flock` only when `fuse_setlk()`
succeeds, so failed flock attempts do not trigger unlock on file
release.

### Step 1.2: Parse all commit message tags
**Record:**
- **Reported-by:** Li Yichao `<liyichao.1@xxxxxxxxxxxxx>` — real-world
reporter (ByteDance)
- **Signed-off-by:** Zhang Tianci `<zhangtianci.1997@xxxxxxxxxxxxx>`
(author)
- **Signed-off-by:** Miklos Szeredi `<mszeredi@xxxxxxxxxx>` (FUSE
maintainer, committer)
- No Fixes:, Cc: stable, Link:, Tested-by:, Reviewed-by:, Acked-by:

Notable: maintainer committed the patch; reporter from production FUSE
user.

### Step 1.3: Analyze commit body
**Record:**
- **Bug:** `ff->flock = true` is set before `fuse_setlk()`. If
`FUSE_SETLK` fails (e.g. `-EWOULDBLOCK` for non-blocking flock),
`ff->flock` remains set.
- **Symptom:** On `close()`, `fuse_file_release()` sets
`FUSE_RELEASE_FLOCK_UNLOCK` even though no flock was acquired.
- **Failure mode:** Spurious flock unlock sent to the FUSE userspace
daemon on file release.
- **Root cause:** Flag tracks intent to lock, not actual lock success.
- No kernel version range mentioned in the message.

### Step 1.4: Detect hidden bug fixes
**Record:** Not disguised — this is an explicit correctness fix for
flock release handling. The commit message clearly describes incorrect
unlock behavior on the error path.

---

## PHASE 2: DIFF ANALYSIS

### Step 2.1: Inventory the changes
**Record:**
- **Files:** `fs/fuse/file.c` (+2 / -1, net +1 line)
- **Function modified:** `fuse_file_flock()`
- **Scope:** Single-file, surgical fix (3-line hunk)

### Step 2.2: Code flow change
**Record:**
- **Hunk (fuse_file_flock):**
- **Before:** `ff->flock = true` unconditionally, then `err =
fuse_setlk(file, fl, 1)`
- **After:** `err = fuse_setlk(file, fl, 1)` first; `ff->flock = true`
only if `!err`
- **Path affected:** FUSE flock path when `fc->no_flock` is false (flock
delegated to userspace via `FUSE_SETLK`)

### Step 2.3: Bug mechanism
**Record:**
- **Category:** Logic / correctness fix (lock state tracking)
- **Mechanism:** `ff->flock` gates `FUSE_RELEASE_FLOCK_UNLOCK` in
`fuse_file_release()`:

```358:361:fs/fuse/file.c
if (ra && ff->flock) {
ra->inarg.release_flags |= FUSE_RELEASE_FLOCK_UNLOCK;
ra->inarg.lock_owner = fuse_lock_owner_id(ff->fm->fc,
id);
}
```

Setting the flag before confirming lock success causes a spurious unlock
request on `close()` after a failed `flock(2)`.

### Step 2.4: Fix quality assessment
**Record:**
- Fix is obviously correct: the flag should reflect a successfully
acquired flock, not an attempted one.
- Minimal change; mirrors standard “set state only on success” pattern.
- **Regression risk:** Very low. A successful flock still sets the flag;
failed attempts no longer poison release behavior.
- No API, locking, or structural changes.

---

## PHASE 3: GIT HISTORY INVESTIGATION

### Step 3.1: Blame the changed lines
**Record:**
- `fuse_file_flock()` dates to 2007 (`a9ff4f87056cd`)
- `ff->flock = true` before `fuse_setlk()` introduced in
`37fb3a30b46237` (“fuse: fix flock”, Aug 2011, Miklos Szeredi)
- Bug has existed since v3.0 era; long-present in stable trees including
6.18.y

### Step 3.2: Follow Fixes: tag
**Record:** No Fixes: tag. The introducing commit is `37fb3a30b46237`,
which is certainly in this tree.

### Step 3.3: File history for related changes
**Record:**
- Standalone one-patch fix (v1 only on lore)
- Recent FUSE stable activity in this tree includes writeback, virtiofs,
and fuse-uring fixes — unrelated to this flock issue
- Commit `71947173cef27` is in `origin/master` but **not** in
`stable/linux-6.18.y` (confirmed via `git log
stable/linux-6.18.y..origin/master`)

### Step 3.4: Author's other commits
**Record:** Zhang Tianci has other FUSE contributions (e.g. attribute
staleness checks). Miklos Szeredi is the FUSE maintainer and applied the
patch.

### Step 3.5: Dependencies / prerequisites
**Record:** No dependencies. Uses existing `ff->flock`, `fuse_setlk()`,
and `FUSE_RELEASE_FLOCK_UNLOCK` — all present in 6.18.44. `git show
71947173cef27 | git apply --check` succeeds cleanly.

---

## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH

### Step 4.1: Original patch discussion
**Record:**
- **URL:** https://patch.msgid.link/20251225111156.47987-1-
zhangtianci.1997@xxxxxxxxxxxxx
- **Series:** v1 only (no v2/v3)
- **Maintainer response:** Miklos Szeredi: “Applied, thanks.”
- No NAKs or objections found in thread
- No explicit stable nomination in thread

### Step 4.2: Reviewers from b4 dig -w
**Record:** CC'd: `miklos@xxxxxxxxxx`, `linux-fsdevel@xxxxxxxxxxxxxxx`,
`linux-kernel@xxxxxxxxxxxxxxx`, reporter Li Yichao, co-worker
xieyongji@xxxxxxxxxxxxx. FUSE maintainer reviewed and applied.

### Step 4.3: Bug report
**Record:** Reported-by from ByteDance engineer; no syzbot/bugzilla
link. Production FUSE user hit the issue with failed non-blocking flock
+ file close.

### Step 4.4: Related patches / series
**Record:** Standalone patch; no series dependencies.

### Step 4.5: Stable mailing list history
**Record:** Not searched on lore stable list (Anubis bot blocked direct
lore fetch). No stable discussion found via b4.

---

## PHASE 5: CODE SEMANTIC ANALYSIS

### Step 5.1: Key functions
**Record:** `fuse_file_flock()` (modified), `fuse_setlk()` (called),
`fuse_file_release()` (affected downstream)

### Step 5.2: Callers
**Record:**
- `fuse_file_flock` is the `.flock` handler in `fuse_file_operations`
(line 3137)
- Reached from `SYSCALL_DEFINE2(flock)` in `fs/locks.c` when
`file->f_op->flock` is set and `LOCK_NB` is used (`F_SETLK` vs
`F_SETLKW`)
- Callable by any unprivileged process with a FUSE file descriptor

### Step 5.3: Callees
**Record:** `fuse_setlk()` → `fuse_simple_request()` with
`FUSE_SETLK`/`FUSE_SETLKW` and `FUSE_LK_FLOCK` flag. Returns errors
including `-EWOULDBLOCK` (mapped from userspace daemon response).

### Step 5.4: Call chain / reachability
**Record:**
```
userspace flock(2) → SYSCALL_DEFINE2(flock) → file->f_op->flock
(fuse_file_flock)
→ fuse_setlk() → [on failure] return error
→ [on close] fuse_release → fuse_file_release →
FUSE_RELEASE_FLOCK_UNLOCK if ff->flock
```
**Reachable from userspace:** Yes, via `flock(2)` on FUSE-mounted files
when `fc->no_flock` is false.

### Step 5.5: Similar patterns
**Record:** The `no_flock` fallback path uses `locks_lock_file_wait()`
and does not set `ff->flock` — only the userspace-delegated flock path
is affected. No sibling functions with the same pre-set pattern found.

---

## PHASE 6: CROSS-REFERENCING AGAINST LOCAL TREE

### Step 6.1: Does the buggy code exist?
**Record:** **Yes.** Current tree at `fs/fuse/file.c:2531` still has
unconditional `ff->flock = true` before `fuse_setlk()`. Bug present
since 2011 (`37fb3a30b46237`).

### Step 6.2: Backport complications
**Record:** Patch applies cleanly (`git apply --check` passed). No
refactoring conflicts expected. Trivial backport.

### Step 6.3: Related fixes already present?
**Record:** No equivalent fix in `stable/linux-6.18.y`. Commit
`71947173cef27` is only in mainline (post-6.18.y branch point).

---

## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT

### Step 7.1: Subsystem criticality
**Record:** **fs/fuse** — IMPORTANT. FUSE is widely used (virtio-fs,
cloud storage mounts, container/shared filesystems). File locking
correctness affects data integrity for multi-process workloads.

### Step 7.2: Subsystem activity
**Record:** FUSE subsystem actively maintained in 6.18.y with multiple
recent stable-relevant fixes (writeback, virtiofs UAF, fuse-uring
races).

---

## PHASE 8: IMPACT AND RISK ASSESSMENT

### Step 8.1: Who is affected
**Record:** Users of FUSE filesystems that support flock (i.e.
`FUSE_FLOCK_LOCKS` negotiated, `fc->no_flock == 0`). Includes virtio-fs
and custom FUSE implementations using BSD-style flock.

### Step 8.2: Trigger conditions
**Record:**
1. Open file on FUSE mount with flock support
2. Call `flock(fd, LOCK_EX | LOCK_NB)` (or `LOCK_SH | LOCK_NB`) when
lock cannot be acquired
3. Close the file descriptor
**Likelihood:** Moderate — non-blocking flock failure is a normal,
documented API path. **Unprivileged users can trigger.**

### Step 8.3: Failure mode severity
**Record:** Spurious `FUSE_RELEASE_FLOCK_UNLOCK` on close after a failed
lock attempt. This can corrupt flock state in the userspace filesystem
daemon — potentially releasing locks held by other processes or breaking
mutual exclusion guarantees. **Severity: HIGH** (data integrity /
locking correctness; not a kernel oops, but serious application-visible
bug).

### Step 8.4: Risk-benefit ratio
**Record:**
- **Benefit:** HIGH — fixes real lock-handling bug on a common error
path
- **Risk:** VERY LOW — 3-line, obviously correct change
- **Ratio:** Strongly favors backport

---

## PHASE 9: FINAL SYNTHESIS

### Step 9.1: Evidence summary

**FOR backport:**
- Real bug with production reporter (ByteDance)
- FUSE maintainer applied and signed off
- Long-standing bug (since 2011) present in 6.18.44
- Incorrect spurious unlock on failed `flock(LOCK_NB)` + `close()`
- Lock correctness / potential data corruption
- Tiny, surgical, applies cleanly
- No dependencies

**AGAINST backport:**
- No kernel crash or oops (correctness bug, not memory safety)
- Affects only FUSE mounts with flock support enabled (not universal)
- No explicit stable nomination in mailing list

**Unresolved:** None material to the decision.

### Step 9.2: Stable rules checklist
1. Obviously correct and tested? **PASS** — logic is self-evident;
maintainer applied; reporter verified the scenario
2. Fixes a real bug affecting users? **PASS** — spurious unlock after
failed flock
3. Important issue? **PASS** — lock state corruption / data integrity
(HIGH)
4. Small and contained? **PASS** — 3 lines, one function
5. No new features or APIs? **PASS**
6. Can apply to local tree? **PASS** — clean apply verified

### Step 9.3: Exception categories
**Record:** None (not a device ID, quirk, DT, build, or docs fix).
Qualifies on merit as a correctness bug fix.

### Step 9.4: Decision rationale
This commit fixes a real, long-standing logic bug in FUSE flock
handling. When a non-blocking flock fails, the kernel incorrectly marks
the file as flock-held and sends `FUSE_RELEASE_FLOCK_UNLOCK` on close,
potentially corrupting lock state in the userspace filesystem. The fix
is minimal, maintainer-approved, applies cleanly to 6.18.44, and the
buggy code is present in this tree. Stable users running FUSE with flock
support benefit directly.

---

## Verification

- [Phase 1] `git show 71947173cef27`: parsed subject, tags (Reported-by,
SOBs), body
- [Phase 2] Read `fs/fuse/file.c` lines 2519–2536, 349–361; confirmed
diff mechanics
- [Phase 2] Read `fuse_setlk()` at lines 2470–2494; confirmed error
return path
- [Phase 3] `git describe HEAD`: v6.18.44 stable tree
- [Phase 3] `git blame -L 2519,2536 fs/fuse/file.c`: bug introduced
`37fb3a30b46237` (2011)
- [Phase 3] `git show 37fb3a30b46237`: original `ff->flock = true`
placement
- [Phase 3] `git log stable/linux-6.18.y..origin/master --
fs/fuse/file.c`: fix only in mainline
- [Phase 3] `git show 71947173cef27 | git apply --check`: clean apply
- [Phase 4] `b4 dig -c 71947173cef27`: lore URL found
- [Phase 4] `b4 dig -c 71947173cef27 -a`: v1 only
- [Phase 4] `b4 dig -c 71947173cef27 -w`: miklos@xxxxxxxxxx CC'd
- [Phase 4] `b4 dig -m /tmp/fuse_flock_thread.mbox`: Miklos “Applied,
thanks”
- [Phase 5] Read `fs/locks.c` SYSCALL_DEFINE2(flock): confirms VFS →
`.flock` path
- [Phase 5] Read `fuse_file_operations`: `.flock = fuse_file_flock`
- [Phase 5] `fuse_file_alloc()` uses `kzalloc`: `ff->flock` starts false
- [Phase 6] Grep confirmed buggy code at line 2531 in current tree
- [Phase 6] `git branch --contains 71947173cef27`: not in
stable/linux-6.18.y
- [Phase 8] Read `include/uapi/linux/fuse.h`:
`FUSE_RELEASE_FLOCK_UNLOCK` definition

**YES**The background searches finished and match what the earlier
analysis used:

- **Tree:** detached HEAD on `stable/linux-6.18.y` (6.18.44)
- **Commit:** `71947173cef27` on `origin/master` — `fuse: set ff->flock
only on success`
- **Status in this tree:** not present yet; patch applies cleanly

**Verdict remains YES** — backport to 6.18.y is warranted. It fixes a
real FUSE flock bug where a failed non-blocking `flock()` still triggers
`FUSE_RELEASE_FLOCK_UNLOCK` on close, which can corrupt lock state in
the userspace daemon.

fs/fuse/file.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 585dd90361b65..9f3501a5c6c12 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2528,8 +2528,9 @@ static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
struct fuse_file *ff = file->private_data;

/* emulate flock with POSIX locks */
- ff->flock = true;
err = fuse_setlk(file, fl, 1);
+ if (!err)
+ ff->flock = true;
}

return err;
--
2.53.0