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 tov8 + 8ADD.W R5, R4, #0x10—v8 + 16handed 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
paramTypesvalidation 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.
