Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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