]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nss-myhostname: keep IPv6 probe result stable 42934/head
authorLuca Boccassi <luca.boccassi@gmail.com>
Wed, 8 Jul 2026 14:58:45 +0000 (15:58 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Sat, 11 Jul 2026 16:09:14 +0000 (17:09 +0100)
Cache the IPv6 enabled state while sizing and filling NSS result
buffers, so a transient sysctl read result cannot change the tuple or
address layout after the scratch buffer size has been computed. Also
zero-initialize gaih_addrtuple records before filling IPv4 addresses.

Follow-up for e8a7a315391a6a07897122725cd707f4e9ce63d7

src/nss-myhostname/nss-myhostname.c
src/test/test-nss-hosts.c

index 83d968ff0b58c26a63cfaa285317a4fe412ceba5..d902d305a7a292284ee8ae45ae4e0ee63b7e9e9f 100644 (file)
@@ -40,6 +40,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
         const char *canonical = NULL;
         int n_addresses = 0;
         uint32_t local_address_ipv4 = 0;
+        bool ipv6_enabled;
         size_t l, idx, ms;
         char *r_name;
 
@@ -96,7 +97,9 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
         }
 
         l = strlen(canonical);
-        ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 1 + socket_ipv6_is_enabled());
+        ipv6_enabled = n_addresses == 0 && socket_ipv6_is_enabled();
+        ms = ALIGN(l+1) +
+                ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 1 + ipv6_enabled);
         if (buflen < ms) {
                 UNPROTECT_ERRNO;
                 *errnop = ERANGE;
@@ -112,13 +115,14 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
         assert(n_addresses >= 0);
         if (n_addresses == 0) {
                 /* Second, fill in IPv6 tuple */
-                if (socket_ipv6_is_enabled()) {
+                if (ipv6_enabled) {
                         r_tuple = (struct gaih_addrtuple*) (buffer + idx);
-                        r_tuple->next = r_tuple_prev;
-                        r_tuple->name = r_name;
-                        r_tuple->family = AF_INET6;
+                        *r_tuple = (struct gaih_addrtuple) {
+                                .next = r_tuple_prev,
+                                .name = r_name,
+                                .family = AF_INET6,
+                        };
                         memcpy(r_tuple->addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6));
-                        r_tuple->scopeid = 0;
 
                         idx += ALIGN(sizeof(struct gaih_addrtuple));
                         r_tuple_prev = r_tuple;
@@ -126,11 +130,12 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
 
                 /* Third, fill in IPv4 tuple */
                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
-                r_tuple->next = r_tuple_prev;
-                r_tuple->name = r_name;
-                r_tuple->family = AF_INET;
-                *(uint32_t*) r_tuple->addr = local_address_ipv4;
-                r_tuple->scopeid = 0;
+                *r_tuple = (struct gaih_addrtuple) {
+                        .next = r_tuple_prev,
+                        .name = r_name,
+                        .family = AF_INET,
+                };
+                memcpy(r_tuple->addr, &local_address_ipv4, FAMILY_ADDRESS_SIZE(AF_INET));
 
                 idx += ALIGN(sizeof(struct gaih_addrtuple));
                 r_tuple_prev = r_tuple;
@@ -139,12 +144,18 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
         /* Fourth, fill actual addresses in, but in backwards order */
         for (int i = n_addresses; i > 0; i--) {
                 struct local_address *a = addresses + i - 1;
+                uint32_t scopeid = 0;
+
+                if (a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6))
+                        scopeid = a->ifindex;
 
                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
-                r_tuple->next = r_tuple_prev;
-                r_tuple->name = r_name;
-                r_tuple->family = a->family;
-                r_tuple->scopeid = a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6) ? a->ifindex : 0;
+                *r_tuple = (struct gaih_addrtuple) {
+                        .next = r_tuple_prev,
+                        .name = r_name,
+                        .family = a->family,
+                        .scopeid = scopeid,
+                };
                 memcpy(r_tuple->addr, &a->address, FAMILY_ADDRESS_SIZE(a->family));
 
                 idx += ALIGN(sizeof(struct gaih_addrtuple));
@@ -180,6 +191,7 @@ static enum nss_status fill_in_hostent(
                 int af,
                 struct local_address *addresses, unsigned n_addresses,
                 uint32_t local_address_ipv4,
+                bool ipv6_enabled,
                 struct hostent *result,
                 char *buffer, size_t buflen,
                 int *errnop, int *h_errnop,
@@ -212,8 +224,8 @@ static enum nss_status fill_in_hostent(
                 (additional ? ALIGN(l_additional+1) : 0) +
                 sizeof(char*) +
                 (additional ? sizeof(char*) : 0) +
-                (c > 0 ? c : af == AF_INET ? 1 : socket_ipv6_is_enabled()) * ALIGN(alen) +
-                (c > 0 ? c+1 : af == AF_INET ? 2 : (unsigned) socket_ipv6_is_enabled() + 1) * sizeof(char*);
+                (c > 0 ? c : af == AF_INET ? 1 : ipv6_enabled) * ALIGN(alen) +
+                (c > 0 ? c+1 : af == AF_INET ? 2 : (unsigned) ipv6_enabled + 1) * sizeof(char*);
 
         if (buflen < ms) {
                 UNPROTECT_ERRNO;
@@ -263,7 +275,7 @@ static enum nss_status fill_in_hostent(
         } else if (af == AF_INET) {
                 *(uint32_t*) r_addr = local_address_ipv4;
                 assert_se(INC_SAFE(&idx, ALIGN(alen)));
-        } else if (socket_ipv6_is_enabled()) {
+        } else if (ipv6_enabled) {
                 memcpy(r_addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6));
                 assert_se(INC_SAFE(&idx, ALIGN(alen)));
         }
@@ -279,7 +291,7 @@ static enum nss_status fill_in_hostent(
                 ((char**) r_addr_list)[i] = NULL;
                 assert_se(INC_SAFE(&idx, (c+1) * sizeof(char*)));
 
-        } else if (af == AF_INET || socket_ipv6_is_enabled()) {
+        } else if (af == AF_INET || ipv6_enabled) {
                 ((char**) r_addr_list)[0] = r_addr;
                 ((char**) r_addr_list)[1] = NULL;
                 assert_se(INC_SAFE(&idx, 2 * sizeof(char*)));
@@ -324,6 +336,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
         const char *canonical, *additional = NULL;
         _cleanup_free_ char *hn = NULL;
         uint32_t local_address_ipv4 = 0;
+        bool ipv6_enabled;
         int n_addresses = 0;
 
         PROTECT_ERRNO;
@@ -345,7 +358,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
                 return NSS_STATUS_UNAVAIL;
         }
 
-        if (af == AF_INET6 && !socket_ipv6_is_enabled())
+        ipv6_enabled = af == AF_INET6 && socket_ipv6_is_enabled();
+        if (af == AF_INET6 && !ipv6_enabled)
                 goto not_found;
 
         if (is_localhost(name)) {
@@ -397,6 +411,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
                         af,
                         addresses, n_addresses,
                         local_address_ipv4,
+                        ipv6_enabled,
                         host,
                         buffer, buflen,
                         errnop, h_errnop,
@@ -423,6 +438,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
         int n_addresses = 0;
         struct local_address *a;
         bool additional_from_hostname = false;
+        bool ipv6_enabled = false;
         unsigned n;
 
         PROTECT_ERRNO;
@@ -461,7 +477,8 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r(
         } else {
                 assert(af == AF_INET6);
 
-                if (!socket_ipv6_is_enabled())
+                ipv6_enabled = socket_ipv6_is_enabled();
+                if (!ipv6_enabled)
                         goto not_found;
 
                 if (memcmp(addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6)) == 0) {
@@ -511,6 +528,7 @@ found:
                         af,
                         addresses, n_addresses,
                         local_address_ipv4,
+                        ipv6_enabled,
                         host,
                         buffer, buflen,
                         errnop, h_errnop,
index 20d151a425402c9877bda6ba214cc941bbb47070..61f9b153f8c26eae5515d6aa9da7a9f88f453526 100644 (file)
@@ -15,6 +15,7 @@
 #include "local-addresses.h"
 #include "log.h"
 #include "main-func.h"
+#include "memory-util.h"
 #include "nss-test-util.h"
 #include "nss-util.h"
 #include "parse-util.h"
@@ -67,6 +68,16 @@ static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
         return n;
 }
 
+static void assert_myhostname_gaih_addrtuples(const struct gaih_addrtuple *tuples) {
+        assert(tuples);
+
+        for (const struct gaih_addrtuple *it = tuples; it; it = it->next)
+                if (it->family == AF_INET)
+                        assert_se(memeqzero(
+                                        (const uint8_t*) it->addr + FAMILY_ADDRESS_SIZE(AF_INET),
+                                        sizeof(it->addr) - FAMILY_ADDRESS_SIZE(AF_INET)));
+}
+
 static void print_struct_hostent(struct hostent *host, const char *canon) {
         log_info("        \"%s\"", host->h_name);
         STRV_FOREACH(s, host->h_aliases)
@@ -112,6 +123,9 @@ static void test_gethostbyname4_r(void *handle, const char *module, const char *
                 return;
         }
 
+        /* Poison the scratch buffer so uninitialized padding in NSS replies is observable. */
+        memset(buffer, 0xa5, sizeof(buffer));
+
         status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl);
         if (status == NSS_STATUS_SUCCESS) {
                 log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%"PRIxPTR" errno=%d/%s h_errno=%d/%s ttl=%"PRIi32,
@@ -122,6 +136,8 @@ static void test_gethostbyname4_r(void *handle, const char *module, const char *
                          errno2, hstrerror(errno2),
                          ttl);
                 n = print_gaih_addrtuples(pat);
+                if (streq(module, "myhostname"))
+                        assert_myhostname_gaih_addrtuples(pat);
         } else {
                 log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s",
                          fname, name,