Files
vt1200/harness/mame/pcfwork/serve_candidate.py
T
arti 307df67f9d harness/mame: the TFTP PCF loader skips BITMAPS, so the n*4 overflow is not on that path
The TFTP font path loads through 3F96B850 and parses with the metadata
reader 3F97EF00, which handles only PROPERTIES/ACCELERATORS/METRICS/
ENCODINGS and silently skips BITMAPS. A font with num_chars=0x40000001
therefore passes the loader (LOADER-A-SUCCESS) without the field ever
being read, and pcfReadBitmaps (3F984970, multiply 3F985050) is never
entered. pcfReadBitmaps has exactly one caller, 3F98BB70, in the X11
font-request reader 3F98AC20, not the TFTP fetch path.

The 'refused after 1024 bytes' of evil-real.pcf is the METRICS table:
its big-endian compressed count 0x00DF is read little-endian as 57088,
so fosAlloc(57088*16) fails at ~906 bytes; BITMAPS (offset 2024) is
never reached.  Baseline.pcf fails the same way; baseline_le.pcf puts
that one 2-byte field in the order the loader reads it, parses cleanly,
and still ignores the crafted BITMAPS count.

No ROM patch, no check disabled, no serve.py edit.  Baselines unchanged:
no env samples=198, stub samples=53.
2026-10-17 14:14:07 +03:00

53 lines
1.7 KiB
Python

#!/usr/bin/env python3
"""pcfwork: serve one candidate font over TFTP on the real path.
The terminal's TFTP font path in the (copied) NVR is 192.168.99.1:69, so it
first RRQs '69' (the path object), then the font name it derives, then
'<dir>/fonts.dir'. This serves baseline.pcf for the path/index names (so the
terminal derives a name and asks again) and the candidate font for everything
else -- the same shape the prior font-work used to make the terminal fetch the
crafted font over its own loader.
python3 serve_candidate.py <candidate.pcf> [bind-ip]
"""
import os
import sys
import time
ROOT = "/home/arti/code/k-space/vt1200"
sys.path.insert(0, os.path.join(ROOT, "tools"))
import fontgen # noqa: E402
cand_path = sys.argv[1]
host = sys.argv[2] if len(sys.argv) > 2 else "192.168.99.1"
with open(os.path.join(ROOT, "reference/fonts/baseline.pcf"), "rb") as fh:
base = fh.read()
with open(cand_path, "rb") as fh:
cand = fh.read()
print(f"path/index -> baseline.pcf ({len(base)}B); font -> {os.path.basename(cand_path)} ({len(cand)}B)",
flush=True)
def resolve(name):
# Path object / index fetches get the baseline so the terminal derives a
# name and re-fetches; everything else gets the candidate.
if name == "69" or name.endswith("fonts.dir"):
return base
return cand
srv = fontgen.TFTPServer((host, 69), {}, timeout=60.0, retries=10,
resolver=resolve).start()
try:
while True:
time.sleep(0.5)
while srv.log:
ts, name, addr = srv.log.pop(0)
stamp = time.strftime("%H:%M:%S", time.localtime(ts))
print(f"{stamp} {addr[0]}:{addr[1]} requested {name!r}", flush=True)
except KeyboardInterrupt:
pass
finally:
srv.stop()