← freediag: C→Rust Port Findings
Warm-up · in progress
freediag Port — Overview
freediag is an open-source OBD-II / K-line diagnostics stack that most teams stopped touching years ago. The code still compiled against old toolchains, but the documentation, tests, and build tooling around it had gone dormant right along with the commit history. We picked it as a warm-up run for the agent-squad pipeline behind Boro's legacy-modernization engagements: a smaller, lower-stakes codebase to shake out the process before pointing it at paying work.
The run ported roughly 32,000 lines of C to a Rust workspace under a lead / implementer / reviewer model. Along the way it did something a build recovery alone never would. Translating the code line by line turned into a thorough audit of it, and that audit is the part worth publishing.
What the port found
Seventy distinct findings were recorded and, with two exceptions, each was independently re-derived from the C source by a second reviewer before being logged. They sort into four groups:
- 14 memory-safety / undefined-behavior bugs — unclamped
memcpyof a wire-derived length, uninitialized reads trusted as data, a use-after-free. - 26 logic bugs — wrong arithmetic, mis-framed lengths, dropped error returns.
- 4 data-file defects in the shipped simulator databases.
- 26 design quirks — dead code, latent hazards, and comments that contradict the code beneath them.
The headline is a wire-derived length copied into a 7-byte buffer with no bound check. ISO 9141 is the only protocol that happens to be safe, because its frames are capped at exactly 7 bytes; the five other protocols share the same storage without inheriting that cap, and one of them is already exercised with real traffic in the project's own test corpus.
What Rust did, and did not, buy
This is the honest centre of the report, and it resists the triumphal version.
Three whole classes of bug cannot survive a straightforward Rust port: unclamped
memcpy, uninitialized reads, and resource-lifecycle leaks. For those, the
language does the work and a naive translation is simply immune.
But the narrowing-cast class survives verbatim. Rust's as truncates exactly as
C's implicit conversion does, so a line-by-line translation reproduces every one
of those bugs unless a human or agent stops at each cast site. And pure logic and
documentation defects, a multiplier that should be 256, a comment that names its
two modes backwards, are exactly as hard to get right in Rust as in C. Those were
caught by review, not by the compiler. Rust converts an entire tier of findings
from silent corruption into a loud failure. The tier below it, intent, was
carried entirely by process.
Read the full report
- Whitepaper — the complete findings report, print-ready, with every citation linked to the source.
- Findings browser — all 70 findings, filterable by taxonomy and severity, each linked to the exact line in the frozen C tree.
- Timeline & cost — how the way of working changed over six days, and the token economics of the run.
- Source browser — the frozen C reference, 86 files rendered line-addressable with findings flagged in the gutter.
This run started in 2026 and is still in progress. The findings above are real work product from the warm-up, not a client engagement or a third-party result. The blog covers the week-by-week story as it continues.