general protection fault in psi_task_change

Status: upstream: reported C repro on 2026/11/05 11:33
Subsystems: kernel
Labels: prio:normal
[Documentation on labels]
Reported-by: syzbot+66e09ade4298957334df@syzkaller.appspotmail.com
First crash: 1d06h, last: 1d06h
✨ AI-generated root cause summary:

In drivers/pci/proc.c, proc_bus_pci_write() allows writing directly to the PCI configuration space of PCI devices including the host bridge (device 00:00.0). Writing arbitrary data to host bridge configuration registers reconfigures physical memory routing registers (such as PCIEXBAR, PAM, and TOLUD), causing physical RAM regions backing kernel allocations (such as struct css_set) to become unmapped and return all-ones (0xff). When the scheduler invokes psi_task_change() -> task_psi_group() -> cgroup_psi(), reading cset->dfl_cgrp from the affected memory yields 0xffffffffffffffff (-1), causing cgroup_ino() to access an invalid offset that triggers a general protection fault and kernel panic.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 05441d697f7f:

Root Cause Analysis

1. Mechanism and Sequence of Events

The crash manifests during task wakeup in the scheduler's Pressure Stall Information (PSI) accounting path:

hrtimer_wakeup()
  -> wake_up_process()
    -> try_to_wake_up()
      -> ttwu_queue() -> ttwu_do_activate() -> activate_task()
        -> enqueue_task() -> psi_enqueue()
          -> psi_task_change()

Inside psi_task_change() (kernel/sched/psi.c:916), the kernel evaluates the PSI group for the awakened task:

for_each_group(group, task_psi_group(task))
    psi_group_change(group, cpu, clear, set, now, true);

task_psi_group(task) evaluates:

cgroup_psi(task_dfl_cgroup(task));

Where task_dfl_cgroup(task) reads task_css_set(task)->dfl_cgrp (include/linux/cgroup.h:607).

In the assembly of psi_task_change():

  1. Register %r13 contains task->cgroups (struct css_set *), pointing to 0xffff888054570000.
  2. The instruction mov 0x88(%r13), %r15 loads cset->dfl_cgrp (which is located at byte offset 0x88 of struct css_set) into %r15.
  3. cgroup_psi(cgrp) calls cgroup_ino(cgrp) (include/linux/psi.h:39):
    static inline struct psi_group *cgroup_psi(struct cgroup *cgrp)
    {
        return cgroup_ino(cgrp) == 1 ? &psi_system : cgrp->psi;
    }
    
  4. cgroup_ino(cgrp) accesses cgrp->kn (include/linux/cgroup.h:724), which is at offset 0x1a0 of struct cgroup:
    static inline ino_t cgroup_ino(struct cgroup *cgrp)
    {
        return kernfs_ino(cgrp->kn);
    }
    
  5. In KASAN-instrumented code, before dereferencing cgrp->kn, the compiler computes &cgrp->kn using lea 0x1a0(%r15), %rdi and checks its shadow memory byte.

However, the value loaded into %r15 was 0xffffffffffffffff (-1). Computing 0xffffffffffffffff + 0x1a0 wraps around in 64-bit arithmetic to 0x000000000000019f. Checking the KASAN shadow address for 0x19f results in 0xdffffc0000000000 + (0x19f >> 3) = 0xdffffc0000000033, which is a non-canonical address on x86-64. Accessing this address immediately triggers a General Protection Fault (#GP), which kasan_non_canonical_hook() decodes as a null-ptr-deref in range [0x0000000000000198-0x000000000000019f].

2. Root Cause

In normal kernel operation, cset->dfl_cgrp is never set to -1 or ERR_PTR; it always points to &cgrp_dfl_root.cgrp or an allocated struct cgroup.

The reproducer triggers this corruption through proc_bus_pci_write() in drivers/pci/proc.c:

  1. The reproducer mounts proc at ./proc and opens ./proc/bus/pci/00/00.0 with O_RDWR.
  2. It repeatedly writes "R-" and "-1\0" into the file descriptor.
  3. On the emulated platform (Q35 + ICH9), PCI device 00:00.0 is the Host Bridge / Memory Controller Hub (MCH).
  4. proc_bus_pci_write() does not protect against writing to critical host bridge configuration registers. Writing arbitrary bytes into the host bridge configuration space reconfigures internal physical address decoding registers (such as PCIEXBAR, PAM regions, and TOLUD).
  5. As a result, the physical memory range containing the physical frame for the struct css_set at 0xffff888054570000 is unmapped from system DRAM and remapped as unassigned/floating MMIO space.
  6. Subsequent memory reads to that physical address range return unassigned bus reads (0xff bytes). Thus, reading cset->dfl_cgrp at 0xffff888054570088 reads eight 0xff bytes (0xffffffffffffffff), while KASAN shadow memory (residing in a different physical address range) remains intact.
  7. When the scheduler encounters cset->dfl_cgrp == 0xffffffffffffffff, the kernel attempts to dereference the wild pointer, causing a fatal general protection fault.
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
ca79d52b assessment-security DenialOfService: ✅ Exploitable: ✅ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ✅ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ general protection fault in psi_task_change 2026/11/04 08:52 2026/11/04 08:53 2026/11/04 09:30 5af5b6c6
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [kernel?] general protection fault in psi_task_change 0 (1) 2026/11/05 11:33
▶ ▼ Similar bugs (1)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream KMSAN: uninit-value in psi_task_change kernel 7 1 395d 391d 0/29 auto-obsoleted due to no activity on 2026/01/14 14:12

Sample crash report:
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000033: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000198-0x000000000000019f]
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:cgroup_ino include/linux/cgroup.h:724 [inline]
RIP: 0010:cgroup_psi include/linux/psi.h:39 [inline]
RIP: 0010:task_psi_group kernel/sched/psi.c:884 [inline]
RIP: 0010:task_psi_group kernel/sched/psi.c:880 [inline]
RIP: 0010:psi_task_change+0x20f/0x530 kernel/sched/psi.c:916
Code: 03 80 3c 02 00 0f 85 ff 02 00 00 4d 8b bd 88 00 00 00 48 b8 00 00 00 00 00 fc ff df 49 8d bf a0 01 00 00 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 e0 02 00 00 4d 8b af a0 01 00 00 48 ba 00 00 00
RSP: 0018:ffffc900006a0c68 EFLAGS: 00010007
RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000001
RDX: 0000000000000033 RSI: ffffffff8e60120c RDI: 000000000000019f
RBP: 0000000000000004 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000001
R13: ffff888054570000 R14: 0000000000000001 R15: ffffffffffffffff
FS:  0000000000000000(0000) GS:ffff8880d457c000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f5894989140 CR3: 00000000372ab000 CR4: 0000000000352ef0
Call Trace:
 <IRQ>
 psi_enqueue kernel/sched/stats.h:159 [inline]
 enqueue_task+0x1dc/0x4f0 kernel/sched/core.c:2199
 activate_task kernel/sched/core.c:2237 [inline]
 ttwu_do_activate+0x174/0x800 kernel/sched/core.c:3897
 ttwu_queue kernel/sched/core.c:4150 [inline]
 try_to_wake_up+0xce0/0x1ca0 kernel/sched/core.c:4488
 hrtimer_wakeup+0x47/0x60 kernel/time/hrtimer.c:2335
 __run_hrtimer kernel/time/hrtimer.c:2079 [inline]
 __hrtimer_run_queues+0x448/0x9a0 kernel/time/hrtimer.c:2136
 hrtimer_interrupt+0x441/0xaa0 kernel/time/hrtimer.c:2255
 local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1051 [inline]
 __sysvec_apic_timer_interrupt+0x109/0x470 arch/x86/kernel/apic/apic.c:1068
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
 sysvec_apic_timer_interrupt+0x9e/0xc0 arch/x86/kernel/apic/apic.c:1062
 </IRQ>
 <TASK>
 asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:64
Code: 67 a1 02 e9 43 61 03 00 0f 1f 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d c3 7d 1d 00 fb f4 <c3> cc cc cc cc 66 2e 0f 1f 84 00 00 00 00 00 66 90 90 90 90 90 90
RSP: 0018:ffffc90000177e00 EFLAGS: 00000206
RAX: 000000000003cdf9 RBX: ffff88802069a580 RCX: ffffffff8bee4325
RDX: 0000000000000001 RSI: ffffffff8e628221 RDI: ffffffff8c835380
RBP: ffffed10040d34b0 R08: 0000000000000001 R09: ffffed100d426825
R10: ffff88806a13412b R11: 0000000000000001 R12: 0000000000000000
R13: 0000000000000000 R14: 1ffff9200002efc4 R15: dffffc0000000000
 arch_safe_halt arch/x86/include/asm/paravirt.h:62 [inline]
 default_idle+0x9/0x10 arch/x86/kernel/process.c:768
 default_idle_call+0x6c/0xb0 kernel/sched/idle.c:122
 cpuidle_idle_call kernel/sched/idle.c:199 [inline]
 do_idle+0x3ae/0x5b0 kernel/sched/idle.c:355
 cpu_startup_entry+0x4f/0x60 kernel/sched/idle.c:458
 start_secondary+0x21d/0x2d0 arch/x86/kernel/smpboot.c:312
 common_startup_64+0x13e/0x158
 </TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:cgroup_ino include/linux/cgroup.h:724 [inline]
RIP: 0010:cgroup_psi include/linux/psi.h:39 [inline]
RIP: 0010:task_psi_group kernel/sched/psi.c:884 [inline]
RIP: 0010:task_psi_group kernel/sched/psi.c:880 [inline]
RIP: 0010:psi_task_change+0x20f/0x530 kernel/sched/psi.c:916
Code: 03 80 3c 02 00 0f 85 ff 02 00 00 4d 8b bd 88 00 00 00 48 b8 00 00 00 00 00 fc ff df 49 8d bf a0 01 00 00 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 e0 02 00 00 4d 8b af a0 01 00 00 48 ba 00 00 00
RSP: 0018:ffffc900006a0c68 EFLAGS: 00010007
RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000001
RDX: 0000000000000033 RSI: ffffffff8e60120c RDI: 000000000000019f
RBP: 0000000000000004 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000001
R13: ffff888054570000 R14: 0000000000000001 R15: ffffffffffffffff
FS:  0000000000000000(0000) GS:ffff8880d457c000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f5894989140 CR3: 00000000372ab000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess):
   0:	03 80 3c 02 00 0f    	add    0xf00023c(%rax),%eax
   6:	85 ff                	test   %edi,%edi
   8:	02 00                	add    (%rax),%al
   a:	00 4d 8b             	add    %cl,-0x75(%rbp)
   d:	bd 88 00 00 00       	mov    $0x88,%ebp
  12:	48 b8 00 00 00 00 00 	movabs $0xdffffc0000000000,%rax
  19:	fc ff df
  1c:	49 8d bf a0 01 00 00 	lea    0x1a0(%r15),%rdi
  23:	48 89 fa             	mov    %rdi,%rdx
  26:	48 c1 ea 03          	shr    $0x3,%rdx
* 2a:	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1) <-- trapping instruction
  2e:	0f 85 e0 02 00 00    	jne    0x314
  34:	4d 8b af a0 01 00 00 	mov    0x1a0(%r15),%r13
  3b:	48                   	rex.W
  3c:	ba                   	.byte 0xba
  3d:	00 00                	add    %al,(%rax)

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/04 08:52 upstream 05441d697f7f 5af5b6c6 .config console log report syz / log C [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu-upstream general protection fault in psi_task_change
* Struck through repros no longer work on HEAD.