Skip to content

Conversation

@bmastbergen
Copy link
Collaborator

Background

8fc8c2576054 do_change_type(): refuse to operate on unmounted/not ours mounts was backported to address CVE-2025-38498. There is an upstream bugfix for that in f28687afdef1 use uniform permission checks for all mount propagation changes, which has a prerequisite commit in d6f47bcb34a5 move_mount: allow to add a mount into an existing group and has its own upstream bugfix in 79b417103d1f fix propagation graph breakage by MOVE_MOUNT_SET_GROUP move_mount(2). Since none of the bugfixes (or bf prereqs) have CVE of their own, I've marked them all as cve-bf for CVE-2025-38498.

Besides that, everything stands alone.

All clean cherry-picks.

Commits

    firmware: arm_scpi: Ensure scpi_info is not assigned if the probe fails

    jira VULN-70087
    cve CVE-2022-50087
    commit-author Sudeep Holla <sudeep.holla@arm.com>
    commit 689640efc0a2c4e07e6f88affe6d42cd40cc3f85
    skbuff: Fix a race between coalescing and releasing SKBs

    jira VULN-154359
    cve CVE-2023-53186
    commit-author Liang Chen <liangchen.linux@gmail.com>
    commit 0646dc31ca886693274df5749cd0c8c1eaaeb5ca
    usb: dwc3: gadget: check that event count does not exceed event buffer length

    jira VULN-67717
    cve CVE-2025-37810
    commit-author Frode Isaksen <frode@meta.com>
    commit 63ccd26cd1f6600421795f6ca3e625076be06c9f
    net_sched: ets: Fix double list add in class with netem as child qdisc

    jira VULN-73373
    cve CVE-2025-37914
    commit-author Victor Nogueira <victor@mojatatu.com>
    commit 1a6d0c00fa07972384b0c308c72db091d49988b6
    i40e: fix MMIO write access to an invalid page in i40e_clear_hw

    jira VULN-72064
    cve CVE-2025-38200
    commit-author Kyungwook Boo <bookyungwook@gmail.com>
    commit 015bac5daca978448f2671478c553ce1f300c21e
    vsock: Fix transport_* TOCTOU

    jira VULN-80684
    cve CVE-2025-38461
    commit-author Michal Luczaj <mhal@rbox.co>
    commit 687aa0c5581b8d4aa87fd92973e4ee576b550cdf
    netfilter: nf_conntrack: fix crash due to removal of uninitialised entry

    jira VULN-89209
    cve CVE-2025-38472
    commit-author Florian Westphal <fw@strlen.de>
    commit 2d72afb340657f03f7261e9243b44457a9228ac7
    do_change_type(): refuse to operate on unmounted/not ours mounts

    jira VULN-98609
    cve CVE-2025-38498
    commit-author Al Viro <viro@zeniv.linux.org.uk>
    commit 12f147ddd6de7382dad54812e65f3f08d05809fc
    move_mount: allow to add a mount into an existing group

    jira VULN-98609
    cve-bf CVE-2025-38498
    commit-author Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
    commit 9ffb14ef61bab83fa818736bf3e7e6b6e182e8e2
    use uniform permission checks for all mount propagation changes

    jira VULN-98609
    cve-bf CVE-2025-38498
    commit-author Al Viro <viro@zeniv.linux.org.uk>
    commit cffd0441872e7f6b1fce5e78fb1c99187a291330
    fix propagation graph breakage by MOVE_MOUNT_SET_GROUP move_mount(2)

    jira VULN-98609
    cve-bf CVE-2025-38498
    commit-author Al Viro <viro@zeniv.linux.org.uk>
    commit d8cc0362f918d020ca1340d7694f07062dc30f36

Build Log

/home/brett/kernel-src-tree
Running make mrproper...
[TIMER]{MRPROPER}: 12s
x86_64 architecture detected, copying config
'configs/kernel-x86_64-rhel.config' -> '.config'
Setting Local Version for build
CONFIG_LOCALVERSION="-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103"
Making olddefconfig
--
  HOSTCC  scripts/kconfig/util.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#
Starting Build
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_32.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_64.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_x32.h
  SYSTBL  arch/x86/include/generated/asm/syscalls_32.h
  SYSHDR  arch/x86/include/generated/asm/unistd_32_ia32.h
--
  BTF [M] sound/virtio/virtio_snd.ko
  LD [M]  sound/xen/snd_xen_front.ko
  LD [M]  virt/lib/irqbypass.ko
  BTF [M] sound/xen/snd_xen_front.ko
  BTF [M] virt/lib/irqbypass.ko
[TIMER]{BUILD}: 1000s
Making Modules
  INSTALL /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/arch/x86/crypto/camellia-aesni-avx2.ko
  INSTALL /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/arch/x86/crypto/blake2s-x86_64.ko
  INSTALL /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko
  INSTALL /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/arch/x86/crypto/blowfish-x86_64.ko
--
  STRIP   /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/sound/xen/snd_xen_front.ko
  STRIP   /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/virt/lib/irqbypass.ko
  SIGN    /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/virt/lib/irqbypass.ko
  SIGN    /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+/kernel/sound/xen/snd_xen_front.ko
  DEPMOD  /lib/modules/5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+
[TIMER]{MODULES}: 7s
Making Install
sh ./arch/x86/boot/install.sh \
	5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+ arch/x86/boot/bzImage \
	System.map "/boot"
[TIMER]{INSTALL}: 60s
Checking kABI
kABI check passed
Setting Default Kernel to /boot/vmlinuz-5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+ and Index to 2
Hopefully Grub2.0 took everything ... rebooting after time metrices
[TIMER]{MRPROPER}: 12s
[TIMER]{BUILD}: 1000s
[TIMER]{MODULES}: 7s
[TIMER]{INSTALL}: 60s
[TIMER]{TOTAL} 1098s
Rebooting in 10 seconds

Testing

selftest-5.14.0-284.30.1.el9_2.92ciq_lts.12.1.x86_64-1.log

selftest-5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+-1.log

brett@lycia ~/ciq/many-92-vulns-10-15-25
 % grep ^ok selftest-5.14.0-284.30.1.el9_2.92ciq_lts.12.1.x86_64-1.log | wc -l
330
brett@lycia ~/ciq/many-92-vulns-10-15-25
 % grep ^ok selftest-5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+-1.log | wc -l
332
brett@lycia ~/ciq/many-92-vulns-10-15-25
 % grep ok <(diff -adU0 <(grep -a ^ok selftest-5.14.0-284.30.1.el9_2.92ciq_lts.12.1.x86_64-1.log | sort -h) <(grep -a ^ok selftest-5.14.0-bmastbergen_ciqlts9_2_many-vulns-10-15-2025-79b417103+-1.log | sort -h))
-ok 1 selftests: livepatch: test-livepatch.sh # SKIP
+ok 1 selftests: livepatch: test-livepatch.sh
-ok 1 selftests: zram: zram.sh # SKIP
+ok 1 selftests: zram: zram.sh
-ok 2 selftests: livepatch: test-callbacks.sh # SKIP
+ok 2 selftests: livepatch: test-callbacks.sh
+ok 32 selftests: net: l2tp.sh
-ok 3 selftests: livepatch: test-shadow-vars.sh # SKIP
+ok 3 selftests: livepatch: test-shadow-vars.sh
+ok 47 selftests: net: drop_monitor_tests.sh
-ok 4 selftests: livepatch: test-state.sh # SKIP
+ok 4 selftests: livepatch: test-state.sh
-ok 58 selftests: kvm: max_guest_memory_test
-ok 5 selftests: livepatch: test-ftrace.sh # SKIP
+ok 5 selftests: livepatch: test-ftrace.sh
+ok 9 selftests: net: test_bpf.sh
brett@lycia ~/ciq/many-92-vulns-10-15-25
 %

jira VULN-70087
cve CVE-2022-50087
commit-author Sudeep Holla <sudeep.holla@arm.com>
commit 689640e

When scpi probe fails, at any point, we need to ensure that the scpi_info
is not set and will remain NULL until the probe succeeds. If it is not
taken care, then it could result use-after-free as the value is exported
via get_scpi_ops() and could refer to a memory allocated via devm_kzalloc()
but freed when the probe fails.

Link: https://lore.kernel.org/r/20220701160310.148344-1-sudeep.holla@arm.com
	Cc: stable@vger.kernel.org # 4.19+
	Reported-by: huhai <huhai@kylinos.cn>
	Reviewed-by: Jackie Liu <liuyun01@kylinos.cn>
	Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
(cherry picked from commit 689640e)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-154359
cve CVE-2023-53186
commit-author Liang Chen <liangchen.linux@gmail.com>
commit 0646dc3

Commit 1effe8c ("skbuff: fix coalescing for page_pool fragment
recycling") allowed coalescing to proceed with non page pool page and page
pool page when @from is cloned, i.e.

to->pp_recycle    --> false
from->pp_recycle  --> true
skb_cloned(from)  --> true

However, it actually requires skb_cloned(@from) to hold true until
coalescing finishes in this situation. If the other cloned SKB is
released while the merging is in process, from_shinfo->nr_frags will be
set to 0 toward the end of the function, causing the increment of frag
page _refcount to be unexpectedly skipped resulting in inconsistent
reference counts. Later when SKB(@to) is released, it frees the page
directly even though the page pool page is still in use, leading to
use-after-free or double-free errors. So it should be prohibited.

The double-free error message below prompted us to investigate:
BUG: Bad page state in process swapper/1  pfn:0e0d1
page:00000000c6548b28 refcount:-1 mapcount:0 mapping:0000000000000000
index:0x2 pfn:0xe0d1
flags: 0xfffffc0000000(node=0|zone=1|lastcpupid=0x1fffff)
raw: 000fffffc0000000 0000000000000000 ffffffff00000101 0000000000000000
raw: 0000000000000002 0000000000000000 ffffffffffffffff 0000000000000000
page dumped because: nonzero _refcount

CPU: 1 PID: 0 Comm: swapper/1 Tainted: G            E      6.2.0+
Call Trace:
 <IRQ>
dump_stack_lvl+0x32/0x50
bad_page+0x69/0xf0
free_pcp_prepare+0x260/0x2f0
free_unref_page+0x20/0x1c0
skb_release_data+0x10b/0x1a0
napi_consume_skb+0x56/0x150
net_rx_action+0xf0/0x350
? __napi_schedule+0x79/0x90
__do_softirq+0xc8/0x2b1
__irq_exit_rcu+0xb9/0xf0
common_interrupt+0x82/0xa0
</IRQ>
<TASK>
asm_common_interrupt+0x22/0x40
RIP: 0010:default_idle+0xb/0x20

Fixes: 53e0961 ("page_pool: add frag page recycling support in page pool")
	Signed-off-by: Liang Chen <liangchen.linux@gmail.com>
	Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://lore.kernel.org/r/20230413090353.14448-1-liangchen.linux@gmail.com
	Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit 0646dc3)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
…r length

jira VULN-67717
cve CVE-2025-37810
commit-author Frode Isaksen <frode@meta.com>
commit 63ccd26

The event count is read from register DWC3_GEVNTCOUNT.
There is a check for the count being zero, but not for exceeding the
event buffer length.
Check that event count does not exceed event buffer length,
avoiding an out-of-bounds access when memcpy'ing the event.
Crash log:
Unable to handle kernel paging request at virtual address ffffffc0129be000
pc : __memcpy+0x114/0x180
lr : dwc3_check_event_buf+0xec/0x348
x3 : 0000000000000030 x2 : 000000000000dfc4
x1 : ffffffc0129be000 x0 : ffffff87aad60080
Call trace:
__memcpy+0x114/0x180
dwc3_interrupt+0x24/0x34

	Signed-off-by: Frode Isaksen <frode@meta.com>
Fixes: 72246da ("usb: Introduce DesignWare USB3 DRD Driver")
	Cc: stable <stable@kernel.org>
	Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/20250403072907.448524-1-fisaksen@baylibre.com
	Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 63ccd26)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-73373
cve CVE-2025-37914
commit-author Victor Nogueira <victor@mojatatu.com>
commit 1a6d0c0

As described in Gerrard's report [1], there are use cases where a netem
child qdisc will make the parent qdisc's enqueue callback reentrant.
In the case of ets, there won't be a UAF, but the code will add the same
classifier to the list twice, which will cause memory corruption.

In addition to checking for qlen being zero, this patch checks whether
the class was already added to the active_list (cl_is_active) before
doing the addition to cater for the reentrant case.

[1] https://lore.kernel.org/netdev/CAHcdcOm+03OD2j6R0=YHKqmy=VgJ8xEOKuP6c7mSgnp-TEJJbw@mail.gmail.com/

Fixes: 37d9cf1 ("sched: Fix detection of empty queues in child qdiscs")
	Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
	Signed-off-by: Victor Nogueira <victor@mojatatu.com>
Link: https://patch.msgid.link/20250425220710.3964791-4-victor@mojatatu.com
	Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit 1a6d0c0)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-72064
cve CVE-2025-38200
commit-author Kyungwook Boo <bookyungwook@gmail.com>
commit 015bac5

When the device sends a specific input, an integer underflow can occur, leading
to MMIO write access to an invalid page.

Prevent the integer underflow by changing the type of related variables.

	Signed-off-by: Kyungwook Boo <bookyungwook@gmail.com>
Link: https://lore.kernel.org/lkml/ffc91764-1142-4ba2-91b6-8c773f6f7095@gmail.com/T/
	Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
	Reviewed-by: Simon Horman <horms@kernel.org>
	Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
	Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
	Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
(cherry picked from commit 015bac5)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-80684
cve CVE-2025-38461
commit-author Michal Luczaj <mhal@rbox.co>
commit 687aa0c

Transport assignment may race with module unload. Protect new_transport
from becoming a stale pointer.

This also takes care of an insecure call in vsock_use_local_transport();
add a lockdep assert.

BUG: unable to handle page fault for address: fffffbfff8056000
Oops: Oops: 0000 [#1] SMP KASAN
RIP: 0010:vsock_assign_transport+0x366/0x600
Call Trace:
 vsock_connect+0x59c/0xc40
 __sys_connect+0xe8/0x100
 __x64_sys_connect+0x6e/0xc0
 do_syscall_64+0x92/0x1c0
 entry_SYSCALL_64_after_hwframe+0x4b/0x53

Fixes: c0cfa2d ("vsock: add multi-transports support")
	Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
	Signed-off-by: Michal Luczaj <mhal@rbox.co>
Link: https://patch.msgid.link/20250703-vsock-transports-toctou-v4-2-98f0eb530747@rbox.co
	Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit 687aa0c)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-89209
cve CVE-2025-38472
commit-author Florian Westphal <fw@strlen.de>
commit 2d72afb

A crash in conntrack was reported while trying to unlink the conntrack
entry from the hash bucket list:
    [exception RIP: __nf_ct_delete_from_lists+172]
    [..]
 #7 [ff539b5a2b043aa0] nf_ct_delete at ffffffffc124d421 [nf_conntrack]
 #8 [ff539b5a2b043ad0] nf_ct_gc_expired at ffffffffc124d999 [nf_conntrack]
 #9 [ff539b5a2b043ae0] __nf_conntrack_find_get at ffffffffc124efbc [nf_conntrack]
    [..]

The nf_conn struct is marked as allocated from slab but appears to be in
a partially initialised state:

 ct hlist pointer is garbage; looks like the ct hash value
 (hence crash).
 ct->status is equal to IPS_CONFIRMED|IPS_DYING, which is expected
 ct->timeout is 30000 (=30s), which is unexpected.

Everything else looks like normal udp conntrack entry.  If we ignore
ct->status and pretend its 0, the entry matches those that are newly
allocated but not yet inserted into the hash:
  - ct hlist pointers are overloaded and store/cache the raw tuple hash
  - ct->timeout matches the relative time expected for a new udp flow
    rather than the absolute 'jiffies' value.

If it were not for the presence of IPS_CONFIRMED,
__nf_conntrack_find_get() would have skipped the entry.

Theory is that we did hit following race:

cpu x 			cpu y			cpu z
 found entry E		found entry E
 E is expired		<preemption>
 nf_ct_delete()
 return E to rcu slab
					init_conntrack
					E is re-inited,
					ct->status set to 0
					reply tuplehash hnnode.pprev
					stores hash value.

cpu y found E right before it was deleted on cpu x.
E is now re-inited on cpu z.  cpu y was preempted before
checking for expiry and/or confirm bit.

					->refcnt set to 1
					E now owned by skb
					->timeout set to 30000

If cpu y were to resume now, it would observe E as
expired but would skip E due to missing CONFIRMED bit.

					nf_conntrack_confirm gets called
					sets: ct->status |= CONFIRMED
					This is wrong: E is not yet added
					to hashtable.

cpu y resumes, it observes E as expired but CONFIRMED:
			<resumes>
			nf_ct_expired()
			 -> yes (ct->timeout is 30s)
			confirmed bit set.

cpu y will try to delete E from the hashtable:
			nf_ct_delete() -> set DYING bit
			__nf_ct_delete_from_lists

Even this scenario doesn't guarantee a crash:
cpu z still holds the table bucket lock(s) so y blocks:

			wait for spinlock held by z

					CONFIRMED is set but there is no
					guarantee ct will be added to hash:
					"chaintoolong" or "clash resolution"
					logic both skip the insert step.
					reply hnnode.pprev still stores the
					hash value.

					unlocks spinlock
					return NF_DROP
			<unblocks, then
			 crashes on hlist_nulls_del_rcu pprev>

In case CPU z does insert the entry into the hashtable, cpu y will unlink
E again right away but no crash occurs.

Without 'cpu y' race, 'garbage' hlist is of no consequence:
ct refcnt remains at 1, eventually skb will be free'd and E gets
destroyed via: nf_conntrack_put -> nf_conntrack_destroy -> nf_ct_destroy.

To resolve this, move the IPS_CONFIRMED assignment after the table
insertion but before the unlock.

Pablo points out that the confirm-bit-store could be reordered to happen
before hlist add resp. the timeout fixup, so switch to set_bit and
before_atomic memory barrier to prevent this.

It doesn't matter if other CPUs can observe a newly inserted entry right
before the CONFIRMED bit was set:

Such event cannot be distinguished from above "E is the old incarnation"
case: the entry will be skipped.

Also change nf_ct_should_gc() to first check the confirmed bit.

The gc sequence is:
 1. Check if entry has expired, if not skip to next entry
 2. Obtain a reference to the expired entry.
 3. Call nf_ct_should_gc() to double-check step 1.

nf_ct_should_gc() is thus called only for entries that already failed an
expiry check. After this patch, once the confirmed bit check passes
ct->timeout has been altered to reflect the absolute 'best before' date
instead of a relative time.  Step 3 will therefore not remove the entry.

Without this change to nf_ct_should_gc() we could still get this sequence:

 1. Check if entry has expired.
 2. Obtain a reference.
 3. Call nf_ct_should_gc() to double-check step 1:
    4 - entry is still observed as expired
    5 - meanwhile, ct->timeout is corrected to absolute value on other CPU
      and confirm bit gets set
    6 - confirm bit is seen
    7 - valid entry is removed again

First do check 6), then 4) so the gc expiry check always picks up either
confirmed bit unset (entry gets skipped) or expiry re-check failure for
re-inited conntrack objects.

This change cannot be backported to releases before 5.19. Without
commit 8a75a2c ("netfilter: conntrack: remove unconfirmed list")
|= IPS_CONFIRMED line cannot be moved without further changes.

	Cc: Razvan Cojocaru <rzvncj@gmail.com>
Link: https://lore.kernel.org/netfilter-devel/20250627142758.25664-1-fw@strlen.de/
Link: https://lore.kernel.org/netfilter-devel/4239da15-83ff-4ca4-939d-faef283471bb@gmail.com/
Fixes: 1397af5 ("netfilter: conntrack: remove the percpu dying list")
	Signed-off-by: Florian Westphal <fw@strlen.de>
	Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
(cherry picked from commit 2d72afb)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-98609
cve CVE-2025-38498
commit-author Al Viro <viro@zeniv.linux.org.uk>
commit 12f147d

Ensure that propagation settings can only be changed for mounts located
in the caller's mount namespace. This change aligns permission checking
with the rest of mount(2).

	Reviewed-by: Christian Brauner <brauner@kernel.org>
Fixes: 07b2088 ("beginning of the shared-subtree proper")
	Reported-by: "Orlando, Noah" <Noah.Orlando@deshaw.com>
	Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
(cherry picked from commit 12f147d)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-98609
cve-bf CVE-2025-38498
commit-author Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
commit 9ffb14e

Previously a sharing group (shared and master ids pair) can be only
inherited when mount is created via bindmount. This patch adds an
ability to add an existing private mount into an existing sharing group.

With this functionality one can first create the desired mount tree from
only private mounts (without the need to care about undesired mount
propagation or mount creation order implied by sharing group
dependencies), and next then setup any desired mount sharing between
those mounts in tree as needed.

This allows CRIU to restore any set of mount namespaces, mount trees and
sharing group trees for a container.

We have many issues with restoring mounts in CRIU related to sharing
groups and propagation:
- reverse sharing groups vs mount tree order requires complex mounts
  reordering which mostly implies also using some temporary mounts
(please see https://lkml.org/lkml/2021/3/23/569 for more info)

- mount() syscall creates tons of mounts due to propagation
- mount re-parenting due to propagation
- "Mount Trap" due to propagation
- "Non Uniform" propagation, meaning that with different tricks with
  mount order and temporary children-"lock" mounts one can create mount
  trees which can't be restored without those tricks
(see https://www.linuxplumbersconf.org/event/7/contributions/640/)

With this new functionality we can resolve all the problems with
propagation at once.

Link: https://lore.kernel.org/r/20210715100714.120228-1-ptikhomirov@virtuozzo.com
	Cc: Eric W. Biederman <ebiederm@xmission.com>
	Cc: Alexander Viro <viro@zeniv.linux.org.uk>
	Cc: Christian Brauner <christian.brauner@ubuntu.com>
	Cc: Mattias Nissler <mnissler@chromium.org>
	Cc: Aleksa Sarai <cyphar@cyphar.com>
	Cc: Andrei Vagin <avagin@gmail.com>
	Cc: linux-fsdevel@vger.kernel.org
	Cc: linux-api@vger.kernel.org
	Cc: lkml <linux-kernel@vger.kernel.org>
Co-developed-by: Andrei Vagin <avagin@gmail.com>
	Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
	Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
	Signed-off-by: Andrei Vagin <avagin@gmail.com>
	Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
(cherry picked from commit 9ffb14e)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-98609
cve-bf CVE-2025-38498
commit-author Al Viro <viro@zeniv.linux.org.uk>
commit cffd044

do_change_type() and do_set_group() are operating on different
aspects of the same thing - propagation graph.  The latter
asks for mounts involved to be mounted in namespace(s) the caller
has CAP_SYS_ADMIN for.  The former is a mess - originally it
didn't even check that mount *is* mounted.  That got fixed,
but the resulting check turns out to be too strict for userland -
in effect, we check that mount is in our namespace, having already
checked that we have CAP_SYS_ADMIN there.

What we really need (in both cases) is
	* only touch mounts that are mounted.  That's a must-have
constraint - data corruption happens if it get violated.
	* don't allow to mess with a namespace unless you already
have enough permissions to do so (i.e. CAP_SYS_ADMIN in its userns).

That's an equivalent of what do_set_group() does; let's extract that
into a helper (may_change_propagation()) and use it in both
do_set_group() and do_change_type().

Fixes: 12f147d "do_change_type(): refuse to operate on unmounted/not ours mounts"
	Acked-by: Andrei Vagin <avagin@gmail.com>
	Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
	Tested-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
	Reviewed-by: Christian Brauner <brauner@kernel.org>
	Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
(cherry picked from commit cffd044)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
jira VULN-98609
cve-bf CVE-2025-38498
commit-author Al Viro <viro@zeniv.linux.org.uk>
commit d8cc036

9ffb14e "move_mount: allow to add a mount into an existing group"
breaks assertions on ->mnt_share/->mnt_slave.  For once, the data structures
in question are actually documented.

Documentation/filesystem/sharedsubtree.rst:
        All vfsmounts in a peer group have the same ->mnt_master.  If it is
	non-NULL, they form a contiguous (ordered) segment of slave list.

do_set_group() puts a mount into the same place in propagation graph
as the old one.  As the result, if old mount gets events from somewhere
and is not a pure event sink, new one needs to be placed next to the
old one in the slave list the old one's on.  If it is a pure event
sink, we only need to make sure the new one doesn't end up in the
middle of some peer group.

"move_mount: allow to add a mount into an existing group" ends up putting
the new one in the beginning of list; that's definitely not going to be
in the middle of anything, so that's fine for case when old is not marked
shared.  In case when old one _is_ marked shared (i.e. is not a pure event
sink), that breaks the assumptions of propagation graph iterators.

Put the new mount next to the old one on the list - that does the right thing
in "old is marked shared" case and is just as correct as the current behaviour
if old is not marked shared (kudos to Pavel for pointing that out - my original
suggested fix changed behaviour in the "nor marked" case, which complicated
things for no good reason).

	Reviewed-by: Christian Brauner <brauner@kernel.org>
Fixes: 9ffb14e ("move_mount: allow to add a mount into an existing group")
	Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
(cherry picked from commit d8cc036)
	Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
@bmastbergen bmastbergen requested a review from a team October 16, 2025 16:09
Copy link
Collaborator

@kerneltoast kerneltoast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interdiff reports no differences 🚢

Copy link
Collaborator

@PlaidCat PlaidCat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@bmastbergen bmastbergen merged commit d1b70b4 into ciqlts9_2 Oct 17, 2025
4 checks passed
@bmastbergen bmastbergen deleted the bmastbergen_ciqlts9_2/many-vulns-10-15-2025 branch October 17, 2025 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants