aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/LATENT-RAS-PARAM-ARRAY-OOB.md
blob: 78a572dbe5d1aa1d267462c0b37c948b0a541c89 (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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# LATENT: RAS TA — out-of-bounds parameter-array read (NOT host-reachable)

**Component:** RAS TA (`ras2.elf`), `ras_map_shared_buffer` @ `0x402ED0`
**Reached via:** host `LOAD_TA(RAS)` -> `OPEN_SESSION` -> `INVOKE_CMD`
**Class:** out-of-bounds read of the GP parameter array -> type-confused pointer,
subsequently dereferenced for **read and write**
**Status:** **NOT HOST-REACHABLE — see section 7. The reachability claim
originally made in this file is RETRACTED.** The latent defect is real and
confirmed at instruction level, but sOS's parameter marshalling makes the
triggering condition unsatisfiable.

--------------------------------------------------------------------------------
## 1. The defect

`ras_map_shared_buffer(paramTypes, params, &out_idx)` scans for the first
parameter slot whose type nibble is non-zero. **The loop has no failure path.**

    0x402ed2  CBZ  R0, fail          ; paramTypes == 0 -> fail  (the ONLY guard)
    0x402ed4  MOVS R3, #0            ; bit offset
    0x402ed6  MOVS R4, #0            ; i
    loop:
    0x402ed8  CMP  R4, #4
    0x402eda  BEQ  0x402EEC          ; <-- i == 4 FALLS THROUGH with R3 = 16
    0x402edc  LSR.W R5, R0, R3
    0x402ee0  LSLS R5, R5, #0x1C     ; nibble i != 0 ?
    0x402ee2  BNE  0x402EEA
    0x402ee4  ADDS R3, #4
    0x402ee6  ADDS R4, #1
    0x402ee8  B    loop
    0x402eea  STR  R4, [R2]          ; record slot index   (SKIPPED on fall-through)
    0x402eec  LSRS R0, R3            ; R3 == 16 -> nibble 4
    0x402eee  AND.W R0, R0, #0xF
    0x402ef2  SUBS R0, #5
    0x402ef4  CMP  R0, #2
    0x402ef6  BHI  fail              ; nibble must be 5, 6 or 7 (memref types)
    0x402ef8  ADD.W R0, R1, R3,LSL#1 ; params + (16<<1) = params + 32
    0x402f00  LDR  R0, [R0,#4]       ; *** params[4].size   -- OUT OF BOUNDS ***
    0x402f02  CMP  R0, R2            ; >= 0x828 ?
    0x402f04  BCC  fail
    0x402f06  LDR.W R0, [R1,R3,LSL#1]; *** params[4].buffer -- OUT OF BOUNDS ***
    0x402f0a  POP                    ; returned as the command buffer

The GP parameter array has **four** entries — the loop's own `CMP R4,#4` bound is
the author's statement of that. Indexing slot 4 reads **8 bytes past the array**.

### Trigger
    paramTypes nibbles 0..3 all zero, nibble 4 in {5,6,7}
    e.g. paramTypes = 0x50000
    and the 8 bytes following the array must satisfy  size >= 0x828

## 2. Why it is host-reachable
`TA_InvokeCommandEntryPoint` performs **no `paramTypes` validation** before the
call — unlike its siblings:

    RAS  : BL ras_map_shared_buffer            <- paramTypes used raw
    DTM  : AND.W R1,R2,#0xF ; CMP R1,#7 ; BNE reject
    HDCP : AND.W R0,R2,#0xF ; CMP R0,#7 ; BNE reject

RAS disassembly:

    0x402b12  MOV R7, R2     ; paramTypes, straight from the host
    0x402b18  MOV R1, R3     ; params
    0x402b1c  BL  ras_map_shared_buffer

`paramTypes` and `params` are supplied by the host in `INVOKE_CMD`. RAS is a
host-loadable TA.

## 3. Impact — the OOB pointer is read AND written
`TA_InvokeCommandEntryPoint` uses the returned pointer `v8` directly:

* `switch ( *(_DWORD *)v8 )` — **dereferenced read** (command id)
* `STR R1, [R4,#8]` — **write** of the handler result to `v8 + 8`
* `ADD.W R5, R4, #0x10` — `v8 + 16` handed to the command handlers

## 4. A second, compounding defect: the stale slot index
On the OOB path the `STR R4,[R2]` at `0x402EEA` is **skipped**, so
`dword_4060EC` keeps a **stale** index. `ras_attach_response_buffer` then uses
that stale index against a *different* slot than the one validated:

    0x403c52  LDR   R1, [R5]             ; stale index
    0x403c58  ADD.W R1, R4, R1,LSL#3     ; params[stale]
    0x403c5c  LDR   R1, [R1,#4]          ; .size  -- NOT the slot that passed >= 0x828
    0x403c5e  SUBW  R1, R1, #0x828       ; len = size - 0x828  -> UNDERFLOW if size < 0x828
    0x403c66  B     ras_bind_response_area

and `ras_bind_response_area` performs `bzero(ptr, 0x14)` at
`oob_buffer + 0x810` — an **out-of-bounds-derived write**, with a response-area
length that can underflow to ~4 GB.

## 5. ~~Distinguishing this from the project's previous dead ends~~ RETRACTED
I claimed this differed from the previous six dead ends because `paramTypes`
comes straight from the host. **That was wrong, and I wrote it before completing
the analysis.** The host does not control `paramTypes` directly — sOS constructs
it.

## 6. Why it is NOT reachable: sOS re-packs paramTypes to 16 bits
`tee_unmarshal_params` (`0x17884`) unpacks the host's type nibbles into four
*bytes*, then re-packs them into nibbles before handing the value to the TA:

    *a1 = (unsigned __int8)v27          // t0 -> bits 0..3
        | (16 * BYTE1(v27))             // t1 -> bits 4..7
        | (BYTE2(v27) << 8)             // t2 -> bits 8..11
        | (HIBYTE(v27) << 12);          // t3 -> bits 12..15

Each `tN` is a nibble (`v9 & 0xF`), so the maximum value is `0xFFFF`:
**bits 16 and above are always zero.**

In `ras_map_shared_buffer` the nibble-4 test happens *before* the out-of-bounds
access:

    0x402eec  LSRS R0, R3        ; paramTypes >> 16  == 0 ALWAYS
    0x402ef2  SUBS R0, #5        ; 0 - 5 = 0xFFFFFFFB
    0x402ef4  CMP  R0, #2
    0x402ef6  BHI  fail          ; <-- ALWAYS TAKEN on the exhaust path
    0x402ef8  ADD.W R0, R1, R3,LSL#1   ; params+32   (never reached)
    0x402f00  LDR  R0, [R0,#4]         ; the OOB read (never reached)

So when the loop exhausts, the function returns 0 and
`TA_InvokeCommandEntryPoint` returns `-65525`. The OOB read is never performed,
and the stale-index path in section 4 is likewise unreachable.

## 7. Verification status
* the loop fall-through, the OOB indexing, the absent `paramTypes` validation in
  RAS, and the read/write use of the pointer: **CONFIRMED at instruction level**
* **reachability: REFUTED.** sOS guarantees `paramTypes <= 0xFFFF`.
* No hardware test is warranted.

## 8. What would make it live
Any caller that reaches `ras_map_shared_buffer` with a `paramTypes` whose bits
16..19 are in {5,6,7}. sOS's marshalling forbids this today. A future sOS that
widens the type field, or any direct caller bypassing `tee_unmarshal_params`,
would expose it. **Worth a regression check on future firmware.**