sots-re/verify/results/research-completion-abi-correction-verifier/reproduce_run_ee78b8773688ca09f8046e21.py

87 lines
5.1 KiB
Python

#!/usr/bin/env python3
import hashlib, json, re, subprocess
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path('/home/alex/sots-re')
OUT = ROOT / 'verify/results/research-completion-abi-correction-verifier/run-ee78b8773688ca09f8046e21'
AUTHOR = ROOT / 'verify/results/research-completion-abi/run-16f8e9b6376b278c4870be09'
TOOL = Path('/usr/bin/objdump')
BINARY = ROOT / 'dumps/sots.exe'
WINDOWS = {
'turn-get-create': (0x00885380, 0x0088544a),
'turn-append': (0x00884cb0, 0x00884d8f),
'nested-copy': (0x00779850, 0x00779a20),
'nested-dtor': (0x00629580, 0x006295ca),
}
EXPECTED = {
'binary': '970b7de729956a53094c7eb98aba4270aee98e2fed5daf0d39e290013c90c841',
'tool': '1eaaef2e7f57c4c7f69115c495e2466f5a8c8e5f3bc42221d092382f30f9d4cd',
}
ROW = re.compile(rb'^\s*[0-9a-f]+:\s', re.M)
def sha(data): return hashlib.sha256(data).hexdigest()
def rows(data): return [line for line in data.splitlines() if ROW.match(line)]
OUT.mkdir(parents=True, exist_ok=True)
checks = []
records = []
for name, (start, stop) in WINDOWS.items():
variants = {}
for suffix, end in (('exact', stop), ('wide', stop + 4)):
argv = [str(TOOL), '-D', '-Mintel', f'--start-address=0x{start:08x}',
f'--stop-address=0x{end:08x}', str(BINARY)]
p = subprocess.run(argv, cwd=ROOT, capture_output=True, check=False)
for stream, data in (('stdout', p.stdout), ('stderr', p.stderr)):
(OUT / f'{name}-{suffix}.{stream}.txt').write_bytes(data)
variants[suffix] = p.stdout
records.append({'name': name, 'variant': suffix, 'argv': argv,
'returncode': p.returncode,
'stdout': {'bytes': len(p.stdout), 'sha256': sha(p.stdout)},
'stderr': {'bytes': len(p.stderr), 'sha256': sha(p.stderr)}})
checks += [(f'{name}-{suffix}-return-zero', p.returncode == 0),
(f'{name}-{suffix}-stdout-nonempty', bool(p.stdout)),
(f'{name}-{suffix}-stderr-empty', not p.stderr)]
archived = (AUTHOR / f'{name}.stdout.txt').read_bytes()
exact_rows, wide_rows = rows(variants['exact']), rows(variants['wide'])
checks += [(f'{name}-byte-exact-author-compare', variants['exact'] == archived),
(f'{name}-wide-row-prefix', exact_rows == wide_rows[:len(exact_rows)]),
(f'{name}-wide-adds-row', len(wide_rows) > len(exact_rows))]
get = (OUT / 'turn-get-create-exact.stdout.txt').read_text()
pre_call = get.split('885413:', 1)[0]
stack20_writes = [line.strip() for line in pre_call.splitlines()
if '[ebp-0x20]' in line.lower() and re.search(r'\bmov\s+DWORD PTR \[ebp-0x20\]', line, re.I)]
append = (OUT / 'turn-append-exact.stdout.txt').read_text()
copy = (OUT / 'nested-copy-exact.stdout.txt').read_text()
dtor = (OUT / 'nested-dtor-exact.stdout.txt').read_text()
checks += [
('binary-hash', sha(BINARY.read_bytes()) == EXPECTED['binary']),
('tool-hash', sha(TOOL.read_bytes()) == EXPECTED['tool']),
('get-no-precall-ebp-minus-20-write', not stack20_writes),
('get-last-match-overwrite-visible', all(x in get for x in ('8853d3:', '8853d8:', '8853de:', '8853e6:'))),
('get-cleanup-before-final-store', get.index('885422:') < get.index('88542d:') < get.index('885433:')),
('append-source4-copy-before-nested-copy-before-advance', append.index('884d62:') < append.index('884d6c:') < append.index('884d73:') < append.index('884d78:')),
('nested-copy-three-null-initializers', all(x in copy for x in ('779885:', '779887:', '77988a:'))),
('nested-copy-empty-and-nonempty-branches', all(x in copy for x in ('7798b2:', '7798cb:', '7798d0:', '7798ed:'))),
('nested-dtor-null-branch-loop-free-zero', all(x in dtor for x in ('629586:', '6295a8:', '6295aa:', '6295b4:', '6295c0:', '6295c2:', '6295c5:'))),
]
failed = [name for name, ok in checks if not ok]
manifest = {
'schema': 'sots-abi-independent-static/1', 'session': 'run-ee78b8773688ca09f8046e21',
'actor': 'research-abi-correction-verifier', 'role': 'verifier',
'model': 'openai/gpt-5.6-sol', 'timestamp': datetime.now(timezone.utc).isoformat(),
'input': {'path': 'dumps/sots.exe', 'bytes': BINARY.stat().st_size, 'sha256': sha(BINARY.read_bytes())},
'tool': {'path': str(TOOL), 'bytes': TOOL.stat().st_size, 'sha256': sha(TOOL.read_bytes()),
'version': subprocess.run([str(TOOL), '--version'], capture_output=True, text=True, check=True).stdout.splitlines()[0]},
'author_manifest_sha256': sha((AUTHOR / 'manifest.json').read_bytes()),
'stack_writes_to_ebp_minus_20_before_0x885413': stack20_writes,
'commands': records, 'checks': [{'name': n, 'status': 'pass' if ok else 'fail'} for n, ok in checks],
'failed': failed,
'scope': 'Independent static full compare plus widened-stop boundary ablation; no live game/allocator execution or replacement acceptance.'
}
(OUT / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
print(json.dumps({'commands': len(records), 'checks': len(checks), 'failed': failed,
'manifest': str((OUT / 'manifest.json').relative_to(ROOT))}, indent=2))
raise SystemExit(bool(failed))