Page MenuHomeFreeBSD

Generate SBOM files as part of the build
Needs ReviewPublic

Authored by khorben on Apr 17 2026, 5:31 PM.
Tags
None
Referenced Files
F173892767: D56474.id.diff
Tue, Sep 29, 4:08 AM
F173846388: D56474.id187712.diff
Mon, Sep 28, 9:01 PM
Unknown Object (File)
Sun, Sep 27, 4:39 PM
Unknown Object (File)
Sun, Sep 27, 7:30 AM
Unknown Object (File)
Sun, Sep 27, 3:03 AM
Unknown Object (File)
Sat, Sep 26, 8:05 PM
Unknown Object (File)
Sat, Sep 26, 3:32 PM
Unknown Object (File)
Thu, Sep 24, 12:56 PM

Details

Summary

This introduces the following option:

  • MK_SBOM: enables the generation of SBOM files during the build.

This option uses bomtool(1) and spdxtool(1) from the pkgconf project, as provided by the MK_PKGCONF option. Consequently, MK_SBOM is automatically disabled when MK_PKGCONF is disabled.

The following parameters are available as well:

  • BOMTOOL: Path to bomtool(1) (for SPDX version 2 files)
  • JSONLDDIR: Destination for SPDX version 3 files (/usr/share/sbom/spdx-3.0.1)
  • SBOMDIR: Source directory for pkg-config files (share/sbom/pkgconfig)
  • SPDXDIR: Destination for SPDX version 2 files (/usr/share/sbom/spdx-2.2)
  • SPDXTOOL: Path to spdxtool(1) (for SPDX version 3 files)

Sponsored by: Alpha-Omega, Sovereign Tech Agency, The FreeBSD Foundation

Test Plan

With the single Makefile.sbom file included for usr.bin/pkgconf:

$ make buildworld
[...]
$ ls obj/amd64.amd64/tmp/usr/share/sbom/spdx-2.2/*.spdx
pkgconf.spdx
$ ls obj/amd64.amd64/tmp/usr/share/sbom/spdx-3.0.1/*.jsonld
pkgconf.jsonld

The SPDX files are also present in the corresponding package:

$ make packages
[...]
$ pkg info -l -F /usr/obj/usr/src/repo/FreeBSD\:16\:amd64/latest/FreeBSD-pkgconf-16.snap20260817204843.pkg
FreeBSD-pkgconf-16.snap20260817204843:
      [...]
      /usr/share/sbom/spdx-2.2/pkgconf.spdx
      /usr/share/sbom/spdx-3.0.1/pkgconf.jsonld

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

khorben edited the summary of this revision. (Show Details)
khorben edited the test plan for this revision. (Show Details)
khorben edited reviewers, added: ivy; removed: philip.
  • The SPDX files are now placed in their respective base packages.
  • The source .pc files from illuusio were imported into release/sbom.
release/sbom/pkgconfig/FreeBSD.pc
9 ↗(On Diff #177720)

This should probably be changed and set to 16.0 everywhere a library or binary is issued by FreeBSD.
Note that illuusio is currently working on a patch for pkgconf that will allow us to set the version dynamically.

a couple of comments:

  • i'm not sure this should be in release/, since it doesn't seem specific to building releases. iiuc, any src build will include the SBOM data if enabled.
  • this adds nearly 1,000 files which contain (among other things) manually listed shared library dependencies. who/what will be responsible for keeping these up to date?
  • we already have PCFILES set in bsd.lib.mk; adding another variable called PCFILE seems potentially confusing.

One thing we may want to investigate is building the SBOM files into the ELF headers instead. Sony has compiler extensions to do that -- https://github.com/sony/esstra/tree/poc/rust-llvm

release/sbom/pkgconfig/CC.pc
1 ↗(On Diff #177720)

I don't understand how this works. This file makes it seem like we're claiming that Clang is copyright by the FreeBSD Foundation?

release/sbom/pkgconfig/CC.pc
1 ↗(On Diff #177720)

the copyright header applies to the file it's in. CC.pc, which is not part of clang, was written by the Foundation, who therefore own the copyright on it.

One thing we may want to investigate is building the SBOM files into the ELF headers instead. Sony has compiler extensions to do that -- https://github.com/sony/esstra/tree/poc/rust-llvm

I wasn't aware of this project and approach; it's a possible avenue indeed. It doesn't seem to support the SPDX format nor LLVM yet though.

In D56474#1308969, @ivy wrote:

a couple of comments:

  • i'm not sure this should be in release/, since it doesn't seem specific to building releases. iiuc, any src build will include the SBOM data if enabled.

That's correct, and thank you for clarifying the distinction. Should we move it to e.g., share/sbom?

  • this adds nearly 1,000 files which contain (among other things) manually listed shared library dependencies. who/what will be responsible for keeping these up to date?

That is also a good point; at the moment there is no magic solution, and this information has to be maintained manually.
However, we could progressively switch to .pc files for storing compilation rules (outside of each Makefile) and merge that upstream when it is not us.
The same issue goes with version numbers, which we are working on automating already.

  • we already have PCFILES set in bsd.lib.mk; adding another variable called PCFILE seems potentially confusing.

That's also a valid concern; suggestions welcome. SBOMFILE comes to mind but AFAICT pkgconf expects the .pc extension, which could also be confusing.

stephane.rochoy_stormshield.eu added inline comments.
release/sbom/pkgconfig/libmd.pc
10 ↗(On Diff #177720)

This is invalid according to the SPDX Python Tools[1]:

ERROR:root:The document is invalid. The following issues have been found:
Unrecognized license reference: Beeware. license_expression must only use IDs from the license list or extracted licensing info, but is: BSD-4-Clause AND BSD-2-Clause AND Beeware AND RSA-MD

[1] https://github.com/spdx/tools-python

release/sbom/pkgconfig/libmd.pc
10 ↗(On Diff #177720)

s/Beeware/Beerware should be enough as PHK registered this license on SPDX.org ;)

For info, I reported a few minor problems to the pkgconf project: #516.

  • Avoid file conflicts in the -lib32 base packages (no duplicate installation of SPDX files)
  • Rename the PCFILE variable to SBOMFILE to avoid confusion with PCFILES
  • Fix typo in libmd.pc (thanks stephane.rochoy_stormshield.eu for the heads up!)
release/sbom/pkgconfig/CC.pc
3 ↗(On Diff #177720)

Where is this custom license?

release/sbom/pkgconfig/FreeBSD.pc
4 ↗(On Diff #177720)

Some files ise this for, others use the copyright text form. They should be cpnsisten.

Imported adjustments from Tuukka.

I don't really see how this is going to stay up-to-date: this seems to depend on developers remembering to update the file each time they add a dependency. There needs to be some kind of build-time check that validates the metadata. It needs to be easier to figure out how a given component of the system maps to an SBOM file. For instance, why does dtrace(8) get an entry, but not libdtrace?

Where do the descriptions come from? Aren't they basically duplicating all of our pkgbase metadata?

share/sbom/pkgconfig/dtrace.pc
11 ↗(On Diff #179390)

Why doesn't this entry have a Requires line?

share/sbom/pkgconfig/elfdump.pc
10 ↗(On Diff #179390)

Where did this list come from? elfdump doesn't use libcapsicum or libcasper.

share/sbom/pkgconfig/zfs.pc
13 ↗(On Diff #179390)

Why is this zfs.pc and not bectl.pc?

khorben edited the summary of this revision. (Show Details)
khorben edited the test plan for this revision. (Show Details)

Generate SPDX files in version 3.0.1 as well by default, now that spdxtool(1) has been imported into the base system.
Sets the correct version of FreeBSD automatically into the SBOM files generated for FreeBSD's own software.

release/sbom/pkgconfig/FreeBSD.pc
9 ↗(On Diff #177720)

The patch for pkgconf has been imported (as part of 2.9.93) and this is now integrated in this change request.

I just saw that this breaks cross-compilation, when building libpkgconf early for bomtool(1) and spdxtool(1) as tools for SBOM generation:

/Users/runner/work/freebsd-src/freebsd-src/contrib/pkgconf/libpkgconf/personality.c:127:21: error: use of undeclared identifier 'PKG_DEFAULT_PATH'
  127 |         pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, false);
      |                            ^~~~~~~~~~~~~~~~
/Users/runner/work/freebsd-src/freebsd-src/contrib/pkgconf/libpkgconf/personality.c:425:21: error: use of undeclared identifier 'PERSONALITY_PATH'
  425 |         pkgconf_path_split(PERSONALITY_PATH, &plist, true);
      |                            ^~~~~~~~~~~~~~~~
1 warning and 2 errors generated.

(See the build logs at https://github.com/freebsd/freebsd-src/pull/1994 for the full details)

This should fix the cross-compilation of libpkgconf while building bomtool(1) and spdxtool(1) as tools.

No longer build bomtool(1) and spdxtool(1) with NO_SHARED in the tooling phase. (Reported by imp@, thanks!)

No longer build bomtool(1) and spdxtool(1) with NO_SHARED in the tooling phase. (Reported by imp@, thanks!)

yea, it just looked weird! Thanks!

I don't really see how this is going to stay up-to-date: this seems to depend on developers remembering to update the file each time they add a dependency.

i agree and i object to landing this as-is; this simply isn't a workable solution.

when the original SBOM proposal was announced, i was disappointed when FF declared that pkgbase was out-of-scope for that work, but it seems like this and pkgbase and trying to solve similar problems when it comes to dependencies. can we not find a solution that works for both without requiring manual upkeep?

In D56474#1336929, @ivy wrote:

I don't really see how this is going to stay up-to-date: this seems to depend on developers remembering to update the file each time they add a dependency.

i agree and i object to landing this as-is; this simply isn't a workable solution.

when the original SBOM proposal was announced, i was disappointed when FF declared that pkgbase was out-of-scope for that work, but it seems like this and pkgbase and trying to solve similar problems when it comes to dependencies. can we not find a solution that works for both without requiring manual upkeep?

I totally see and understand how this solution is not perfect, and can lead to additional, undesired maintenance burden and possible inconsistencies in the data.

On one hand, this approach is pkgbase-agnostic; on the other hand, it is something pkgbase could do - but does not support yet as far as I know.

If this is not acceptable in the meantime, I am fine putting this on hold and looking for other solutions, pointers welcome as to how it could look.

However, compared to the original approach, by now we have managed to introduce dynamic content (as done here with the FreeBSD version number).
I could teach bsd.sbom.mk about library dependencies, extract that information from the Makefile, and integrate it into the SBOM files generated.
That should help with the tracking of at least some of the dependencies without developer intervention.
Would that be acceptable for now?

In D56474#1336929, @ivy wrote:

I don't really see how this is going to stay up-to-date: this seems to depend on developers remembering to update the file each time they add a dependency.

i agree and i object to landing this as-is; this simply isn't a workable solution.

when the original SBOM proposal was announced, i was disappointed when FF declared that pkgbase was out-of-scope for that work, but it seems like this and pkgbase and trying to solve similar problems when it comes to dependencies. can we not find a solution that works for both without requiring manual upkeep?

I totally see and understand how this solution is not perfect, and can lead to additional, undesired maintenance burden and possible inconsistencies in the data.

On one hand, this approach is pkgbase-agnostic; on the other hand, it is something pkgbase could do - but does not support yet as far as I know.

If this is not acceptable in the meantime, I am fine putting this on hold and looking for other solutions, pointers welcome as to how it could look.

I think we must ensure that these SBOM files are generated automatically by some means, with minimal intervention by developers. Otherwise it is guaranteed that these files will rot.

So I don't think this should land in its current form.

khorben edited the summary of this revision. (Show Details)
khorben edited the test plan for this revision. (Show Details)

With this new version of the SBOM generation framework, I have introduced the following changes:

  • SBOM files are only generated for a given library or program if a Makefile.sbom file is present in the corresponding folder.
  • The Makefile.sbom files are meant to contain the meta-data that could not be determined automatically.
  • The intermediate .pc (pkg-config) files used by bomtool(1) and spdxtool(1) are automatically generated from that information.
  • Only one Makefile.sbom file is included here, in usr.bin/pkgconf, as an example.

I suggest to review here the SBOM generation framework for its own qualities, and to review the incoming Makefile.sbom files in a separate revision. I am preparing a revision with the goal of generating one SBOM file per package described in packages, since the SBOM information is especially relevant for the third-party components imported.

That should amount to a total of about 140 SBOM files for each version supported (2.2 and 3.0.1) amounting to 280. That's 8000 less than the last proposal.

Anyway, let me know what you think.

  • Fixed an issue looking up dependencies between SBOM files
  • Added an SBOM file for libpkgconf to illustrate and test dependencies
  • Added support for SCRIPTS and SHLIB

With this new version of the SBOM generation framework, I have introduced the following changes:

  • SBOM files are only generated for a given library or program if a Makefile.sbom file is present in the corresponding folder.
  • The Makefile.sbom files are meant to contain the meta-data that could not be determined automatically.
  • The intermediate .pc (pkg-config) files used by bomtool(1) and spdxtool(1) are automatically generated from that information.
  • Only one Makefile.sbom file is included here, in usr.bin/pkgconf, as an example.

I suggest to review here the SBOM generation framework for its own qualities, and to review the incoming Makefile.sbom files in a separate revision. I am preparing a revision with the goal of generating one SBOM file per package described in packages, since the SBOM information is especially relevant for the third-party components imported.

That should amount to a total of about 140 SBOM files for each version supported (2.2 and 3.0.1) amounting to 280. That's 8000 less than the last proposal.

Anyway, let me know what you think.

I am still looking through this, but this feels like a much better direction!

Thanks!

lib/libpkgconf/Makefile.sbom
3

I would avoid hardcoding versions in these files.

What about a SBOM_VERSION_CMD= that would instead contain a "recipe" for getting at the canonical version string from the contrib/vendor tree? In bsd.sbom.mk, then you could do roughly SBOM_VERISION!= ${SBOM_VERSION_CMD}

I have been thinking about sbom for ports a lot the past week, and this gives me a good idea of which way I should head into.

I see that spdxtool and bomtool are in the devel/pkgconf port so I could start with that, but I think having these tools, available in the base system on the next 14 and 15 releases would greatly ease the work on those release branches. Otherwise, we'd have to have devel/pkgconf as a dependency for almost all ports.

In D56474#1369278, @mat wrote:

I have been thinking about sbom for ports a lot the past week, and this gives me a good idea of which way I should head into.

Awesome!

I see that spdxtool and bomtool are in the devel/pkgconf port so I could start with that, but I think having these tools, available in the base system on the next 14 and 15 releases would greatly ease the work on those release branches. Otherwise, we'd have to have devel/pkgconf as a dependency for almost all ports.

I agree. Let me know if I can help in any way; I can start by preparing a vendor import for pkgconf 3.0.7, which is the latest release from pkgconf upstream.

khorben marked an inline comment as not done.Mon, Sep 14, 5:53 PM
khorben added inline comments.
lib/libpkgconf/Makefile.sbom
3

I don't see how that's possible, since we may have a copy of the source tree without any meta-data from Git. (Like src.txz from a release distribution)
You can find some of my research on this possibility at https://github.com/FreeBSDFoundation/alpha-omega-beach-cleaning/tree/main/src/versions; in some cases we might be able to parse header files to avoid building a tool, but that's far from trivial AFAICT.

In D56474#1369278, @mat wrote:

I have been thinking about sbom for ports a lot the past week, and this gives me a good idea of which way I should head into.

Awesome!

As a package set is built incrementally, I am wondering if the sbom of a port needs to include the software it has been built with, for example, a port may need ninja, yacc or gettext to build, but not to run.

I see that spdxtool and bomtool are in the devel/pkgconf port so I could start with that, but I think having these tools, available in the base system on the next 14 and 15 releases would greatly ease the work on those release branches. Otherwise, we'd have to have devel/pkgconf as a dependency for almost all ports.

I agree. Let me know if I can help in any way; I can start by preparing a vendor import for pkgconf 3.0.7, which is the latest release from pkgconf upstream.

Well, I have to figure out things a bit.

The way I see it:

  1. the framework would generate a ${PKGBASE}.pc file during staging, put it in, say, /usr/local/share/sbom/pc and add it to the package.
  2. a pkg trigger would run a script on install to run bomtool on the pc file and generate a .spdx file in /usr/local/share/sbom/spdx-2.2, and run spdxtool to generate the .jsonld file in /usr/local/share/sbom/spdx-3.0.1.
  3. another pkg trigger would remove the .jsonld and the .spdx files on deinstall.

So, the base system would need to have spdxtool and bomtool for this to work.
From what I remember of the EuroBSDCon talks on the subject last week, those would need to be in the base systems of the releases of 14 and 15 before the end of next year.

khorben marked an inline comment as not done.Wed, Sep 23, 1:07 PM
In D56474#1369872, @mat wrote:
In D56474#1369278, @mat wrote:

I have been thinking about sbom for ports a lot the past week, and this gives me a good idea of which way I should head into.

Awesome!

As a package set is built incrementally, I am wondering if the sbom of a port needs to include the software it has been built with, for example, a port may need ninja, yacc or gettext to build, but not to run.

In FreeBSD, AFAICT binary packages are first-class citizens, so I would say that their corresponding SBOM should only include run-time dependencies.
Releases made from the src repository can now easily be created with specific ports installed by default (from binaries) and manufacturers can do the same too.

I see that spdxtool and bomtool are in the devel/pkgconf port so I could start with that, but I think having these tools, available in the base system on the next 14 and 15 releases would greatly ease the work on those release branches. Otherwise, we'd have to have devel/pkgconf as a dependency for almost all ports.

I agree. Let me know if I can help in any way; I can start by preparing a vendor import for pkgconf 3.0.7, which is the latest release from pkgconf upstream.

Well, I have to figure out things a bit.

The way I see it:

  1. the framework would generate a ${PKGBASE}.pc file during staging, put it in, say, /usr/local/share/sbom/pc and add it to the package.

I don't think this intermediate ${PKGBASE}.pc file needs to be kept.

  1. a pkg trigger would run a script on install to run bomtool on the pc file and generate a .spdx file in /usr/local/share/sbom/spdx-2.2, and run spdxtool to generate the .jsonld file in /usr/local/share/sbom/spdx-3.0.1.
  2. another pkg trigger would remove the .jsonld and the .spdx files on deinstall.

Why not simply generate the .jsonld and .spdx files during package creation, and include them in the plist?
Then there is no need for any triggers.

So, the base system would need to have spdxtool and bomtool for this to work.
From what I remember of the EuroBSDCon talks on the subject last week, those would need to be in the base systems of the releases of 14 and 15 before the end of next year.

bomtool and spdxtool will be available in the base system in 16.
We can talk about an import into 14 and 15; it may be important at least for 15 indeed.

In D56474#1369872, @mat wrote:
  1. the framework would generate a ${PKGBASE}.pc file during staging, put it in, say, /usr/local/share/sbom/pc and add it to the package.

I don't think this intermediate ${PKGBASE}.pc file needs to be kept.

  1. a pkg trigger would run a script on install to run bomtool on the pc file and generate a .spdx file in /usr/local/share/sbom/spdx-2.2, and run spdxtool to generate the .jsonld file in /usr/local/share/sbom/spdx-3.0.1.
  2. another pkg trigger would remove the .jsonld and the .spdx files on deinstall.

Why not simply generate the .jsonld and .spdx files during package creation, and include them in the plist?
Then there is no need for any triggers.

Well, say, port devel/foo 1.0 builds a binary that depends on libbar.so that comes from devel/bar 1.0. If the SBOM files are generated at build time, the ones for foo will contain the version of bar that foo was built with.
Now bar goes to 1.1 where abi is still compatible, and the so version stays the same, foo will probably be rebuilt on the builders (but that will probably change in the near future) you run pkg upgrade, and only bar gets upgraded, then you have foo 1.0 and bar 1.1, foo's SBOM files still reference bar 1.0.

Note that this is not a corner case example, it happens something like hundreds of time at each package builds.

So, we either need triggers that rebuild all the SBOM files from the .pc files after each install/upgrade, or a new port that does the generation and that runs from a crontab, or that you run when you need the SBOM files.

It seems better to have a trigger do this so the SBOM say consistent with what is installed.

So, the base system would need to have spdxtool and bomtool for this to work.
From what I remember of the EuroBSDCon talks on the subject last week, those would need to be in the base systems of the releases of 14 and 15 before the end of next year.

bomtool and spdxtool will be available in the base system in 16.
We can talk about an import into 14 and 15; it may be important at least for 15 indeed.

According to https://www.freebsd.org/security/#sup, 14 will be supported up to the end of 2028, so, I think we will need those tools there too.

@mat wrote:

@khorben wrote:

bomtool and spdxtool will be available in the base system in 16.
We can talk about an import into 14 and 15; it may be important at least for 15 indeed.

According to https://www.freebsd.org/security/#sup, 14 will be supported up to the end of 2028, so, I think we will need those tools there too.

+1, having this tools in 14 would be great! :)

In D56474#1375885, @mat wrote:
In D56474#1369872, @mat wrote:
  1. the framework would generate a ${PKGBASE}.pc file during staging, put it in, say, /usr/local/share/sbom/pc and add it to the package.

I don't think this intermediate ${PKGBASE}.pc file needs to be kept.

  1. a pkg trigger would run a script on install to run bomtool on the pc file and generate a .spdx file in /usr/local/share/sbom/spdx-2.2, and run spdxtool to generate the .jsonld file in /usr/local/share/sbom/spdx-3.0.1.
  2. another pkg trigger would remove the .jsonld and the .spdx files on deinstall.

Why not simply generate the .jsonld and .spdx files during package creation, and include them in the plist?
Then there is no need for any triggers.

Well, say, port devel/foo 1.0 builds a binary that depends on libbar.so that comes from devel/bar 1.0. If the SBOM files are generated at build time, the ones for foo will contain the version of bar that foo was built with.
Now bar goes to 1.1 where abi is still compatible, and the so version stays the same, foo will probably be rebuilt on the builders (but that will probably change in the near future) you run pkg upgrade, and only bar gets upgraded, then you have foo 1.0 and bar 1.1, foo's SBOM files still reference bar 1.0.

Note that this is not a corner case example, it happens something like hundreds of time at each package builds.

So, we either need triggers that rebuild all the SBOM files from the .pc files after each install/upgrade, or a new port that does the generation and that runs from a crontab, or that you run when you need the SBOM files.

It seems better to have a trigger do this so the SBOM say consistent with what is installed.

I see. This sounds a lot more complex than what I was hoping, but it sounds like it would be necessary.

So, the base system would need to have spdxtool and bomtool for this to work.
From what I remember of the EuroBSDCon talks on the subject last week, those would need to be in the base systems of the releases of 14 and 15 before the end of next year.

bomtool and spdxtool will be available in the base system in 16.
We can talk about an import into 14 and 15; it may be important at least for 15 indeed.

According to https://www.freebsd.org/security/#sup, 14 will be supported up to the end of 2028, so, I think we will need those tools there too.

I'll look into doing that.

khorben edited the test plan for this revision. (Show Details)

This update adds support for libraries installing multiple instances under different names, through the use of SYMLINKS. This is the case for lib/ncurses/ncurses for instance, which builds and installs libncursesw, but also aliases it as libncurses, libcursesw, and libcurses.

It also adds the correct copyright for the example SBOM for pkgconf.

Dependencies on internal libraries are now ignored; in the context of SBOMs, they are useless duplicates of information.