From: 徐晗翔 <xuhx@hust.edu.cn>
To: cem@kernel.org
Cc: linux-xfs@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: xfs: data races on xfs_params and xfs_globals tunables
Date: Fri, 13 Nov 2026 20:48:14 +0800 (GMT+08:00) [thread overview]
Message-ID: <5495c57c.263aa.1a1d4f38fde.Coremail.xuhx@hust.edu.cn> (raw)
In-Reply-To: <2026102727-data-sequel-ce41@gregkh>
The global XFS tunables in xfs_params and xfs_globals are written through
sysctl/sysfs handlers and read on normal filesystem paths with no common
lock and without a consistent READ_ONCE()/WRITE_ONCE() protocol. For the
most concrete case, _xfs_alert_tag() reads xfs_panic_mask twice:
if (xfs_panic_mask && (xfs_panic_mask & panic_tag)) {
xfs_alert(mp, "Transforming an alert into a BUG.");
do_panic = 1;
}
A concurrent write to /proc/sys/fs/xfs/panic_mask can change the value
between the two loads, so the first test can see a non-zero mask and the
second a zero (or a different) mask. The result is either a missed
panic or a panic the administrator had just disabled. The same pattern
exists for xfs_error_level and for the timer/rotorstep/inherit knobs; the
DEBUG-only xfs_globals sysfs attributes have the same plain-read/plain-
write pairing. This is a policy/diagnostic data race, not memory
corruption, and I am reporting it separately from the stats_clear UAF.
The detailed description:
* Test environment / affected version
Linux 7.3.0 snapshot (the tree used for this audit; Makefile reports
VERSION=7 PATCHLEVEL=3 SUBLEVEL=0), x86_64. The report is based on
source inspection, not on a KCSAN run: the data race is visible in the
C access pattern, but the package does not claim a dynamic splat.
xfs_globals sysfs attributes exist only in DEBUG builds.
* Mechanism
The storage is:
fs/xfs/xfs_sysctl.h:15-34 struct xfs_params
fs/xfs/xfs_globals.c:15-29 struct xfs_globals xfs_globals
fs/xfs/xfs_platform.h:104-115 xfs_panic_mask, xfs_error_level, ...
The sysctl table is fs/xfs/xfs_sysctl.c:69-182. Most entries use
proc_dointvec_minmax(); its proc_int_conv() in kernel/sysctl.c uses
WRITE_ONCE(*k_ptr, tmp_k). But xfs_panic_mask_proc_handler() then
does a plain assignment and a plain OR:
fs/xfs/xfs_sysctl.c:33-49
ret = proc_dointvec_minmax(ctl, write, buffer, lenp, ppos);
if (!ret && write) {
xfs_panic_mask = *valp;
#ifdef DEBUG
xfs_panic_mask |= (XFS_PTAG_SHUTDOWN_CORRUPT | XFS_PTAG_LOGRES);
#endif
}
The read side is plain C everywhere:
fs/xfs/xfs_message.c:64 xfs_panic_mask (two reads)
fs/xfs/xfs_message.c:50 xfs_error_level
fs/xfs/xfs_error.c:249,269,295,326,333,376,383
fs/xfs/xfs_fsops.c:533
fs/xfs/xfs_log.c:3408
fs/xfs/xfs_da_btree.c:2781
fs/xfs/xfs_icache.c:209,230 syncd/blockgc timers
fs/xfs/xfs_filestream.c:281,388 rotorstep/fstrm timer
fs/xfs/libxfs/xfs_inode_util.c:177-189 inherit_*
There is no xfs_params lock, no seqlock, and no READ_ONCE() on these
reads. A write-side WRITE_ONCE() is not sufficient by itself: the two
reads in _xfs_alert_tag() can still be separated, and the compiler can
merge or reorder plain loads.
The DEBUG-only sysfs attributes in fs/xfs/xfs_sysfs.c:84-314 are the
same pattern: bug_on_assert_store() writes a bool at :98/:100,
bug_on_assert_show() reads it at :112, and assfail() reads it at
fs/xfs/xfs_message.c:101; log_recovery_delay, mount_delay, always_cow,
pwork_threads, larp, and bload_* have analogous store/consumer pairs.
* Trigger model
- Root writes one of /proc/sys/fs/xfs/* (or a DEBUG sysfs attribute)
while another CPU is in _xfs_alert_tag(), xfs_error_report(), a
mount/recovery path, or an xattr path that reads the same tunable.
- No module unload and no memory reclaim are required.
- The visible effect is a changed policy decision: a missing or extra
BUG(), different stack-trace verbosity, a timer interval sampled old
or new, etc. It is not a memory-safety violation and does not cross
a privilege boundary by itself.
- KCSAN would report the plain read/write pairs; this package does not
include such a run, so the report should be treated as a static
data-race finding and a cleanup proposal, not as a security bug.
* Suggested direction
- Use READ_ONCE() for every read of xfs_params.<member>.val and
xfs_globals.<member>; use WRITE_ONCE() for the write side, including
the plain assignment/OR in xfs_panic_mask_proc_handler().
- In _xfs_alert_tag(), take one local snapshot and use it for both
tests:
uint32_t mask = READ_ONCE(xfs_panic_mask);
if (mask && (mask & panic_tag)) {
xfs_alert(mp, "Transforming an alert into a BUG.");
do_panic = 1;
}
- For booleans that must take effect immediately (bug_on_assert), a
static_key or a WRITE_ONCE() plus local snapshot is preferable to a
plain bool. For the timer/rotorstep knobs, READ_ONCE() is enough to
remove the undefined behavior and KCSAN report; multi-field policy
consistency is not required.
This is intentionally a low-priority report. If the maintainers prefer
to fold it into a general tunables cleanup rather than treat it as a
standalone bug, that is fine; the important part is the access-pattern
fix and the two-read snapshot in _xfs_alert_tag().
Please keep me in Cc; I am not subscribed to the lists.
Regards,
Hanxiang Xu
xuhx@hust.edu.cn
prev parent reply other threads:[~2026-11-13 12:48 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 12:35 usb: devio: usbdev_release() has no barrier against an in-flight async_completed() callback (use-after-free window on struct usb_dev_state)gregkh@linuxfoundation.org 徐晗翔
2026-10-27 14:12 ` Greg KH
2026-11-03 11:21 ` mm/slub: has_pcs_used() reads remote per-CPU sheaf state from flush_all_cpus_locked() without synchronization (data race; KCSAN blind spot: slub.o is uninstrumented) 徐晗翔
2026-11-04 9:41 ` Qing Wang
2026-11-04 10:07 ` Qing Wang
2026-11-03 11:30 ` futex/hrtimer: hrtimer_sleeper::task is read unsynchronized in 16 places on v7.3 -- the accessor series that fixes this is not in 7.3; please make sure it reaches the affected trees (backport nudge + stock-kernel KCSAN evidence) 徐晗翔
2026-11-03 11:48 ` cgroup: fork vs cgroup.procs write race in favordynmods mode -- v7.3 is affected; please make sure bf6fbf46b5ff reaches the stable trees (repro incl. uninstrumented natural hit) 徐晗翔
2026-11-03 14:51 ` Sasha Levin
2026-11-03 11:57 ` net: rtnetlink: rtnl_link_unregister() vs in-flight RTM_GETLINK dump readers -- v7.3 lacks the synchronize_rcu() fix (Fixes: 6b57ff21a310 family); A/B reproduction on stock 7.3 徐晗翔
2026-11-03 15:49 ` Eric Dumazet
2026-11-04 15:03 ` Sasha Levin
2026-11-03 12:07 ` wifi: wilc1000: SDIO/SPI remove destroys the srcu_struct before wiphy_unregister() -- late cfg80211 callbacks write through dangling srcu state (unfixed upstream; controlled + natural Oops) 徐晗翔
2026-11-03 12:10 ` gpio: gpiolib: final put of the gpio_device happens inside the SRCU read critical section on the setup-failure path -- deterministic use-after-free read at guard release (unfixed upstream) 徐晗翔
2026-11-05 8:52 ` Linus Walleij
2026-11-04 12:54 ` soc: qcom: pmic_glink_altmode: fix use-after-free on unbind (no cancel_work_sync for port work and enable_work) 徐晗翔
2026-11-04 13:04 ` Greg KH
2026-11-04 12:57 ` usb: typec: anx7411: unregister partner/altmodes before stopping the IRQ work -- use-after-free, double unregister, orphan partner on remove 徐晗翔
2026-11-04 13:28 ` Greg KH
2026-11-04 12:58 ` usb: typec: ucsi: deadlock on cros_ec unbind -- connector lock held across cancel_delayed_work_sync vs the mode-selection work's unbounded trylock polling 徐晗翔
2026-11-04 13:00 ` usb: typec: stusb160x/rt1719/hd3ss3220: IRQ thread vs remove -- double unregister / use-after-free on the partner and port 徐晗翔
2026-11-04 13:01 ` usb: typec: ucsi: displayport: data race on dp->header/ vdo_data/vdo_size -- work reads them unlocked while writers hold con->lock (VDM mismatch) 徐晗翔
2026-11-04 13:02 ` usb: typec: class: three lifetime/locking gaps around the mux/switch handles and mode_selection (drvdata UAF after provider unbind; no concurrent protocol for set/put; unlocked state_update vs delete) 徐晗翔
2026-11-10 12:56 ` driver core: uevent: netns teardown frees uevent_sock while a tagged uevent is still being broadcast (UAF) 徐晗翔
2026-11-10 13:00 ` 徐晗翔
2026-11-10 18:48 ` Greg KH
2026-11-10 13:05 ` ACPI: CPPC: per-CPU cpc_desc is freed on processor unbind without clearing the published pointer, causing UAF in cpufreq accessors 徐晗翔
2026-11-10 13:07 ` ACPI: CPPC: shared PCC subspace data can be freed while another CPU probes, reads, or tears it down 徐晗翔
2026-11-10 14:47 ` Christian Loehle
2026-11-10 13:11 ` ACPI: ACPICA: Reset can delete an event semaphore while another thread is blocked in Wait 徐晗翔
2026-11-10 13:14 ` power: supply: bq25890: pump-express work can call power_supply_changed after the psy is unregistered 徐晗翔
2026-11-10 13:17 ` power: supply: core: get/set property callbacks can run after unregister and read freed driver data 徐晗翔
2026-11-10 13:18 ` power: supply: core: system-battery enumeration can return an unregistered battery 徐晗翔
2026-11-10 13:21 ` power: supply: core: changed work can run after unregister and use freed LED triggers 徐晗翔
2026-11-10 13:23 ` power: supply: bq24190: IRQ handler can call power_supply_changed after the psy is unregistered 徐晗翔
2026-11-10 13:34 ` 徐晗翔
2026-11-11 7:17 ` genirq: NMI dispatch reads struct irqaction with no quiescence -- use-after-free across free_nmi()'s kfree (x86 verification on the arch-independent core path) 徐晗翔
2026-11-11 7:26 ` genirq: NMI API lacks the request_mutex / dev_id matching / idempotency semantics that free_irq() has -- double free_nmi releases chip PM and module references twice (verified) 徐晗翔
2026-11-11 7:30 ` kernfs: kernfs_drain()'s fast-path decision reads the active count without acquire ordering -- on weakly-ordered architectures the forced ->release()/unmap can be skipped 徐晗翔
2026-11-11 13:41 ` Greg KH
2026-11-13 11:58 ` ALSA: ctxfi: free atimer only after the timer IRQ is quiesced 徐晗翔
2026-11-13 12:03 ` ALSA: ctxfi: do not free a running system timer when timer_delete_sync_try() returns -1 (UAF) 徐晗翔
2026-11-13 12:04 ` ALSA: asihpi: free IRQ before snd_card_free()/HPI delete on HPI6205 removal (UAF) 徐晗翔
2026-11-13 12:06 ` ALSA: asihpi: quiesce PCM timer and IRQ before freeing dpcm on close (UAF) 徐晗翔
2026-11-13 12:15 ` ALSA: pcm: link-group walk in timer callback can use a closed substream's freed runtime (UAF) 徐晗翔
2026-11-13 12:28 ` ALSA: usb-audio: us144mkii: PCM close frees runtime while playback/feedback URB completion and capture work still use it (UAF) 徐晗翔
2026-11-13 12:29 ` ALSA: usb-audio: us144mkii: queued stop_pcm_work calls snd_pcm_stop() after PCM close and dereferences NULL runtime 徐晗翔
2026-11-13 12:33 ` sched/topology: old domain chains are queued for RCU freeing before the per-CPU pointers are updated, so a late reader can still dereference a freed sched_domain and sched_domain_shared (UAF) 徐晗翔
2026-11-13 12:47 ` xfs: use-after-free in stats_clear sysctl handler during module unload 徐晗翔
2026-11-13 12:48 ` 徐晗翔 [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5495c57c.263aa.1a1d4f38fde.Coremail.xuhx@hust.edu.cn \
--to=xuhx@hust.edu.cn \
--cc=cem@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®