Describe the bug
After executing an HLVX instruction, the global hlvx flag at mmu.c:73 is not cleared for subsequent store instructions. This causes get_effective_address() at mmu.c:198 to bypass pointer masking (PMM / Zjpm) entirely for stores that follow an HLVX read.
The problem: rvh_hlvx_check() (which sets/clears hlvx) is called only from vaddr_read_internal() (the read path). It is never called from vaddr_write() (the write path), and nothing else resets hlvx between instructions. So after an HLVX executes, the next store still sees hlvx=true and returns the raw virtual address — bypassing all PMM logic.
Affected code locations:
mmu.c:73-74 — hlvx and hld_st are global flags, not per-instruction state
priv.c:2987-2991 — rvh_hlvx_check() sets hlvx from the current instruction's opcode; called only on the read path
vaddr.c:160-162 — rvh_hlvx_check() is invoked inside vaddr_read_internal() (read only)
vaddr.c:243 — vaddr_write() calls get_effective_address(addr, MEM_TYPE_WRITE) without resetting hlvx
mmu.c:198 — if (type == MEM_TYPE_IFETCH || hlvx) return vaddr; — the early return that bypasses all downstream PMM/mode/MPRV logic
Trigger chain:
HLVX.WU a1, (a0) # rvh_hlvx_check → hlvx = true
# get_effective_address(MEM_TYPE_READ) sees hlvx → returns vaddr (correct for HLVX)
sd t1, 0(t0) # vaddr_write → get_effective_address(MEM_TYPE_WRITE)
# hlvx is STILL true → returns vaddr directly — PMM BYPASSED
# (Should have applied PMM, e.g. stripped pointer metadata bits)
PoC design rationale:
Since PMM is meant to strip metadata from the upper masked bits of pointers, the PoC constructs a pointer with metadata in bits [63:48] that a correct PMM implementation would clear:
- menvcfg.PMM = 3 → masked_width = 16 (unsigned path for BARE mode)
- Store address:
0xFFFF000080000040 (metadata 0xFFFF in upper 16 bits, real address 0x80000040)
- Correct PMM (unsigned path):
(uint64_t)vaddr << 16 >> 16 → 0x00000000_80000040 — in pmem (MBASE=0x80000000, MSIZE=0x7ff80000000)
- Stale hlvx bypass: effective address stays
0xFFFF000080000040 — NOT in pmem → store access fault (mcause=0x7)
The metadata address 0xFFFF000080000040 is constructed purely via register arithmetic to avoid any intervening data load that would accidentally reset hlvx:
addi t0, zero, -1 # t0 = 0xFFFFFFFFFFFFFFFF
slli t0, t0, 48 # t0 = 0xFFFF000000000000
addi t1, zero, 1 # t1 = 1
slli t1, t1, 31 # t1 = 0x80000000
addi t1, t1, 0x40 # t1 = 0x80000040
add t0, t0, t1 # t0 = 0xFFFF000080000040 (5 insns, zero loads)
To Reproduce
Build the PoC:
source /xs-env/env.sh
cd /testcase/pmm-hlvx
make ARCH=riscv64-xs
Run with standalone NEMU (confirming the bug directly):
$NEMU_HOME/build/riscv64-nemu-interpreter -I 30000 -b \
/testcase/pmm-hlvx/build/pmm-hlvx-riscv64-xs.bin
Run with XiangShan emu + NEMU .so difftest (shows store commit mismatch):
cd $NOOP_HOME
./build/emu -i /testcase/pmm-hlvx/build/pmm-hlvx-riscv64-xs.elf \
--diff=$NOOP_HOME/ready-to-run/riscv64-nemu-interpreter-so -C 100000
pmm-hlvx.tar.gz
Expected behavior
The store after HLVX should apply PMM: 0xFFFF000080000040 → 0x80000040, writing to pmem successfully.
Actual behavior
- Standalone NEMU:
_halt(1) — trap_mcause = 0x7 (store access fault). PoC's self-check prints === BUG CONFIRMED ===.
- XiangShan difftest: Store commit mismatch between DUT (XiangShan, PMM correctly applied → commits at
0x80000040) and REF (NEMU .so, hlvx stale → store faults, never commits).
NEMU standalone output:
[*] PoC for Finding 11: PMM/hlvx stale flag bug
[*]
[*] After HLVX read, global 'hlvx' at mmu.c:73 remains true
[*] on subsequent store (vaddr_write → get_effective_address).
[*] mmu.c:198: if (hlvx) return vaddr; -- bypasses PMM entirely.
[*]
[*] Test: HLVX then store to 0xFFFF000080000040 w/ menvcfg.PMM=3
[*] PMM applied: 0xFFFF000080000040 -> 0x80000040 (in pmem)
[*] PMM bypassed: 0xFFFF000080000040 -> 0xFFFF000080000040 (not in pmem!) → access fault
[*]
[*] Entering HS-mode via mret ...
[*] === BUG CONFIRMED ===
[*] Store faulted: hlvx was stale, PMM bypassed,
[*] SD addr 0xFFFF000080000040 was NOT mapped to pmem.
[*] trap_mcause = 0x7 (expect 0x7 = store access fault)
[*] ---
[*] Root cause: mmu.c:198 'if (hlvx) return vaddr' skips PMM
[*] for MEM_TYPE_WRITE calls from vaddr_write (vaddr.c:243).
[*] hlvx is set by rvh_hlvx_check (priv.c:2989) on read path only.
nemu_trap case 1
HIT BAD TRAP at pc = 0x00000000800003ec
XiangShan difftest output (store commit mismatch):
NEMU does not commit any store instruction.
============== Store Commit Event (Core 0) ==============
Mismatch for store commits
REF commits addr 0x0000000080000040, data 0x00000000deadbeef, mask 0x00ff, pc 0x000000008000019a
DUT commits addr 0x0000000080000040, data 0x00000000deadbeef, mask 0x00ff, pc 0x00000000800001be, robidx 0x94
Core 0: ABORT at pc = 0x80000ee0
Note: The REF (NEMU) commits at pc=0x8000019a (the store that sets test_result=1, which happened before the critical SD) but the critical SD at pc=0x800001be faulted in REF. DUT (XiangShan RTL) correctly applies PMM and commits both stores at their original PCs.
Necessary information on versions
- NEMU: commit
8dba04c (standard riscv64-xs-ref_defconfig)
- XiangShan RTL: commit
7be121c71f
- PoC sources:
/testcase/pmm-hlvx/src/main.c, /testcase/pmm-hlvx/src/entry.S
Additional context
The hlvx flag serves a legitimate purpose: HLVX instructions should bypass PMM (they operate on guest physical addresses). The bug is that the flag's lifetime extends beyond the HLVX instruction itself into subsequent stores, because no code path resets it on the write side. A fix should reset hlvx (and hld_st) at instruction boundaries, or have get_effective_address independently determine the HLVX status for each access type rather than relying on a persistent global.
Describe the bug
After executing an HLVX instruction, the global
hlvxflag atmmu.c:73is not cleared for subsequent store instructions. This causesget_effective_address()atmmu.c:198to bypass pointer masking (PMM / Zjpm) entirely for stores that follow an HLVX read.The problem:
rvh_hlvx_check()(which sets/clearshlvx) is called only fromvaddr_read_internal()(the read path). It is never called fromvaddr_write()(the write path), and nothing else resetshlvxbetween instructions. So after an HLVX executes, the next store still seeshlvx=trueand returns the raw virtual address — bypassing all PMM logic.Affected code locations:
mmu.c:73-74—hlvxandhld_stare global flags, not per-instruction statepriv.c:2987-2991—rvh_hlvx_check()setshlvxfrom the current instruction's opcode; called only on the read pathvaddr.c:160-162—rvh_hlvx_check()is invoked insidevaddr_read_internal()(read only)vaddr.c:243—vaddr_write()callsget_effective_address(addr, MEM_TYPE_WRITE)without resettinghlvxmmu.c:198—if (type == MEM_TYPE_IFETCH || hlvx) return vaddr;— the early return that bypasses all downstream PMM/mode/MPRV logicTrigger chain:
PoC design rationale:
Since PMM is meant to strip metadata from the upper masked bits of pointers, the PoC constructs a pointer with metadata in bits [63:48] that a correct PMM implementation would clear:
0xFFFF000080000040(metadata0xFFFFin upper 16 bits, real address0x80000040)(uint64_t)vaddr << 16 >> 16→0x00000000_80000040— in pmem (MBASE=0x80000000, MSIZE=0x7ff80000000)0xFFFF000080000040— NOT in pmem → store access fault (mcause=0x7)The metadata address
0xFFFF000080000040is constructed purely via register arithmetic to avoid any intervening data load that would accidentally resethlvx:To Reproduce
Build the PoC:
Run with standalone NEMU (confirming the bug directly):
$NEMU_HOME/build/riscv64-nemu-interpreter -I 30000 -b \ /testcase/pmm-hlvx/build/pmm-hlvx-riscv64-xs.binRun with XiangShan emu + NEMU .so difftest (shows store commit mismatch):
pmm-hlvx.tar.gz
Expected behavior
The store after HLVX should apply PMM:
0xFFFF000080000040→0x80000040, writing to pmem successfully.Actual behavior
_halt(1)—trap_mcause = 0x7(store access fault). PoC's self-check prints=== BUG CONFIRMED ===.0x80000040) and REF (NEMU .so, hlvx stale → store faults, never commits).NEMU standalone output:
XiangShan difftest output (store commit mismatch):
Note: The REF (NEMU) commits at
pc=0x8000019a(the store that setstest_result=1, which happened before the critical SD) but the critical SD atpc=0x800001befaulted in REF. DUT (XiangShan RTL) correctly applies PMM and commits both stores at their original PCs.Necessary information on versions
8dba04c(standardriscv64-xs-ref_defconfig)7be121c71f/testcase/pmm-hlvx/src/main.c,/testcase/pmm-hlvx/src/entry.SAdditional context
The
hlvxflag serves a legitimate purpose: HLVX instructions should bypass PMM (they operate on guest physical addresses). The bug is that the flag's lifetime extends beyond the HLVX instruction itself into subsequent stores, because no code path resets it on the write side. A fix should resethlvx(andhld_st) at instruction boundaries, or haveget_effective_addressindependently determine the HLVX status for each access type rather than relying on a persistent global.