The issue
RVVM does not raise an illegal-instruction exception when the seed CSR is accessed using a read-only CSR access form such as:
According to the Zkr specification, seed is defined at CSR address 0x015. In Machine mode, it must be accessed via a CSR read-write instruction form. An attempted read without a write operand must raise an illegal-instruction exception.
In my test, no trap was raised. Instead of taking an illegal-instruction exception with mcause = 2, the access completed successfully and returned a seed value.
This looks like a specification mismatch in RVVM's CSR handling for seed.
Steps to reproduce
Build this bare-metal test case:
Build command used:
riscv64-unknown-elf-gcc \
-march=rv64imafdch_zicfiss_zicbom_zicboz_v_zicsr_zca_zimop_zcmop_zbb_zbs_zkne_zbkb_zabha_zacas_zawrs_zkr_smepmp_zcb_zicond_zba_zknd_zbc_zbkc_zfh_zfbfmin_zfhmin_zfa_zifencei_zvfbfmin_zbkx_zvksed_zvksh_zvknha_zvknhb_zvkg_zvfbfwma_zvbc_zvbb_zvkned_zksed_zksh_zknh_zvkb_zicbop_zicfilp_svinval_zve32f \
-mabi=lp64 \
-mcmodel=medany \
-nostdlib \
-nostartfiles \
-T linker.ld \
code.S machine_to_supervisor.S machine_to_user.S \
-o code.elf
Run RVVM:
rvvm code.elf -m 256M -nogui -serial null -gdbstub
Connect with GDB:
riscv64-unknown-elf-gdb code.elf
set pagination off
target remote :1234
Continue execution and inspect the result register(s) used by the test or trap harness.
Expected architectural result:
Observed result:
No illegal-instruction exception was raised.
Investigation
I stumbled upon this while checking whether RVVM enforces the Zkr access rules for the seed CSR.
The current implementation appears to treat seed like a normal readable CSR whenever riscv_csr_seed_enabled(vm) is true.
Old code:
// RVVM/src/riscv_csr.c
static inline bool riscv_csr_seed(rvvm_hart_t* vm, rvvm_uxlen_t* dest)
{
if (riscv_csr_seed_enabled(vm)) {
uint16_t seed = 0;
rvvm_randombytes(&seed, sizeof(seed));
return riscv_csr_const(dest, seed);
}
return false;
}
case CSR_SEED:
return riscv_csr_seed(vm, dest);
This path does not distinguish between CSR read-write access and read-only CSR access forms, so csrr succeeds instead of faulting.
Workarounds
I do not know of a guest-side workaround other than avoiding read-only CSR access forms for seed.
Using the architecturally correct read-write CSR form instead of csrr avoids the mismatch.
For emulator-side testing, modifying RVVM to reject non-CSR_SWAP accesses to seed fixes the issue.
Suggested fix / Expected behavior
RVVM should reject read-only CSR access forms for seed and raise an illegal-instruction exception instead of returning random data.
A possible fix is to gate seed access on the CSR operation type.
Suggested change:
// RVVM/src/riscv_csr.c
static inline bool riscv_csr_seed(rvvm_hart_t* vm, rvvm_uxlen_t* dest, uint8_t op)
{
// Zkr: SEED must be accessed via CSR read-write instruction form.
if (op != CSR_SWAP) {
return false;
}
if (riscv_csr_seed_enabled(vm)) {
uint16_t seed = 0;
rvvm_randombytes(&seed, sizeof(seed));
return riscv_csr_const(dest, seed);
}
return false;
}
case CSR_SEED:
return riscv_csr_seed(vm, dest, op);
I think the expected behavior is:
csrr a0, seed should raise an illegal-instruction exception.
- A correct read-write CSR access form should be allowed when
seed access is enabled.
Additional information
Spec summary:
The seed CSR is defined by the Zkr extension at CSR address 0x015.
In Machine mode, seed is available via a CSR read-write instruction.
An attempted read without a write must raise an illegal-instruction exception.
Real impact:
Medium real impact. Incorrect software that uses csrr instead of the required read-write form will work on RVVM but trap on hardware or stricter emulators. This can mask a programming error and create non-portable behavior.
Host OS / Architecture:
Operating system: Linux
OS/kernel version: Linux DESKTOP-PL0JDQL 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Architecture: x86_64
RVVM version/commit: latest version at the time of testing, exact commit unknown
Verbose logs:
If needed, rerun with verbose logging enabled:
rvvm code.elf -m 256M -nogui -serial null -gdbstub -verbose
The issue
RVVM does not raise an illegal-instruction exception when the
seedCSR is accessed using a read-only CSR access form such as:According to the Zkr specification,
seedis defined at CSR address0x015. In Machine mode, it must be accessed via a CSR read-write instruction form. An attempted read without a write operand must raise an illegal-instruction exception.In my test, no trap was raised. Instead of taking an illegal-instruction exception with
mcause = 2, the access completed successfully and returned a seed value.This looks like a specification mismatch in RVVM's CSR handling for
seed.Steps to reproduce
Build this bare-metal test case:
Build command used:
Run RVVM:
Connect with GDB:
Continue execution and inspect the result register(s) used by the test or trap harness.
Expected architectural result:
Observed result:
Investigation
I stumbled upon this while checking whether RVVM enforces the Zkr access rules for the
seedCSR.The current implementation appears to treat
seedlike a normal readable CSR wheneverriscv_csr_seed_enabled(vm)is true.Old code:
This path does not distinguish between CSR read-write access and read-only CSR access forms, so
csrrsucceeds instead of faulting.Workarounds
I do not know of a guest-side workaround other than avoiding read-only CSR access forms for
seed.Using the architecturally correct read-write CSR form instead of
csrravoids the mismatch.For emulator-side testing, modifying RVVM to reject non-
CSR_SWAPaccesses toseedfixes the issue.Suggested fix / Expected behavior
RVVM should reject read-only CSR access forms for
seedand raise an illegal-instruction exception instead of returning random data.A possible fix is to gate
seedaccess on the CSR operation type.Suggested change:
I think the expected behavior is:
csrr a0, seedshould raise an illegal-instruction exception.seedaccess is enabled.Additional information
Spec summary:
Real impact:
Medium real impact. Incorrect software that uses
csrrinstead of the required read-write form will work on RVVM but trap on hardware or stricter emulators. This can mask a programming error and create non-portable behavior.Host OS / Architecture:
Verbose logs:
If needed, rerun with verbose logging enabled: