No description
  • C 96.8%
  • Assembly 0.9%
  • Rust 0.7%
  • Shell 0.6%
  • Python 0.5%
  • Other 0.3%
Find a file
Christian Brauner 7eb84d54fa
fs: don't let a migrating task hide its reference from do_umount()
propagate_mount_busy() uses mnt_get_count() via do_refcount_check() to
figure out whether a synchronous umount may proceed or not by summing
the per-CPU mnt_count counter under mount_lock.

The fastpath for mntget() and mntput() doesn't require that lock. A task
that already holds a reference and takes another one on a cpu the loop
has summed can get migrated and drops that reference again on another
cpu. The loop might not have reached that cpu yet. That ends up
undercounting. The original reference of the task is missed and umount()
ends up succeeding with the file still open. The mount is destroyed
later when the task mntput()s so it degrades to a lazy umount.

That's basically the umount(2) variant of commit 9ea0a46ca2 ("fix
mntput/mntput race") closed for the final mntput(). The same
cpu-migration caused a UAF.

Luckily this isn't a UAF. It's just a hidden reference pinning the
mount but it's wrong nonetheless.

Like the other race it's a narrow window but I can reproduce it reliably
with some patching. The debug patch below prints the sum the loop
computed next to an immediate re-sum of the same counters. It reports
who drops the last reference to that mount later:

  --- a/fs/namespace.c
  +++ b/fs/namespace.c
  @@ -34,6 +34,8 @@
   #include <linux/mnt_idmapping.h>
   #include <linux/pidfs.h>
   #include <linux/nstree.h>
  +#include <linux/debugfs.h>
  +#include <linux/delay.h>

   #include "pnode.h"
   #include "internal.h"
  @@ -263,17 +265,48 @@
   #endif
   }

  +static u32 mnt_get_count_delay_ms;
  +static int mnt_get_count_debug_id;
  +
  +static int __init mnt_get_count_debug_init(void)
  +{
  +	debugfs_create_u32("mnt_get_count_delay_ms", 0600, NULL,
  +			   &mnt_get_count_delay_ms);
  +	return 0;
  +}
  +late_initcall(mnt_get_count_debug_init);
  +
   /*
    * vfsmount lock must be held for write
    */
   int mnt_get_count(struct mount *mnt)
   {
   #ifdef CONFIG_SMP
  -	int count = 0;
  +	static int seen[NR_CPUS];
  +	bool slow = mnt_get_count_delay_ms &&
  +		    !(mnt->mnt.mnt_flags & MNT_UMOUNT);
  +	int count = 0, again = 0;
   	int cpu;

   	for_each_possible_cpu(cpu) {
  -		count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
  +		seen[cpu] = per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
  +		count += seen[cpu];
  +		if (slow)
  +			mdelay(mnt_get_count_delay_ms);
  +	}
  +	if (slow) {
  +		for_each_possible_cpu(cpu) {
  +			int now;
  +
  +			now = per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
  +			again += now;
  +			pr_err("mount %d: cpu%d read %d, now %d\n",
  +			       mnt->mnt_id, cpu, seen[cpu], now);
  +		}
  +		mnt_get_count_debug_id = mnt->mnt_id;
  +		pr_err("mount %d: sum %d, re-sum %d, %ps\n",
  +		       mnt->mnt_id, count, again,
  +		       __builtin_return_address(0));
   	}

   	return count;
  @@ -1350,6 +1383,9 @@
   	smp_mb();
   	mnt_add_count(mnt, -1);
   	count = mnt_get_count(mnt);
  +	if (count == 0 && mnt->mnt_id == mnt_get_count_debug_id)
  +		pr_err("mount %d: last put by %s (%d)\n", mnt->mnt_id,
  +		       current->comm, task_pid_nr(current));
   	if (count != 0) {
   		WARN_ON(count < 0);
   		rcu_read_unlock();

A task holds open a directory fd on a tmpfs. A synchronous rumount() of
the tmpfs sums up. Concurrently a task calls fchdir() on that fd on
CPU 0 and fchdir()s back to / on CPU 3. This will cause a path_get() and
a path_put() with a migration in between without mount_lock held.

Adding a delay per CPU makes the count loop miss the get in a cpu it
already visited but count the put in one it hasn't.

The reproducer, run as root with debugfs mounted:

  /*
   * gcc -static -o umount-torn umount-torn.c
   */
  #define _GNU_SOURCE
  #include <errno.h>
  #include <fcntl.h>
  #include <sched.h>
  #include <stdio.h>
  #include <string.h>
  #include <unistd.h>
  #include <sys/mount.h>
  #include <sys/stat.h>
  #include <sys/wait.h>

  #define KNOB "/sys/kernel/debug/mnt_get_count_delay_ms"
  #define MNT "/mnt/x"

  static int kmsg;

  static void pin(int cpu)
  {
  	cpu_set_t s;

  	CPU_ZERO(&s);
  	CPU_SET(cpu, &s);
  	sched_setaffinity(0, sizeof(s), &s);
  }

  static void knob(const char *v)
  {
  	int fd = open(KNOB, O_WRONLY);

  	write(fd, v, strlen(v));
  	close(fd);
  }

  int main(void)
  {
  	int last = sysconf(_SC_NPROCESSORS_ONLN) - 1;
  	int p[2], fd, ret;
  	pid_t pid;
  	char c;

  	kmsg = open("/dev/kmsg", O_WRONLY);
  	mkdir(MNT, 0755);
  	mount("none", MNT, "tmpfs", 0, NULL);
  	fd = open(MNT "/f", O_WRONLY | O_CREAT, 0644);
  	write(fd, "still here\n", 11);
  	close(fd);

  	pipe(p);
  	pid = fork();
  	if (pid == 0) {
  		int dirfd = open(MNT, O_RDONLY | O_DIRECTORY);
  		int rootfd = open("/", O_RDONLY | O_DIRECTORY);
  		struct stat st;
  		char buf[16];

  		/* one get/put pair while the umount is summing */
  		usleep(150000);
  		pin(0);
  		fchdir(dirfd);		/* mntget() */
  		dprintf(kmsg, "holder %d: get on cpu %d\n", getpid(),
  			sched_getcpu());
  		pin(last);
  		fchdir(rootfd);		/* mntput() */
  		dprintf(kmsg, "holder %d: put on cpu %d\n", getpid(),
  			sched_getcpu());

  		/* the umount has returned, is the fd still good? */
  		read(p[0], &c, 1);
  		fd = openat(dirfd, "f", O_RDONLY);
  		ret = read(fd, buf, sizeof(buf));
  		dprintf(kmsg, "holder %d: fstat %d openat %d read %d\n",
  			getpid(), fstat(dirfd, &st), fd, ret);
  		_exit(0);
  	}

  	pin(1);
  	knob("100");
  	ret = umount2(MNT, 0);
  	knob("0");
  	if (ret)
  		dprintf(kmsg, "umount: %s\n", strerror(errno));
  	else
  		dprintf(kmsg, "umount: 0\n");
  	write(p[1], "x", 1);
  	waitpid(pid, NULL, 0);
  	return 0;
  }

The log shows the busy check summing 2 and when read right after it
reads 3. The loop read CPU 0 before the get landed there and CPU 3
after the put did. The umount succeeds while there's still someone
with an fd. The mount is then dropped when the fd goes:

  [    2.511944] holder 90: get on cpu 0
  [    2.512023] holder 90: put on cpu 3
  [    2.671388] mount 28: cpu0 read 1, now 2
  [    2.671398] mount 28: cpu1 read 2, now 2
  [    2.671401] mount 28: cpu2 read 0, now 0
  [    2.671404] mount 28: cpu3 read -1, now -1
  [    2.671407] mount 28: sum 2, re-sum 3, propagate_mount_busy
  [    2.671773] umount: 0
  [    2.672963] holder 90: fstat 0 openat 8 read 11
  [    2.673663] mount 28: last put by umount-torn (90)

Easy fix would be to order mntget() against the loop that sums up. But
that's a full barrier and a flag test in mntget() afaict. No bueno...
That would cost every path_get().

So learn from SRCU. Keep gets and puts in separate per-cpu counters and
make mnt_get_count() sum all puts first and all gets second with a full
barrier in between.

A put is always after its own get (hopefully...) independent of the cpu
the task is on. So a put the first pass counted must have its get
visible to the second pass. Ergo, we can't undercount.

This is the two pass sum srcu_readers_active_idx_check() does for
srcu_locks and srcu_unlocks. mntget() keeps a single
this_cpu_inc(). mntput() gains an smp_wmb() before its increment, a
compiler barrier on x86 and a store fence on the weakly ordered
architectures, since the two counters are two words and a put stored
after a get could otherwise become visible before it. The counters
are unsigned so the difference stays right after they wrap.

The count can still come out high when a get is counted whose put
the first pass did not see. We don't care. A task can only take a new
reference if:

(1) it holds one already
(2) through __legitimize_mnt() which the seqcount orders
    against the write side

So a get in this window means someone has a reference and -EBUSY is
correct. The final mntput() is unchanged. Once ->mnt_ns is NULL every
put takes the slow path under mount_lock and so no put is in flight
while it counts. Here's an attempted proof for that crap:

(i) A put never becomes visible before its get. Within one task the
    smp_wmb() in mntput_no_expire() orders every earlier store before the
    increment of the puts.

    Any task that migrated in between had its stores flushed by the
    scheduler. When that reference is handed to another task that's
    trivially true.

(ii) The double count in mnt_get_count() is ordered via smp_mb(). That
     keeps every read of the puts counter before every read of the gets
     counter.

(iii) We never undercount by (ii).

(iv) There are no spurious counts/differences. A get is counted only if
     it happened and a put is left out only if it hasn't happened when
     the counter was read. With (iii) this means it's equivalent to the
     old count whenever nothing is taken or dropped during the loop.

(v) do_umount() holds a reference and mount_lock hence. New references
    come from: (v.a) __legitimize_mnt() managed by the seqcount. Either
    it's smp_mb() made the get visible to the loop or it takes the lock
    and waits for the loop. By (iii) a mount with a additional
    references is refused. By (iv) refusing the umount just means that
    any additional reference existed, same as the old counter.

(vi) mntput_no_expire_slowpath() runs with ->mnt_ns already NULL. Hence,
     every other put must take the slowpath as well and hangs on mount_lock.

    The grace period in namespace_unlock() will make every fast path do
    a put that still saw ->mnt_ns.

    No put is in flight during the loop.

    A get that is in flight comes from a task holding another reference.
    That must be included by the count or from __legitimize_mnt() as in
    (v).

    Ergo, zero means the last reference and non-zero means a reference
    is live.

(vii) may_umount() uses propagate_mount_busy() as well and
      may_umount_tree() sums mnt_get_count() itself.

With this patch the race is dead:

  [    3.066786] mount 28: sum 3, re-sum 3, propagate_mount_busy
  [    3.105867] umount: Device or resource busy
  [    3.106047] holder 90: fstat 0 openat 8 read 11

Link: https://patch.msgid.link/20260923-work-mount-fixes-v1-7-f424cf8d3242@kernel.org
Fixes: b3e19d924b ("fs: scale mntget/mntput")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
History 2026-09-25 17:20:33 +02:00
arch * AMD device topology and machine check fixes 2026-09-08 12:54:56 -07:00
block block: save page offset gaps in cloned bio 2026-09-01 08:42:34 -06:00
certs treewide: fix indentation and whitespace in Kconfig files 2026-05-28 21:24:42 -07:00
crypto This push fixes a memory allocation overrun in crypto acomp. 2026-08-28 09:28:40 -07:00
Documentation Miscellaneous IRQ subsystem fixes: 2026-09-06 10:35:24 -07:00
drivers vduse: return compat ioctl results directly 2026-09-08 03:58:27 -04:00
fs fs: don't let a migrating task hide its reference from do_umount() 2026-09-25 17:20:33 +02:00
include Landlock fix for v7.3-rc3 2026-09-09 11:00:35 -07:00
init vfs-7.3-rc3.fixes 2026-09-09 09:38:03 -07:00
io_uring treewide: refresh kmalloc_obj() conversions 2026-09-04 21:37:00 -07:00
ipc mm.git review status for master..mm-nonmm-stable 2026-08-23 08:07:11 -07:00
kernel vfs-7.3-rc3.fixes 2026-09-09 09:38:03 -07:00
lib block: Fix start and length check added to iov_iter_extract_bvecs() 2026-09-10 09:20:39 +02:00
LICENSES LICENSES: Add modern form of the LGPL-2.1 tags to the usage guide section 2025-10-22 07:58:19 +02:00
mm treewide: refresh kmalloc_obj() conversions 2026-09-04 21:37:00 -07:00
net kmalloc_obj conversions for v7.3-rc2 2026-09-05 20:45:18 -07:00
rust rust: pci: reject IRQ vector indices that do not fit in u32 2026-09-01 18:21:01 +02:00
samples tracing fixes for v7.3: 2026-08-30 09:22:00 -07:00
scripts Coccinelle patches for Linux v7.3 2026-08-30 10:42:40 -07:00
security landlock: Test trace path output boundaries 2026-09-08 11:49:39 +02:00
sound treewide: refresh kmalloc_obj() conversions 2026-09-04 21:37:00 -07:00
tools selftests/filesystems: check that a busy propagated copy blocks a synchronous umount 2026-09-25 17:20:33 +02:00
usr usr: Correct a spelling by changing a letter 2026-08-05 14:51:58 +02:00
virt Arm: 2026-08-25 11:48:04 -07:00
.clang-format drm/bridge: rename drm_for_each_bridge_in_chain_scoped() to drm_for_each_bridge_in_chain() 2026-07-14 17:55:51 +02:00
.clippy.toml rust: bump Clippy's MSRV and clean incompatible_msrv allows 2026-04-07 09:51:39 +02:00
.cocciconfig scripts: add Linux .cocciconfig for coccinelle 2016-07-22 12:13:39 +02:00
.editorconfig editorconfig: add rst extension 2026-01-26 19:07:09 -08:00
.get_maintainer.ignore .get_maintainer.ignore: add Nathan Chancellor 2026-08-03 21:10:12 -07:00
.gitattributes .gitattributes: set diff driver for Rust source code files 2023-05-31 17:48:25 +02:00
.gitignore SPDX patches for 7.2-rc1 2026-06-22 12:06:22 -07:00
.mailmap drm fixes for 7.3-rc2 2026-09-04 13:42:16 -07:00
.pylintrc docs: Move the python libraries to tools/lib/python 2025-11-18 09:22:40 -07:00
.rustfmt.toml rust: add .rustfmt.toml 2022-09-28 09:02:20 +02:00
COPYING COPYING: state that all contributions really are covered by this file 2020-02-10 13:32:20 -08:00
CREDITS Driver core fixes for 7.3-rc2 2026-09-05 11:59:05 -07:00
Kbuild checksyscalls: move instance functionality into generic code 2026-04-05 09:21:32 +02:00
Kconfig io_uring: Rename KConfig to Kconfig 2025-02-19 14:53:27 -07:00
MAINTAINERS vhost,vdpa,virtio: fixes 2026-09-09 08:50:05 -07:00
Makefile Linux 7.3-rc2 2026-09-06 15:07:20 -07:00
README README: remove out of place emdashes 2026-07-15 10:51:29 -06:00

Linux kernel
============

The Linux kernel is the core of any Linux operating system. It manages hardware,
system resources, and provides the fundamental services for all other software.

Quick Start
-----------

* Report a bug: See Documentation/admin-guide/reporting-issues.rst
* Get the latest kernel: https://kernel.org
* Build the kernel: See Documentation/admin-guide/quickly-build-trimmed-linux.rst
* Join the community: https://lore.kernel.org/

Essential Documentation
-----------------------

All users should be familiar with:

* Building requirements: Documentation/process/changes.rst
* Code of Conduct: Documentation/process/code-of-conduct.rst
* License: See COPYING

Documentation can be built with make htmldocs or viewed online at:
https://www.kernel.org/doc/html/latest/


Who Are You?
============

Find your role below:

* New Kernel Developer: Getting started with kernel development
* Academic Researcher: Studying kernel internals and architecture
* Security Expert: Hardening and vulnerability analysis
* Backport/Maintenance Engineer: Maintaining stable kernels
* System Administrator: Configuring and troubleshooting
* Maintainer: Leading subsystems and reviewing patches
* Hardware Vendor: Writing drivers for new hardware
* Distribution Maintainer: Packaging kernels for distros
* AI Coding Assistant: LLMs and AI-powered development tools


For Specific Users
==================

New Kernel Developer
--------------------

Welcome! Start your kernel development journey here:

* Getting Started: Documentation/process/development-process.rst
* Your First Patch: Documentation/process/submitting-patches.rst
* Coding Style: Documentation/process/coding-style.rst
* Build System: Documentation/kbuild/index.rst
* Development Tools: Documentation/dev-tools/index.rst
* Kernel Hacking Guide: Documentation/kernel-hacking/hacking.rst
* Core APIs: Documentation/core-api/index.rst

Academic Researcher
-------------------

Explore the kernel's architecture and internals:

* Researcher Guidelines: Documentation/process/researcher-guidelines.rst
* Memory Management: Documentation/mm/index.rst
* Scheduler: Documentation/scheduler/index.rst
* Networking Stack: Documentation/networking/index.rst
* Filesystems: Documentation/filesystems/index.rst
* RCU (Read-Copy Update): Documentation/RCU/index.rst
* Locking Primitives: Documentation/locking/index.rst
* Power Management: Documentation/power/index.rst

Security Expert
---------------

Security documentation and hardening guides:

* Security Documentation: Documentation/security/index.rst
* LSM Development: Documentation/security/lsm-development.rst
* Self Protection: Documentation/security/self-protection.rst
* Reporting Vulnerabilities: Documentation/process/security-bugs.rst
* CVE Procedures: Documentation/process/cve.rst
* Embargoed Hardware Issues: Documentation/process/embargoed-hardware-issues.rst
* Security Features: Documentation/userspace-api/seccomp_filter.rst

Backport/Maintenance Engineer
-----------------------------

Maintain and stabilize kernel versions:

* Stable Kernel Rules: Documentation/process/stable-kernel-rules.rst
* Backporting Guide: Documentation/process/backporting.rst
* Applying Patches: Documentation/process/applying-patches.rst
* Subsystem Profile: Documentation/maintainer/maintainer-entry-profile.rst
* Git for Maintainers: Documentation/maintainer/configure-git.rst

System Administrator
--------------------

Configure, tune, and troubleshoot Linux systems:

* Admin Guide: Documentation/admin-guide/index.rst
* Kernel Parameters: Documentation/admin-guide/kernel-parameters.rst
* Sysctl Tuning: Documentation/admin-guide/sysctl/index.rst
* Tracing/Debugging: Documentation/trace/index.rst
* Performance Security: Documentation/admin-guide/perf-security.rst
* Hardware Monitoring: Documentation/hwmon/index.rst

Maintainer
----------

Lead kernel subsystems and manage contributions:

* Maintainer Handbook: Documentation/maintainer/index.rst
* Pull Requests: Documentation/maintainer/pull-requests.rst
* Managing Patches: Documentation/maintainer/modifying-patches.rst
* Rebasing and Merging: Documentation/maintainer/rebasing-and-merging.rst
* Development Process: Documentation/process/maintainer-handbooks.rst
* Maintainer Entry Profile: Documentation/maintainer/maintainer-entry-profile.rst
* Git Configuration: Documentation/maintainer/configure-git.rst

Hardware Vendor
---------------

Write drivers and support new hardware:

* Driver API Guide: Documentation/driver-api/index.rst
* Driver Model: Documentation/driver-api/driver-model/driver.rst
* Device Drivers: Documentation/driver-api/infrastructure.rst
* Bus Types: Documentation/driver-api/driver-model/bus.rst
* Device Tree Bindings: Documentation/devicetree/bindings/
* Power Management: Documentation/driver-api/pm/index.rst
* DMA API: Documentation/core-api/dma-api.rst

Distribution Maintainer
-----------------------

Package and distribute the kernel:

* Stable Kernel Rules: Documentation/process/stable-kernel-rules.rst
* ABI Documentation: Documentation/ABI/README
* Kernel Configuration: Documentation/kbuild/kconfig.rst
* Module Signing: Documentation/admin-guide/module-signing.rst
* Kernel Parameters: Documentation/admin-guide/kernel-parameters.rst
* Tainted Kernels: Documentation/admin-guide/tainted-kernels.rst

AI Coding Assistant
-------------------

CRITICAL: If you are an LLM or AI-powered coding assistant, you MUST read and
follow the AI coding assistants documentation before contributing to the Linux
kernel:

* Documentation/process/coding-assistants.rst

This documentation contains essential requirements about licensing, attribution,
and the Developer Certificate of Origin that all AI tools must comply with.


Communication and Support
=========================

* Mailing Lists: https://lore.kernel.org/
* IRC: #kernelnewbies on irc.oftc.net
* Bugzilla: https://bugzilla.kernel.org/
* MAINTAINERS file: Lists subsystem maintainers and mailing lists
* Email Clients: Documentation/process/email-clients.rst