Skip to content

[BUG] PMP check replaces an instruction guest-page fault with an instruction access fault #3597

Description

@qinkejiu

Code of Conduct

  • [] I have searched the existing bug issues.
  • I am a human engaging in an interpersonal interaction. During this interaction, my words are my own and are not generated. If relevant, I provide links to my sources.

CVA6 commit affected

81245a4

Bug Description

With vsatp.MODE=Bare and Sv39x4 G-stage translation enabled, a VS-mode instruction fetch at guest physical address 0xC0000000 encounters an invalid G-stage root PTE. The expected exception is an instruction guest-page fault (mcause=20). In the full CVA6 ariane_testharness, a later PMP check can instead make the same fetch report an instruction access fault (mcause=1).

The G-stage table and the faulting fetch are identical in both runs. Only the second PMP entry changes:

  • Fault-path address permitted: observed mepc=0xC0000000 and mcause=20 (instruction guest-page fault).
  • Fault-path address denied: observed mepc=0xC0000000 and mcause=1 (instruction access fault).

The expected cause is 20 in both runs. The RISC-V H-extension exception-priority table gives an instruction guest-page fault during address translation priority over an instruction access fault associated with the final physical access. A failed G-stage translation has not produced a valid final translated physical address.

To reproduce in the RVH-enabled cv64a6_imafdch_sv39 configuration:

  1. Place the program at physical address 0x80000000. Place a zero-initialized, 16 KiB-aligned Sv39x4 G-stage root page table at 0x80004000.
  2. Set the 64-bit root PTE at 0x80004010 (index 2) to 0x00000000200000DF. This is an executable 1 GiB identity-mapping leaf covering the program. Leave the PTE at 0x80004018 (index 3) invalid, so 0xC0000000 has no G-stage mapping.
  3. Set vsatp.MODE=Bare, hgatp.MODE=8 and hgatp.PPN=0x80004. Execute hfence.gvma after setting the page table, PMP and hgatp. Route the exception to M-mode; do not delegate cause 20 through medeleg. Set mstatus.MPP=S, mstatus.MPV=1 and mepc to the VS-mode entry point, then execute mret.
  4. Run two program variants that differ only in the PMP setup below. Have the M-mode trap handler read mcause and mepc.
  • Fault-path address denied: pmpaddr0=0x20000FFF; PMP entry 1 disabled; pmpcfg0=0x1F.
  • Fault-path address permitted: pmpaddr0=0x20000FFF; pmpaddr1=0x1FF; pmpcfg0=0x1F1F.

PMP entry 0 permits the program and page table at physical addresses 0x80000000–0x80007FFF. The control run additionally permits physical addresses 0x00000000–0x00000FFF through entry 1. Both enabled entries use NAPOT addressing and R/W/X permissions.

The relevant program body is below. The test setup supplies the page-table image, PMP values, vsatp and trap-delegation settings, and linker placement described above.

.option norvc
.section .text
.globl _start

_start:
    la   t0, trap_handler
    csrw mtvec, t0

    # Configure PMP for one run using the settings above.

    li   t0, 0x80004
    li   t1, 8
    slli t1, t1, 60
    or   t0, t0, t1
    csrw hgatp, t0
    hfence.gvma

    la   t0, vs_mode
    csrw mepc, t0
    csrr t1, mstatus
    li   t2, ~(3 << 11)
    and  t1, t1, t2
    li   t2, (1 << 11) | (1 << 39)
    or   t1, t1, t2
    csrw mstatus, t1
    mret

vs_mode:
    li   t0, 0xC0000000
    jr   t0

trap_handler:
    csrr t1, mcause
    csrr t0, mepc
1:  j    1b

The full-CPU test observed mepc=0xC0000000 and mcause=1 with only PMP entry 0, versus mepc=0xC0000000 and mcause=20 when entry 1 was also enabled.

Issue #3115 raised the broader possibility of an earlier instruction-fetch exception being overwritten in pmp_data_if.sv. This report supplies a paired full-CPU reproducer for the RVH instruction guest-page-fault case. Issue #3337 covers a related data-side priority error; issue #3428 concerns a different G-stage fetch failure.

Vulnerable Code

At the affected commit, core/pmp/src/pmp_data_if.sv preserves an ordinary instruction page fault but does not preserve an instruction guest-page fault:

if (icache_areq_i.fetch_valid) begin
  if (icache_areq_o.fetch_exception.cause != riscv::INSTR_PAGE_FAULT) begin
    if (!match_any_execute_region || !pmp_if_allow) begin
      icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT;
      icache_areq_o.fetch_exception.valid = 1'b1;
    end
  end
end

The paired results are consistent with a G-stage guest-page fault reaching this block and being overwritten when PMP denies the fault-path address. A trace of the exception before and after this block would confirm that internal sequence directly.

Suggested fix

Preserve a valid INSTR_GUEST_PAGE_FAULT when applying the final instruction PMP/PMA check, alongside the existing handling of INSTR_PAGE_FAULT. Add a full-CPU regression using the same unmapped guest fetch under both PMP configurations and require mcause=20 in each run.

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

    Component:RTLFor issues in the RTL (e.g. for files in the rtl directory)Status:NewNewly created issue, nobody has looked at it yet.Type:BugFor bugs in the RTL, Documentation, Verification environment or Tool and Build system

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions