Skip to content

seed CSR read-only access via csrr does not raise illegal-instruction exception #213

@whensun

Description

@whensun

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:

csrr a0, seed

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:

csrr a0, seed

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:

mcause = 2

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions