Page MenuHomeFreeBSD

D57890.id180771.diff
No OneTemporary

D57890.id180771.diff

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

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)

Event Timeline