152 lines
7.7 KiB
Markdown
152 lines
7.7 KiB
Markdown
# Host-side tests for obu-firmware
|
|||
|
|
|
||
|
|
**Status (2026-09-11):** the chain test, the capture replay and the fuzzer are written and pass;
|
||
|
|
results under "Running". Toolchain: MSYS2 UCRT64 gcc, Option B below (chosen and
|
||
|
|
installed 2026-09-11).
|
||
|
|
|
||
|
|
`geonet.c`, `dot11p.c` and `gn_unwrap.c` include nothing but standard C headers, so they compile
|
||
|
|
unmodified on a PC. That makes three things possible without a board: a TX -> RX round trip
|
||
|
|
through our own code, replaying real captures through the RX parser, and fuzzing the parser that
|
||
|
|
reads untrusted radio bytes. ESP-IDF only builds `main/` (and `components/`), so nothing under
|
||
|
|
`test/` ever affects the firmware image.
|
||
|
|
|
||
|
|
## Layout
|
||
|
|
|
||
|
|
```
|
||
|
|
obu-firmware/
|
||
|
|
├── main/ firmware sources; the tests compile these directly, never copies
|
||
|
|
└── test/
|
||
|
|
├── pcap_gn_tally.py GN header fields per station over .pcap captures (Python only)
|
||
|
|
└── host/
|
||
|
|
├── README.md this file
|
||
|
|
├── Makefile `make`: builds ../../main/{geonet,dot11p,gn_unwrap}.c + tests, runs them
|
||
|
|
├── test_chain.c geonet_wrap_shb -> dot11p_build_frame -> gn_unwrap_its, byte-checked
|
||
|
|
├── test_util.c/.h shared: guard page, crash report, pcap read/write
|
||
|
|
├── test_replay.c every recorded frame through gn_unwrap_its, results to a TSV
|
||
|
|
├── check_replay.py re-derives each result independently, then asn1tools on the messages
|
||
|
|
├── fuzz_gn_unwrap.c mutation fuzzer; each input ends against a no-access guard page, so
|
||
|
|
│ an over-read faults (no ASan on MinGW)
|
||
|
|
└── build/ compiler output; already ignored by the repo's `build/` rule
|
||
|
|
```
|
||
|
|
|
||
|
|
The repo's `vanetza/` folder is a gitignored reading copy only; it is not built. `check_replay.py`
|
||
|
|
reads the IEEE 1609.2 ASN.1 modules from it (they carry no licence header, so they are not copied
|
||
|
|
into `asn1/`). Without it, signed frames are still replayed but not checked independently.
|
||
|
|
|
||
|
|
## Running
|
||
|
|
|
||
|
|
From PowerShell, with MSYS2 on PATH for the session (Option B step 3):
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
$env:PATH = "C:\msys64\ucrt64\bin;C:\msys64\usr\bin;$env:PATH"
|
||
|
|
cd C:\Users\Ashin\AndroidStudioProjects\MicrOBU\obu-firmware\test\host
|
||
|
|
make
|
||
|
|
```
|
||
|
|
|
||
|
|
`make` runs three things (`make chain`, `make replay`, `make fuzz` run one). A non-zero exit, or a
|
||
|
|
`CRASH ... during: <what>` line (from the fuzzer, followed by the input as hex), is a failure.
|
||
|
|
|
||
|
|
- **chain** (`test_chain.c`): frames built by the firmware's own TX code, checked byte by byte
|
||
|
|
against EN 302 636-4-1 and parsed back. Covers the CAM layout (non-QoS and QoS), Source Position
|
||
|
|
Vector edges, output-buffer bounds, a 512-byte payload through `main.c`'s buffer sizes,
|
||
|
|
hand-built GeoBroadcast frames in all three shapes, signed frames in all three COER length
|
||
|
|
forms plus a top-level unsecuredData, one-byte mutations that must be rejected or accepted, the
|
||
|
|
Common Header's payload length as the message boundary, the 8-byte RX trailer, and every
|
||
|
|
truncation length of each frame against the guard page. `pcap_gn_tally.py` then reads the
|
||
|
|
frames back as a second parser; `build/test_chain.pcap` opens in Wireshark too.
|
||
|
|
2026-09-11: 1731 checks, 0 failed.
|
||
|
|
- **replay** (`test_replay.c` + `check_replay.py`): every record in
|
||
|
|
`its-g5-receiver-firmware/recordings/*.pcap` through `gn_unwrap_its`, each cut to `main.c`'s
|
||
|
|
800-byte capture buffer as on the board. `check_replay.py` re-derives each result on its own
|
||
|
|
(its own GN/BTP parse; the security envelope decoded by asn1tools from the IEEE 1609.2 modules),
|
||
|
|
compares record by record, then decodes and re-encodes every distinct message with asn1tools -
|
||
|
|
a byte-identical re-encode is only possible when the message was cut at exactly the right byte.
|
||
|
|
Needs `py -3.11` with asn1tools. 2026-09-11: 15 145 records, 15 131 accepted (10 831 CAM,
|
||
|
|
4 300 DENM; 157 of them signed); C and Python agree on every record; 11 043 of the 11 106
|
||
|
|
distinct messages re-encode byte-identically. The other 63 fail the same way with the old 8
|
||
|
|
trailing bytes put back, so the boundary is not the cause: 5 are our own CAMs from before the
|
||
|
|
2026-08-20 yawRateConfidence fix, 56 come from the CiT One and 1 from another station (see
|
||
|
|
`TODO.md`), and 1 uses an extension asn1tools cannot re-encode.
|
||
|
|
- **fuzz** (`fuzz_gn_unwrap.c`): random edits of every recorded frame, each run against the guard
|
||
|
|
page; an over-read crashes, an accepted payload outside its input fails. Default 2 000 000
|
||
|
|
iterations (about 2 s); `make fuzz FUZZ_ITER=50000000 FUZZ_SEED=7` for a longer run.
|
||
|
|
2026-09-11: 50 000 000 iterations, no crash.
|
||
|
|
|
||
|
|
Not covered: `main.c` (serial prefix parsing, queues) and `serial_link.c`, which need ESP-IDF;
|
||
|
|
and the phone's encoder, whose bytes are opaque here (asn1tools and the app's golden test cover
|
||
|
|
it).
|
||
|
|
|
||
|
|
## Option A (not used): WSL2 + Ubuntu 24.04
|
||
|
|
|
||
|
|
Kept as the fallback if AddressSanitizer or libFuzzer are ever needed; Option B has neither.
|
||
|
|
|
||
|
|
1. In **PowerShell as Administrator**:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
wsl --install -d Ubuntu-24.04
|
||
|
|
```
|
||
|
|
|
||
|
|
Reboot if it asks. Ubuntu then opens and asks for a Linux username and password (separate from
|
||
|
|
the Windows account). Checked 2026-09-11: Hyper-V is already running on this PC, so no BIOS
|
||
|
|
change should be needed. If the install says virtualization is disabled, enable Intel VT-x /
|
||
|
|
AMD SVM in the BIOS.
|
||
|
|
|
||
|
|
2. Confirm it is WSL **2**: `wsl -l -v` should list `Ubuntu-24.04` with VERSION `2`.
|
||
|
|
|
||
|
|
3. Inside Ubuntu, the compilers:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo apt update
|
||
|
|
sudo apt install -y build-essential clang cmake ninja-build git pkg-config python3
|
||
|
|
```
|
||
|
|
|
||
|
|
4. Smoke test: the firmware sources compile on the host (expect no output):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd /mnt/c/Users/Ashin/AndroidStudioProjects/MicrOBU/obu-firmware/test/host
|
||
|
|
cc -std=c11 -Wall -Wextra -fsyntax-only ../../main/geonet.c ../../main/dot11p.c ../../main/gn_unwrap.c
|
||
|
|
```
|
||
|
|
|
||
|
|
## Option B (chosen): native Windows gcc via MSYS2
|
||
|
|
|
||
|
|
Enough for the round-trip test, the capture replay and the guard-page fuzzer. No libFuzzer and no
|
||
|
|
AddressSanitizer with MinGW gcc, which is why the fuzzer uses a guard page instead.
|
||
|
|
|
||
|
|
1. In PowerShell: `winget install -e --id MSYS2.MSYS2` (installs to `C:\msys64`).
|
||
|
|
2. Open **MSYS2 UCRT64** from the Start menu and run `pacman -Syu`. If the window closes, reopen
|
||
|
|
it and run `pacman -Syu` again. Then:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
pacman -S --needed mingw-w64-ucrt-x86_64-gcc make
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **`C:\msys64\ucrt64\bin` must be on PATH.** Calling `C:\msys64\ucrt64\bin\gcc.exe` by its full
|
||
|
|
path alone exits 1 with no message, because gcc's compiler stages load their DLLs from that
|
||
|
|
folder. `make` lives on the MSYS side, in `C:\msys64\usr\bin`. Either work inside the
|
||
|
|
**MSYS2 UCRT64** shell, which has both, or put them on PATH for the current session:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
$env:PATH = "C:\msys64\ucrt64\bin;C:\msys64\usr\bin;$env:PATH" # PowerShell
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export PATH=/c/msys64/ucrt64/bin:/c/msys64/usr/bin:$PATH # Git Bash
|
||
|
|
```
|
||
|
|
|
||
|
|
Adding them to the user PATH permanently also works; it was deliberately not done by setup.
|
||
|
|
4. Smoke test (expect no output):
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
cd C:\Users\Ashin\AndroidStudioProjects\MicrOBU\obu-firmware\test\host
|
||
|
|
gcc -std=c11 -Wall -Wextra -fsyntax-only ../../main/geonet.c ../../main/dot11p.c ../../main/gn_unwrap.c
|
||
|
|
```
|
||
|
|
|
||
|
|
Installed on this PC 2026-09-11: MSYS2 20260611, gcc 16.2.0 (UCRT64), GNU Make 4.4.1. All three
|
||
|
|
firmware sources compile with `-std=c11 -O2 -Wall -Wextra -Wpedantic` and no warnings.
|
||
|
|
|
||
|
|
## Line endings
|
||
|
|
|
||
|
|
The repo runs with `core.autocrlf=true`, so Windows checkouts have CRLF line endings. C compilers
|
||
|
|
don't care; shell scripts run from WSL do (`bash: $'\r': command not found`). When the first `.sh`
|
||
|
|
file lands here, add `*.sh text eol=lf` to a root `.gitattributes` (none exists yet).
|