From a07961cfbed092f7e0388b367096b7e5fc364540 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 2 Dec 2025 11:05:08 +0100 Subject: [PATCH 1/8] systemd: selectively backport "Fix constness issues with newer glibc" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NetworkManager is failing to build on Rawhide with the following errors: ../src/libnm-systemd-shared/src/basic/string-util.h:33:16: error: return discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers] 33 | return strstr(haystack, needle); | ^~~~~~ In file included from ../src/libnm-systemd-shared/src/basic/fd-util.c:30: ../src/libnm-systemd-shared/src/basic/sort-util.h: In function ‘bsearch_safe’: ../src/libnm-systemd-shared/src/basic/sort-util.h:34:16: error: return discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers] 34 | return bsearch(key, base, nmemb, size, compar); | ^~~~~~~ This is fixed in systemd by commit 0bac1ed2422f15308414dd1e9d09812a966b0348: > Latest glibc uses _Generic to have strstr() and other functions return > const char* or char* based on whether the input is a const char* or a > char*. This causes build failures as we previously always expected a char*. > > Let's fix the compilation failures and add our own macros similar to glibc's > to have string functions that return a mutable or const pointer depending on > the input. Selectively backport the changes we need to fix building. --- src/libnm-systemd-shared/src/basic/sort-util.h | 7 +++++-- .../src/basic/string-util.c | 4 ++-- .../src/basic/string-util.h | 18 ++++++++++++------ .../src/fundamental/macro-fundamental.h | 9 ++++++++- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h index 9c818bd747..a3bfb32c1d 100644 --- a/src/libnm-systemd-shared/src/basic/sort-util.h +++ b/src/libnm-systemd-shared/src/basic/sort-util.h @@ -25,15 +25,18 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, * Normal bsearch requires base to be nonnull. Here were require * that only if nmemb > 0. */ -static inline void* bsearch_safe(const void *key, const void *base, +static inline void* bsearch_safe_internal(const void *key, const void *base, size_t nmemb, size_t size, comparison_fn_t compar) { if (nmemb <= 0) return NULL; assert(base); - return bsearch(key, base, nmemb, size, compar); + return (void*) bsearch(key, base, nmemb, size, compar); } +#define bsearch_safe(key, base, nmemb, size, compar) \ + const_generic((base), bsearch_safe_internal(key, base, nmemb, size, compar)) + #define typesafe_bsearch(k, b, n, func) \ ({ \ const typeof((b)[0]) *_k = k; \ diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index 5efd11eabc..52160fe3e2 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -1470,7 +1470,7 @@ ssize_t strlevenshtein(const char *x, const char *y) { return t1[yl]; } -char* strrstr(const char *haystack, const char *needle) { +char* strrstr_internal(const char *haystack, const char *needle) { /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */ if (!haystack || !needle) @@ -1479,7 +1479,7 @@ char* strrstr(const char *haystack, const char *needle) { /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the * last char, not before. */ if (*needle == 0) - return strchr(haystack, 0); + return (char*) strchr(haystack, 0); for (const char *p = strstr(haystack, needle), *q; p; p = q) { q = strstr(p + 1, needle); diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 91e63c9b12..5d2b5dfb16 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -27,24 +27,28 @@ #define URI_UNRESERVED ALPHANUMERICAL "-._~" /* [RFC3986] */ #define URI_VALID URI_RESERVED URI_UNRESERVED /* [RFC3986] */ -static inline char* strstr_ptr(const char *haystack, const char *needle) { +static inline char* strstr_ptr_internal(const char *haystack, const char *needle) { if (!haystack || !needle) return NULL; - return strstr(haystack, needle); + return (char*) strstr(haystack, needle); } -static inline char* strstrafter(const char *haystack, const char *needle) { - char *p; +#define strstr_ptr(haystack, needle) \ + const_generic(haystack, strstr_ptr_internal(haystack, needle)) +static inline char* strstrafter_internal(const char *haystack, const char *needle) { /* Returns NULL if not found, or pointer to first character after needle if found */ - p = strstr_ptr(haystack, needle); + char *p = (char*) strstr_ptr(haystack, needle); if (!p) return NULL; return p + strlen(needle); } +#define strstrafter(haystack, needle) \ + const_generic(haystack, strstrafter_internal(haystack, needle)) + static inline const char* strnull(const char *s) { return s ?: "(null)"; } @@ -300,6 +304,8 @@ bool version_is_valid_versionspec(const char *s); ssize_t strlevenshtein(const char *x, const char *y); -char* strrstr(const char *haystack, const char *needle); +char* strrstr_internal(const char *haystack, const char *needle); +#define strrstr(haystack, needle) \ + const_generic(haystack, strrstr_internal(haystack, needle)) size_t str_common_prefix(const char *a, const char *b); diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index abdfb9b6bd..a89994e273 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -511,4 +511,11 @@ assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1); #define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1]) #else /* NM_IGNORED */ #define PROJECT_FILE __FILE__ -#endif /* NM_IGNORED */ \ No newline at end of file +#endif /* NM_IGNORED */ + +/* This macro is used to have a const-returning and non-const returning version of a function based on + * whether its first argument is const or not (e.g. strstr()). */ +#define const_generic(ptr, call) \ + _Generic(0 ? (ptr) : (void*) 1, \ + const void*: (const typeof(*call)*) (call), \ + void*: (call)) From 487ca30256ade2a1715a8d2a61725fc9c476fb7e Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 2 Dec 2025 14:46:16 +0100 Subject: [PATCH 2/8] all: const-ify str(r)chr output variables where possible --- src/core/dns/nm-dns-manager.c | 4 ++-- src/core/nm-checkpoint.c | 2 +- src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 2 +- src/libnm-core-aux-intern/nm-libnm-core-utils.c | 4 ++-- src/libnm-core-impl/nm-setting.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index c746e71497..ec33c46427 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -1510,8 +1510,8 @@ _domain_track_is_shadowed(GHashTable *ht, const char **out_parent, int *out_parent_priority) { - char *parent; - int parent_priority; + const char *parent; + int parent_priority; if (!ht) return FALSE; diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c index ffcf6e3aad..45ea3a73b6 100644 --- a/src/core/nm-checkpoint.c +++ b/src/core/nm-checkpoint.c @@ -160,7 +160,7 @@ parse_connection_from_shadowed_file(const char *path, GError **error) { nm_auto_unref_keyfile GKeyFile *keyfile = NULL; gs_free char *base_dir = NULL; - char *sep; + const char *sep; keyfile = g_key_file_new(); if (!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, error)) diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index b9e3f91920..728dccac99 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -77,7 +77,7 @@ get_full_file_path(const char *ifcfg_path, const char *file_path) { const char *base = file_path; gs_free char *dirname = NULL; - char *p; + const char *p; g_return_val_if_fail(ifcfg_path != NULL, NULL); g_return_val_if_fail(file_path != NULL, NULL); diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.c b/src/libnm-core-aux-intern/nm-libnm-core-utils.c index 2d709e20ef..2d4546d553 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -483,8 +483,8 @@ nm_utils_validate_shared_dhcp_range(const char *shared_dhcp_range, GPtrArray *addresses, GError **error) { - char *start_address_str; - char *end_address_str; + const char *start_address_str; + const char *end_address_str; NMIPAddress *interface_address_with_prefix; NMIPAddr interface_address; NMIPAddr start_address; diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index 98424c76fc..3b3aecf1a6 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -4495,7 +4495,7 @@ nm_range_from_str(const char *str, GError **error) gs_free char *str_free = NULL; guint64 start; guint64 end = 0; - char *c; + const char *c; g_return_val_if_fail(str, NULL); g_return_val_if_fail(!error || !*error, NULL); From 5f6beb0e57f6be3cd5deccec7dcee7be6a5ee190 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 2 Dec 2025 14:50:34 +0100 Subject: [PATCH 3/8] nm-udev-utils: constify strstr-output variable `subsystem_full` is const, so `s` needs to be const too. Reorder the NULL-byte write so that we are not writing into a const char* (the underlying memory is the same). --- src/libnm-udev-aux/nm-udev-utils.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libnm-udev-aux/nm-udev-utils.c b/src/libnm-udev-aux/nm-udev-utils.c index 744d03ce9e..9e43dd26b9 100644 --- a/src/libnm-udev-aux/nm-udev-utils.c +++ b/src/libnm-udev-aux/nm-udev-utils.c @@ -92,7 +92,8 @@ _subsystem_split(const char *subsystem_full, const char **out_devtype, char **to_free) { - char *tmp, *s; + char *tmp; + const char *s; nm_assert(subsystem_full); nm_assert(out_subsystem); @@ -101,12 +102,12 @@ _subsystem_split(const char *subsystem_full, s = strstr(subsystem_full, "/"); if (s) { - tmp = g_strdup(subsystem_full); - s = &tmp[s - subsystem_full]; - *s = '\0'; - *out_subsystem = tmp; - *out_devtype = &s[1]; - *to_free = tmp; + tmp = g_strdup(subsystem_full); + tmp[s - subsystem_full] = '\0'; + s = &tmp[s - subsystem_full]; + *out_subsystem = tmp; + *out_devtype = &s[1]; + *to_free = tmp; } else { *out_subsystem = subsystem_full; *out_devtype = NULL; From 754b87e1c4fa742959f9c3b52f5c6cac2192fd1b Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 2 Dec 2025 14:56:20 +0100 Subject: [PATCH 4/8] supplicant: separate input and local value We reallocate this value in the function, which is necessary because we write into it, and the input is const. Move the allocation into a local variable instead of overwriting the input pointer, because we are also pointing to it via `char* s`, which is not const. --- src/core/supplicant/nm-supplicant-settings-verify.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/supplicant/nm-supplicant-settings-verify.c b/src/core/supplicant/nm-supplicant-settings-verify.c index 6e5e44d2f8..cca53d820f 100644 --- a/src/core/supplicant/nm-supplicant-settings-verify.c +++ b/src/core/supplicant/nm-supplicant-settings-verify.c @@ -212,18 +212,19 @@ validate_type_utf8(const struct Opt *opt, const char *value, const guint32 len) } static gboolean -validate_type_keyword(const struct Opt *opt, const char *value, const guint32 len) +validate_type_keyword(const struct Opt *opt, const char *value_in, const guint32 len) { gs_free char *value_free = NULL; + char *value; nm_assert(opt); - nm_assert(value); + nm_assert(value_in); /* Allow everything */ if (!opt->str_allowed) return TRUE; - value = nm_strndup_a(300, value, len, &value_free); + value = nm_strndup_a(300, value_in, len, &value_free); /* validate each space-separated word in 'value' */ From ac427b25fb49b319e8ab0957a5f0b916601e8e0c Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 2 Dec 2025 14:59:19 +0100 Subject: [PATCH 5/8] core, impl: drop `const` qualifier from split outputs We write into the buffer returned by nm_strsplit_set_full(), even though it is returned as `const char**`. The function description claims this is fine: > * It is however safe and allowed to modify the individual strings in-place, > * like "g_strstrip((char *) iter[0])". Remove the const qualifier via cast so that it does not raise errors. --- src/core/dhcp/nm-dhcp-utils.c | 8 ++++---- src/libnm-core-impl/nm-setting-bridge.c | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index 949d87207f..8a9bd58cc6 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -32,11 +32,11 @@ ip4_process_dhcpcd_rfc3442_routes(const char *iface, in_addr_t address, guint32 *out_gwaddr) { - gs_free const char **routes = NULL; - const char **r; - gboolean have_routes = FALSE; + gs_free char **routes = NULL; + char **r; + gboolean have_routes = FALSE; - routes = nm_strsplit_set(str, " "); + routes = (char **) nm_strsplit_set(str, " "); if (!routes) return FALSE; diff --git a/src/libnm-core-impl/nm-setting-bridge.c b/src/libnm-core-impl/nm-setting-bridge.c index c161843498..2145741204 100644 --- a/src/libnm-core-impl/nm-setting-bridge.c +++ b/src/libnm-core-impl/nm-setting-bridge.c @@ -472,17 +472,17 @@ nm_bridge_vlan_to_str(const NMBridgeVlan *vlan, GError **error) NMBridgeVlan * nm_bridge_vlan_from_str(const char *str, GError **error) { - NMBridgeVlan *vlan = NULL; - gs_free const char **tokens = NULL; - guint i, vid_start, vid_end = 0; - gboolean pvid = FALSE; - gboolean untagged = FALSE; - char *c; + NMBridgeVlan *vlan = NULL; + gs_free char **tokens = NULL; + guint i, vid_start, vid_end = 0; + gboolean pvid = FALSE; + gboolean untagged = FALSE; + char *c; g_return_val_if_fail(str, NULL); g_return_val_if_fail(!error || !*error, NULL); - tokens = nm_utils_escaped_tokens_split(str, NM_ASCII_SPACES); + tokens = (char **) nm_utils_escaped_tokens_split(str, NM_ASCII_SPACES); if (!tokens || !tokens[0]) { g_set_error_literal(error, NM_CONNECTION_ERROR, From 9e70f31c8cad748cd78e06fc517085822b918bc2 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 2 Dec 2025 15:03:28 +0100 Subject: [PATCH 6/8] initrd: remove const qualifier from temporary variable `argument` is not const, but `tmp` is. We use `tmp` for reading arguments one by one, but we cannot add a null byte to separate the key and value if it is const. Make it non-const, so that `val[0] = '\0';` does not fail. --- src/nm-initrd-generator/nmi-cmdline-reader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 5cdb3bcdb3..f9b0fa161b 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -1177,7 +1177,7 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) { const char *nettype; const char *subchannels[4] = {0, 0, 0, 0}; - const char *tmp; + char *tmp; gs_free char *ifname = NULL; gs_free char *str_subchannels = NULL; const char *prefix; @@ -1248,8 +1248,8 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) NULL); while ((tmp = get_word(&argument, ',')) != NULL) { - const char *key; - char *val; + char *key; + char *val; val = strchr(tmp, '='); if (!val) { From 8e72e6b4fb5d26aedde939a4c8d0d5aa2f12d0c0 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Wed, 3 Dec 2025 09:40:57 +0100 Subject: [PATCH 7/8] aux-intern: add explicit cast for strchr() `addr` is always reallocated in this branch, so it is safe to cast the result of strchr to char* here to silence the const-qualifier warning. --- src/libnm-core-aux-intern/nm-libnm-core-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.c b/src/libnm-core-aux-intern/nm-libnm-core-utils.c index 2d4546d553..a39f645468 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -825,7 +825,7 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns, GError **er addr = nm_strndup_a(100, addr_port, end - addr_port, &addr_heap); /* IPv6 link-local scope-id */ - perc = strchr(addr, '%'); + perc = (char *) strchr(addr, '%'); if (perc) { *perc = '\0'; if (g_strlcpy(dns->interface, perc + 1, sizeof(dns->interface)) From d40e88fd02ccbc07a5e0c7e3cb8a75dee830d1b8 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Wed, 3 Dec 2025 11:19:47 +0100 Subject: [PATCH 8/8] test-link: test bond with use_carrier=1 `use_carrier` is removed from kernel since 6.18 [1], and returns the following error if set to 0: > option obsolete, use_carrier cannot be disabled This causes a failure of test-link-linux, so let's set it to 1. [1] https://lore.kernel.org/all/2029487.1756512517@famine/ --- src/core/platform/tests/test-link.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c index fab6bd2efe..77e7e64121 100644 --- a/src/core/platform/tests/test-link.c +++ b/src/core/platform/tests/test-link.c @@ -123,7 +123,8 @@ software_add(NMLinkType link_type, const char *name) gboolean bond0_exists = !!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0"); int r; const NMPlatformLnkBond nm_platform_lnk_bond_default = { - .mode = nmtst_rand_select(3, 1), + .mode = nmtst_rand_select(3, 1), + .use_carrier = 1, }; r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, &nm_platform_lnk_bond_default, NULL);