Skip to content

feat(firmware): read the kernel version from outside the rootfs - #750

Merged
haksungjang merged 2 commits into
mainfrom
feat/firmware-kernel-identification
Aug 28, 2026
Merged

feat(firmware): read the kernel version from outside the rootfs#750
haksungjang merged 2 commits into
mainfrom
feat/firmware-kernel-identification

Conversation

@haksungjang

Copy link
Copy Markdown
Member

Summary

  • Every identification pass in the firmware pipeline (cve-bin-tool's own checkers, identify-elf-presence.py, identify-version-strings.py) reads only inside the chosen rootfs (the parent of the shallowest etc). Two firmware layouts put the kernel image outside that directory entirely: an install ISO where the boot catalog and the install payload are sibling top-level trees, and an image where the kernel is an intermediate decompression stage on the way down to the rootfs rather than a file inside it. Either shape left the kernel unidentified, so no kernel CVE could ever be matched no matter how complete the CPE index is.
  • New docker/lib/identify-kernel-version.py reads the whole extraction (not the rootfs) for the kernel's own two signatures only — a loadable module's vermagic=, and the kernel's own Linux version ... #<n> <weekday> boot banner (both are cve-bin-tool's own patterns, cve_bin_tool/checkers/linux_kernel.py, adjusted for reading raw bytes instead of a pre-extracted string stream). Narrow by pattern, wide by directory scope — this keeps identify-version-strings.py's closed-table precision argument intact while still reaching a kernel that sits above or beside the rootfs.
  • Candidates are ranked by evidence grade (vermagic beats banner) then by how many distinct files support each version, so the answer is deterministic regardless of filesystem walk order — depth has no structural reason to favor one kernel image over another the way it does for rootfs selection.
  • Deliberately a new script, not an extension of identify-version-strings.py: that script's marker-stripping dedupe collapses different branches of the tree into the same key once its search directory widens past a single rootfs, which would make reading the kernel-bearing file a coin flip on walk order.
  • Wired in at scan-firmware.sh's existing merge slot for identify-version-strings.py's output, so no change to the downstream merge/dedupe logic. Emits cpe:/a:linux:linux_kernel:<version> (the CPE 2.2 form cve-bin-tool's own scan output and the Dockerfile's kernel smoke test both use) — firmware-cpe-match.py's CPE parser only reads part a.
  • Verified end-to-end against two real firmware images with different signature shapes (one with loadable kernel modules, one with the boot banner only): the kernel component is now identified in both, and the merged security report carries per-CVE findings against linux_kernel that were previously missing entirely.

Test plan

  • tests/test-firmware-unpack.sh (184 assertions, 0 failed) — new section covers reading a signature outside the rootfs, evidence-grade ranking, the path-collision regression identify-version-strings.py's dedupe would have hit, order-independence of choose(), the cpe:/a: downstream contract via a synthetic CPE index fed through firmware-cpe-match.py, cap-forwarding, and a wiring guard confirming the pass is called with the whole extraction rather than the rootfs
  • python3 -m py_compile docker/lib/identify-kernel-version.py
  • shellcheck --severity=warning on all three changed shell files (clean)
  • Built the firmware-opt-in image locally and ran it against two real firmware images end to end (not just unit tests) — kernel identified in both, CVEs matched in both

Every identification pass (cve-bin-tool's own checkers, the ELF
presence reader, the version-string supplement) reads only inside the
chosen rootfs. Two firmware layouts put the kernel image outside it
instead: an install ISO where the boot catalog and the install
payload are sibling top-level trees, and an image where the kernel is
an intermediate decompression stage on the way down to the rootfs
rather than a file inside it. Either shape left the kernel entirely
unidentified, so no kernel CVE could ever be matched regardless of
index coverage.

identify-kernel-version.py reads the whole extraction for the
kernel's own two signatures only (a loadable module's vermagic, the
kernel's boot banner) -- narrow by pattern, wide by directory scope,
which keeps the identify-version-strings.py precision argument intact
while still reaching a kernel that sits above or beside the rootfs.
Candidates are ranked by evidence grade and support-file count so the
answer is order-independent, unlike a depth-based pick which has no
structural reason to favor one kernel image over another.

Verified against two real firmware images with different signature
shapes (one with loadable modules, one with the boot banner only):
the kernel component is now identified in each, and the merged
security report carries per-CVE findings against linux_kernel that
were previously missing entirely.
@haksungjang
haksungjang merged commit 032d9a0 into main Aug 28, 2026
34 checks passed
@haksungjang
haksungjang deleted the feat/firmware-kernel-identification branch August 28, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant