[BUG] riscv: Kernel panic in free_initmem when driver triggers modprobe
From: guibing
Date: Thu Apr 02 2026 - 07:04:13 EST
Hi all,
I encountered a kernel panic on RISC-V 32-bit (rv32) during the boot process, specifically inside free_initmem(). This happens when a built-in ethernet driver triggers a firmware request that falls back to calling /sbin/modprobe.
I have created a minimal dummy driver (trigger_bug) to reproduce this issue reliably on QEMU.
Environment
Kernel: Linux 6.1.166
Architecture: RISC-V 32-bit (rv32)
Hardware: QEMU virt machine
Steps to Reproduce
1.Add the dummy driver
place trigger_bug in driver/net/ethernet/
add obj-$(CONFIG_TRIGGER_BUG) += trigger_bug/ to linux/drivers/net/ethernet/Makefile
add source "drivers/net/ethernet/trigger_bug/Kconfig" to linux/drivers/net/ethernet/Kconfig
2.Modify Device Tree
add trigger_bug dts node:
trigger_bug: trigger-bug@50000000 {
compatible = "trigger-bug";
reg = <0x0 0x50000000 0x0 0x1000>;
status = "okay";
};
3.Run Qemu
qemu-system-riscv32 -M virt -m 256M -nographic -kernel linux/arch/riscv/boot/Image -dtb qemu.dtb -initrd initramfs.cpio.gz.lz4 -append "console=ttyS0"
you will see the following OOPS:
[ 0.907212] goldfish_rtc 101000.rtc: setting system clock to 2026-04-02T07:59:36 UTC (1775116776)
[ 0.911464] syscon-poweroff poweroff: pm_power_off already claimed for sbi_srst_power_off
[ 0.912662] syscon-poweroff: probe of poweroff failed with error -16
[ 0.914795] sdhci: Secure Digital Host Controller Interface driver
[ 0.915143] sdhci: Copyright(c) Pierre Ossman
[ 0.915754] sdhci-pltfm: SDHCI platform and OF driver helper
[ 0.916948] usbcore: registered new interface driver usbhid
[ 0.917289] usbhid: USB HID core driver
[ 0.917937] riscv-pmu-sbi: SBI PMU extension is available
[ 0.918940] riscv-pmu-sbi: 16 firmware and 31 hardware counters
[ 0.919278] riscv-pmu-sbi: Perf sampling/filtering is not supported as sscof extension is not available
[ 0.923904] NET: Registered PF_INET6 protocol family
[ 0.933131] Segment Routing with IPv6
[ 0.933519] In-situ OAM (IOAM) with IPv6
[ 0.934309] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[ 0.938873] NET: Registered PF_PACKET protocol family
[ 0.940997] 9pnet: Installing 9P2000 support
[ 0.941546] Key type dns_resolver registered
[ 0.956257] debug_vm_pgtable: [debug_vm_pgtable ]: Validating architecture page table helpers
[ 0.967011] clk: Disabling unused clocks
[ 0.976583] Unable to handle kernel paging request at virtual address c0800000
[ 0.978314] Oops [#1]
[ 0.978461] Modules linked in:
[ 0.978823] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.1.166 #2
[ 0.979240] Hardware name: riscv-virtio,qemu (DT)
[ 0.979616] epc : __memset+0x58/0xf4
[ 0.979965] ra : free_reserved_area+0x166/0x1c0
[ 0.980229] epc : c0792ab0 ra : c016c33e sp : c1c43f30
[ 0.980488] gp : c18b8610 tp : c1c28000 t0 : c0800000
[ 0.980744] t1 : c0c12000 t2 : c18ba868 s0 : c1c43f50
[ 0.981000] s1 : 00000000 a0 : c0800000 a1 : cccccccc
[ 0.981248] a2 : 00001000 a3 : c0801000 a4 : 00000000
[ 0.981507] a5 : 80c00000 a6 : 00000800 a7 : 000000cc
[ 0.981758] s2 : c1121b18 s3 : 00000000 s4 : 00000000
[ 0.982009] s5 : 00000000 s6 : 00000000 s7 : 00000000
[ 0.982255] s8 : 00000000 s9 : 00000000 s10: 00000000
[ 0.982506] s11: 00000000 t3 : 00080400 t4 : c11a5b20
[ 0.982763] t5 : 000000ff t6 : c0000000
[ 0.982965] status: 00000120 badaddr: c0800000 cause: 0000000f
[ 0.983397] [<c0792ab0>] __memset+0x58/0xf4
[ 0.983755] [<c0003f42>] free_initmem+0x74/0x82
[ 0.983965] [<c079d2ea>] kernel_init+0x3a/0x106
[ 0.984208] [<c0003490>] ret_from_exception+0x0/0x16
[ 0.985116] ---[ end trace 0000000000000000 ]---
[ 0.985683] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[ 0.986385] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---
Cause Analysis
During kernel boot, before free_initmem() is called, if any device driver triggers /sbin/modprobe execution, the current process's mm_struct switches from init_mm to the user-space modprobe process. This causes a page table context switch, updating the satp (page table base address register).
The free_initmem() function calls set_memory_rw_nx() to modify page attributes to RW, non-X of the __init_begin section. but, set_memory_rw_nx() operates on init_mm's page tables (swapper_pg_dir). However, the subsequent free_initmem_default memset() that poisons the memory region uses satp that is the current process's mm_struct (the modprobe process) to translate the virtual address.
GDB Debug On Qemu
(gdb) b free_initmem
Breakpoint 7 at 0xc00022e8: free_initmem. (2 locations)
(gdb) b trigger_probe
Breakpoint 8 at 0xc079a198: file drivers/net/ethernet/trigger_bug/trigger_bug.c, line 9.
(gdb) c
Continuing.
Breakpoint 8, trigger_probe (pdev=0xc1cb7000)
at drivers/net/ethernet/trigger_bug/trigger_bug.c:9
9 printk(KERN_INFO "trigger_probe request_module start...\n");
(gdb) info r satp
satp 0x80081cbe -2146952002
(gdb) finish
Run till exit from #0 trigger_probe (pdev=0xc1cb7000)
at drivers/net/ethernet/trigger_bug/trigger_bug.c:9
platform_probe (_dev=0xc1cb7010)
at drivers/base/platform.c:1401
1401 if (ret)
Value returned is $1 = 0
(gdb) info r satp
satp 0x804821b5 -2142756427
(gdb) c
Continuing.
Breakpoint 7.2, free_initmem ()
at arch/riscv/kernel/setup.c:356
356 set_kernel_memory(lm_alias(__init_begin), lm_alias(__init_end), set_memory_rw_nx);
(gdb) s
set_kernel_memory (startp=<optimized out>,
endp=<optimized out>, set_memory=<optimized out>)
at ./arch/riscv/include/asm/set_memory.h:27
27 return set_memory(start, num_pages);
(gdb) s
set_memory_rw_nx (addr=3229614080, numpages=1043)
at arch/riscv/mm/pageattr.c:350
350 return __set_memory(addr, numpages, __pgprot(_PAGE_READ | _PAGE_WRITE),
(gdb) p /x 3229614080
$3 = 0xc0800000
(gdb) p /x __init_begin
$4 = 0xc0800000 <set_reset_devices>
(gdb) b pageattr_pmd_entry
Breakpoint 9 at 0xc0007b92: file arch/riscv/mm/pageattr.c, line 58.
(gdb) b pageattr_pte_entry
Breakpoint 10 at 0xc0007bc2: file arch/riscv/mm/pageattr.c, line 71.
(gdb) b free_initmem_default
Breakpoint 11 at 0xc00022f0: free_initmem_default. (2 locations)
(gdb) c
Continuing.
Breakpoint 9, pageattr_pmd_entry (
pmd=0xc18bec08 <swapper_pg_dir+3080>, addr=3229614080,
next=3233808384, walk=0xc1c43ef0)
at arch/riscv/mm/pageattr.c:58
58 pmd_t val = READ_ONCE(*pmd);
(gdb) c
Continuing.
Breakpoint 9, pageattr_pmd_entry (
pmd=0xc18bec0c <swapper_pg_dir+3084>, addr=3233808384,
next=3233886208, walk=0xc1c43ef0)
at arch/riscv/mm/pageattr.c:58
58 pmd_t val = READ_ONCE(*pmd);
(gdb) c
Continuing.
Breakpoint 11.2, free_initmem_default (poison=204)
at ./include/linux/mm.h:2681
2681 return free_reserved_area(&__init_begin, &__init_end,
(gdb) info r satp
satp 0x804821b5 -2142756427
(gdb) monitor xp /x (0x821B5000 + 770*4)
00000000821b5c08: 0x203000eb
(gdb) si
...
(gdb) si
0xc016c336 8617 memset(direct_map_addr, poison, PAGE_SIZE);
(gdb) si
0xc016c33a 8617 memset(direct_map_addr, poison, PAGE_SIZE);
(gdb) info r a0 a1 a2 satp
a0 0xc0800000 -1065353216
a1 0xcc 204
a2 0x1000 4096
satp 0x804821b5 -2142756427
(gdb) monitor xp /x (0x821B5000 + 770*4)
00000000821b5c08: 0x203000eb
(gdb)
Test Cases
I have try the following cases:
1).rv32 on kernel v5.10.252, there is no problem, but not strict kernel rwx.
2).rv64 on kernel v6.1.166, there is no problem.
3).rv32 on kernel v6.6.129/v6.18.19, it seems write wrong address, although it not happen crash.
4).use ko to load my driver, there is no problem.
I have attached the dummy driver source code for reference. Any suggestions would be appreciated.
Best regards,
Matthew.Gui
Attachment:
trigger_bug.tar.gz
Description: GNU Zip compressed data
Attachment:
qemu.dtb
Description: Binary data
/ {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "riscv-virtio";
model = "riscv-virtio,qemu";
poweroff {
value = <0x5555>;
offset = <0x00>;
regmap = <0x04>;
compatible = "syscon-poweroff";
};
reboot {
value = <0x7777>;
offset = <0x00>;
regmap = <0x04>;
compatible = "syscon-reboot";
};
platform-bus@4000000 {
interrupt-parent = <0x03>;
ranges = <0x00 0x00 0x4000000 0x2000000>;
#address-cells = <0x01>;
#size-cells = <0x01>;
compatible = "qemu,platform\0simple-bus";
};
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x00 0x10000000>;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <0x989680>;
cpu@0 {
phandle = <0x01>;
device_type = "cpu";
reg = <0x00>;
status = "okay";
compatible = "riscv";
riscv,cbop-block-size = <0x40>;
riscv,cboz-block-size = <0x40>;
riscv,cbom-block-size = <0x40>;
riscv,isa-extensions = "i\0m\0a\0f\0d\0c\0zic64b\0zicbom\0zicbop\0zicboz\0ziccamoa\0ziccif\0zicclsm\0ziccrse\0zicntr\0zicsr\0zifencei\0zihintntl\0zihintpause\0zihpm\0za64rs\0zca\0zcf\0zcd\0ssccptr\0sscounterenw\0sstvala\0sstvecd\0svadu";
riscv,isa-base = "rv32i";
riscv,isa = "rv32imafdc_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ziccrse_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_za64rs_zca_zcf_zcd_ssccptr_sscounterenw_sstvala_sstvecd_svadu";
mmu-type = "riscv,sv32";
interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x02>;
};
};
cpu-map {
cluster0 {
core0 {
cpu = <0x01>;
};
};
};
};
pmu {
riscv,event-to-mhpmcounters = <0x01 0x01 0xfffffff9 0x02 0x02 0xfffffffc 0x10019 0x10019 0xfffffff8 0x1001b 0x1001b 0xfffffff8 0x10021 0x10021 0xfffffff8>;
compatible = "riscv,pmu";
};
fw-cfg@10100000 {
dma-coherent;
reg = <0x00 0x10100000 0x00 0x18>;
compatible = "qemu,fw-cfg-mmio";
};
flash@20000000 {
bank-width = <0x04>;
reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>;
compatible = "cfi-flash";
};
chosen {
stdout-path = "/soc/serial@10000000";
rng-seed = <0x32b76173 0x6ee1b689 0xdf857f20 0xd2cc4ed5 0xc98f3fe 0xc0073360 0x37e758e0 0xe930863f>;
};
soc {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "simple-bus";
ranges;
rtc@101000 {
interrupts = <0x0b>;
interrupt-parent = <0x03>;
reg = <0x00 0x101000 0x00 0x1000>;
compatible = "google,goldfish-rtc";
};
serial@10000000 {
interrupts = <0x0a>;
interrupt-parent = <0x03>;
clock-frequency = <0x384000>;
reg = <0x00 0x10000000 0x00 0x100>;
compatible = "ns16550a";
};
test@100000 {
phandle = <0x04>;
reg = <0x00 0x100000 0x00 0x1000>;
compatible = "sifive,test1\0sifive,test0\0syscon";
};
virtio_mmio@10008000 {
interrupts = <0x08>;
interrupt-parent = <0x03>;
reg = <0x00 0x10008000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10007000 {
interrupts = <0x07>;
interrupt-parent = <0x03>;
reg = <0x00 0x10007000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10006000 {
interrupts = <0x06>;
interrupt-parent = <0x03>;
reg = <0x00 0x10006000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10005000 {
interrupts = <0x05>;
interrupt-parent = <0x03>;
reg = <0x00 0x10005000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10004000 {
interrupts = <0x04>;
interrupt-parent = <0x03>;
reg = <0x00 0x10004000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10003000 {
interrupts = <0x03>;
interrupt-parent = <0x03>;
reg = <0x00 0x10003000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10002000 {
interrupts = <0x02>;
interrupt-parent = <0x03>;
reg = <0x00 0x10002000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10001000 {
interrupts = <0x01>;
interrupt-parent = <0x03>;
reg = <0x00 0x10001000 0x00 0x1000>;
compatible = "virtio,mmio";
};
plic@c000000 {
phandle = <0x03>;
riscv,ndev = <0x5f>;
reg = <0x00 0xc000000 0x00 0x600000>;
interrupts-extended = <0x02 0x0b 0x02 0x09>;
interrupt-controller;
compatible = "sifive,plic-1.0.0\0riscv,plic0";
#address-cells = <0x00>;
#interrupt-cells = <0x01>;
};
clint@2000000 {
interrupts-extended = <0x02 0x03 0x02 0x07>;
reg = <0x00 0x2000000 0x00 0x10000>;
compatible = "sifive,clint0\0riscv,clint0";
};
pci@30000000 {
interrupt-map-mask = <0x1800 0x00 0x00 0x07>;
interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>;
ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x03 0x00 0x03 0x00 0x01 0x00>;
reg = <0x00 0x30000000 0x00 0x10000000>;
dma-coherent;
bus-range = <0x00 0xff>;
linux,pci-domain = <0x00>;
device_type = "pci";
compatible = "pci-host-ecam-generic";
#size-cells = <0x02>;
#interrupt-cells = <0x01>;
#address-cells = <0x03>;
};
trigger_bug: trigger-bug@50000000 {
compatible = "trigger-bug";
reg = <0x0 0x50000000 0x0 0x1000>;
status = "okay";
};
};
};