aboutsummaryrefslogtreecommitdiffstats

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.