aboutsummaryrefslogtreecommitdiffstats
path: root/fw/ta_bufferfield_xfn.py
blob: 243995de071d0c95b52bc72144d0874f42a3e7c0 (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
#!/usr/bin/env python3
"""Cross-function form of detector class 9: a copy length read from a buffer that some OTHER
function fills by copy.

Class 9 (`ta_bufferfield_audit.py`) only saw objects copy-filled inside the same function, and
so missed the original `sub_E04B68` defect, whose staging buffer is filled by `sub_E1D6BC`.
This version marks every call site that passes an accessor-derived buffer as an argument, then
looks inside those callees for copy lengths read from it, with forward-alias propagation (the
callee typically does `mov r4, r1` first -- omitting that made the control fail).

Controls: `sub_E04B68` with `[r4+0x88]` must be found; functions whose length is a stacked
argument must not be.

  from ta_bufferfield_xfn import run
  passes, hits = run("sys_drv", 0xe0b148)      # slot accessor, all field offsets
  passes, hits = run("sys_drv", 0xe0b0f4)      # channel lookup

Swept over both accessors and every field offset, SYS_DRV yields exactly two hits
(`sub_E04B68` and `sub_E04DFC`, both on `slot[0x4c]`), and the channel object yields none.
"""
import sys, re
sys.path.insert(0,'/home/user/dev/amd-psp-re/fw')
import ta_capacity_audit as CAP
ARGS=["r0","r1","r2","r3"]
def run(mod, accessor, slot_off=None):
    b,base,fns=CAP.load(mod); fns=CAP.split_merged(b,base,fns)
    hp=CAP.helpers(b,base,fns); passes=set()
    for ea,sz in fns:
        o=ea-base
        if o<0 or sz<=0 or o+sz>len(b): continue
        ins=list(CAP.THUMB.disasm(b[o:o+sz], ea)); taint=set(); buf={}
        for i in ins:
            op=i.op_str or ''
            if i.mnemonic in("bl","blx") and op=="#%#x"%accessor: taint={"r0"}; continue
            m=re.match(r"(\w+),\s*(\w+)$",op)
            if i.mnemonic.startswith("mov") and m:
                if m.group(2) in taint: taint.add(m.group(1))
                if m.group(2) in buf: buf[m.group(1)]=buf[m.group(2)]
            mm=re.match(r"(\w+),\s*\[(\w+),\s*#(0x[0-9a-f]+|\d+)\]$",op)
            if i.mnemonic.startswith("ldr") and mm and mm.group(2) in taint:
                off=int(mm.group(3),0)
                if slot_off is None or off==slot_off: buf[mm.group(1)]=off
            if i.mnemonic in("bl","blx") and op.startswith("#"):
                t=int(op[1:],16)
                for a in ARGS:
                    if a in buf: passes.add((t,a,buf[a]))
                for a in ARGS: taint.discard(a); buf.pop(a,None)
    hits=[]
    for ea,sz in fns:
        rel={a:o for (t,a,o) in passes if t==ea}
        if not rel: continue
        ins=list(CAP.THUMB.disasm(b[ea-base:ea-base+sz], ea))
        for n,i in enumerate(ins):
            if not (i.mnemonic in("blx","bl") and (i.op_str or '').startswith('#')): continue
            if int(i.op_str[1:],16) not in hp: continue
            alias=dict(rel)
            for j in ins[:n]:
                m=re.match(r"(\w+),\s*(\w+)$", j.op_str or '')
                if j.mnemonic.startswith("mov") and m:
                    if m.group(2) in alias: alias[m.group(1)]=alias[m.group(2)]
                    elif m.group(1) in alias: alias.pop(m.group(1))
            cur,at="r2",n; src=None
            for _ in range(5):
                for k in range(at-1,-1,-1):
                    j=ins[k]
                    mm2=re.match(r"(%s),\s*(.+)$"%re.escape(cur), j.op_str or '')
                    if not mm2 or j.mnemonic.startswith(("cmp","cb","str","push","tst","b")): continue
                    if j.mnemonic.startswith("ldr"):
                        m3=re.match(r"\w+,\s*\[(\w+),\s*#(0x[0-9a-f]+|\d+)\]$", j.op_str or '')
                        if m3: src=(m3.group(1),int(m3.group(2),0))
                        break
                    m4=re.fullmatch(r"(r\d+|sb|sl|fp|ip)", mm2.group(2).strip())
                    if m4: cur,at=m4.group(1),k; break
                    break
                else: break
                if src: break
            if src and src[0] in alias: hits.append((ea,i.address,src[0],src[1],alias[src[0]]))
    return passes,hits