fix: v0.12.2 — conformance-audit fixes (DBC StartBit, CAN XL socketcan, FD DLC, ISO-TP) - #93
Merged
Merged
Conversation
…n, FD DLC, ISO-TP STmin/empty-CF)
Gap-audit pass against ISO 11898-1 / ISO 15765-2 found five conformance
issues, closed here:
- dbc: parseSignal now rejects StartBit < 0 or > 511. A negative start
bit previously parsed successfully and panicked on the first
Decode/Encode call ("negative shift amount", 1 << (-1)) — malformed
or adversarial DBC input could crash any consumer. Regression tests
added (TestParseRejectsNegativeStartBit, TestParseRejectsOversizeStartBit)
plus a dedicated FuzzParse seed corpus entry.
- socketcan: Bus.Send now rejects CAN XL frames (ErrInvalidFrame)
instead of silently emitting a corrupted classic CAN frame. Linux
CAN_RAW sockets can only carry can_frame/canfd_frame; the previous
encodeFrame fallback truncated the length byte (e.g. 2048 -> 0 mod
256) and copied at most 8 of up to 2048 data bytes with no error
returned. TestSendRejectsXL added (vcan-gated, matching existing
test conventions in this file).
- can.go: ValidateFrame now rejects non-canonical CAN FD payload
lengths. CAN FD only represents 0-8, 12, 16, 20, 24, 32, 48, 64
bytes on the wire (Bosch CAN FD DLC-code mapping); lengths like 9,
10, 11 previously passed validation and would be silently padded by
the controller, desyncing sender/receiver length expectations.
Table-driven cases added for both rejected and accepted lengths.
- isotp: Consecutive Frames carrying only the PCI byte (empty
payload) are now rejected during reassembly instead of allowing buf
to stall indefinitely while SN keeps incrementing.
TestRecvRejectsEmptyConsecutiveFrame added. Reserved STmin values
(0x80-0xF0, 0xFA-0xFF) now map to the conventional 127 ms fail-safe
maximum instead of 0 (no delay), matching the interpretation used by
reference ISO-TP implementations (e.g. Linux kernel isotp.c).
TestSTminToDurationReservedValuesMapToFailSafeMax and
TestSTminToDurationStandardRanges added as internal unit tests.
- .github/workflows/ci.yml: third-party actions (checkout, setup-go,
upload-artifact) pinned to commit SHAs with version comments;
govulncheck pinned to v1.6.0 instead of @latest (supply-chain
hardening, SLSA/OSSF Scorecard Pinned-Dependencies practice).
cmd/cantool toolVersion bumped to 0.12.2; ROADMAP.md updated per
existing release-plan convention.
go build/vet/test -race all green (non-socketcan packages, run
locally on darwin); socketcan cross-compiled and vetted clean for
GOOS=linux (execution requires vcan0, unavailable on darwin/CI
hosted runners per this repo's own documented CI limitation).
go test -fuzz=FuzzParse run for 10s with no panics.
Signed-off-by: SoundMatt <47545907+SoundMatt@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the go-CAN findings from the x-Net gap-audit pass. Primary spec authority: ISO 11898-1 (CAN/CAN FD/CAN XL framing) and ISO 15765-2 (ISO-TP). Most-severe first:
go-CAN-01 (high) — DBC parser accepts negative StartBit → runtime panic
dbc.parseSignalvalidatedlength(1-64) but had no bound check onstartBit. A DBC file withSG_ S : -1|8@1+ ...parsed successfully; the firstDecode/Encodecall computed a negative bit index and executed1 << (-1), panicking with "negative shift amount". This is a malformed/adversarial-input crash — any consumer parsing untrusted DBC files could be crashed.Fix:
parseSignalnow rejectsstartBit < 0 || startBit > 511(512 bits = 64-byte CAN FD payload upper bound), matching the existing length-validation pattern.Regression-test proof:
TestParseRejectsNegativeStartBitandTestParseRejectsOversizeStartBitindbc/parser_test.goassertParsenow returns an error for these inputs (previously would have parsed successfully and panicked downstream onDecode). Also added a dedicatedFuzzParseseed corpus entry for the negative-start-bit case, and rango test -fuzz=^FuzzParse$ -fuzztime=10s ./dbc/...locally — 1.5M+ executions, no panics.go-CAN-02 (high) — socketcan silently mis-encodes CAN XL as corrupted classic CAN
can.ValidateFramecorrectly accepts XL frames (1-2048 bytes), butsocketcan/bus_linux.go'sencodeFrame()only branched onf.FD, so XL frames fell through to the 16-byte classiccan_framepath: the length byte truncated (e.g. 2048 → 0 mod 256) and at most 8 of up to 2048 data bytes were copied, withSendreturning no error — a corrupt, silently mislabelled frame transmitted on the wire. LinuxCAN_RAWsockets cannot represent CAN XL at all (per ISO 11898-1:2024 / CiA CAN XL), so the correct minimal fix is to fail loudly rather than attempt (out of scope) native XL wire encoding.Fix:
Bus.Sendnow rejectsf.XLwithErrInvalidFramebefore ever callingencodeFrame.Test:
TestSendRejectsXLinsocketcan/bus_linux_test.go(vcan-gated, matching this file's existing test convention — see note on CI/local verification below).go-CAN-03 (medium) — ValidateFrame accepts non-canonical CAN FD lengths
CAN FD (ISO 11898-1 / Bosch CAN FD) only represents the discrete lengths 0-8, 12, 16, 20, 24, 32, 48, 64 bytes on the wire (DLC codes 9-15 map to 12/16/20/24/32/48/64).
ValidateFrame's FD branch only rejectedlen(Data) > 64, so e.g. a 9-byte FD frame passed validation and would be silently padded to 12 bytes by the controller — sender and receiver disagree on length.Fix: added
validFDDataLenhelper and aValidateFramecheck rejecting FD lengths outside the canonical DLC-mapped set.Test: table-driven cases in
TestValidateFrame(can_test.go) for rejected lengths 9/10/11/13/15 and accepted canonical lengths 12/20/48.go-CAN-05 (low) — ISO-TP empty-CF stall + reserved STmin mapped to 0
Two issues in
isotp/transport.go: (a) a Consecutive Frame carrying only the PCI byte (len(Data)==1) yielded an empty chunk;bufnever grew whilesnkept incrementing, letting a peer with correctly-incrementing SNs but zero-length payloads stall reassembly indefinitely as long as frames kept arriving. (b)stminToDuration's default case mapped all ISO 15765-2 reserved STmin values (0x80-0xF0, 0xFA-0xFF) to 0 (no delay) instead of the conventional 127ms fail-safe used by reference implementations (e.g. Linux kernelisotp.c) — independently confirmed via WebSearch against the ISO 15765-2 STmin encoding table.Fix: reject CFs with no payload bytes during reassembly (
errors.New("isotp: empty consecutive frame payload")); reserved STmin values now map to 127ms instead of 0.Tests:
TestRecvRejectsEmptyConsecutiveFrame(external test, viaRecv) andTestSTminToDurationReservedValuesMapToFailSafeMax/TestSTminToDurationStandardRanges(new internal test file,isotp/transport_internal_test.go, sincestminToDurationis unexported). Updated a stale comment in the existingTestSendMultiFrameMicroSTminthat referenced the old "reserved → 0" behavior.go-CAN-04 (low) — CI actions pinned to mutable tags
.github/workflows/ci.ymlreferencedactions/checkout@v5,actions/setup-go@v6,actions/upload-artifact@v4/v6by mutable tag, and installedgovulncheck@latest. Supply-chain hardening (SLSA / OSSF Scorecard Pinned-Dependencies) — not a protocol-conformance item, so not re-verified against ISO specs, accepted as standard practice.Fix: pinned all four action references to their current release's commit SHA (with a
# vX.Y.Zcomment) andgovulnchecktov1.6.0.Verification
go build ./.../go vet ./...— cleango test -race -count=1 $(go list ./... | grep -v socketcan)— all packages passGOOS=linux go vet ./socketcan/.../GOOS=linux go build ./socketcan/...— clean (dev machine is darwin; socketcan is Linux-only by filename-suffix build constraint, and its tests requirevcan0, which — per this repo's own CI comments — GitHub-hosted runners don't provide either, so CI'stest-socketcanjob is the actual first execution ofTestSendRejectsXL, same as all pre-existing tests in that file)go test -fuzz=^FuzzParse$ -fuzztime=10s ./dbc/...— 1.5M+ execs, no panics, no new crashersgofusa/relayCLI available in this environment — thegofusaandrelay-conformCI jobs will be the first real check; no FuSa-gated safety artifact (coverage/traceability/ASIL) content needed manual editing, since all new tests live in files already covered by blanket//fusa:testtags for the same requirement IDs, matching this repo's existing convention.ROADMAP.mdupdated with a newv0.12.2patch row per existing convention (see e.g. the v0.12.1 row/commit for precedent);cmd/cantooltoolVersionbumped to match.Do not merge — leaving for review/CI per process.