Skip to main content

TECH VEDA

Embedded Linux on Edge-AI 23rd Sept 2026 enrollingLinux kernel & Device drivers starts on 24th Oct 2026 enrollingCorporate on-site training - Submit proposal Pick your modulessignup for free monthly live Masterclass Register
Insights

Your SBOM Is Ready. Your Kernel CVE Triage Is Not.

CRA reporting starts 11 September 2026. Kernel CVE triage, not SBOM generation, is the hard part. How to filter false positives and record patches in Yocto.

Your SBOM Is Ready. Your Kernel CVE Triage Is Not.

When the Cyber Resilience Act’s reporting duty starts on 11 September 2026, the capability it assumes is not a Software Bill of Materials, which Yocto already generates by default. It is kernel CVE triage: being able to say within 24 hours whether an actively exploited vulnerability is present in the image you shipped. The Linux kernel, by Greg Kroah-Hartman’s account, is now the largest single issuer of CVEs, and most of those CVEs do not affect your product. Proving that requires build data you have to record before the incident, not after.

We covered the shape of the regulation in The Cyber Resilience Act Turns Security Into a Core Embedded Linux Skill: reporting obligations from 11 September 2026, the main obligations from 11 December 2027, and the point that a frozen vendor kernel is difficult to defend. This post goes one level down, into the part that decides whether any of it works in practice. Generating an SBOM is a configuration task; kernel CVE triage is an engineering capability, and the two are frequently confused.

What you have 24 hours to answer

The reporting duty sits in Article 14 of Regulation (EU) 2024/2847, and it is narrower than informal summaries make it sound. Only two things trigger it. An actively exploited vulnerability is one being used in an attack โ€” a vulnerability you find and fix before anyone exploits it goes through your ordinary vulnerability handling instead. A severe incident is one affecting the product’s ability to protect sensitive or important data or functions, or one that can lead to malicious code running on the device or on a user’s network. Routine bugs and ordinary patches are outside Article 14 entirely. The earlier post sets out both definitions in the regulation’s own terms, along with the wider scope โ€” including the point that reporting covers products you shipped years ago and have not touched since.

What makes the duty demanding is the schedule: an early warning within 24 hours of becoming aware, a fuller notification within 72 hours, and a final report no later than 14 days after a corrective or mitigating measure is available for an actively exploited vulnerability (within one month of the 72-hour submission for a severe incident). Reports go once through the CRA Single Reporting Platform, which ENISA is required to establish under Article 16, addressed to the CSIRT of the member state where the manufacturer has its main establishment and to ENISA.

So the question that arrives on your desk is narrow and urgent: a vulnerability in this subsystem is being exploited in the wild โ€” is it in our product? Every hour spent determining that is an hour not spent on the fix.

๐Ÿ’ก Key insight
The regulation does not ask you to have zero vulnerabilities. It asks you to know, quickly and defensibly, which ones you have. Those are different engineering problems, and only the second one has a 24-hour deadline.

The SBOM is already being generated

One correction worth making, because the instruction has changed since it was widely written down. On current Yocto releases you do not enable SPDX output โ€” it is on already. The OpenEmbedded build system inherits the create-spdx class through INHERIT_DISTRO by default, so if you are building with a recent poky you are producing an SBOM on every image build, whether or not anyone on the team has looked at it.

The document is written to tmp/deploy/images/MACHINE/ as IMAGE-MACHINE.spdx.json, and since the Styhead (5.1) release the output is SPDX 3.0. A single recipe’s document can be produced directly:

raghu@techveda.org:~$ bitbake busybox -c create_recipe_sbom

The SBOM records components, licences, dependencies, sources, the changes applied to each component and the vulnerabilities fixed. That last pair is the part that matters for triage, and it is where teams lose information without noticing.

Why kernel CVE triage is the binding constraint

The Linux kernel project became a CVE Numbering Authority on 13 February 2024. Greg Kroah-Hartman, who does much of that work, reports that the kernel went from issuing nothing to being the third largest issuer of CVEs by quantity in 2024, and the largest in 2025. Those figures are his, from the kernel CVE team’s own accounting, and they are the best available source on the question.

Most of those CVEs are irrelevant to any given embedded product, because the affected code was never compiled into it. A naive version match cannot tell. An unfiltered list of several thousand possible kernel CVEs is operationally the same as having no information at all โ€” you cannot search it under a 24-hour clock, and you cannot hand it to a customer’s security team as an answer.

The build-time check gives you the first pass. Each compiled recipe is mapped against the CVE database and every entry is marked Patched, Unpatched or Ignored. On the 5.0 (scarthgap) LTS release this is the cve-check class, enabled with INHERIT += "cve-check". In the current development series it has moved to sbom-cve-check, enabled through a configuration fragment:

raghu@techveda.org:~$ bitbake-config-build enable-fragment core/yocto/sbom-cve-check

This produces a report in the deploy directory listing, per package, the CVEs possibly affecting that version and the status of each. It is a starting point, not an answer, because at this stage the kernel entries are dominated by code your configuration never built.

Version note. Class names, fragment names and output paths move between Yocto releases, and a distro layer or vendor BSP can override them. create-spdx being default-on and emitting SPDX 3.0 is true from 5.1 onward; the check is cve-check on the 5.0 LTS and sbom-cve-check in the development series; the SPDX deploy path carries the spec version, so it reads tmp/deploy/spdx/3.0.1/... rather than a fixed directory. Check the release notes for the release you are actually on rather than copying paths out of any article, this one included.

Which kernel CVEs can actually be exploited on your device

Before reaching for tooling, it is worth being clear about what “actively exploited” tends to mean on an embedded product, because it narrows the field a great deal. Exploitation needs a reachable path. In practice the kernel code an attacker can drive is the code sitting at an exposed edge:

  • The network stack and network-facing services โ€” anything processing packets from a network the device does not control.
  • The USB device stack โ€” on any product with a port a person can physically reach, the descriptor parsers and class drivers are attacker-controlled input.
  • Filesystem and image parsers โ€” if the product mounts removable media or accepts an update image, the parsing code runs on untrusted bytes.
  • Bluetooth and Wi-Fi drivers and their firmware interfaces, which take input over the air.
  • Drivers exposed to unprivileged processes through a character device or an ioctl interface โ€” the boundary matters even inside the device, if any code running on it is less trusted than the kernel.

A use-after-free in a subsystem your kernel compiles but never feeds untrusted input to is a real bug, and it is not the same risk as one in your Wi-Fi driver. This is why triage is two questions, not one. Is the affected code even in my image? And can anything untrusted reach it? The first question is mechanical and a tool can answer it from build data. The second needs an engineer who knows the product’s interfaces. Automate the first so you have time for the second.

Filtering the CVEs your kernel never compiled

This is the technique that makes kernel CVE triage tractable, and it is not widely used. OpenEmbedded-Core ships scripts/contrib/improve_kernel_cve_report.py, which takes the list of source files actually compiled into your kernel and the Linux CNA’s own data, and reclassifies CVEs whose affected files your build never touched. The Yocto documentation states that it reduces kernel CVE false positives by 70% to 80%.

It depends on the build having recorded which sources were compiled, which requires debug information in the kernel and one variable. For linux-yocto:

KERNEL_EXTRA_FEATURES:append = " features/debug/debug-kernel.scc"
SPDX_INCLUDE_COMPILED_SOURCES:pn-linux-yocto = "1"

Then fetch the kernel CNA’s vulnerability data and run the script against your SPDX document:

raghu@techveda.org:~$ git clone https://git.kernel.org/pub/scm/linux/security/vulns.git ~/vulns
raghu@techveda.org:~$ python3 openembedded-core/scripts/contrib/improve_kernel_cve_report.py \
   --spdx tmp/deploy/spdx/3.0.1/qemux86_64/recipes/recipe-linux-yocto.spdx.json \
   --datadir ~/vulns \
   --old-cve-report build/tmp/deploy/images/qemux86_64/core-image-minimal-qemux86_64.rootfs.sbom-cve-check.yocto.json

Entries it can dismiss come back with the reason attached:

{
   "id": "CVE-2025-38384",
   "status": "Ignored",
   "detail": "not-applicable-config",
   "description": "Source code not compiled by config. {'drivers/mtd/nand/spi/core.c'}"
}

That is a defensible answer generated from build data, not a judgement made under time pressure. The script runs outside the BitBake environment, so it can be run during an incident โ€” but only against data the build captured earlier. You cannot retrofit compiled-source records onto an image that is already in the field.

Your patches only count if the metadata says so

This detail catches teams whose security practice is genuinely good. The vulnerability check marks a CVE as Patched only when the patch file’s commit message carries a CVE: tag, alongside an Upstream-Status: line:

CVE: CVE-2022-3341

Upstream-Status: Backport [https://github.com/FFmpeg/FFmpeg/commit/9cf652cef49d74afe3d454f27d49eb1a1394951e]

A team that has carefully backported forty security fixes and named the patch files 0001-fix-crash.patch will generate a report showing forty unpatched vulnerabilities. The work was done; the record was not kept. Where analysis shows a CVE does not apply, the mechanism is CVE_STATUS with a stated reason โ€” this replaced CVE_CHECK_IGNORE in the 5.0 release:

CVE_STATUS[CVE-2016-10642] = "cpe-incorrect: This is specific to the npm package that installs cmake, so isn't relevant to OpenEmbedded"

The reason string is the whole point, and it is worth being deliberate about it. A dismissal with a stated reason is a claim someone can check: it says why the CVE does not apply, and a reviewer can agree or disagree. A dismissal with no reason is indistinguishable from a CVE nobody looked at. Both make the number go down; only one of them is evidence. When a customer’s security team asks why a given CVE is absent from your report, the reason string is your answer, and the absence of one is also an answer. For kernel recipes, generate-cve-exclusions.py in OpenEmbedded-Core produces this information in bulk from the CVE Project’s data for a given kernel version.

๐Ÿ’ก Key insight
Every capability the 24-hour clock assumes is a property of your build configuration, decided months earlier: compiled-source records, CVE: tags on patches, supplier fields, and an update mechanism that works. None of them can be added to a product that has already shipped.

A practical checklist for Yocto projects

The useful work here is unglamorous and mostly takes days, not quarters:

  1. Confirm the SBOM. Build your image and read the SPDX document that is already being produced. Confirm it describes what you think you shipped.
  2. Turn on the vulnerability check โ€” cve-check or sbom-cve-check depending on your release โ€” and look at the raw number of kernel CVEs reported. That number is your current triage problem stated honestly.
  3. Record compiled sources for the kernel. Enable kernel debug information and SPDX_INCLUDE_COMPILED_SOURCES for the kernel recipe, then run improve_kernel_cve_report.py against vulns.git. Compare the two numbers.
  4. Tag your existing patches. Add CVE: and Upstream-Status: lines to the security patches already sitting in your layers, so the fixes you have done start being counted.
  5. Write down your dismissals. Move any informal “that one doesn’t apply to us” knowledge into CVE_STATUS with a real reason string, while the person who worked it out is still on the team.
  6. Wire the output into incident response. Decide now who watches for exploited kernel vulnerabilities (linux-cve-announce is the feed), who runs the filter, who decides a report is due and who submits it. A 24-hour clock is lost to internal escalation, not to analysis.

None of this requires a compliance programme. It requires knowing the build system well enough to make it produce evidence, which is the same skill that makes a BSP maintainable in the first place. This is ground we work through in the Embedded Linux and Yocto training.

Key takeaways

  • CRA reporting starts 11 September 2026 under Article 14: 24-hour early warning, 72-hour notification, 14 days to a final report once a fix exists. It covers products already on the market, including ones shipped years ago.
  • The duty covers actively exploited vulnerabilities and severe incidents, not every CVE in your image.
  • create-spdx is inherited by default through INHERIT_DISTRO on current releases โ€” SBOM generation is not the work.
  • By Greg Kroah-Hartman’s account the kernel became the largest single CVE issuer in 2025, so kernel CVE triage is what the 24-hour clock actually tests.
  • Triage is two questions: is the code in my image, and can anything untrusted reach it? Tooling answers the first; an engineer answers the second.
  • improve_kernel_cve_report.py with SPDX_INCLUDE_COMPILED_SOURCES cuts kernel CVE false positives by 70% to 80% โ€” but only if the build recorded compiled sources first.
  • Backports count as patched only when the patch carries a CVE: tag; dismissals belong in CVE_STATUS with a stated reason.
Was this worth your time?

Frequently asked questions

Do we have to report every CVE found in our image?
No. The reporting duty covers actively exploited vulnerabilities and severe incidents affecting the security of the product. A vulnerability you find and fix before anyone exploits it goes through your ordinary vulnerability handling instead.

What counts as a severe incident under the CRA?
The regulation describes an incident having an impact on the security of a product as one that negatively affects, or is capable of negatively affecting, the product’s ability to protect the availability, authenticity, integrity or confidentiality of data or functions. The severe qualifier means the data or functions are sensitive or important, or that malicious code can be introduced into or run on the product or a user’s networks.

How do I reduce kernel CVE false positives in a Yocto build?
Enable SPDX_INCLUDE_COMPILED_SOURCES for the kernel recipe along with kernel debug information, then run improve_kernel_cve_report.py from OpenEmbedded-Core against the kernel CNA data in vulns.git. The Yocto documentation states this reduces false positives by 70% to 80%.

Why does the vulnerability report show CVEs we already patched?
The check marks a CVE as patched only when the patch file’s commit message contains a CVE: CVE-XXXX-YYYY tag. Backports without that tag are reported as unpatched even though the fix is applied.

Do I need to enable SBOM generation in Yocto?
Not on current releases. The create-spdx class is inherited by default through INHERIT_DISTRO, and the SPDX document is written to the deploy directory when you build an image. Since the 5.1 release the output format is SPDX 3.0.

Further reading

RB
Raghu Bharadwaj

Founder, TECH VEDA โ€” 20+ years teaching the Linux kernel, device drivers and embedded systems.

Follow on LinkedIn

Get new posts by email

Kernel, embedded Linux and AI-era engineering โ€” a few sharp reads a month. No spam.

We email occasionally and never share your address.