Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161692157
D57890.id180771.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
34 KB
Referenced Files
None
Subscribers
None
D57890.id180771.diff
View Options
diff --git a/Makefile.am b/Makefile.am
--- a/Makefile.am
+++ b/Makefile.am
@@ -613,6 +613,7 @@
cli/getopt_long.h \
cli/renderer-msvc.h \
cli/spdxtool/core.h \
+ cli/spdxtool/generate.h \
cli/spdxtool/serialize.h \
cli/spdxtool/simplelicensing.h \
cli/spdxtool/software.h \
@@ -632,6 +633,7 @@
cli/spdxtool/serialize.c \
cli/spdxtool/simplelicensing.c \
cli/spdxtool/util.c \
+ cli/spdxtool/generate.c \
cli/getopt_long.c
spdxtool_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli -I$(top_srcdir)/cli/spdxtool
diff --git a/Makefile.lite b/Makefile.lite
--- a/Makefile.lite
+++ b/Makefile.lite
@@ -60,7 +60,7 @@
libpkgconf/config.h:
@echo '#define PACKAGE_NAME "pkgconf-lite"' >> $@
@echo '#define PACKAGE_BUGREPORT "https://git.dereferenced.org/pkgconf/pkgconf/issues"' >> $@
- @echo '#define PACKAGE_VERSION "2.9.92"' >> $@
+ @echo '#define PACKAGE_VERSION "2.9.93"' >> $@
@echo '#define PACKAGE PACKAGE_NAME " " PACKAGE_VERSION' >> $@
pkgconf-lite: preflight libpkgconf/config.h ${PKGCONF_OBJS}
diff --git a/cli/bomtool/main.c b/cli/bomtool/main.c
--- a/cli/bomtool/main.c
+++ b/cli/bomtool/main.c
@@ -450,37 +450,25 @@
if ((want_flags & PKG_HELP) == PKG_HELP)
return usage();
- while (1)
- {
- const char *package = argv[pkg_optind];
-
- if (package == NULL)
- break;
-
- while (isspace((unsigned char)package[0]))
- package++;
+ /* Join the remaining arguments into a single query string, as the main
+ * pkgconf CLI does, and let the dependency parser handle module names,
+ * comparison operators and versions.
+ */
+ pkgconf_buffer_t queryparams = PKGCONF_BUFFER_INITIALIZER;
- /* skip empty packages */
- if (package[0] == '\0') {
- pkg_optind++;
- continue;
- }
+ while (pkg_optind < argc && argv[pkg_optind] != NULL)
+ {
+ if (pkgconf_buffer_len(&queryparams) > 0)
+ pkgconf_buffer_push_byte(&queryparams, ' ');
- if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1])))
- {
- pkgconf_queue_push(&pkgq, package);
- pkg_optind++;
- }
- else
- {
- char packagebuf[PKGCONF_BUFSIZE];
+ pkgconf_buffer_append(&queryparams, argv[pkg_optind]);
+ pkg_optind++;
+ }
- snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]);
- pkg_optind += 3;
+ if (pkgconf_buffer_len(&queryparams) > 0)
+ pkgconf_queue_push(&pkgq, pkgconf_buffer_str(&queryparams));
- pkgconf_queue_push(&pkgq, packagebuf);
- }
- }
+ pkgconf_buffer_finalize(&queryparams);
if (pkgq.head == NULL)
{
diff --git a/cli/spdxtool/core.c b/cli/spdxtool/core.c
--- a/cli/spdxtool/core.c
+++ b/cli/spdxtool/core.c
@@ -419,15 +419,24 @@
if (!(spdxtool_serialize_object_add_string(object_list, "type", spdx->type) &&
spdxtool_serialize_object_add_string(object_list, "creationInfo", spdx->creation_info) &&
- spdxtool_serialize_object_add_string(object_list, "spdxId", spdx->spdx_id) &&
- spdxtool_serialize_object_add_array(object_list, "rootElement", root_element_array) &&
- spdxtool_serialize_object_add_array(object_list, "element", element_array)))
+ spdxtool_serialize_object_add_string(object_list, "spdxId", spdx->spdx_id)))
{
goto err;
}
+ /* object_add_array always takes ownership of the array (it is freed even on
+ * failure), so clear our reference before checking the result to avoid a
+ * double free at the error label.
+ */
+ bool ok = spdxtool_serialize_object_add_array(object_list, "rootElement", root_element_array);
root_element_array = NULL;
+ if (!ok)
+ goto err;
+
+ ok = spdxtool_serialize_object_add_array(object_list, "element", element_array);
element_array = NULL;
+ if (!ok)
+ goto err;
ret = spdxtool_serialize_value_object(object_list);
object_list = NULL;
@@ -813,13 +822,20 @@
if (!(spdxtool_serialize_object_add_string(object_list, "type", relationship->type) &&
spdxtool_serialize_object_add_string(object_list, "creationInfo", relationship->creation_info) &&
spdxtool_serialize_object_add_string(object_list, "spdxId", relationship->spdx_id) &&
- spdxtool_serialize_object_add_string(object_list, "from", relationship->from) &&
- spdxtool_serialize_object_add_array(object_list, "to", to) &&
- spdxtool_serialize_object_add_string(object_list, "relationshipType", relationship->relationship_type)))
+ spdxtool_serialize_object_add_string(object_list, "from", relationship->from)))
{
+ /* none of the above transfers ownership of `to` */
+ spdxtool_serialize_array_free(to);
goto err;
}
+ /* object_add_array always takes ownership of `to` (it is freed even on failure) */
+ if (!spdxtool_serialize_object_add_array(object_list, "to", to))
+ goto err;
+
+ if (!spdxtool_serialize_object_add_string(object_list, "relationshipType", relationship->relationship_type))
+ goto err;
+
if (relationship->scope != NULL &&
!spdxtool_serialize_object_add_string(object_list, "scope", relationship->scope))
{
diff --git a/cli/spdxtool/generate.h b/cli/spdxtool/generate.h
new file mode 100644
--- /dev/null
+++ b/cli/spdxtool/generate.h
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by
+ * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
+ * the FreeBSD Foundation
+ */
+
+#ifndef SPDXTOOL_GENERATE_H
+#define SPDXTOOL_GENERATE_H
+
+#include <stdio.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * Build an SPDX SBOM for a solved dependency graph and write it to *out*.
+ *
+ * The dependency graph rooted at *world* must already have been solved (e.g.
+ * with pkgconf_queue_solve) so that every package's match is populated. The
+ * spdxtool_util_set_* configuration (URI root, separator, version, license)
+ * must have been applied to *client* beforehand.
+ */
+bool spdxtool_generate(pkgconf_client_t *client, pkgconf_pkg_t *world, FILE *out,
+ int maxdepth, const char *creation_time, const char *creation_id,
+ const char *agent_name);
+
+#endif
diff --git a/cli/spdxtool/generate.c b/cli/spdxtool/generate.c
new file mode 100644
--- /dev/null
+++ b/cli/spdxtool/generate.c
@@ -0,0 +1,190 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by
+ * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
+ * the FreeBSD Foundation
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+#include "util.h"
+#include "core.h"
+#include "software.h"
+#include "serialize.h"
+#include "simplelicensing.h"
+#include "generate.h"
+
+// NOTE: this function is passed to pkgconf_pkg_traverse
+static void
+generate_spdx_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *ptr)
+{
+ spdxtool_core_spdx_document_t *document = (spdxtool_core_spdx_document_t *)ptr;
+ pkgconf_node_t *node = NULL;
+ spdxtool_software_sbom_t *sbom = NULL;
+ char *package_spdx = NULL;
+ char *spdx_id_string = NULL;
+ char sep = spdxtool_util_get_uri_separator(client);
+
+ if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
+ return;
+
+ spdx_id_string = spdxtool_util_get_spdx_id_string(client, "software_Sbom", pkg->id);
+ if (!spdx_id_string)
+ goto err;
+
+ sbom = spdxtool_software_sbom_new(client, spdx_id_string, document->creation_info, "build");
+ free(spdx_id_string);
+ spdx_id_string = NULL;
+ if (!sbom)
+ goto err;
+
+ sbom->spdx_document = document;
+ sbom->rootElement = pkg;
+
+ package_spdx = spdxtool_util_get_spdx_id_string(client, "Package", pkg->id);
+ if (!package_spdx)
+ goto err;
+
+ pkgconf_tuple_add(client, &pkg->vars, "spdxId", package_spdx, false, 0);
+ free(package_spdx);
+ package_spdx = NULL;
+
+ pkgconf_tuple_add(client, &pkg->vars, "creationInfo", document->creation_info, false, 0);
+ pkgconf_tuple_add(client, &pkg->vars, "agent", document->agent, false, 0);
+
+ if (pkg->maintainer != NULL)
+ {
+ const char *supplier = spdxtool_core_spdx_document_add_maintainer(client, document, pkg->maintainer);
+ if (!supplier)
+ goto err;
+
+ pkgconf_tuple_add(client, &pkg->vars, "suppliedBy", supplier, false, 0);
+ }
+
+ if (pkg->license.head != NULL)
+ {
+ pkgconf_buffer_t spdx_id_buf = PKGCONF_BUFFER_INITIALIZER;
+
+ pkgconf_buffer_append_fmt(&spdx_id_buf, "%s%chasDeclaredLicense", pkg->id, sep);
+ char *spdx_id_name = pkgconf_buffer_freeze(&spdx_id_buf);
+ if (!spdx_id_name)
+ goto err;
+
+ package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
+ free(spdx_id_name);
+ if (!package_spdx)
+ goto err;
+
+ pkgconf_tuple_add(client, &pkg->vars, "hasDeclaredLicense", package_spdx, false, 0);
+ free(package_spdx);
+ package_spdx = NULL;
+
+ pkgconf_buffer_t concluded_buf = PKGCONF_BUFFER_INITIALIZER;
+ pkgconf_buffer_append_fmt(&concluded_buf, "%s%chasConcludedLicense", pkg->id, sep);
+ spdx_id_name = pkgconf_buffer_freeze(&concluded_buf);
+ if (!spdx_id_name)
+ goto err;
+
+ package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
+ free(spdx_id_name);
+ if (!package_spdx)
+ goto err;
+
+ pkgconf_tuple_add(client, &pkg->vars, "hasConcludedLicense", package_spdx, false, 0);
+ free(package_spdx);
+ package_spdx = NULL;
+
+ PKGCONF_FOREACH_LIST_ENTRY(pkg->license.head, node)
+ {
+ const pkgconf_license_t *license = node->data;
+ if (license->type == PKGCONF_LICENSE_EXPRESSION)
+ {
+ if (!spdxtool_core_spdx_document_add_license(client, document, license->data))
+ goto err;
+ }
+ }
+ }
+
+ node = calloc(1, sizeof(pkgconf_node_t));
+ if (!node)
+ goto err;
+
+ pkgconf_node_insert_tail(node, sbom, &document->rootElement);
+ return;
+
+err:
+ pkgconf_error(client, "generate_spdx_package: failed for %s", pkg->id);
+ free(package_spdx);
+ free(spdx_id_string);
+ spdxtool_software_sbom_free(sbom);
+}
+
+bool
+spdxtool_generate(pkgconf_client_t *client, pkgconf_pkg_t *world, FILE *out, int maxdepth,
+ const char *creation_time, const char *creation_id, const char *agent_name)
+{
+ const char *agent_name_string = agent_name ? agent_name : "Default";
+ const char *creation_id_string = creation_id ? creation_id : "_:creationinfo_1";
+
+ spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, creation_id_string, agent_name_string);
+ if (!agent)
+ {
+ pkgconf_error(client, "Could not create agent struct");
+ return false;
+ }
+
+ spdxtool_core_creation_info_t *creation = spdxtool_core_creation_info_new(client, agent->spdx_id, creation_id_string, creation_time);
+ if (!creation)
+ {
+ pkgconf_error(client, "Could not create creation info struct");
+ spdxtool_core_agent_free(agent);
+ return false;
+ }
+
+ char *spdx_id_int = spdxtool_util_get_spdx_id_int(client, "spdxDocument");
+ spdxtool_core_spdx_document_t *document = spdxtool_core_spdx_document_new(client, spdx_id_int, creation_id_string, agent->spdx_id);
+ free(spdx_id_int);
+ if (!document)
+ {
+ pkgconf_error(client, "Could not create document");
+ spdxtool_core_creation_info_free(creation);
+ spdxtool_core_agent_free(agent);
+ return false;
+ }
+
+ int eflag = pkgconf_pkg_traverse(client, world, generate_spdx_package, document, maxdepth, 0);
+ if (eflag != PKGCONF_PKG_ERRF_OK)
+ {
+ spdxtool_core_spdx_document_free(document);
+ spdxtool_core_creation_info_free(creation);
+ spdxtool_core_agent_free(agent);
+ return false;
+ }
+
+ spdxtool_serialize_value_t *root = spdxtool_serialize_sbom(client, agent, creation, document);
+ if (!root)
+ {
+ spdxtool_core_spdx_document_free(document);
+ spdxtool_core_creation_info_free(creation);
+ spdxtool_core_agent_free(agent);
+ return false;
+ }
+
+ pkgconf_buffer_t buffer = PKGCONF_BUFFER_INITIALIZER;
+ spdxtool_serialize_value_to_buf(&buffer, root, 0);
+ spdxtool_serialize_value_free(root);
+
+ bool ret = pkgconf_output_file_fmt(out, "%s\n", pkgconf_buffer_str(&buffer));
+ pkgconf_buffer_finalize(&buffer);
+
+ spdxtool_core_spdx_document_free(document);
+ spdxtool_core_creation_info_free(creation);
+ spdxtool_core_agent_free(agent);
+
+ if (!ret)
+ pkgconf_error(client, "spdxtool: Could not output to file: %s", strerror(errno));
+ return ret;
+}
diff --git a/cli/spdxtool/main.c b/cli/spdxtool/main.c
--- a/cli/spdxtool/main.c
+++ b/cli/spdxtool/main.c
@@ -17,6 +17,7 @@
#include "software.h"
#include "serialize.h"
#include "simplelicensing.h"
+#include "generate.h"
#define PKG_VERSION (((uint64_t) 1) << 1)
#define PKG_ABOUT (((uint64_t) 1) << 2)
@@ -55,177 +56,6 @@
return true;
}
-// NOTE: this function is passed to pkgconf_pkg_traverse
-static void
-generate_spdx_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *ptr)
-{
- spdxtool_core_spdx_document_t *document = (spdxtool_core_spdx_document_t *)ptr;
- pkgconf_node_t *node = NULL;
- spdxtool_software_sbom_t *sbom = NULL;
- char *package_spdx = NULL;
- char *spdx_id_string = NULL;
- char sep = spdxtool_util_get_uri_separator(client);
-
- if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
- return;
-
- spdx_id_string = spdxtool_util_get_spdx_id_string(client, "software_Sbom", pkg->id);
- if (!spdx_id_string)
- goto err;
-
- sbom = spdxtool_software_sbom_new(client, spdx_id_string, document->creation_info, "build");
- free(spdx_id_string);
- spdx_id_string = NULL;
- if (!sbom)
- goto err;
-
- sbom->spdx_document = document;
- sbom->rootElement = pkg;
-
- package_spdx = spdxtool_util_get_spdx_id_string(client, "Package", pkg->id);
- if (!package_spdx)
- goto err;
-
- pkgconf_tuple_add(client, &pkg->vars, "spdxId", package_spdx, false, 0);
- free(package_spdx);
- package_spdx = NULL;
-
- pkgconf_tuple_add(client, &pkg->vars, "creationInfo", document->creation_info, false, 0);
- pkgconf_tuple_add(client, &pkg->vars, "agent", document->agent, false, 0);
-
- if (pkg->maintainer != NULL)
- {
- const char *supplier = spdxtool_core_spdx_document_add_maintainer(client, document, pkg->maintainer);
- if (!supplier)
- goto err;
-
- pkgconf_tuple_add(client, &pkg->vars, "suppliedBy", supplier, false, 0);
- }
-
- if (pkg->license.head != NULL)
- {
- pkgconf_buffer_t spdx_id_buf = PKGCONF_BUFFER_INITIALIZER;
-
- pkgconf_buffer_append_fmt(&spdx_id_buf, "%s%chasDeclaredLicense", pkg->id, sep);
- char *spdx_id_name = pkgconf_buffer_freeze(&spdx_id_buf);
- if (!spdx_id_name)
- goto err;
-
- package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
- free(spdx_id_name);
- if (!package_spdx)
- goto err;
-
- pkgconf_tuple_add(client, &pkg->vars, "hasDeclaredLicense", package_spdx, false, 0);
- free(package_spdx);
- package_spdx = NULL;
-
- pkgconf_buffer_t concluded_buf = PKGCONF_BUFFER_INITIALIZER;
- pkgconf_buffer_append_fmt(&concluded_buf, "%s%chasConcludedLicense", pkg->id, sep);
- spdx_id_name = pkgconf_buffer_freeze(&concluded_buf);
- if (!spdx_id_name)
- goto err;
-
- package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
- free(spdx_id_name);
- if (!package_spdx)
- goto err;
-
- pkgconf_tuple_add(client, &pkg->vars, "hasConcludedLicense", package_spdx, false, 0);
- free(package_spdx);
- package_spdx = NULL;
-
- PKGCONF_FOREACH_LIST_ENTRY(pkg->license.head, node)
- {
- const pkgconf_license_t *license = node->data;
- if (license->type == PKGCONF_LICENSE_EXPRESSION)
- {
- if (!spdxtool_core_spdx_document_add_license(client, document, license->data))
- goto err;
- }
- }
- }
-
- node = calloc(1, sizeof(pkgconf_node_t));
- if (!node)
- goto err;
-
- pkgconf_node_insert_tail(node, sbom, &document->rootElement);
- return;
-
-err:
- pkgconf_error(client, "generate_spdx_package: failed for %s", pkg->id);
- free(package_spdx);
- free(spdx_id_string);
- spdxtool_software_sbom_free(sbom);
-}
-
-static bool
-generate_spdx(pkgconf_client_t *client, pkgconf_pkg_t *world, const char *creation_time, const char *creation_id, const char *agent_name)
-{
- const char *agent_name_string = agent_name ? agent_name : "Default";
- const char *creation_id_string = creation_id ? creation_id : "_:creationinfo_1";
-
- spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, creation_id_string, agent_name_string);
- if (!agent)
- {
- pkgconf_error(client, "Could not create agent struct");
- return false;
- }
-
- spdxtool_core_creation_info_t *creation = spdxtool_core_creation_info_new(client, agent->spdx_id, creation_id_string, creation_time);
- if (!creation)
- {
- pkgconf_error(client, "Could not create creation info struct");
- spdxtool_core_agent_free(agent);
- return false;
- }
-
- char *spdx_id_int = spdxtool_util_get_spdx_id_int(client, "spdxDocument");
- spdxtool_core_spdx_document_t *document = spdxtool_core_spdx_document_new(client, spdx_id_int, creation_id_string, agent->spdx_id);
- free(spdx_id_int);
- if (!document)
- {
- pkgconf_error(client, "Could not create document");
- spdxtool_core_creation_info_free(creation);
- spdxtool_core_agent_free(agent);
- return false;
- }
-
- int eflag = pkgconf_pkg_traverse(client, world, generate_spdx_package, document, maximum_traverse_depth, 0);
- if (eflag != PKGCONF_PKG_ERRF_OK)
- {
- spdxtool_core_spdx_document_free(document);
- spdxtool_core_creation_info_free(creation);
- spdxtool_core_agent_free(agent);
- return false;
- }
-
- spdxtool_serialize_value_t *root = spdxtool_serialize_sbom(client, agent, creation, document);
- if (!root)
- {
- spdxtool_core_spdx_document_free(document);
- spdxtool_core_creation_info_free(creation);
- spdxtool_core_agent_free(agent);
- return false;
- }
-
- pkgconf_buffer_t buffer = PKGCONF_BUFFER_INITIALIZER;
- spdxtool_serialize_value_to_buf(&buffer, root, 0);
- spdxtool_serialize_value_free(root);
-
- bool ret = pkgconf_output_file_fmt(sbom_out, "%s\n", pkgconf_buffer_str(&buffer));
- pkgconf_buffer_finalize(&buffer);
-
- spdxtool_core_spdx_document_free(document);
- spdxtool_core_creation_info_free(creation);
- spdxtool_core_agent_free(agent);
-
- if (!ret)
- pkgconf_error(client, "spdxtool: Could not output to file: %s", strerror(errno));
- return ret;
-}
-
static int
version(void)
{
@@ -365,38 +195,25 @@
if ((want_flags & PKG_HELP) == PKG_HELP)
return usage();
- while (1)
- {
- const char *package = argv[pkg_optind];
-
- if (package == NULL)
- break;
-
- while (isspace((unsigned char)package[0]))
- package++;
+ /* Join the remaining arguments into a single query string, as the main
+ * pkgconf CLI does, and let the dependency parser handle module names,
+ * comparison operators and versions.
+ */
+ pkgconf_buffer_t queryparams = PKGCONF_BUFFER_INITIALIZER;
- /* skip empty packages */
- if (package[0] == '\0')
- {
- pkg_optind++;
- continue;
- }
+ while (pkg_optind < argc && argv[pkg_optind] != NULL)
+ {
+ if (pkgconf_buffer_len(&queryparams) > 0)
+ pkgconf_buffer_push_byte(&queryparams, ' ');
- if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1])))
- {
- pkgconf_queue_push(&pkgq, package);
- pkg_optind++;
- }
- else
- {
- char packagebuf[PKGCONF_BUFSIZE];
+ pkgconf_buffer_append(&queryparams, argv[pkg_optind]);
+ pkg_optind++;
+ }
- snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]);
- pkg_optind += 3;
+ if (pkgconf_buffer_len(&queryparams) > 0)
+ pkgconf_queue_push(&pkgq, pkgconf_buffer_str(&queryparams));
- pkgconf_queue_push(&pkgq, packagebuf);
- }
- }
+ pkgconf_buffer_finalize(&queryparams);
if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth))
{
@@ -409,7 +226,7 @@
spdxtool_util_set_spdx_license(&pkg_client, bom_license);
spdxtool_util_set_spdx_version(&pkg_client, spdx_version);
- if (!generate_spdx(&pkg_client, &world, creation_time, creation_id, agent_name))
+ if (!spdxtool_generate(&pkg_client, &world, sbom_out, maximum_traverse_depth, creation_time, creation_id, agent_name))
{
ret = EXIT_FAILURE;
goto out;
diff --git a/cli/spdxtool/serialize.c b/cli/spdxtool/serialize.c
--- a/cli/spdxtool/serialize.c
+++ b/cli/spdxtool/serialize.c
@@ -47,8 +47,8 @@
pkgconf_buffer_append(buffer, "\\t");
break;
default:
- if (*p < 0x20)
- pkgconf_buffer_append_fmt(buffer, "\\u%04x", (unsigned int)*p);
+ if ((unsigned char) *p < 0x20)
+ pkgconf_buffer_append_fmt(buffer, "\\u%04x", (unsigned int)(unsigned char) *p);
else
pkgconf_buffer_push_byte(buffer, *p);
}
diff --git a/cli/spdxtool/software.c b/cli/spdxtool/software.c
--- a/cli/spdxtool/software.c
+++ b/cli/spdxtool/software.c
@@ -137,6 +137,10 @@
pkgconf_pkg_t *match = dep->match;
pkgconf_buffer_t relationship_buf = PKGCONF_BUFFER_INITIALIZER;
+ /* an unresolved (but tolerated) dependency has no match */
+ if (match == NULL)
+ continue;
+
pkgconf_buffer_append_fmt(&relationship_buf, "%s%cdependsOn%c%s", sbom->rootElement->id, sep, sep, match->id);
char *relationship_str = pkgconf_buffer_freeze(&relationship_buf);
if (!relationship_str)
@@ -168,6 +172,10 @@
pkgconf_pkg_t *match = dep->match;
pkgconf_buffer_t relationship_buf = PKGCONF_BUFFER_INITIALIZER;
+ /* an unresolved (but tolerated) dependency has no match */
+ if (match == NULL)
+ continue;
+
pkgconf_buffer_append_fmt(&relationship_buf, "%s%cdependsOn%c%s", sbom->rootElement->id, sep, sep, match->id);
char *relationship_str = pkgconf_buffer_freeze(&relationship_buf);
if (!relationship_str)
@@ -236,17 +244,24 @@
goto err;
}
- if (!spdxtool_serialize_object_add_array(object_list, "software_sbomType", sbom_type_array))
- goto err;
+ /* object_add_array always takes ownership of the array (it is freed even on
+ * failure), so clear our reference before checking the result to avoid a
+ * double free at the error label.
+ */
+ bool ok = spdxtool_serialize_object_add_array(object_list, "software_sbomType", sbom_type_array);
sbom_type_array = NULL;
-
- if (!spdxtool_serialize_object_add_array(object_list, "rootElement", root_element_array))
+ if (!ok)
goto err;
- root_element_array = NULL;
- if (!spdxtool_serialize_object_add_array(object_list, "element", element_array))
+ ok = spdxtool_serialize_object_add_array(object_list, "rootElement", root_element_array);
+ root_element_array = NULL;
+ if (!ok)
goto err;
+
+ ok = spdxtool_serialize_object_add_array(object_list, "element", element_array);
element_array = NULL;
+ if (!ok)
+ goto err;
if (!spdxtool_core_spdx_document_add_package(client, sbom->spdx_document, sbom->rootElement))
goto err;
@@ -343,9 +358,14 @@
goto err;
}
- if (!spdxtool_serialize_object_add_array(object_list, "originatedBy", originated_by))
- goto err;
+ /* object_add_array always takes ownership of the array (it is freed even on
+ * failure), so clear our reference before checking the result to avoid a
+ * double free at the error label.
+ */
+ bool ok = spdxtool_serialize_object_add_array(object_list, "originatedBy", originated_by);
originated_by = NULL;
+ if (!ok)
+ goto err;
supplier = spdxtool_util_tuple_lookup(client, &pkg->vars, "suppliedBy");
if (supplier)
@@ -357,9 +377,10 @@
if (!spdxtool_serialize_array_add_string(supplied_by, supplier))
goto err;
- if (!spdxtool_serialize_object_add_array(object_list, "suppliedBy", supplied_by))
- goto err;
+ ok = spdxtool_serialize_object_add_array(object_list, "suppliedBy", supplied_by);
supplied_by = NULL;
+ if (!ok)
+ goto err;
}
if (!serialize_copyright_lines_to_object(object_list, &pkg->copyright))
@@ -442,6 +463,10 @@
pkgconf_pkg_t *match = dep->match;
pkgconf_buffer_t relationship_buf = PKGCONF_BUFFER_INITIALIZER;
+ /* an unresolved (but tolerated) dependency has no match */
+ if (match == NULL)
+ continue;
+
pkgconf_buffer_append_fmt(&relationship_buf, "%s%cdependsOn%c%s", pkg->id, sep, sep, match->id);
char *relationship_str = pkgconf_buffer_freeze(&relationship_buf);
if (!relationship_str)
@@ -484,6 +509,10 @@
pkgconf_pkg_t *match = dep->match;
pkgconf_buffer_t relationship_buf = PKGCONF_BUFFER_INITIALIZER;
+ /* an unresolved (but tolerated) dependency has no match */
+ if (match == NULL)
+ continue;
+
pkgconf_buffer_append_fmt(&relationship_buf, "%s%cdependsOn%c%s", pkg->id, sep, sep, match->id);
char *relationship_str = pkgconf_buffer_freeze(&relationship_buf);
if (!relationship_str)
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -12,7 +12,7 @@
dnl from the use of this software.
AC_PREREQ([2.71])
-AC_INIT([pkgconf],[2.9.92],[https://github.com/pkgconf/pkgconf/issues/new])
+AC_INIT([pkgconf],[2.9.93],[https://github.com/pkgconf/pkgconf/issues/new])
AC_MSG_WARN([
===================================================================
Autotools support is deprecated and will be removed in pkgconf 3.1.
diff --git a/fuzzer/meson.build b/fuzzer/meson.build
--- a/fuzzer/meson.build
+++ b/fuzzer/meson.build
@@ -30,9 +30,27 @@
link_args: fuzzer_link_args,
)
+spdxtool_fuzzer_exe = executable(
+ 'spdxtool-fuzzer',
+ 'spdxtool-fuzzer.c',
+ 'alloc-inject.c',
+ '../cli/spdxtool/core.c',
+ '../cli/spdxtool/software.c',
+ '../cli/spdxtool/serialize.c',
+ '../cli/spdxtool/simplelicensing.c',
+ '../cli/spdxtool/util.c',
+ '../cli/spdxtool/generate.c',
+ dependencies: dep_libpkgconf,
+ include_directories: include_directories('..', '../cli/spdxtool'),
+ install: false,
+ c_args: ['-fsanitize=fuzzer'],
+ link_args: fuzzer_link_args,
+)
+
fuzz_root = join_paths(meson.project_build_root(), 'fuzz')
corpus_dir = join_paths(fuzz_root, 'corpus')
solver_corpus_dir = join_paths(fuzz_root, 'solver-corpus')
+spdxtool_corpus_dir = join_paths(fuzz_root, 'spdxtool-corpus')
seed_dir = join_paths(meson.project_source_root(), 'tests', 'lib1')
solver_seed_dir = join_paths(meson.project_source_root(), 'fuzzer', 'solver-corpus')
@@ -50,3 +68,8 @@
'fuzz-solver',
command: [solver_fuzzer_exe, solver_corpus_dir, solver_seed_dir]
)
+
+run_target(
+ 'fuzz-spdxtool',
+ command: [spdxtool_fuzzer_exe, spdxtool_corpus_dir, solver_seed_dir]
+)
diff --git a/fuzzer/spdxtool-fuzzer.c b/fuzzer/spdxtool-fuzzer.c
new file mode 100644
--- /dev/null
+++ b/fuzzer/spdxtool-fuzzer.c
@@ -0,0 +1,200 @@
+/*
+ * spdxtool-fuzzer.c
+ * SPDX SBOM serializer fuzzing harness
+ *
+ * SPDX-License-Identifier: pkgconf
+ *
+ * Copyright (c) 2026 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+#include "util.h"
+#include "generate.h"
+
+#include "alloc-inject.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+/* bound the number of injection rounds per input to keep executions cheap */
+#define ALLOC_FAIL_MAX 4096
+
+/* the fuzzer input is split on NUL bytes into up to this many .pc files, named
+ * a.pc, b.pc, ... so that Requires/Conflicts/Provides between them resolve, and
+ * the resulting graph is serialized to an SPDX SBOM.
+ */
+#define UNIVERSE_MAX 4
+#define SOLVE_MAXDEPTH 10
+
+static const char universe_names[UNIVERSE_MAX] = { 'a', 'b', 'c', 'd' };
+
+static const char *
+environ_lookup_handler(const pkgconf_client_t *client, const char *key)
+{
+ (void) client;
+ (void) key;
+
+ return NULL;
+}
+
+static int
+write_all(int fd, const uint8_t *data, size_t size)
+{
+ while (size > 0)
+ {
+ ssize_t n = write(fd, data, size);
+
+ if (n < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ data += n;
+ size -= n;
+ }
+
+ return 0;
+}
+
+static void
+write_pkg(const char *dir, char name, const uint8_t *data, size_t size)
+{
+ char path[PKGCONF_ITEM_SIZE];
+ int fd;
+
+ snprintf(path, sizeof path, "%s/%c.pc", dir, name);
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0)
+ return;
+
+ write_all(fd, data, size);
+ close(fd);
+}
+
+static void
+write_universe(const char *dir, const uint8_t *data, size_t size)
+{
+ size_t start = 0, idx = 0;
+
+ for (size_t i = 0; i < size && idx < UNIVERSE_MAX; i++)
+ {
+ if (data[i] == 0x00)
+ {
+ write_pkg(dir, universe_names[idx++], data + start, i - start);
+ start = i + 1;
+ }
+ }
+
+ if (idx < UNIVERSE_MAX)
+ write_pkg(dir, universe_names[idx], data + start, size - start);
+}
+
+static void
+cleanup_universe(const char *dir)
+{
+ char path[PKGCONF_ITEM_SIZE];
+
+ for (size_t i = 0; i < UNIVERSE_MAX; i++)
+ {
+ snprintf(path, sizeof path, "%s/%c.pc", dir, universe_names[i]);
+ unlink(path);
+ }
+
+ rmdir(dir);
+}
+
+/*
+ * Solve a freshly-written universe rooted at "a", then drive the real spdxtool
+ * SBOM serialization pipeline (cli/spdxtool/generate.c) over the result.
+ */
+static void
+run_spdx(const pkgconf_cross_personality_t *pers, const char *dir, FILE *out)
+{
+ pkgconf_client_t *client = pkgconf_client_new(NULL, NULL, pers, NULL, environ_lookup_handler);
+ if (client == NULL)
+ return;
+
+ pkgconf_client_set_flags(client, PKGCONF_PKG_PKGF_SEARCH_PRIVATE);
+ pkgconf_path_add(dir, &client->dir_list, false);
+
+ /* the serializers read these back via the client's global tuples */
+ spdxtool_util_set_uri_root(client, "https://example.com/test");
+ spdxtool_util_set_uri_separator_colon(client, false);
+ spdxtool_util_set_spdx_license(client, "CC0-1.0");
+ spdxtool_util_set_spdx_version(client, "3.0.1");
+
+ pkgconf_pkg_t world = {
+ .id = "virtual:world",
+ .realname = "virtual world package",
+ .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL,
+ };
+ pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER;
+
+ pkgconf_queue_push(&pkgq, "a");
+
+ if (pkgconf_queue_solve(client, &pkgq, &world, SOLVE_MAXDEPTH))
+ /* fixed timestamp keeps the harness deterministic */
+ spdxtool_generate(client, &world, out, SOLVE_MAXDEPTH,
+ "2020-01-01T00:00:00Z", "_:creationinfo_1", "Default");
+
+ pkgconf_solution_free(client, &world);
+ pkgconf_queue_free(&pkgq);
+ pkgconf_client_free(client);
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size == 0)
+ return 0;
+
+ char dir[] = "/tmp/pkgconf-fuzz-spdx-XXXXXX";
+ if (mkdtemp(dir) == NULL)
+ return 0;
+
+ write_universe(dir, data, size);
+
+ /* discard the serialized output; opened outside the injection loop so the
+ * allocations behind it are never the fault-injection target.
+ */
+ FILE *out = fopen("/dev/null", "w");
+ if (out == NULL)
+ {
+ cleanup_universe(dir);
+ return 0;
+ }
+
+ pkgconf_cross_personality_t *pers = pkgconf_cross_personality_default();
+
+ /* baseline run with all allocations succeeding */
+ run_spdx(pers, dir, out);
+
+ /* then fail each allocation site reachable by this input, one at a time */
+ for (unsigned long i = 1; i <= ALLOC_FAIL_MAX; i++)
+ {
+ alloc_inject_arm(i);
+ run_spdx(pers, dir, out);
+ alloc_inject_disarm();
+
+ if (!alloc_inject_fired())
+ break;
+ }
+
+ pkgconf_cross_personality_deinit(pers);
+ fclose(out);
+ cleanup_universe(dir);
+
+ return 0;
+}
diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h
--- a/libpkgconf/libpkgconf.h
+++ b/libpkgconf/libpkgconf.h
@@ -71,8 +71,8 @@
#define PKGCONF_FOREACH_LIST_ENTRY_REVERSE(tail, value) \
for ((value) = (tail); (value) != NULL; (value) = (value)->prev)
-#define LIBPKGCONF_VERSION 20992
-#define LIBPKGCONF_VERSION_STR "2.9.92"
+#define LIBPKGCONF_VERSION 20993
+#define LIBPKGCONF_VERSION_STR "2.9.93"
struct pkgconf_queue_ {
pkgconf_node_t iter;
diff --git a/libpkgconf/license.c b/libpkgconf/license.c
--- a/libpkgconf/license.c
+++ b/libpkgconf/license.c
@@ -228,15 +228,15 @@
}
pkgconf_license_insert(client, license_list, PKGCONF_LICENSE_BRACKET_CLOSE, ")");
}
- else if (!strncasecmp(cur_word, "and", 3))
+ else if (!strcasecmp(cur_word, "and"))
{
pkgconf_license_insert(client, license_list, PKGCONF_LICENSE_AND, "AND");
}
- else if (!strncasecmp(cur_word, "or", 2))
+ else if (!strcasecmp(cur_word, "or"))
{
pkgconf_license_insert(client, license_list, PKGCONF_LICENSE_OR, "OR");
}
- else if (!strncasecmp(cur_word, "with", 2))
+ else if (!strcasecmp(cur_word, "with"))
{
pkgconf_license_insert(client, license_list, PKGCONF_LICENSE_WITH, "WITH");
}
@@ -316,7 +316,7 @@
frag_string = PKGCONF_BUFFER_FROM_STR(license->data);
pkgconf_buffer_append(buf, pkgconf_buffer_str_or_empty(frag_string));
- if (license->type == PKGCONF_LICENSE_BRACKET_OPEN || (node->next != NULL && ((const pkgconf_license_t *)node->next)->type == PKGCONF_LICENSE_BRACKET_CLOSE))
+ if (license->type == PKGCONF_LICENSE_BRACKET_OPEN || (node->next != NULL && ((const pkgconf_license_t *)node->next->data)->type == PKGCONF_LICENSE_BRACKET_CLOSE))
{
is_delim = false;
}
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('pkgconf', 'c',
- version : '2.9.92',
+ version : '2.9.93',
license : 'ISC',
default_options : [
'c_std=c99',
@@ -95,16 +95,29 @@
endforeach
feature_defines = '\n'.join(feature_define_lines)
+have_reallocarray = false
foreach f : check_functions
name = f[0].to_upper().underscorify()
if cc.has_function(f[0], prefix : feature_defines + '\n#include <@0@>'.format(f[1])) and cc.has_header_symbol(f[1], f[0], prefix : feature_defines)
cdata.set('HAVE_@0@'.format(name), 1)
cdata.set('HAVE_DECL_@0@'.format(name), 1)
+ if f[0] == 'reallocarray'
+ have_reallocarray = true
+ endif
else
cdata.set('HAVE_DECL_@0@'.format(name), 0)
endif
endforeach
+# The fuzzers link fuzzer/alloc-inject, which wraps reallocarray() and so
+# references __real_reallocarray; --wrap can only bind that to a reallocarray the
+# libc actually provides. On platforms too old to have it (e.g. glibc < 2.26)
+# the link would fail, so disable fuzzing there (mirrors the OOM-test gate below).
+if fuzzing and not have_reallocarray
+ warning('fuzzing requested, but the C library lacks reallocarray() (e.g. glibc < 2.26); disabling')
+ fuzzing = false
+endif
+
# nl_langinfo_l() needs locale.h for locale_t in addition to langinfo.h (on
# macOS it also needs xlocale.h), so it can't share the single-header
# check_functions loop above.
@@ -257,6 +270,7 @@
'cli/spdxtool/serialize.c',
'cli/spdxtool/simplelicensing.c',
'cli/spdxtool/util.c',
+ 'cli/spdxtool/generate.c',
'cli/getopt_long.c',
windows_manifest,
link_with : libpkgconf,
@@ -323,8 +337,13 @@
# fault injector through the linker's --wrap, so they are built wherever --wrap
# is supported: glibc and musl Linux (GNU ld / lld), and skipped on macOS ld64
# and MSVC.
+#
+# alloc-inject wraps reallocarray() and so references __real_reallocarray; --wrap
+# can only bind that to a reallocarray the libc actually provides. On platforms
+# too old to have it (e.g. glibc < 2.26) the link would fail, so skip there.
build_oom_tests = host_machine.system() == 'linux' \
- and cc.has_link_argument('-Wl,--wrap=calloc')
+ and cc.has_link_argument('-Wl,--wrap=calloc') \
+ and have_reallocarray
# Allocator set wrapped by fuzzer/alloc-inject.c.
oom_wrap_args = [
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Jul 7, 12:05 AM (10 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34772440
Default Alt Text
D57890.id180771.diff (34 KB)
Attached To
Mode
D57890: Vendor import of pkgconf 2.9.93
Attached
Detach File
Event Timeline
Log In to Comment