Fetch a per-node-type manifest instead of the shared file #64
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/per-type-manifest"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
CCS-UF-20. A node fetched
/firmware/manifest.json, which held every node type's entry, and read only its own — 13 updatable node types already breach the 2048-byte cap. Fetches/firmware/manifest/0x<type>.jsoninstead, holding just that node's entry with noimageswrapper and no key.This is mostly a deletion:
firmware_manifest.cpploses the key-matching walk (the source of CCS-UF-5's out-of-bounds read and CCS-UF-19's version-overflow findings), andmanifest_shrinks from 2048 to 512 bytes. Net -37 lines.A 404 is now distinguished from any other transport failure (
transportNotFoundvstransportFailedinupdate_transport.h), so a node with nothing published fails at once instead of burning three retries a second apart. Spec section 11.1 updated to match.No fallback to the old shared path — CCS-UHA-24 keeps Home Assistant serving both, so this lands independently.
Tested:
pio test -e native(434 cases),pio run(both nodes) andpio run -e host_simall pass. Vectors regenerated and diffed clean — no frame bytes changed.Reviewed the diff, ran
pio test -e native(full suite, 434 cases, ASan/UBSan) andclang-format --dry-runon the touched files — all clean. Parser simplification (descendTo/writeKeyremoval,matchString/nextMemberlosingfoldCase) checks out against the old CCS-UF-5/CCS-UF-19 bug sites; no case-folding path remains anywhere it matters.maxManifestPathLength = 28andmaxManifestLength = 512are both correctly sized (28 chars + NUL fits the+1buffer exactly for a%02x-formatted byte; 512 comfortably covers a worst-case single entry, ~194 bytes). No dead code or half-migrated constants left behind.One thing worth a look, nothing else blocking:
lib/node-runtime/src/firmware_updater.cpp,open()— thetransportNotFoundbranch is shared by both the manifest fetch and the image fetch (fetchingManifest ? "no firmware published for this node type" : "image missing from the server"). That means a 404 while fetching the image .bin is now also terminal (skips the retry/attempt-limit path it used to go through), which is a real behaviour change beyond the ticket's manifest-path swap. It looks like the right call, but there's no test exercising that branch (both new tests,test_a_404_for_the_manifest_fails_at_once_without_retryingand thetest_update_failed_stateequivalent, only setnotFoundduring the manifest fetch), anddocs/can-protocol.md§11.1 only documents the 404 case for the manifest, not the image. Suggest either a small test for the image-404 case, or a one-line spec mention, so this isn't silently relying on the shared code path.Nit:
char manifestPath[maxManifestPathLength + 1];inopen()is left uninitialised, unlike every other stack char buffer in this file/library which use= {}. It's not a bug today (only read on the branch that writes it viasnprintf), but it's inconsistent with the surrounding style and a future edit that reads it unconditionally (e.g. for logging) would pick up garbage.Everything else — the manifest parser's edge cases (escapes, unknown-member skip, truncation, nested skip), the transport 404-vs-failure plumbing, the
snprintf/static_cast<unsigned>path formatting, the spec wording, and the rewritten tests (heap exact-length buffers preserved for the ASan redzone, no lost coverage) — checks out.Follow-up on
8eb162e— both notes from the previous review are closed out.manifestPathis now zero-initialised (= {}), matching the file's convention.test_a_404_for_the_image_fails_at_once_without_retryinggenuinely exercises the image-fetch path: it runs twoloop()calls to get through the manifest fetch and parse (stage becomesImage), then setsnotFoundand callsloop()a third time.beginCalls == 2andsink.beginCalls == 0confirm the 404 hits on the image request before the slot is ever opened, not a repeat of the manifest case.pio test -e nativepasses all 435 cases (up from 434).pio run(both node envs) andpio run -e host_simbuild clean. No new issues found. (Can't set an approving state — Forgejo blocks self-approval on your own PR — posting as a comment instead.)