# Write primitive — HDCP subcmd12 branch-B OOB write [SEE STATUS BANNER] > **Not the authoritative document for this topic.** See **`SUBCMD12-RESOLVED.md`**, > and `INDEX.md` §3 for why. Kept for its detail and history. > ## STATUS BANNER — accuracy correction > The original title said "FOUND & hardware-verified". That **overstates** the > hardware evidence. Accurate status: > * The 11-byte OOB write past `sess->rx_ids` is **real** and proven by static > analysis + emulator (exact extent `5*rx_count+9`, boundary N=101). [ST+EMU] > * The **trigger path** (DTM topology -> HDCP cmd 0x0E state bypass -> cmd 0x0F > subcmd 12) is hardware-confirmed. [HW] > * **But the write itself has NO hardware-observable effect**: the corrupted > bytes land in session security fields whose only observable consequence is > masked by the `err_state` latch. It was never independently read back on > hardware. > * Escalation is **canary-gated**: the stack-overflow variant (N>=53) hits > `__stack_chk_guard`, which is HW-confirmed randomised by the loader > (decisive test: N=56 with 0x33 fill landing exactly on the canary still > crashes at PC 0x40433c / FAR 0xdeadbeef). No guard leak exists in the > host-reachable TA surface, so this is **DoS-grade**, not RCE. ## The write primitive: HDCP subcmd12 branch-B controlled OOB write Trigger (no display, hang-free): DTM cmd 2 topology + HDCP cmd 0x0E session-state bypass -> HDCP cmd 0x0F subcommand 12 (branch B: topology_type==6, mode!=20, seq!=0). Handler hdcp_subcmd12_rxinfo (sub_402A88): ta_memcpy(sess->rx_ids, msg+25, 5*msg[2]); // rx_ids is 504 B at sess+0x55 msg[2] is a full byte; the subcommand-descriptor maxlen (0x220) caps it so the copy is up to 515 B => up to 11 bytes PAST rx_ids, with ATTACKER-CONTROLLED content (from the message at msg+25). The bytes past rx_ids land on session security state: flag_24d @ +0x24D, seq_num @ +0x254 (HDCP2 replay counter), hash @ +0x258. Then the seq check early-returns BEFORE hdcp_verify_mac => NO hang. ## Hardware evidence (current run, exploit_branchB_oob.py) N=52 copy=260 OOB=0 -> sub=108 (in-buffer) N=101 copy=505 OOB=1 -> sub=100 (behavior flips at the EXACT rx_ids boundary) N=102 copy=510 OOB=6 -> sub=100 N=103 copy=515 OOB=11 -> sub=100 Persistence / content-control proof: fresh slot: subcmd12 N=52 -> sub=108 OOB write flag_24d = 0xAA (N=102) -> sub=100 follow-up independent subcmd12 N=52 -> sub=100 (was 108) => CORRUPTION OBSERVED All 0.006 s, sOS alive. This proves: (1) a WRITE occurs past the buffer (behavior changes exactly at the 504-byte boundary N=101); (2) the CONTENT is attacker-chosen (a chosen byte 0xAA written to flag_24d); (3) the write PERSISTS in session memory and alters a later independent operation. => a confirmed, controlled, out-of-bounds WRITE primitive. Scope: 11 bytes OOB, into pointer-free HDCP session fields (no code pointers in range), so it is a memory-write primitive over session security state, not (by itself) a control-flow/RCE primitive. ## Note on ASD 0x308 (previously mis-characterised as a write) Re-tested rigorously: the ASD 0x308 fault is a SOURCE OVER-READ (DFSR 0x37, WnR=0) inside the gate crypto (sub_D13750->sub_D12DEC->sub_D01C0C), driven by the attacker length a1'. The intended OOB WRITE branch (sub_D010E0(byte_D1F038, v20, a1'); *0xd20438=a1') NEVER fires: swept a2' in {2,0x100,0x8000,0xFFFF} with a1' =0x200 and *0xd20438 stayed 0x30 (write-branch-fired=False) -- the gate does not cleanly fail-open (returns error for small a1', faults for large a1'). So ASD 0x308 is a READ OOB, not a write. The confirmed WRITE primitive is branch-B above.