Code of Conduct
CVA6 commit affected
81245a4
Bug Description
With UseSharedTlb=1, a load can populate the shared TLB with a readable but non-executable mapping (R=1, X=0). A subsequent instruction fetch from the same virtual page can reuse this mapping without checking its execute permission.
In cva6_mmu.sv, iaccess_err checks the PTE U bit against the current privilege level, but does not check itlb_content.x. The ITLB-hit path therefore does not reject a non-executable mapping when the privilege check passes.
With UseSharedTlb=0, ITLB refills come from instruction page-table walks, where the PTW checks pte.x and rejects X=0 mappings before filling the ITLB. With UseSharedTlb=1, this check is bypassed when an instruction-side miss is satisfied by an existing shared-TLB entry. In cva6_shared_tlb.sv, the matching entry is copied into the ITLB without checking X or distinguishing whether the entry was originally populated by a data access.
The resulting path is:
Load from an R=1, X=0 page
-> Data page-table walk succeeds
-> Mapping is installed in the shared TLB
-> Instruction fetch from the same virtual page misses in the ITLB
-> Shared-TLB hit fills the ITLB without an X check
-> ITLB hit passes the U-bit check and accepts the translation
Under the RISC-V virtual-memory translation rules, an instruction fetch from a leaf PTE with X=0 must raise an instruction page fault, regardless of whether the translation comes from a page-table walk or a cached entry.
Specification:
RISC-V Supervisor-Level ISA
Affected source, checked:
Steps to reproduce
Reproduced with Verilator 5.036 under cv64a6_imafdch_sv39 (RVH=1, UseSharedTlb=1).
Spike a0 = 0xc (cause=12, INSTR_PAGE_FAULT) ✓
RTL a0 = 0x2 (cause=2, INSTR_ACCESS_FAULT) ✗
A simple program would reproduce the issue by performing the following steps:
- Set up a leaf PTE covering a 4 KiB page with
R=1, W=1, X=0, U=0, A=1, D=1.
- From S-mode, perform a load from a virtual address that maps to this page. The load succeeds because
R=1. The shared TLB now holds this translation, including X=0.
- From S-mode, attempt to fetch an instruction from the same page. Because the shared TLB already holds the translation, the ITLB hit path is taken and the PTW is not re-invoked.
Expected behavior
INSTR_PAGE_FAULT (mcause = 12), because X=0 on the PTE forbids instruction fetch.
Observed behavior
INSTR_ACCESS_FAULT (mcause = 2) in this case, or no fault is raised at all. On a cold TLB miss the PTW path correctly enforces the X bit; the bug is exclusive to the warm shared TLB hit path.
Code of Conduct
CVA6 commit affected
81245a4
Bug Description
With
UseSharedTlb=1, a load can populate the shared TLB with a readable but non-executable mapping (R=1, X=0). A subsequent instruction fetch from the same virtual page can reuse this mapping without checking its execute permission.In
cva6_mmu.sv,iaccess_errchecks the PTE U bit against the current privilege level, but does not checkitlb_content.x. The ITLB-hit path therefore does not reject a non-executable mapping when the privilege check passes.With
UseSharedTlb=0, ITLB refills come from instruction page-table walks, where the PTW checkspte.xand rejectsX=0mappings before filling the ITLB. WithUseSharedTlb=1, this check is bypassed when an instruction-side miss is satisfied by an existing shared-TLB entry. Incva6_shared_tlb.sv, the matching entry is copied into the ITLB without checking X or distinguishing whether the entry was originally populated by a data access.The resulting path is:
Under the RISC-V virtual-memory translation rules, an instruction fetch from a leaf PTE with
X=0must raise an instruction page fault, regardless of whether the translation comes from a page-table walk or a cached entry.Specification:
RISC-V Supervisor-Level ISA
Affected source, checked:
Steps to reproduce
Reproduced with Verilator 5.036 under
cv64a6_imafdch_sv39(RVH=1,UseSharedTlb=1).A simple program would reproduce the issue by performing the following steps:
R=1,W=1,X=0,U=0,A=1,D=1.R=1. The shared TLB now holds this translation, includingX=0.Expected behavior
INSTR_PAGE_FAULT(mcause = 12), becauseX=0on the PTE forbids instruction fetch.Observed behavior
INSTR_ACCESS_FAULT(mcause = 2) in this case, or no fault is raised at all. On a cold TLB miss the PTW path correctly enforces theXbit; the bug is exclusive to the warm shared TLB hit path.