[BUG] riscv: module: single accumulated relocation indexes outside one-bucket hash table
Puranam, Ananta Srikar
srikar.puranam at wsu.edu
Fri Nov 6 19:28:23 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 whose relocation section contains exactly one
accumulated relocation (for example a single R_RISCV_ADD8) makes
apply_relocate_add() in arch/riscv/kernel/module.c access memory far
outside its relocation hash table. The module load oopses; with
panic_on_oops=1 the kernel panics.
This is a separate issue from the negative ULEB128 accumulation report I
sent earlier; the code and the fix are independent.
Trigger
=======
A minimal external module with one byte of data carrying a single
relocation:
R_RISCV_ADD8 value 1
A matched control with the same relocation twice at the same location
(two accumulated relocations, result 2) loads normally on the same
kernel and prints target=2.
Observed (current mainline, generic KASAN)
==========================================
With Ubuntu's default panic_on_oops=0:
Unable to handle kernel paging request at virtual address fffffffa2691673c
[fffffffa2691673c] pgd=0000000000000000, p4d=0000000000000000, ...
Oops [#1]
epc : apply_relocate_add+0x550/0xbfe
ra : apply_relocate_add+0x496/0xbfe
Call Trace:
apply_relocate_add+0x550/0xbfe
apply_relocations+0x1be/0x2ca
load_module+0x2db8/0x36c0
---[ end trace 0000000000000000 ]---
insmod is killed with SIGSEGV and the system stays up.
With panic_on_oops=1, the same fault (at fffffffa269941ac in that run)
ends in:
Kernel panic - not syncing: Fatal exception
The faulting address differs between runs because the bucket index is
derived from the relocation's location address; the fault itself
happened on every load. KASAN does not report it: the computed bucket
address is unmapped (pgd=0), so the access faults directly.
Tested
======
- Mainline a91337fb5e8e ("Merge tag 'configfs-7.4-rc2' ..."),
7.4.0-rc1-00590-ga91337fb5e8e, generic KASAN:
reproduced in 2/2 boots (one with panic_on_oops=0, one with
panic_on_oops=1); two-relocation control clean in 2/2
- Earlier validation: v6.12.57 2/2, v7.3.1 2/2, mainline
f66469d9905b (v7.4-rc1) 1/1; two-relocation control clean on
v6.12.57 and v7.3.1. On v7.3.1, singleton cases covering all 15
supported accumulated relocation types faulted in 34/34 runs, and
matched two-term controls loaded cleanly in 30/30.
Cause
=====
initialize_relocation_hashtable() sizes the table as
roundup_pow_of_two(num_relocations). For one accumulated relocation
that is one bucket, and hashtable_bits = ilog2(1) = 0. The comment
there says:
/*
* When hashtable_size == 1, hashtable_bits == 0.
* This is valid because the hashing algorithm returns 0 in this case.
*/
add_relocation_to_accumulate() then calls:
hash = hash_min((uintptr_t)location, hashtable_bits);
On 64-bit, hash_min() reaches hash_64_generic():
return val * GOLDEN_RATIO_64 >> (64 - bits);
With bits == 0 this is a shift by 64, which is undefined in C. On RV64
the shift instruction uses only the low six bits of the count, so the
shift is by 0 and the full product is returned. The resulting bucket
index is far outside the one-entry allocation, and
&relocation_hashtable[hash] is dereferenced.
The hash table code 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 or code execution. The demonstrated impact is a reliable
oops on a privileged module load, and a panic when panic_on_oops is
set.
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 uses bucket zero directly when hashtable_bits is zero
instead of calling hash_min(). On f66469d9905b with the patch applied,
the reproducer loaded successfully and printed target=1, 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