Skip to content

Page-table walks use the original access type instead of a PMP read check #57

Description

@jimmymtest

Summary

I found a bug in OpenC910's PMP check for page-table walks. Since page-table walks read PTE data, PMP should require read permission on the page-table physical address. However, OpenC910 appears to reuse the original fetch permission: X-only page-table memory succeeds, while R-only memory faults.

The minimal software-visible POC is an S-mode instruction fetch whose root page table is protected by an X-only PMP entry:

  • root page-table page is PMP protected as R=0,W=0,X=1
  • first S-mode fetch triggers a PTW read of that page-table page

Expected result:

  • PTW memory access should require PMP read permission on the page-table physical address
  • X-only page-table memory should not be readable by PTW
  • R-only page-table memory should be readable by PTW

Actual result on OpenC910:

  • the fetch translation succeeds with X-only PMP (BG)
  • the same translation faults with R-only PMP (T)

Test Case

The testcase builds a one-entry Sv39 root page table with a 1GiB identity leaf, then protects the root page-table page with pmp0. The first S-mode instruction fetch must read root_pt[0] through PTW.

.include "openc910_min_common.inc"

.equ PTE_FLAGS_RWXAD,       0x0cf
.equ SATP_MODE_SV39,        0x8000000000000000
.equ PMP_CFG_ALLOW_ALL_E1,  0x1f00
.equ PTW_PMP_CFG,           0x1c    # X-only NAPOT; use 0x19 for R-only control

.text
.align 6
.global main
main:
  la    t0, trap_handler
  csrw  mtvec, t0
  csrw  satp, zero
  sfence.vma

  la    t0, root_pt
  li    t1, PTE_FLAGS_RWXAD
  sd    t1, 0(t0)                  # 1GiB identity leaf
  fence

  la    t0, root_pt
  srli  t0, t0, 2
  li    t1, 0x1ff
  or    t0, t0, t1                 # NAPOT page-table page
  csrw  pmpaddr0, t0
  li    t0, -1
  csrw  pmpaddr1, t0               # allow all non-page-table memory
  li    t0, PMP_CFG_ALLOW_ALL_E1 | PTW_PMP_CFG
  csrw  pmpcfg0, t0
  fence

  la    t0, root_pt
  srli  t0, t0, 12
  li    t1, SATP_MODE_SV39
  or    t0, t0, t1
  csrw  satp, t0
  sfence.vma

  li    t0, 0x1800
  csrc  mstatus, t0
  li    t0, 0x800
  csrs  mstatus, t0
  la    t0, smode_target
  csrw  mepc, t0
  mret

smode_target:
  li    a0, 'B'
  jal   ra, oracle_putc
  ecall

trap_handler:
  csrw  satp, zero
  sfence.vma
  csrw  pmpcfg0, zero
  csrr  t0, mcause
  li    t1, 9
  beq   t0, t1, s_ecall
  li    t1, 1
  beq   t0, t1, inst_access_fault
  li    a0, 'W'
  jal   ra, oracle_putc
1:
  j     1b

s_ecall:
  li    a0, 'G'
  jal   ra, oracle_putc
2:
  j     2b

inst_access_fault:
  li    a0, 'T'
  jal   ra, oracle_putc
3:
  j     3b

.data
.align 12
root_pt:
  .zero 4096

If PTW uses PMP read permission correctly, the X-only case (PTW_PMP_CFG=0x1c) should fault and the R-only case (PTW_PMP_CFG=0x19) should be allowed. OpenC910 shows the reverse for fetch-driven PTW.

appendix.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions