aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/FINDING-RAS-INJECT-TMR-PROTECTION-IS-CORRECT.md
blob: aa1cbc7c87779fb5a8c0d1ec985892ed0b09d698 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# RAS error-injection range check — empirically mapped, protects the TMR exactly

## What this closes
RAS cmd 2 (TRIGGER_ERROR) is host-reachable (see
FINDING-RAS-IS-REACHABLE-AND-AUDITED.md) and feeds a **host-supplied 64-bit DRAM
address** into the UMC error-injection address registers:

    ras_parse_trigger_request -> ras_dram_range_config -> ras_configure_blocks
        v5 = host 64-bit (request+16/+20)
        v33 = v33 & 0xFFFFF000 | WORD2(v5) & 0xFFF   -> reg 0x48FC0574
        v32 = v5 & 0xFFFFFFC1 | ... | 1              -> reg 0x48FC0570

If unvalidated, the host could inject ECC/parity errors into PSP-trusted DRAM.

## A wrong hypothesis I formed, and how it died
`dram_range_query` (SYS_DRV RPC 0x7008) returns:

    0xFFFF0001  address found in a range-table entry
    0           address NOT found in any entry (also: uninit / recovery)
    0xFFFF0006  address has bits set above bit 43

and `ras_dram_range_config` does `BL ...7008 ; CBZ R0 -> proceed`. Reading the
table as a list of **allowed** ranges, that is inverted — "not found" would
mean "proceed". I was about to write that up as a vulnerability.

It is wrong. The table lists **protected** ranges, so non-zero means "you hit a
protected region, refuse". The polarity is correct.

## Empirical proof — the boundary is the TMR, to the page
`psp.tmr_paddr = 0x300000`, `tmr_size = 0x1300000`, so TMR = [0x300000,0x1600000).

    0x00002ff000 -> allow     (one page below TMR)
    0x0000300000 -> REJECT    (exact TMR base)
    0x0000301000 -> REJECT
    0x0000400000 -> REJECT
    0x0000800000 -> REJECT
    0x0000c00000 -> REJECT
    0x0001000000 -> REJECT
    0x00015ff000 -> REJECT    (last page inside)
    0x0001600000 -> REJECT    (boundary; compare is inclusive)
    0x0001601000 -> allow     (one page past TMR)
    0x0001700000 -> allow
    0x0010000000 -> allow

Also correct: an address with bits above bit 43 (`0x1ffffffff000`) is rejected
(`0xFFFF0006` path). Ordinary VRAM we own (`0x9a000`, `0x1648000`) is allowed —
injecting into ordinary VRAM is the legitimate RAS use case.

## Verdict
The host can aim RAS error injection at ordinary VRAM (by design) but **cannot
aim it at the PSP TMR**. The protection is exact at both edges. No bug.

## Method note
The disassembly alone genuinely reads as an inverted check; only the hardware
sweep distinguished "allowed-list" from "protected-list" semantics. Static
reasoning about a range table's *polarity* is not safe without knowing what the
table contains — and the contents were only observable at runtime.