[BUG] riscv: module: negative accumulated ULEB128 relocation causes out-of-bounds write and CPU hang
Puranam, Ananta Srikar
srikar.puranam at wsu.edu
Fri Nov 6 16:52:41 PST 2026
Hi,
I'm Ananta Srikar Puranam, a PhD student at Washington State University.
I'm reporting this bug on behalf of my team: Dr. Swarn Priya and
Dr. Basavesh Ammanaghatta Shivakumar (both Cc'd).
Loading a RISC-V module in which an R_RISCV_SET_ULEB128 /
R_RISCV_SUB_ULEB128 pair accumulates to a negative value makes
apply_uleb128_accumulation() in arch/riscv/kernel/module.c write past
the end of the module allocation. With generic KASAN the write is
reported as a vmalloc out-of-bounds write; with or without KASAN the
loop then faults on the unmapped page after the allocation, the oops
faults again while it is being printed, and the CPU running insmod
stops responding (RCU stalls, NMI not answered).
Trigger
=======
A minimal external module with one 8-byte data object carrying two
relocations at the same offset:
R_RISCV_SET_ULEB128 value 1
R_RISCV_SUB_ULEB128 value 2
The accumulated value is 1 - 2 = -1. A matched control with the SUB
value changed to 1 (result 0) loads normally on the same kernel.
Observed (current mainline, generic KASAN)
==========================================
BUG: KASAN: vmalloc-out-of-bounds in apply_uleb128_accumulation+0x6e/0x8e
Write of size 1 at addr ffffffff03405000 by task insmod/935
Call Trace:
apply_uleb128_accumulation+0x6e/0x8e
apply_relocate_add+0x8fe/0xbfe
apply_relocations+0x1be/0x2ca
load_module+0x2db8/0x36c0
__riscv_sys_finit_module+0x31e/0x4cc
syscall_handler+0x7c/0xac
do_trap_ecall_u+0xb8/0x1de
handle_exception+0x16a/0x178
Unable to handle kernel paging request at virtual address fffffffefffffffc
Oops [#1]
Modules linked in:
Unable to handle kernel paging request at virtual address ffffffff03405000
...
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: 9-...0: (1 GPs behind) ...
After 10 seconds, these CPUS still haven't responded to the NMI: 9
The second paging fault occurs while the first oops is being printed;
the oops never completes, insmod is never killed, and the RCU grace
period never advances (same g= value in every later stall report).
Without KASAN (riscv defconfig only), the same module produces only:
Unable to handle kernel paging request at virtual address ffffffff018c6000
... pte=0000000000000000
Oops [#1]
Modules linked in:
Unable to handle kernel paging request at virtual address ffffffff018c6000
followed by the same permanent RCU stall. The faulting address is the
page-aligned guard page after the module allocation, and no backtrace
naming apply_uleb128_accumulation() is printed.
Tested
======
- Mainline a91337fb5e8e ("Merge tag 'configfs-7.4-rc2' ..."),
7.4.0-rc1-00590-ga91337fb5e8e:
generic KASAN: reproduced in 3/3 boots; control clean in 3/3
no KASAN: reproduced in 1/1 boot; control clean in 1/1
- Earlier validation: v6.12.57 2/2, v7.3.1 4/4, mainline
f66469d9905b (v7.4-rc1) 1/1; zero-result control clean on each.
Cause
=====
apply_r_riscv_set_uleb128() and apply_r_riscv_sub_uleb128() accumulate
into a signed long, and apply_uleb128_accumulation() encodes it with:
while (buffer != 0) {
u8 value = buffer & 0x7f;
buffer >>= 7;
value |= (!!buffer) << 7;
*p++ = value;
}
For a negative buffer the arithmetic shift never reaches zero, so *p++
advances until it faults. In addition, process_accumulated_relocations()
discards the accumulate handlers' return values and apply_relocate_add()
then returns 0, so a handler cannot currently reject the relocation.
The encoder was introduced by:
8fd6c5142395 ("riscv: Add remaining module relocations")
which is in v6.7-rc1 and later.
Impact
======
Triggering requires CONFIG_MODULES=y, RV64, and permission to load an
unsigned module (CAP_SYS_MODULE) in the tested configuration. Per
Documentation/process/threat-model.rst I am treating this as a regular
bug, not a vulnerability. I have not established unprivileged
reachability, a controlled overwrite, or code execution. The
demonstrated impact is an out-of-bounds kernel write followed by a hung
CPU on a privileged module load. Standard toolchains emit these pairs
as end - start and should not produce a negative value; the input is a
malformed module.
Setup
=====
Host: Ubuntu 26.04.1 LTS, x86_64
Toolchain: Ubuntu clang 21.1.8 / LLD 21.1.8
(make ARCH=riscv LLVM=-21, built with O=)
Config: arch/riscv/configs/defconfig plus
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y
CONFIG_KASAN_VMALLOC=y
(resolved with olddefconfig; full .config available)
Module: out-of-tree, built with M= against the same build tree
QEMU: 10.2.1, -machine virt -cpu rva23s64 -smp 16 -m 8G,
OpenSBI v1.8, no KVM (TCG)
Guest: Ubuntu 26.04 riscv64 rootfs (debootstrap) on virtio-blk;
module loaded with insmod over SSH, kernel log captured
from the serial console on the host
A tested source reproducer is available privately on request. Per
Documentation/process/security-bugs.rst I have not posted it to the
list.
Fix
===
A proposed patch rejects negative values in
apply_uleb128_accumulation() before writing, returns the first
accumulated-relocation error after freeing the temporary state, and
propagates it to the module loader. On f66469d9905b with the patch
applied, the reproducer is rejected with -EINVAL and a "negative value
-1" message, with no fault or KASAN report. I will send it as a
separate patch.
Tool transparency
=================
The root-cause analysis, reproducer and proposed fix were prepared with
the help of an AI coding assistant (OpenAI Codex). The QEMU
reproduction environment described above was set up with Anthropic's
Claude Code. I re-ran the reproduction myself and have checked the
statements in this report.
Thanks,
Srikar
More information about the linux-riscv
mailing list