aboutsummaryrefslogtreecommitdiffstats
path: root/psp_emu/harness/probe_sysdrv_53_log_prevalidate.py
blob: 4f0f03cf4945105ac0936226ce49505947ad857e (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
90
91
92
93
#!/usr/bin/env python3
"""Scope signed .54 low opcode 0x53 logging before caller-string validation.

Synthetic SVC/PCB/caller window only. No card request or cross-process claim.
Signed SYS_DRV init/dispatch/logging string copy run; MMIO/log-drain effects
are not hardware evidence.
"""
from __future__ import annotations

import sys
from pathlib import Path
from unicorn import UC_HOOK_CODE, UC_PROT_NONE
from unicorn.arm_const import UC_ARM_REG_R1

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from pspemu import sysdrv_env  # noqa: E402
from pspemu.component import Fault  # noqa: E402

SIGNED = Path(__file__).resolve().parents[2] / 'psp_out/elf/sys_drv_0.49.0.54_18bd9d26.elf'
ENTRY = 0xE087E4
BASE, END = 0x400000, 0x800000
LOG_FLAG, LOG_LEVEL, BACKEND_FLAG = 0xE264E0, 0xE264E4, 0xE269CC
STRING_HELPER, FORMATTER, DRAIN = 0xE2232C, 0xE01A34, 0xE21D44
LOG_BUFFER = 0xEB5FB0


def main() -> None:
    sd = sysdrv_env.make()
    assert Path(sd.image_path).read_bytes() == SIGNED.read_bytes()
    rv, fault, _ = sysdrv_env.run_init(sd)
    assert rv == 0 and fault is None and sysdrv_env.init_flag(sd) == 1, (rv, fault)
    flags = (sd.read(LOG_FLAG, 1)[0], sd.rd32(LOG_LEVEL), sd.read(BACKEND_FLAG, 1)[0])
    print('signed init logging (frontend, level, backend)=', flags, flush=True)
    assert flags[0] == 1 and flags[2] == 1, 'both modeled init log gates required'
    sd.uc.mem_map(BASE, 0x3000)
    regs = sd.uc.mem_regions()
    if not any(lo <= END-4 <= hi for lo, hi, _ in regs):
        sd.uc.mem_map(END-0x1000, 0x1000)
    if any(lo <= END <= hi for lo, hi, _ in sd.uc.mem_regions()):
        sd.uc.mem_protect(END, 0x1000, UC_PROT_NONE)

    def svc_window(comp, n):
        out = comp.uc.reg_read(UC_ARM_REG_R1)
        for i, v in enumerate((BASE, END, 0, 0)):
            comp.wr32(out + 4*i, v)
        return 0
    sd.svc.register(0xB5, svc_window)
    req, cfg = sd.alloc(0x100), sd.alloc(0x400)
    sd.write(cfg, b'\x00' * 0x400)
    src, edge = BASE+0x1000, END-4
    sd.write(src, b'J' * 40)
    sd.write(edge, b'K' * 4)
    seen, fmt, before_drain = [], [], []
    sd.uc.hook_add(UC_HOOK_CODE, lambda uc, ea, size, data: seen.append(ea),
                   begin=STRING_HELPER, end=STRING_HELPER+1)
    sd.uc.hook_add(UC_HOOK_CODE, lambda uc, ea, size, data: fmt.append(ea),
                   begin=FORMATTER, end=FORMATTER+1)
    sd.uc.hook_add(UC_HOOK_CODE,
                   lambda uc, ea, size, data: before_drain.append(bytes(
                       uc.mem_read(LOG_BUFFER+16, 39))),
                   begin=DRAIN, end=DRAIN+1)

    def request(ptr: int, declared: int) -> int:
        sd.write(req, b'\x00' * 0x100)
        for i, word in ((0, 0x53), (2, ptr), (3, declared), (14, 16),
                        (sysdrv_env.REQ_CONFIG_DWORD, cfg)):
            sd.wr32(req + i*4, word)
        return sd.run_function(ENTRY, (req, 2, 0, 0), count=3_000_000,
                               timeout=4_000_000)

    r = request(src, 4)
    assert r == 0 and len(seen) == len(fmt) == 1
    assert before_drain == [b'J' * 39], before_drain
    print('mapped non-NUL input: declared4, signed logger copied 39 bytes '
          'before later NUL check; return', hex(r), flush=True)

    n, m, d = len(seen), len(fmt), len(before_drain)
    denied = request(edge, 40)
    assert denied == 0xFFFF000F and (len(seen),len(fmt),len(before_drain)) == (n,m,d)
    print('synthetic edge declared40 rejected before logger',hex(denied),flush=True)
    try:
        request(edge, 4)
    except Fault as exc:
        assert exc.addr == END, (exc, sd.faults)
        assert (len(seen),len(fmt),len(before_drain)) == (n+1,m+1,d)
        print('synthetic edge declared4: signed logger fault at',hex(exc.addr),
              'before later NUL check and log drain',flush=True)
    else:
        raise AssertionError('short-declared synthetic edge unexpectedly completed')


if __name__ == '__main__':
    main()