[BUG] fanotify: destroy/add race leaves a mark on a detached connector

From: Daehyeon Ko

Date: Thu Aug 27 2026 - 00:50:33 EST


Hello,

I found an unprivileged race in fsnotify_destroy_marks() that can leave an
attached fanotify mark on a detached connector. On unmodified Linux v7.2 and
v6.12.105 source builds, it naturally reaches the fsnotify_conn_mask()
warning from fanotify_mark(). On unmodified builds, the only observed
system-wide failure was a v7.2 panic when panic_on_warn=1; no memory
corruption or privilege escalation was observed.

Tested and source-inspected versions:

- Linux v7.2, commit
8d3ae59288f1e7d58d76558a6ee96d533bc5019f: reproduced.
- Linux v6.12.105, commit
14c37ff05f22da2fa7076d10f6a07c7ede330c83: reproduced.
- Torvalds master at
73e3f0710014fe6d4ed98cfc02292f6121db7558: the relevant destroy
iterator and fdinfo consumer remain present. Commit e422777fdd47
changed mark-mask updates and can mask the immediate warning, but it
did not change this iterator or the detached-connector state.

The unconditional connector detachment involved in the race appears to
originate in commit 6b3f05d24d35 ("fsnotify: Detach mark from object list
when last reference is dropped"), released in v4.12-rc1. My unprivileged
runtime results are limited to the two versions listed above. The limited
unprivileged fanotify functionality used by this reproducer was introduced
by 7cea2a3c505e ("fanotify: support limited functionality for unprivileged
users"), released in v5.13-rc1; I am not asserting ordinary-user
reachability before that change.

Root cause:

fsnotify_destroy_marks() holds a reference to the current mark, drops
conn->lock, destroys that mark, and then continues the hlist walk. While the
lock is dropped, an equal-priority mark can be inserted immediately before
the current entry. The walk resumes from the current entry's next pointer,
so it never visits the new predecessor, but it still detaches the connector
from the object after the walk.

The skipped mark remains ALIVE|ATTACHED and remains on both its group list
and the connector list. At the same time, the object no longer points to the
connector, conn->obj is NULL, and conn->type is DETACHED.

Representative v6.12.105 output is:

WARNING: CPU: 1 PID: 163 at fs/notify/mark.c:128
fsnotify_conn_mask+0x113/0x150
CPU: 1 UID: 65534 PID: 163 Comm: fanotify_destro
...
do_fanotify_mark
__x64_sys_fanotify_mark

The source reproducer ran on ext4 as UID/GID 65534 with CapEff=0 and
NoNewPrivs=1. It uses ordinary FAN_REPORT_FID groups and races final unlink
against fd-based mark insertion. Both builds used a KASAN-enabled research
configuration, but neither positive was a KASAN report.

With panic_on_warn=0 and panic_on_oops=0, the unmodified v7.2 run emitted the
warning and then completed 10,000 iterations and powered off cleanly. A
separate fresh v7.2 run with panic_on_warn=1 reached the same warning and
panicked through check_panic_on_warn(). The v6.12.105 run emitted seven
warnings and reached its 10,000-iteration progress line; it later timed out
during post-loop teardown, for which I am not assigning a kernel cause.

A timing-only diagnostic kernel also demonstrated that fdinfo can combine
the old INODE type with the detached NULL object and reach
fanotify_show_fdinfo() -> igrab(NULL). I have not reproduced that Oops on an
unmodified kernel, so it is not part of the impact claim. The skipped mark's
group-list reference keeps the connector allocated; I have no evidence of a
connector UAF, arbitrary read or write, information disclosure, or LPE.

I do not have a submission-ready patch. During fix development I rejected a
survivor-preservation prototype because it introduced a mask race, a
drain/restart prototype because additions could starve cleanup, and a bounded
retry prototype because review found an unresolved lock-order/deadlock risk
when generic callers hold external locks needed by cleanup. I am reporting
the root cause rather than sending an unsafe fix.

A tested source reproducer, full serial logs, configs, and diagnostic details
are available privately to the maintainers on request. AI-assisted tooling
was used during discovery and analysis; I reviewed the source and the literal
runtime evidence above and am reporting publicly without the reproducer as
required by Documentation/process/security-bugs.rst.

Assisted-by: LLM

Thanks,
Daehyeon Ko