# 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.**