LAT crash probe
Targets the VT1200's LAT receive path, specifically Finding A from
../notes/lat-receive-audit.md: the demultiplexer at 0x16C44 walks
nbr_slots slots with no length bound.
Components
| File | Purpose |
|---|---|
lat_probe.py |
crafts a malformed LAT RUN on the wire (standalone; --dry-run) |
test_lat_probe.py |
unit tests; reconstructs a captured real frame byte-for-byte |
run_latd_fuzz.sh |
runs the patched latd with the probe armed via LATD_FUZZ |
extract_stream.py |
pulls the exact host→VT1200 byte stream out of a pcap (self-contained libpcap + LAT parser) |
replay_stream.py |
replays that stream to stdout so it can be used as a latd service command; --limit N bisects |
announce_replay.py |
re-sends the captured LAT service announcements (type 0xA) verbatim, at a chosen interval |
heartbeat.sh |
output-producing soak service — so "alive" is distinguishable from a blank idle window |
New lead (2026-10-15): service announcements
Replaying the data stream from the crash capture does not crash the terminal
(see ../../NEXT_SESSION.md). The one protocol-level difference is that the crash
session also carried 91 LAT service announcements (message type 0xA, multicast
to 09:00:2b:00:00:0f), one every ~10 s — and the audit's Finding 3 says that path
(LAD update, 0x10660/0x103CA) does memcpys with announcement-derived lengths and
no confirmed upper bound. announce_replay.py tests exactly that.
sudo python3 announce_replay.py ../local/filter-test2.pcap --dry-run
sudo python3 announce_replay.py ../local/filter-test2.pcap --interval 10 --forever
Note the 2-byte field at payload offsets 14–15 that changes on every announcement
(server.cc:221: announce->incarnation = --multicast_incarnation;) — prime suspect.
The patch to latd
code/latd/connection.cc gains LATConnection::send_fuzz_run() and a one-shot
hook in circuit_timer():
// fires once, on the first circuit-timer tick of a live circuit
if (fuzz_claimed > 0 && !fuzz_done) { fuzz_done = true; send_fuzz_run(n); }
send_fuzz_run() builds the LAT header by hand and sets num_slots = n while
appending exactly one 4-byte Data_a slot — i.e. it lies about the slot count,
which is precisely what the firmware fails to check. It uses the connection's
own local_connid/remote_connid, sequence number and socket, so the frame is
valid on the live circuit (no need to guess negotiated circuit IDs).
Procedure
- Power-cycle the VT1200 (a crash leaves it faulted).
- Build:
make -C ../local/build -j8 - Arm and run (needs root for AF_PACKET):
or
sudo LATD_FUZZ=255 ../local/build/latd -d -v -ienp197s0f3u1 -s<service> -lestderr./run_latd_fuzz.sh -d -v -ienp197s0f3u1 -s<service> -lestderr - Have the terminal connect (log in) — the probe fires on the first circuit timer after the circuit is up.
- Watch the terminal for a Level-0 fault, and
tcpdumpfor the frame.
Standalone (no latd) alternative, once you know the live circuit IDs:
python3 lat_probe.py --dst-cir 0x0001 --src-cir 0x5460 --nbr-slots 255 --dry-run
sudo python3 lat_probe.py --nbr-slots 255 --count 3
Status
The Finding-A fuzz probe (lat_probe.py / LATD_FUZZ) is still not fired at
hardware — deliberately, until the demux walk is understood, so a crash can be
interpreted rather than merely observed.
The stream replay route was fired, and is negative: the exact byte stream that
accompanied the crash does not crash the terminal. The announcement soak
(announce_replay.py, or latd's own announcements with latcp -m 10) is the current
test — see ../../NEXT_SESSION.md for the armed procedure and the bisect plan.