[PATCH] riscv: fix load_unaligned_zeropad() fixup for RV32

Karl Mehltretter kmehltretter at gmail.com
Mon Sep 7 12:07:50 PDT 2026


The fixup for load_unaligned_zeropad() runs when REG_L crosses into an
unmapped page. REG_L is lw on RV32 and ld on RV64, but the fixup assumes
an 8-byte load: it rounds the address down to 8 bytes and derives the
shift from three address bits.

On RV32, a load starting in the last three bytes of a mapped page selects
the word four bytes earlier and shifts it by 40, 48, or 56 bits, more than
the register width. The caller gets earlier bytes instead of the string
tail followed by zeroes. This affects RV32 MMU kernels built with
RISCV_EFFICIENT_UNALIGNED_ACCESS=y, which selects DCACHE_WORD_ACCESS
and lets dcache name hashing and comparison and strscpy() use this
path.

Derive the alignment and offset masks from sizeof(data) instead of a fixed
8. This makes RV32 use the correct source word. RV64 code is unchanged: the
complete extable.o is byte-for-byte identical before and after.

Fixes: d0fdc20b0429 ("riscv: select DCACHE_WORD_ACCESS for efficient unaligned access HW")
Cc: stable at vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter at gmail.com>
---
Tested on RV32 QEMU virt/TCG with a KUnit guard-page test. Before, the last
three offsets returned fill bytes from the previous word (0xa5, 0xa5a5, and
0xa5a5a5) instead of the string tail (0x44, 0x4433, and 0x443322), and the
suite failed. After, all three cases and the suite passed.

Built full RV32 Images for both sides with GCC 15.2.0. The complete RV64
extable.o is byte-for-byte identical before and after the change.

 arch/riscv/mm/extable.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/mm/extable.c b/arch/riscv/mm/extable.c
index dd1530af3ef1..e252eba55ede 100644
--- a/arch/riscv/mm/extable.c
+++ b/arch/riscv/mm/extable.c
@@ -68,8 +68,8 @@ ex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
 
 	addr = regs_get_gpr(regs, reg_addr * sizeof(unsigned long));
 
-	offset = addr & 0x7UL;
-	addr &= ~0x7UL;
+	offset = addr & (sizeof(data) - 1);
+	addr &= ~(sizeof(data) - 1);
 
 	data = *(unsigned long *)addr >> (offset * 8);
 
-- 
2.53.0



More information about the linux-riscv mailing list