aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/probe_fuzz1.py
blob: fa3359c20c3b583e67226b2fcab8d6814fe316a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""Crash-oracle-driven fuzzer over the host-reachable HDCP command/subcommand space.

Detector: PSP fault dump in C2PMSG 82..88 (nonzero => a fault fired) + is_sos_alive
+ timing. Avoids the known subcmd12 MAC hang (caps subcmd12 N < 50) so it runs
many cases per boot. Any crash-oracle hit at a PC other than the known MAC-hang
(0x40433c / fault_lr 0x40173e) is a NEW fault worth chasing.
"""
import ctypes, struct, time, random
from tinygrad import Device
from tinygrad.runtime.autogen.am import am
from tinygrad.helpers import lo32, hi32
d=Device["AMD"]; adev=d.iface.dev_impl; psp=adev.psp
SZ=ctypes.sizeof(am.struct_psp_gfx_cmd_resp); C2P=90112+64
def crash(): return tuple(adev.rreg(C2P+n) for n in (82,83,84,85,86,88))
def stat(): return int.from_bytes(bytes(adev.vram.view(psp.cmd_paddr,SZ)[:])[864:868],"little")
def alloc(n):
    pa=adev.mm.palloc(n,align=0x1000,zero=True); return pa,adev.paddr2mc(pa)
P='/Users/user/dev/amd-psp-re/psp_out/'
def load(cmd,blob):
    ap,amc=alloc(0x40000); cp,cmc=alloc(0x8000)
    adev.vram.view(ap,len(blob))[:]=blob; adev.gmc.flush_hdp()
    adev.vram.view(psp.cmd_paddr,SZ)[:]=b"\x00"*SZ
    c=am.struct_psp_gfx_cmd_resp(cmd_id=cmd); t=c.cmd.cmd_load_ta
    t.app_phy_addr_hi,t.app_phy_addr_lo=hi32(amc),lo32(amc); t.app_len=len(blob)
    t.cmd_buf_phy_addr_hi,t.cmd_buf_phy_addr_lo=hi32(cmc),lo32(cmc); t.cmd_buf_len=0x8000
    r=psp._ring_submit(c); return r.resp.session_id,cp
load(am.GFX_CMD_ID_LOAD_ASD, open(P+'asd_full.bin','rb').read())
dtm_sid,dtm_shm=load(am.GFX_CMD_ID_LOAD_TA, open(P+'dtm_full.bin','rb').read())
sid,shm=load(am.GFX_CMD_ID_LOAD_TA, open(P+'hdcp_full.bin','rb').read())
def inv(s,sh,buf,tc):
    adev.vram.view(sh,0x8000)[:]=bytes(buf); adev.gmc.flush_hdp()
    adev.vram.view(psp.cmd_paddr,SZ)[:]=b"\x00"*SZ
    ic=am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_INVOKE_CMD); iv=ic.cmd.cmd_invoke_cmd
    iv.session_id,iv.ta_cmd_id=s,tc; t0=time.time()
    try: psp._ring_submit(ic); return 0, time.time()-t0
    except RuntimeError: return stat(), time.time()-t0
    except TimeoutError: return "TO", time.time()-t0
def topo(h):
    b=bytearray(0x8000); struct.pack_into('<I',b,0,2)
    struct.pack_into('<10I',b,0x10,h,1,1,0,0,0,0,0,0,0); inv(dtm_sid,dtm_shm,b,2)
for h in range(8): topo(h)
# open a couple slots so subcommands are reachable
for dv in (0x11,0x22):
    b=bytearray(0x8000); struct.pack_into('<I',b,0,0x0E); struct.pack_into('<I',b,0xC,dv); inv(sid,shm,b,0x0E)

rng=random.Random(0xC0FFEE)
base=crash()
print("baseline crash oracle:", [hex(x) for x in base])
KNOWN={0x40433c,0x40173e,0x0}
hits=[]; n=0; t0=time.time()
CMDS=[1,2,3,4,5,6,7,9,0xB,0xC,0xE,0xF,0x10,0x11,0x12,0x13]
try:
  for it in range(1200):
    cmd=rng.choice(CMDS)
    b=bytearray(0x8000); struct.pack_into('<I',b,0,cmd)
    # slot index: mostly valid 0-7, sometimes OOB
    struct.pack_into('<I',b,0xC, rng.choice([0,1,2,3,7,rng.randint(0,0x40)]))
    if cmd==0x0F:
        # multi-subcommand framing fuzz (small lengths to avoid MAC hang)
        struct.pack_into('<I',b,0x10, rng.choice([3,6,7,8,10,12,17,18,20,rng.randint(0,40)]))
        struct.pack_into('<I',b,0x14, rng.choice([6,0,rng.randint(0,600)]))   # desc1.len
        struct.pack_into('<I',b,0x18, rng.choice([12,20,rng.randint(0,40)]))
        struct.pack_into('<I',b,0x1C, rng.choice([31,0,rng.randint(0,600)]))  # desc2.len
        struct.pack_into('<I',b,0x20, rng.randint(0,40))
        # data area: id byte + small random payload; subcmd12 N kept < 50
        for k in range(0x28,0x28+0x200): b[k]=rng.randint(0,255)
        b[0x28]=b[0x10]&0xFF
        # if subcmd12 anywhere, keep its count byte small
        b[0x2B]=rng.randint(0,49)
    else:
        for k in range(0xC,0xC+0x80): b[k]=rng.randint(0,255)
        struct.pack_into('<I',b,0xC, rng.choice([0,1,2,3,7,rng.randint(0,0x40)]))
    r,dt=inv(sid,shm,b,cmd)
    n+=1
    if r=="TO" or dt>2.0 or not psp.is_sos_alive():
        c=crash()
        print("!! ANOMALY it=%d cmd=%#x r=%s dt=%.2f alive=%s crash=%s"%(it,cmd,r,dt,psp.is_sos_alive(),[hex(x) for x in c]))
        hits.append((it,cmd,r,c)); break
    c=crash()
    if c!=base and (c[2]&0xffffff)!=0 and c[2] not in KNOWN:
        pcv=c[2]
        if pcv not in KNOWN:
            print("!! CRASH it=%d cmd=%#x faultPC=%#x crash=%s"%(it,cmd,pcv,[hex(x) for x in c]))
            hits.append((it,cmd,r,c))
            base=c  # avoid re-report
except Exception as e:
    print("exception:",repr(e))
print("\nran %d cases in %.1fs; hits=%d; sOS alive=%s"%(n,time.time()-t0,len(hits),psp.is_sos_alive()))
print("final crash oracle:", [hex(x) for x in crash()])