Import glibc-2.34-48.fc35 from f35

* Thu Oct 13 2022 Arjun Shankar <arjun@redhat.com> - 2.34-48
- Handle non-hostname CNAME aliases during name resolution (#2129005)
- Sync with upstream branch release/2.34/master,
  commit e3976287b22422787f3cc6fc9adda58304b55bd9:
- nscd: Drop local address tuple variable [BZ #29607]
- x86-64: Require BMI1/BMI2 for AVX2 strrchr and wcsrchr implementations
- x86-64: Require BMI2 and LZCNT for AVX2 memrchr implementation
- x86-64: Require BMI2 for AVX2 (raw|w)memchr implementations
- x86-64: Require BMI2 for AVX2 wcs(n)cmp implementations
- x86-64: Require BMI2 for AVX2 strncmp implementation
- x86-64: Require BMI2 for AVX2 strcmp implementation
- x86-64: Require BMI2 for AVX2 str(n)casecmp implementations
- x86: include BMI1 and BMI2 in x86-64-v3 level
- nptl: Add backoff mechanism to spinlock loop
- sysdeps: Add 'get_fast_jitter' interace in fast-jitter.h
- nptl: Effectively skip CAS in spinlock loop
- Move assignment out of the CAS condition
- Add LLL_MUTEX_READ_LOCK [BZ #28537]
- Avoid extra load with CAS in __pthread_mutex_clocklock_common [BZ #28537]
- Avoid extra load with CAS in __pthread_mutex_lock_full [BZ #28537]
- resolv: Fix building tst-resolv-invalid-cname for earlier C standards
- nss_dns: Rewrite _nss_dns_gethostbyname4_r using current interfaces
- resolv: Add new tst-resolv-invalid-cname
- nss_dns: In gaih_getanswer_slice, skip strange aliases (bug 12154)
  (#2129005)
- nss_dns: Rewrite getanswer_r to match getanswer_ptr (bug 12154, bug 29305)
- nss_dns: Remove remnants of IPv6 address mapping
- nss_dns: Rewrite _nss_dns_gethostbyaddr2_r and getanswer_ptr
- nss_dns: Split getanswer_ptr from getanswer_r
- resolv: Add DNS packet parsing helpers geared towards wire format
- resolv: Add internal __ns_name_length_uncompressed function
- resolv: Add the __ns_samebinaryname function
- resolv: Add internal __res_binary_hnok function
- resolv: Add tst-resolv-aliases
- resolv: Add tst-resolv-byaddr for testing reverse lookup
- gconv: Use 64-bit interfaces in gconv_parseconfdir (bug 29583)
- elf: Fix hwcaps string size overestimation
- nscd: Fix netlink cache invalidation if epoll is used [BZ #29415]
- Apply asm redirections in wchar.h before first use
- Apply asm redirections in stdio.h before first use [BZ #27087]
- elf: Call __libc_early_init for reused namespaces (bug 29528)

Resolves: #2129005
Resolves: #2116960
This commit is contained in:
Arjun Shankar 2022-10-14 14:18:43 +02:00
parent 6b1ce0df40
commit e43852d149
37 changed files with 6920 additions and 1 deletions

589
glibc-rh2129005.patch Normal file
View File

@ -0,0 +1,589 @@
commit 1d495912a746e2a1ffb780c9a81fd234ec2464e8
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
nss_dns: Rewrite _nss_dns_gethostbyname4_r using current interfaces
Introduce struct alloc_buffer to this function, and use it and
struct ns_rr_cursor in gaih_getanswer_slice. Adjust gaih_getanswer
and gaih_getanswer_noaaaa accordingly.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 1cb3be71f04d98eb..36789965c06757d0 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -101,13 +101,6 @@
#endif
#define MAXHOSTNAMELEN 256
-/* We need this time later. */
-typedef union querybuf
-{
- HEADER hdr;
- u_char buf[MAXPACKET];
-} querybuf;
-
/* For historic reasons, pointers to IP addresses are char *, so use a
single list type for addresses and host names. */
#define DYNARRAY_STRUCT ptrlist
@@ -126,18 +119,18 @@ static enum nss_status getanswer_ptr (unsigned char *packet, size_t packetlen,
char **hnamep, int *errnop,
int *h_errnop, int32_t *ttlp);
-static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
- const querybuf *answer2, int anslen2,
- const char *qname,
+static enum nss_status gaih_getanswer (unsigned char *packet1,
+ size_t packet1len,
+ unsigned char *packet2,
+ size_t packet2len,
+ struct alloc_buffer *abuf,
struct gaih_addrtuple **pat,
- char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp);
-static enum nss_status gaih_getanswer_noaaaa (const querybuf *answer1,
- int anslen1,
- const char *qname,
+static enum nss_status gaih_getanswer_noaaaa (unsigned char *packet,
+ size_t packetlen,
+ struct alloc_buffer *abuf,
struct gaih_addrtuple **pat,
- char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp);
@@ -409,17 +402,13 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
name = cp;
}
- union
- {
- querybuf *buf;
- u_char *ptr;
- } host_buffer;
- querybuf *orig_host_buffer;
- host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048);
+ unsigned char dns_packet_buffer[2048];
+ unsigned char *alt_dns_packet_buffer = dns_packet_buffer;
u_char *ans2p = NULL;
int nans2p = 0;
int resplen2 = 0;
int ans2p_malloced = 0;
+ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen);
int olderr = errno;
@@ -428,22 +417,21 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
if ((ctx->resp->options & RES_NOAAAA) == 0)
{
n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
- host_buffer.buf->buf, 2048, &host_buffer.ptr,
- &ans2p, &nans2p, &resplen2, &ans2p_malloced);
+ dns_packet_buffer, sizeof (dns_packet_buffer),
+ &alt_dns_packet_buffer, &ans2p, &nans2p,
+ &resplen2, &ans2p_malloced);
if (n >= 0)
- status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p,
- resplen2, name, pat, buffer, buflen,
- errnop, herrnop, ttlp);
+ status = gaih_getanswer (alt_dns_packet_buffer, n, ans2p, resplen2,
+ &abuf, pat, errnop, herrnop, ttlp);
}
else
{
n = __res_context_search (ctx, name, C_IN, T_A,
- host_buffer.buf->buf, 2048, NULL,
- NULL, NULL, NULL, NULL);
+ dns_packet_buffer, sizeof (dns_packet_buffer),
+ NULL, NULL, NULL, NULL, NULL);
if (n >= 0)
- status = gaih_getanswer_noaaaa (host_buffer.buf, n,
- name, pat, buffer, buflen,
- errnop, herrnop, ttlp);
+ status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n,
+ &abuf, pat, errnop, herrnop, ttlp);
}
if (n < 0)
{
@@ -474,12 +462,20 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
__set_errno (olderr);
}
+ /* Implement the buffer resizing protocol. */
+ if (alloc_buffer_has_failed (&abuf))
+ {
+ *errnop = ERANGE;
+ *herrnop = NETDB_INTERNAL;
+ status = NSS_STATUS_TRYAGAIN;
+ }
+
/* Check whether ans2p was separately allocated. */
if (ans2p_malloced)
free (ans2p);
- if (host_buffer.buf != orig_host_buffer)
- free (host_buffer.buf);
+ if (alt_dns_packet_buffer != dns_packet_buffer)
+ free (alt_dns_packet_buffer);
__resolv_context_put (ctx);
return status;
@@ -893,259 +889,152 @@ getanswer_ptr (unsigned char *packet, size_t packetlen,
return NSS_STATUS_TRYAGAIN;
}
+/* Parses DNS data found in PACKETLEN bytes at PACKET in struct
+ gaih_addrtuple address tuples. The new address tuples are linked
+ from **TAILP, with backing store allocated from ABUF, and *TAILP is
+ updated to point where the next tuple pointer should be stored. If
+ TTLP is not null, *TTLP is updated to reflect the minimum TTL. If
+ STORE_CANON is true, the canonical name is stored as part of the
+ first address tuple being written. */
static enum nss_status
-gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
- struct gaih_addrtuple ***patp,
- char **bufferp, size_t *buflenp,
- int *errnop, int *h_errnop, int32_t *ttlp, int *firstp)
+gaih_getanswer_slice (unsigned char *packet, size_t packetlen,
+ struct alloc_buffer *abuf,
+ struct gaih_addrtuple ***tailp,
+ int *errnop, int *h_errnop, int32_t *ttlp,
+ bool store_canon)
{
- char *buffer = *bufferp;
- size_t buflen = *buflenp;
-
- struct gaih_addrtuple **pat = *patp;
- const HEADER *hp = &answer->hdr;
- int ancount = ntohs (hp->ancount);
- int qdcount = ntohs (hp->qdcount);
- const u_char *cp = answer->buf + HFIXEDSZ;
- const u_char *end_of_message = answer->buf + anslen;
- if (__glibc_unlikely (qdcount != 1))
- {
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
-
- u_char packtmp[NS_MAXCDNAME];
- int n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- /* We unpack the name to check it for validity. But we do not need
- it later. */
- if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1)
- {
- if (__glibc_unlikely (errno == EMSGSIZE))
- {
- too_small:
- *errnop = ERANGE;
- *h_errnop = NETDB_INTERNAL;
- return NSS_STATUS_TRYAGAIN;
- }
-
- n = -1;
- }
-
- if (__glibc_unlikely (n < 0))
- {
- *errnop = errno;
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
- if (__glibc_unlikely (__libc_res_hnok (buffer) == 0))
+ struct ns_rr_cursor c;
+ if (!__ns_rr_cursor_init (&c, packet, packetlen))
{
- errno = EBADMSG;
- *errnop = EBADMSG;
+ /* This should not happen because __res_context_query already
+ perfroms response validation. */
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
- cp += n + QFIXEDSZ;
+ bool haveanswer = false; /* Set to true if at least one address. */
+ uint16_t qtype = ns_rr_cursor_qtype (&c);
+ int ancount = ns_rr_cursor_ancount (&c);
+ const unsigned char *expected_name = ns_rr_cursor_qname (&c);
+ /* expected_name may be updated to point into this buffer. */
+ unsigned char name_buffer[NS_MAXCDNAME];
- int haveanswer = 0;
- int had_error = 0;
- char *canon = NULL;
- char *h_name = NULL;
- int h_namelen = 0;
+ /* This is a pointer to a possibly-compressed name in the packet.
+ Eventually it is equivalent to the canonical name. If needed, it
+ is uncompressed and translated to text form when the first
+ address tuple is encountered. */
+ const unsigned char *compressed_alias_name = expected_name;
- if (ancount == 0)
+ if (ancount == 0 || !__res_binary_hnok (compressed_alias_name))
{
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
- while (ancount-- > 0 && cp < end_of_message && had_error == 0)
+ for (; ancount > -0; --ancount)
{
- n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- if (n != -1 &&
- (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1)
- {
- if (__glibc_unlikely (errno == EMSGSIZE))
- goto too_small;
-
- n = -1;
- }
- if (__glibc_unlikely (n < 0))
- {
- ++had_error;
- continue;
- }
- if (*firstp && canon == NULL && __libc_res_hnok (buffer))
- {
- h_name = buffer;
- buffer += h_namelen;
- buflen -= h_namelen;
- }
-
- cp += n; /* name */
-
- if (__glibc_unlikely (cp + 10 > end_of_message))
- {
- ++had_error;
- continue;
- }
-
- uint16_t type;
- NS_GET16 (type, cp);
- uint16_t class;
- NS_GET16 (class, cp);
- int32_t ttl;
- NS_GET32 (ttl, cp);
- NS_GET16 (n, cp); /* RDATA length. */
-
- if (end_of_message - cp < n)
+ struct ns_rr_wire rr;
+ if (!__ns_rr_cursor_next (&c, &rr))
{
- /* RDATA extends beyond the end of the packet. */
- ++had_error;
- continue;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- if (class != C_IN)
- {
- cp += n;
- continue;
- }
+ /* Update TTL for known record types. */
+ if ((rr.rtype == T_CNAME || rr.rtype == qtype)
+ && ttlp != NULL && *ttlp > rr.ttl)
+ *ttlp = rr.ttl;
- if (type == T_CNAME)
+ if (rr.rtype == T_CNAME)
{
- char tbuf[MAXDNAME];
-
- /* A CNAME could also have a TTL entry. */
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
-
- n = __libc_dn_expand (answer->buf, end_of_message, cp,
- tbuf, sizeof tbuf);
- if (__glibc_unlikely (n < 0))
- {
- ++had_error;
- continue;
- }
- cp += n;
-
- if (*firstp && __libc_res_hnok (tbuf))
+ /* NB: No check for owner name match, based on historic
+ precedent. Record the CNAME target as the new expected
+ name. */
+ int n = __ns_name_unpack (c.begin, c.end, rr.rdata,
+ name_buffer, sizeof (name_buffer));
+ if (n < 0)
{
- /* Reclaim buffer space. */
- if (h_name + h_namelen == buffer)
- {
- buffer = h_name;
- buflen += h_namelen;
- }
-
- n = strlen (tbuf) + 1;
- if (__glibc_unlikely (n > buflen))
- goto too_small;
- if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
- {
- ++had_error;
- continue;
- }
-
- canon = buffer;
- buffer = __mempcpy (buffer, tbuf, n);
- buflen -= n;
- h_namelen = 0;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- continue;
+ expected_name = name_buffer;
+ if (store_canon && __res_binary_hnok (name_buffer))
+ /* This name can be used as a canonical name. Do not
+ translate to text form here to conserve buffer space.
+ Point to the compressed name because name_buffer can be
+ overwritten with an unusable name later. */
+ compressed_alias_name = rr.rdata;
}
-
- /* Stop parsing if we encounter a record with incorrect RDATA
- length. */
- if (type == T_A || type == T_AAAA)
+ else if (rr.rtype == qtype
+ && __ns_samebinaryname (rr.rname, expected_name)
+ && rr.rdlength == rrtype_to_rdata_length (qtype))
{
- if (n != rrtype_to_rdata_length (type))
+ struct gaih_addrtuple *ntup
+ = alloc_buffer_alloc (abuf, struct gaih_addrtuple);
+ /* Delay error reporting to the callers (they implement the
+ ERANGE buffer resizing handshake). */
+ if (ntup != NULL)
{
- ++had_error;
- continue;
+ ntup->next = NULL;
+ if (store_canon && compressed_alias_name != NULL)
+ {
+ /* This assumes that all the CNAME records come
+ first. Use MAXHOSTNAMELEN instead of
+ NS_MAXCDNAME for additional length checking.
+ However, these checks are not expected to fail
+ because all size NS_MAXCDNAME names should into
+ the hname buffer because no escaping is
+ needed. */
+ char unsigned nbuf[NS_MAXCDNAME];
+ char hname[MAXHOSTNAMELEN + 1];
+ if (__ns_name_unpack (c.begin, c.end,
+ compressed_alias_name,
+ nbuf, sizeof (nbuf)) >= 0
+ && __ns_name_ntop (nbuf, hname, sizeof (hname)) >= 0)
+ /* Space checking is performed by the callers. */
+ ntup->name = alloc_buffer_copy_string (abuf, hname);
+ store_canon = false;
+ }
+ else
+ ntup->name = NULL;
+ if (rr.rdlength == 4)
+ ntup->family = AF_INET;
+ else
+ ntup->family = AF_INET6;
+ memcpy (ntup->addr, rr.rdata, rr.rdlength);
+ ntup->scopeid = 0;
+
+ /* Link in the new tuple, and update the tail pointer to
+ point to its next field. */
+ **tailp = ntup;
+ *tailp = &ntup->next;
+
+ haveanswer = true;
}
}
- else
- {
- /* Skip unknown records. */
- cp += n;
- continue;
- }
-
- assert (type == T_A || type == T_AAAA);
- if (*pat == NULL)
- {
- uintptr_t pad = (-(uintptr_t) buffer
- % __alignof__ (struct gaih_addrtuple));
- buffer += pad;
- buflen = buflen > pad ? buflen - pad : 0;
-
- if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple)))
- goto too_small;
-
- *pat = (struct gaih_addrtuple *) buffer;
- buffer += sizeof (struct gaih_addrtuple);
- buflen -= sizeof (struct gaih_addrtuple);
- }
-
- (*pat)->name = NULL;
- (*pat)->next = NULL;
-
- if (*firstp)
- {
- /* We compose a single hostent out of the entire chain of
- entries, so the TTL of the hostent is essentially the lowest
- TTL in the chain. */
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
-
- (*pat)->name = canon ?: h_name;
-
- *firstp = 0;
- }
-
- (*pat)->family = type == T_A ? AF_INET : AF_INET6;
- memcpy ((*pat)->addr, cp, n);
- cp += n;
- (*pat)->scopeid = 0;
-
- pat = &((*pat)->next);
-
- haveanswer = 1;
}
if (haveanswer)
{
- *patp = pat;
- *bufferp = buffer;
- *buflenp = buflen;
-
*h_errnop = NETDB_SUCCESS;
return NSS_STATUS_SUCCESS;
}
-
- /* Special case here: if the resolver sent a result but it only
- contains a CNAME while we are looking for a T_A or T_AAAA record,
- we fail with NOTFOUND instead of TRYAGAIN. */
- if (canon != NULL)
+ else
{
+ /* Special case here: if the resolver sent a result but it only
+ contains a CNAME while we are looking for a T_A or T_AAAA
+ record, we fail with NOTFOUND. */
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
-
- *h_errnop = NETDB_INTERNAL;
- return NSS_STATUS_TRYAGAIN;
}
static enum nss_status
-gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
- int anslen2, const char *qname,
- struct gaih_addrtuple **pat, char *buffer, size_t buflen,
+gaih_getanswer (unsigned char *packet1, size_t packet1len,
+ unsigned char *packet2, size_t packet2len,
+ struct alloc_buffer *abuf, struct gaih_addrtuple **pat,
int *errnop, int *h_errnop, int32_t *ttlp)
{
- int first = 1;
-
enum nss_status status = NSS_STATUS_NOTFOUND;
/* Combining the NSS status of two distinct queries requires some
@@ -1157,7 +1046,10 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable).
A recoverable TRYAGAIN is almost always due to buffer size issues
and returns ERANGE in errno and the caller is expected to retry
- with a larger buffer.
+ with a larger buffer. (The caller, _nss_dns_gethostbyname4_r,
+ ignores the return status if it detects that the result buffer
+ has been exhausted and generates a TRYAGAIN failure with an
+ ERANGE code.)
Lastly, you may be tempted to make significant changes to the
conditions in this code to bring about symmetry between responses.
@@ -1237,36 +1129,30 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
is a recoverable error we now return TRYAGIN even if the first
response was SUCCESS. */
- if (anslen1 > 0)
- status = gaih_getanswer_slice(answer1, anslen1, qname,
- &pat, &buffer, &buflen,
- errnop, h_errnop, ttlp,
- &first);
-
- if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND
- || (status == NSS_STATUS_TRYAGAIN
- /* We want to look at the second answer in case of an
- NSS_STATUS_TRYAGAIN only if the error is non-recoverable, i.e.
- *h_errnop is NO_RECOVERY. If not, and if the failure was due to
- an insufficient buffer (ERANGE), then we need to drop the results
- and pass on the NSS_STATUS_TRYAGAIN to the caller so that it can
- repeat the query with a larger buffer. */
- && (*errnop != ERANGE || *h_errnop == NO_RECOVERY)))
- && answer2 != NULL && anslen2 > 0)
+ if (packet1len > 0)
{
- enum nss_status status2 = gaih_getanswer_slice(answer2, anslen2, qname,
- &pat, &buffer, &buflen,
- errnop, h_errnop, ttlp,
- &first);
+ status = gaih_getanswer_slice (packet1, packet1len,
+ abuf, &pat, errnop, h_errnop, ttlp, true);
+ if (alloc_buffer_has_failed (abuf))
+ /* Do not try parsing the second packet if a larger result
+ buffer is needed. The caller implements the resizing
+ protocol because *abuf has been exhausted. */
+ return NSS_STATUS_TRYAGAIN; /* Ignored by the caller. */
+ }
+
+ if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
+ && packet2 != NULL && packet2len > 0)
+ {
+ enum nss_status status2
+ = gaih_getanswer_slice (packet2, packet2len,
+ abuf, &pat, errnop, h_errnop, ttlp,
+ /* Success means that data with a
+ canonical name has already been
+ stored. Do not store the name again. */
+ status != NSS_STATUS_SUCCESS);
/* Use the second response status in some cases. */
if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND)
status = status2;
- /* Do not return a truncated second response (unless it was
- unavoidable e.g. unrecoverable TRYAGAIN). */
- if (status == NSS_STATUS_SUCCESS
- && (status2 == NSS_STATUS_TRYAGAIN
- && *errnop == ERANGE && *h_errnop != NO_RECOVERY))
- status = NSS_STATUS_TRYAGAIN;
}
return status;
@@ -1274,18 +1160,13 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
/* Variant of gaih_getanswer without a second (AAAA) response. */
static enum nss_status
-gaih_getanswer_noaaaa (const querybuf *answer1, int anslen1, const char *qname,
- struct gaih_addrtuple **pat,
- char *buffer, size_t buflen,
+gaih_getanswer_noaaaa (unsigned char *packet, size_t packetlen,
+ struct alloc_buffer *abuf, struct gaih_addrtuple **pat,
int *errnop, int *h_errnop, int32_t *ttlp)
{
- int first = 1;
-
enum nss_status status = NSS_STATUS_NOTFOUND;
- if (anslen1 > 0)
- status = gaih_getanswer_slice (answer1, anslen1, qname,
- &pat, &buffer, &buflen,
- errnop, h_errnop, ttlp,
- &first);
+ if (packetlen > 0)
+ status = gaih_getanswer_slice (packet, packetlen,
+ abuf, &pat, errnop, h_errnop, ttlp, true);
return status;
}

View File

@ -0,0 +1,212 @@
commit 536ddc5c02f1ee82483319863a893ccb381beece
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Aug 26 21:15:43 2022 +0200
elf: Call __libc_early_init for reused namespaces (bug 29528)
libc_map is never reset to NULL, neither during dlclose nor on a
dlopen call which reuses the namespace structure. As a result, if a
namespace is reused, its libc is not initialized properly. The most
visible result is a crash in the <ctype.h> functions.
To prevent similar bugs on namespace reuse from surfacing,
unconditionally initialize the chosen namespace to zero using memset.
(cherry picked from commit d0e357ff45a75553dee3b17ed7d303bfa544f6fe)
Conflicts:
elf/Makefile
(usual test differences)
diff --git a/elf/Makefile b/elf/Makefile
index 2b547d5b58f1759b..feec365e4e5fe9b3 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -399,6 +399,7 @@ tests += \
tst-dlmopen3 \
tst-dlmopen-dlerror \
tst-dlmopen-gethostbyname \
+ tst-dlmopen-twice \
tst-dlopenfail \
tst-dlopenfail-2 \
tst-dlopenrpath \
@@ -744,6 +745,8 @@ modules-names = \
tst-dlmopen1mod \
tst-dlmopen-dlerror-mod \
tst-dlmopen-gethostbyname-mod \
+ tst-dlmopen-twice-mod1 \
+ tst-dlmopen-twice-mod2 \
tst-dlopenfaillinkmod \
tst-dlopenfailmod1 \
tst-dlopenfailmod2 \
@@ -2665,3 +2668,7 @@ $(objpfx)tst-audit-tlsdesc.out: $(objpfx)tst-auditmod-tlsdesc.so
tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so
tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
+
+$(objpfx)tst-dlmopen-twice.out: \
+ $(objpfx)tst-dlmopen-twice-mod1.so \
+ $(objpfx)tst-dlmopen-twice-mod2.so
diff --git a/elf/dl-open.c b/elf/dl-open.c
index bc6872632880634e..1ab3c7b5ac2fbc45 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -839,11 +839,14 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
_dl_signal_error (EINVAL, file, NULL, N_("\
no more namespaces available for dlmopen()"));
}
- else if (nsid == GL(dl_nns))
- {
- __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
- ++GL(dl_nns);
- }
+
+ if (nsid == GL(dl_nns))
+ ++GL(dl_nns);
+
+ /* Initialize the new namespace. Most members are
+ zero-initialized, only the lock needs special treatment. */
+ memset (&GL(dl_ns)[nsid], 0, sizeof (GL(dl_ns)[nsid]));
+ __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
_dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
}
diff --git a/elf/tst-dlmopen-twice-mod1.c b/elf/tst-dlmopen-twice-mod1.c
new file mode 100644
index 0000000000000000..0eaf04948ce5263e
--- /dev/null
+++ b/elf/tst-dlmopen-twice-mod1.c
@@ -0,0 +1,37 @@
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 1.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("info: tst-dlmopen-twice-mod1.so loaded");
+ fflush (stdout);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ puts ("info: tst-dlmopen-twice-mod1.so about to be unloaded");
+ fflush (stdout);
+}
+
+/* Large allocation. The second module does not have this, so it
+ should load libc at a different address. */
+char large_allocate[16 * 1024 * 1024];
diff --git a/elf/tst-dlmopen-twice-mod2.c b/elf/tst-dlmopen-twice-mod2.c
new file mode 100644
index 0000000000000000..40c6c01f9625e188
--- /dev/null
+++ b/elf/tst-dlmopen-twice-mod2.c
@@ -0,0 +1,50 @@
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 2.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <ctype.h>
+#include <stdio.h>
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ puts ("info: tst-dlmopen-twice-mod2.so loaded");
+ fflush (stdout);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ puts ("info: tst-dlmopen-twice-mod2.so about to be unloaded");
+ fflush (stdout);
+}
+
+int
+run_check (void)
+{
+ puts ("info: about to call isalpha");
+ fflush (stdout);
+
+ volatile char ch = 'a';
+ if (!isalpha (ch))
+ {
+ puts ("error: isalpha ('a') is not true");
+ fflush (stdout);
+ return 1;
+ }
+ return 0;
+}
diff --git a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c
new file mode 100644
index 0000000000000000..449f3c8fa9f2aa01
--- /dev/null
+++ b/elf/tst-dlmopen-twice.c
@@ -0,0 +1,34 @@
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Main.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", RTLD_NOW);
+ xdlclose (handle);
+ handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW);
+ int (*run_check) (void) = xdlsym (handle, "run_check");
+ TEST_COMPARE (run_check (), 0);
+ xdlclose (handle);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,334 @@
commit 2a44960cbc78713c6a2721683a4319d50e71a01f
Author: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
Date: Thu Jul 7 18:12:58 2022 -0300
Apply asm redirections in stdio.h before first use [BZ #27087]
Compilers may not be able to apply asm redirections to functions after
these functions are used for the first time, e.g. clang 13.
Fix [BZ #27087] by applying all long double-related asm redirections
before using functions in bits/stdio.h.
However, as these asm redirections depend on the declarations provided
by libio/bits/stdio2.h, this header was split in 2:
- libio/bits/stdio2-decl.h contains all function declarations;
- libio/bits/stdio2.h remains with the remaining contents, including
redirections.
This also adds the access attribute to __vsnprintf_chk that was missing.
Tested with build-many-glibcs.py.
Reviewed-by: Paul E. Murphy <murphyp@linux.ibm.com>
(cherry picked from commit d0fa09a7701956036ff36f8ca188e9fff81553d8)
diff --git a/include/bits/stdio2-decl.h b/include/bits/stdio2-decl.h
new file mode 100644
index 0000000000000000..bbb052f192218219
--- /dev/null
+++ b/include/bits/stdio2-decl.h
@@ -0,0 +1 @@
+#include <libio/bits/stdio2-decl.h>
diff --git a/libio/Makefile b/libio/Makefile
index 5336b7d59584927f..981c876940f67fbf 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -23,7 +23,7 @@ subdir := libio
include ../Makeconfig
headers := stdio.h \
- bits/stdio.h bits/stdio2.h bits/stdio-ldbl.h \
+ bits/stdio.h bits/stdio2.h bits/stdio2-decl.h bits/stdio-ldbl.h \
bits/types/FILE.h bits/types/__FILE.h bits/types/struct_FILE.h \
bits/types/__fpos_t.h bits/types/__fpos64_t.h \
bits/types/cookie_io_functions_t.h
diff --git a/libio/bits/stdio2-decl.h b/libio/bits/stdio2-decl.h
new file mode 100644
index 0000000000000000..e398f7182b98e4d7
--- /dev/null
+++ b/libio/bits/stdio2-decl.h
@@ -0,0 +1,111 @@
+/* Checking macros for stdio functions. Declarations only.
+ Copyright (C) 2004-2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _BITS_STDIO2_DEC_H
+#define _BITS_STDIO2_DEC_H 1
+
+#ifndef _STDIO_H
+# error "Never include <bits/stdio2-decl.h> directly; use <stdio.h> instead."
+#endif
+
+extern int __sprintf_chk (char *__restrict __s, int __flag, size_t __slen,
+ const char *__restrict __format, ...) __THROW
+ __attr_access ((__write_only__, 1, 3));
+extern int __vsprintf_chk (char *__restrict __s, int __flag, size_t __slen,
+ const char *__restrict __format,
+ __gnuc_va_list __ap) __THROW
+ __attr_access ((__write_only__, 1, 3));
+
+#if defined __USE_ISOC99 || defined __USE_UNIX98
+
+extern int __snprintf_chk (char *__restrict __s, size_t __n, int __flag,
+ size_t __slen, const char *__restrict __format,
+ ...) __THROW
+ __attr_access ((__write_only__, 1, 2));
+extern int __vsnprintf_chk (char *__restrict __s, size_t __n, int __flag,
+ size_t __slen, const char *__restrict __format,
+ __gnuc_va_list __ap) __THROW
+ __attr_access ((__write_only__, 1, 2));
+
+#endif
+
+#if __USE_FORTIFY_LEVEL > 1
+
+extern int __fprintf_chk (FILE *__restrict __stream, int __flag,
+ const char *__restrict __format, ...);
+extern int __printf_chk (int __flag, const char *__restrict __format, ...);
+extern int __vfprintf_chk (FILE *__restrict __stream, int __flag,
+ const char *__restrict __format, __gnuc_va_list __ap);
+extern int __vprintf_chk (int __flag, const char *__restrict __format,
+ __gnuc_va_list __ap);
+
+# ifdef __USE_XOPEN2K8
+extern int __dprintf_chk (int __fd, int __flag, const char *__restrict __fmt,
+ ...) __attribute__ ((__format__ (__printf__, 3, 4)));
+extern int __vdprintf_chk (int __fd, int __flag,
+ const char *__restrict __fmt, __gnuc_va_list __arg)
+ __attribute__ ((__format__ (__printf__, 3, 0)));
+# endif
+
+# ifdef __USE_GNU
+
+extern int __asprintf_chk (char **__restrict __ptr, int __flag,
+ const char *__restrict __fmt, ...)
+ __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur;
+extern int __vasprintf_chk (char **__restrict __ptr, int __flag,
+ const char *__restrict __fmt, __gnuc_va_list __arg)
+ __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur;
+extern int __obstack_printf_chk (struct obstack *__restrict __obstack,
+ int __flag, const char *__restrict __format,
+ ...)
+ __THROW __attribute__ ((__format__ (__printf__, 3, 4)));
+extern int __obstack_vprintf_chk (struct obstack *__restrict __obstack,
+ int __flag,
+ const char *__restrict __format,
+ __gnuc_va_list __args)
+ __THROW __attribute__ ((__format__ (__printf__, 3, 0)));
+
+# endif
+#endif
+
+#if __GLIBC_USE (DEPRECATED_GETS)
+extern char *__gets_chk (char *__str, size_t) __wur;
+#endif
+
+extern char *__fgets_chk (char *__restrict __s, size_t __size, int __n,
+ FILE *__restrict __stream)
+ __wur __attr_access ((__write_only__, 1, 3));
+
+extern size_t __fread_chk (void *__restrict __ptr, size_t __ptrlen,
+ size_t __size, size_t __n,
+ FILE *__restrict __stream) __wur;
+
+#ifdef __USE_GNU
+extern char *__fgets_unlocked_chk (char *__restrict __s, size_t __size,
+ int __n, FILE *__restrict __stream)
+ __wur __attr_access ((__write_only__, 1, 3));
+#endif
+
+#ifdef __USE_MISC
+# undef fread_unlocked
+extern size_t __fread_unlocked_chk (void *__restrict __ptr, size_t __ptrlen,
+ size_t __size, size_t __n,
+ FILE *__restrict __stream) __wur;
+#endif
+
+#endif /* bits/stdio2-decl.h. */
diff --git a/libio/bits/stdio2.h b/libio/bits/stdio2.h
index 40ff16b01b4f4876..4570f86a4496c1ee 100644
--- a/libio/bits/stdio2.h
+++ b/libio/bits/stdio2.h
@@ -23,14 +23,6 @@
# error "Never include <bits/stdio2.h> directly; use <stdio.h> instead."
#endif
-extern int __sprintf_chk (char *__restrict __s, int __flag, size_t __slen,
- const char *__restrict __format, ...) __THROW
- __attr_access ((__write_only__, 1, 3));
-extern int __vsprintf_chk (char *__restrict __s, int __flag, size_t __slen,
- const char *__restrict __format,
- __gnuc_va_list __ap) __THROW
- __attr_access ((__write_only__, 1, 3));
-
#ifdef __va_arg_pack
__fortify_function int
__NTH (sprintf (char *__restrict __s, const char *__restrict __fmt, ...))
@@ -54,15 +46,6 @@ __NTH (vsprintf (char *__restrict __s, const char *__restrict __fmt,
}
#if defined __USE_ISOC99 || defined __USE_UNIX98
-
-extern int __snprintf_chk (char *__restrict __s, size_t __n, int __flag,
- size_t __slen, const char *__restrict __format,
- ...) __THROW
- __attr_access ((__write_only__, 1, 2));
-extern int __vsnprintf_chk (char *__restrict __s, size_t __n, int __flag,
- size_t __slen, const char *__restrict __format,
- __gnuc_va_list __ap) __THROW;
-
# ifdef __va_arg_pack
__fortify_function int
__NTH (snprintf (char *__restrict __s, size_t __n,
@@ -89,15 +72,6 @@ __NTH (vsnprintf (char *__restrict __s, size_t __n,
#endif
#if __USE_FORTIFY_LEVEL > 1
-
-extern int __fprintf_chk (FILE *__restrict __stream, int __flag,
- const char *__restrict __format, ...);
-extern int __printf_chk (int __flag, const char *__restrict __format, ...);
-extern int __vfprintf_chk (FILE *__restrict __stream, int __flag,
- const char *__restrict __format, __gnuc_va_list __ap);
-extern int __vprintf_chk (int __flag, const char *__restrict __format,
- __gnuc_va_list __ap);
-
# ifdef __va_arg_pack
__fortify_function int
fprintf (FILE *__restrict __stream, const char *__restrict __fmt, ...)
@@ -136,12 +110,6 @@ vfprintf (FILE *__restrict __stream,
}
# ifdef __USE_XOPEN2K8
-extern int __dprintf_chk (int __fd, int __flag, const char *__restrict __fmt,
- ...) __attribute__ ((__format__ (__printf__, 3, 4)));
-extern int __vdprintf_chk (int __fd, int __flag,
- const char *__restrict __fmt, __gnuc_va_list __arg)
- __attribute__ ((__format__ (__printf__, 3, 0)));
-
# ifdef __va_arg_pack
__fortify_function int
dprintf (int __fd, const char *__restrict __fmt, ...)
@@ -162,23 +130,6 @@ vdprintf (int __fd, const char *__restrict __fmt, __gnuc_va_list __ap)
# endif
# ifdef __USE_GNU
-
-extern int __asprintf_chk (char **__restrict __ptr, int __flag,
- const char *__restrict __fmt, ...)
- __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur;
-extern int __vasprintf_chk (char **__restrict __ptr, int __flag,
- const char *__restrict __fmt, __gnuc_va_list __arg)
- __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur;
-extern int __obstack_printf_chk (struct obstack *__restrict __obstack,
- int __flag, const char *__restrict __format,
- ...)
- __THROW __attribute__ ((__format__ (__printf__, 3, 4)));
-extern int __obstack_vprintf_chk (struct obstack *__restrict __obstack,
- int __flag,
- const char *__restrict __format,
- __gnuc_va_list __args)
- __THROW __attribute__ ((__format__ (__printf__, 3, 0)));
-
# ifdef __va_arg_pack
__fortify_function int
__NTH (asprintf (char **__restrict __ptr, const char *__restrict __fmt, ...))
@@ -231,7 +182,6 @@ __NTH (obstack_vprintf (struct obstack *__restrict __obstack,
#endif
#if __GLIBC_USE (DEPRECATED_GETS)
-extern char *__gets_chk (char *__str, size_t) __wur;
extern char *__REDIRECT (__gets_warn, (char *__str), gets)
__wur __warnattr ("please use fgets or getline instead, gets can't "
"specify buffer size");
@@ -245,9 +195,6 @@ gets (char *__str)
}
#endif
-extern char *__fgets_chk (char *__restrict __s, size_t __size, int __n,
- FILE *__restrict __stream)
- __wur __attr_access ((__write_only__, 1, 3));
extern char *__REDIRECT (__fgets_alias,
(char *__restrict __s, int __n,
FILE *__restrict __stream), fgets)
@@ -269,9 +216,6 @@ fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
return __fgets_chk (__s, sz, __n, __stream);
}
-extern size_t __fread_chk (void *__restrict __ptr, size_t __ptrlen,
- size_t __size, size_t __n,
- FILE *__restrict __stream) __wur;
extern size_t __REDIRECT (__fread_alias,
(void *__restrict __ptr, size_t __size,
size_t __n, FILE *__restrict __stream),
@@ -297,9 +241,6 @@ fread (void *__restrict __ptr, size_t __size, size_t __n,
}
#ifdef __USE_GNU
-extern char *__fgets_unlocked_chk (char *__restrict __s, size_t __size,
- int __n, FILE *__restrict __stream)
- __wur __attr_access ((__write_only__, 1, 3));
extern char *__REDIRECT (__fgets_unlocked_alias,
(char *__restrict __s, int __n,
FILE *__restrict __stream), fgets_unlocked)
@@ -324,9 +265,6 @@ fgets_unlocked (char *__restrict __s, int __n, FILE *__restrict __stream)
#ifdef __USE_MISC
# undef fread_unlocked
-extern size_t __fread_unlocked_chk (void *__restrict __ptr, size_t __ptrlen,
- size_t __size, size_t __n,
- FILE *__restrict __stream) __wur;
extern size_t __REDIRECT (__fread_unlocked_alias,
(void *__restrict __ptr, size_t __size,
size_t __n, FILE *__restrict __stream),
diff --git a/libio/stdio.h b/libio/stdio.h
index abefe640e52d18d5..d36e61c56bbb3117 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -879,20 +879,27 @@ extern void funlockfile (FILE *__stream) __THROW;
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
+#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
+/* Declare all functions from bits/stdio2-decl.h first. */
+# include <bits/stdio2-decl.h>
+#endif
+
+/* The following headers provide asm redirections. These redirections must
+ appear before the first usage of these functions, e.g. in bits/stdio.h. */
+#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1
+# include <bits/stdio-ldbl.h>
+#endif
+
/* If we are compiling with optimizing read this file. It contains
several optimizing inline functions and macros. */
#ifdef __USE_EXTERN_INLINES
# include <bits/stdio.h>
#endif
#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
+/* Now include the function definitions and redirects too. */
# include <bits/stdio2.h>
#endif
-#include <bits/floatn.h>
-#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1
-# include <bits/stdio-ldbl.h>
-#endif
-
__END_DECLS
#endif /* <stdio.h> included. */

View File

@ -0,0 +1,414 @@
commit b41c535f46e7e7bbd8ff2ac68b94c2348e2f66e4
Author: Raphael Moreira Zinsly <rzinsly@linux.ibm.com>
Date: Wed Aug 24 11:43:37 2022 -0300
Apply asm redirections in wchar.h before first use
Similar to d0fa09a770, but for wchar.h. Fixes [BZ #27087] by applying
all long double related asm redirections before using functions in
bits/wchar2.h.
Moves the function declarations from wcsmbs/bits/wchar2.h to a new file
wcsmbs/bits/wchar2-decl.h that will be included first in wcsmbs/wchar.h.
Tested with build-many-glibcs.py.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
(cherry picked from commit c7509d49c4e8fa494120c5ead21338559dad16f5)
diff --git a/include/bits/wchar2-decl.h b/include/bits/wchar2-decl.h
new file mode 100644
index 0000000000000000..00b1b93342ef28ff
--- /dev/null
+++ b/include/bits/wchar2-decl.h
@@ -0,0 +1 @@
+#include <wcsmbs/bits/wchar2-decl.h>
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index f38eb5cfe16fd3d7..5fe755e65df6c621 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -22,8 +22,9 @@ subdir := wcsmbs
include ../Makeconfig
-headers := wchar.h bits/wchar.h bits/wchar2.h bits/wchar-ldbl.h uchar.h \
- bits/types/__mbstate_t.h bits/types/mbstate_t.h bits/types/wint_t.h
+headers := wchar.h bits/wchar.h bits/wchar2.h bits/wchar2-decl.h \
+ bits/wchar-ldbl.h uchar.h bits/types/__mbstate_t.h \
+ bits/types/mbstate_t.h bits/types/wint_t.h
routines := wcscat wcschr wcscmp wcscpy wcscspn wcsdup wcslen wcsncat \
wcsncmp wcsncpy wcspbrk wcsrchr wcsspn wcstok wcsstr wmemchr \
diff --git a/wcsmbs/bits/wchar2-decl.h b/wcsmbs/bits/wchar2-decl.h
new file mode 100644
index 0000000000000000..8e1735c33b7f7e78
--- /dev/null
+++ b/wcsmbs/bits/wchar2-decl.h
@@ -0,0 +1,124 @@
+/* Checking macros for wchar functions. Declarations only.
+ Copyright (C) 2004-2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _BITS_WCHAR2_DECL_H
+#define _BITS_WCHAR2_DECL_H 1
+
+#ifndef _WCHAR_H
+# error "Never include <bits/wchar2-decl.h> directly; use <wchar.h> instead."
+#endif
+
+
+extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1,
+ const wchar_t *__restrict __s2, size_t __n,
+ size_t __ns1) __THROW;
+extern wchar_t *__wmemmove_chk (wchar_t *__s1, const wchar_t *__s2,
+ size_t __n, size_t __ns1) __THROW;
+
+
+#ifdef __USE_GNU
+
+extern wchar_t *__wmempcpy_chk (wchar_t *__restrict __s1,
+ const wchar_t *__restrict __s2, size_t __n,
+ size_t __ns1) __THROW;
+
+#endif
+
+
+extern wchar_t *__wmemset_chk (wchar_t *__s, wchar_t __c, size_t __n,
+ size_t __ns) __THROW;
+extern wchar_t *__wcscpy_chk (wchar_t *__restrict __dest,
+ const wchar_t *__restrict __src,
+ size_t __n) __THROW;
+extern wchar_t *__wcpcpy_chk (wchar_t *__restrict __dest,
+ const wchar_t *__restrict __src,
+ size_t __destlen) __THROW;
+extern wchar_t *__wcsncpy_chk (wchar_t *__restrict __dest,
+ const wchar_t *__restrict __src, size_t __n,
+ size_t __destlen) __THROW;
+extern wchar_t *__wcpncpy_chk (wchar_t *__restrict __dest,
+ const wchar_t *__restrict __src, size_t __n,
+ size_t __destlen) __THROW;
+extern wchar_t *__wcscat_chk (wchar_t *__restrict __dest,
+ const wchar_t *__restrict __src,
+ size_t __destlen) __THROW;
+extern wchar_t *__wcsncat_chk (wchar_t *__restrict __dest,
+ const wchar_t *__restrict __src,
+ size_t __n, size_t __destlen) __THROW;
+extern int __swprintf_chk (wchar_t *__restrict __s, size_t __n,
+ int __flag, size_t __s_len,
+ const wchar_t *__restrict __format, ...)
+ __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 6))) */;
+extern int __vswprintf_chk (wchar_t *__restrict __s, size_t __n,
+ int __flag, size_t __s_len,
+ const wchar_t *__restrict __format,
+ __gnuc_va_list __arg)
+ __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 0))) */;
+
+#if __USE_FORTIFY_LEVEL > 1
+
+extern int __fwprintf_chk (__FILE *__restrict __stream, int __flag,
+ const wchar_t *__restrict __format, ...);
+extern int __wprintf_chk (int __flag, const wchar_t *__restrict __format,
+ ...);
+extern int __vfwprintf_chk (__FILE *__restrict __stream, int __flag,
+ const wchar_t *__restrict __format,
+ __gnuc_va_list __ap);
+extern int __vwprintf_chk (int __flag, const wchar_t *__restrict __format,
+ __gnuc_va_list __ap);
+
+#endif
+
+extern wchar_t *__fgetws_chk (wchar_t *__restrict __s, size_t __size, int __n,
+ __FILE *__restrict __stream) __wur;
+
+#ifdef __USE_GNU
+
+extern wchar_t *__fgetws_unlocked_chk (wchar_t *__restrict __s, size_t __size,
+ int __n, __FILE *__restrict __stream)
+ __wur;
+
+#endif
+
+extern size_t __wcrtomb_chk (char *__restrict __s, wchar_t __wchar,
+ mbstate_t *__restrict __p,
+ size_t __buflen) __THROW __wur;
+extern size_t __mbsrtowcs_chk (wchar_t *__restrict __dst,
+ const char **__restrict __src,
+ size_t __len, mbstate_t *__restrict __ps,
+ size_t __dstlen) __THROW;
+extern size_t __wcsrtombs_chk (char *__restrict __dst,
+ const wchar_t **__restrict __src,
+ size_t __len, mbstate_t *__restrict __ps,
+ size_t __dstlen) __THROW;
+
+#ifdef __USE_XOPEN2K8
+
+extern size_t __mbsnrtowcs_chk (wchar_t *__restrict __dst,
+ const char **__restrict __src, size_t __nmc,
+ size_t __len, mbstate_t *__restrict __ps,
+ size_t __dstlen) __THROW;
+extern size_t __wcsnrtombs_chk (char *__restrict __dst,
+ const wchar_t **__restrict __src,
+ size_t __nwc, size_t __len,
+ mbstate_t *__restrict __ps, size_t __dstlen)
+ __THROW;
+
+#endif
+
+#endif /* bits/wchar2-decl.h. */
diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h
index 88c1fdfcd34292f4..50151b424d85a032 100644
--- a/wcsmbs/bits/wchar2.h
+++ b/wcsmbs/bits/wchar2.h
@@ -21,9 +21,6 @@
#endif
-extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1,
- const wchar_t *__restrict __s2, size_t __n,
- size_t __ns1) __THROW;
extern wchar_t *__REDIRECT_NTH (__wmemcpy_alias,
(wchar_t *__restrict __s1,
const wchar_t *__restrict __s2, size_t __n),
@@ -45,8 +42,6 @@ __NTH (wmemcpy (wchar_t *__restrict __s1, const wchar_t *__restrict __s2,
}
-extern wchar_t *__wmemmove_chk (wchar_t *__s1, const wchar_t *__s2,
- size_t __n, size_t __ns1) __THROW;
extern wchar_t *__REDIRECT_NTH (__wmemmove_alias, (wchar_t *__s1,
const wchar_t *__s2,
size_t __n), wmemmove);
@@ -66,9 +61,6 @@ __NTH (wmemmove (wchar_t *__s1, const wchar_t *__s2, size_t __n))
#ifdef __USE_GNU
-extern wchar_t *__wmempcpy_chk (wchar_t *__restrict __s1,
- const wchar_t *__restrict __s2, size_t __n,
- size_t __ns1) __THROW;
extern wchar_t *__REDIRECT_NTH (__wmempcpy_alias,
(wchar_t *__restrict __s1,
const wchar_t *__restrict __s2,
@@ -91,8 +83,6 @@ __NTH (wmempcpy (wchar_t *__restrict __s1, const wchar_t *__restrict __s2,
#endif
-extern wchar_t *__wmemset_chk (wchar_t *__s, wchar_t __c, size_t __n,
- size_t __ns) __THROW;
extern wchar_t *__REDIRECT_NTH (__wmemset_alias, (wchar_t *__s, wchar_t __c,
size_t __n), wmemset);
extern wchar_t *__REDIRECT_NTH (__wmemset_chk_warn,
@@ -110,9 +100,6 @@ __NTH (wmemset (wchar_t *__s, wchar_t __c, size_t __n))
}
-extern wchar_t *__wcscpy_chk (wchar_t *__restrict __dest,
- const wchar_t *__restrict __src,
- size_t __n) __THROW;
extern wchar_t *__REDIRECT_NTH (__wcscpy_alias,
(wchar_t *__restrict __dest,
const wchar_t *__restrict __src), wcscpy);
@@ -127,9 +114,6 @@ __NTH (wcscpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
}
-extern wchar_t *__wcpcpy_chk (wchar_t *__restrict __dest,
- const wchar_t *__restrict __src,
- size_t __destlen) __THROW;
extern wchar_t *__REDIRECT_NTH (__wcpcpy_alias,
(wchar_t *__restrict __dest,
const wchar_t *__restrict __src), wcpcpy);
@@ -144,9 +128,6 @@ __NTH (wcpcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
}
-extern wchar_t *__wcsncpy_chk (wchar_t *__restrict __dest,
- const wchar_t *__restrict __src, size_t __n,
- size_t __destlen) __THROW;
extern wchar_t *__REDIRECT_NTH (__wcsncpy_alias,
(wchar_t *__restrict __dest,
const wchar_t *__restrict __src,
@@ -168,9 +149,6 @@ __NTH (wcsncpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
}
-extern wchar_t *__wcpncpy_chk (wchar_t *__restrict __dest,
- const wchar_t *__restrict __src, size_t __n,
- size_t __destlen) __THROW;
extern wchar_t *__REDIRECT_NTH (__wcpncpy_alias,
(wchar_t *__restrict __dest,
const wchar_t *__restrict __src,
@@ -192,9 +170,6 @@ __NTH (wcpncpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
}
-extern wchar_t *__wcscat_chk (wchar_t *__restrict __dest,
- const wchar_t *__restrict __src,
- size_t __destlen) __THROW;
extern wchar_t *__REDIRECT_NTH (__wcscat_alias,
(wchar_t *__restrict __dest,
const wchar_t *__restrict __src), wcscat);
@@ -209,9 +184,6 @@ __NTH (wcscat (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
}
-extern wchar_t *__wcsncat_chk (wchar_t *__restrict __dest,
- const wchar_t *__restrict __src,
- size_t __n, size_t __destlen) __THROW;
extern wchar_t *__REDIRECT_NTH (__wcsncat_alias,
(wchar_t *__restrict __dest,
const wchar_t *__restrict __src,
@@ -228,10 +200,6 @@ __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
}
-extern int __swprintf_chk (wchar_t *__restrict __s, size_t __n,
- int __flag, size_t __s_len,
- const wchar_t *__restrict __format, ...)
- __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 6))) */;
extern int __REDIRECT_NTH_LDBL (__swprintf_alias,
(wchar_t *__restrict __s, size_t __n,
@@ -258,11 +226,6 @@ __NTH (swprintf (wchar_t *__restrict __s, size_t __n,
: swprintf (s, n, __VA_ARGS__))
#endif
-extern int __vswprintf_chk (wchar_t *__restrict __s, size_t __n,
- int __flag, size_t __s_len,
- const wchar_t *__restrict __format,
- __gnuc_va_list __arg)
- __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 0))) */;
extern int __REDIRECT_NTH_LDBL (__vswprintf_alias,
(wchar_t *__restrict __s, size_t __n,
@@ -283,16 +246,6 @@ __NTH (vswprintf (wchar_t *__restrict __s, size_t __n,
#if __USE_FORTIFY_LEVEL > 1
-extern int __fwprintf_chk (__FILE *__restrict __stream, int __flag,
- const wchar_t *__restrict __format, ...);
-extern int __wprintf_chk (int __flag, const wchar_t *__restrict __format,
- ...);
-extern int __vfwprintf_chk (__FILE *__restrict __stream, int __flag,
- const wchar_t *__restrict __format,
- __gnuc_va_list __ap);
-extern int __vwprintf_chk (int __flag, const wchar_t *__restrict __format,
- __gnuc_va_list __ap);
-
# ifdef __va_arg_pack
__fortify_function int
wprintf (const wchar_t *__restrict __fmt, ...)
@@ -328,8 +281,6 @@ vfwprintf (__FILE *__restrict __stream,
#endif
-extern wchar_t *__fgetws_chk (wchar_t *__restrict __s, size_t __size, int __n,
- __FILE *__restrict __stream) __wur;
extern wchar_t *__REDIRECT (__fgetws_alias,
(wchar_t *__restrict __s, int __n,
__FILE *__restrict __stream), fgetws) __wur;
@@ -351,9 +302,6 @@ fgetws (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream)
}
#ifdef __USE_GNU
-extern wchar_t *__fgetws_unlocked_chk (wchar_t *__restrict __s, size_t __size,
- int __n, __FILE *__restrict __stream)
- __wur;
extern wchar_t *__REDIRECT (__fgetws_unlocked_alias,
(wchar_t *__restrict __s, int __n,
__FILE *__restrict __stream), fgetws_unlocked)
@@ -379,9 +327,6 @@ fgetws_unlocked (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream)
#endif
-extern size_t __wcrtomb_chk (char *__restrict __s, wchar_t __wchar,
- mbstate_t *__restrict __p,
- size_t __buflen) __THROW __wur;
extern size_t __REDIRECT_NTH (__wcrtomb_alias,
(char *__restrict __s, wchar_t __wchar,
mbstate_t *__restrict __ps), wcrtomb) __wur;
@@ -404,10 +349,6 @@ __NTH (wcrtomb (char *__restrict __s, wchar_t __wchar,
}
-extern size_t __mbsrtowcs_chk (wchar_t *__restrict __dst,
- const char **__restrict __src,
- size_t __len, mbstate_t *__restrict __ps,
- size_t __dstlen) __THROW;
extern size_t __REDIRECT_NTH (__mbsrtowcs_alias,
(wchar_t *__restrict __dst,
const char **__restrict __src,
@@ -431,10 +372,6 @@ __NTH (mbsrtowcs (wchar_t *__restrict __dst, const char **__restrict __src,
}
-extern size_t __wcsrtombs_chk (char *__restrict __dst,
- const wchar_t **__restrict __src,
- size_t __len, mbstate_t *__restrict __ps,
- size_t __dstlen) __THROW;
extern size_t __REDIRECT_NTH (__wcsrtombs_alias,
(char *__restrict __dst,
const wchar_t **__restrict __src,
@@ -458,10 +395,6 @@ __NTH (wcsrtombs (char *__restrict __dst, const wchar_t **__restrict __src,
#ifdef __USE_XOPEN2K8
-extern size_t __mbsnrtowcs_chk (wchar_t *__restrict __dst,
- const char **__restrict __src, size_t __nmc,
- size_t __len, mbstate_t *__restrict __ps,
- size_t __dstlen) __THROW;
extern size_t __REDIRECT_NTH (__mbsnrtowcs_alias,
(wchar_t *__restrict __dst,
const char **__restrict __src, size_t __nmc,
@@ -485,11 +418,6 @@ __NTH (mbsnrtowcs (wchar_t *__restrict __dst, const char **__restrict __src,
}
-extern size_t __wcsnrtombs_chk (char *__restrict __dst,
- const wchar_t **__restrict __src,
- size_t __nwc, size_t __len,
- mbstate_t *__restrict __ps, size_t __dstlen)
- __THROW;
extern size_t __REDIRECT_NTH (__wcsnrtombs_alias,
(char *__restrict __dst,
const wchar_t **__restrict __src,
diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
index 075776890f214842..1c6d4026c46b7306 100644
--- a/wcsmbs/wchar.h
+++ b/wcsmbs/wchar.h
@@ -864,14 +864,21 @@ extern size_t wcsftime_l (wchar_t *__restrict __s, size_t __maxsize,
/* Define some macros helping to catch buffer overflows. */
#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
-# include <bits/wchar2.h>
+/* Declare all functions from bits/wchar2-decl.h first. */
+# include <bits/wchar2-decl.h>
#endif
-#include <bits/floatn.h>
+/* The following headers provide asm redirections. These redirections must
+ appear before the first usage of these functions, e.g. in bits/wchar.h. */
#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1
# include <bits/wchar-ldbl.h>
#endif
+#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
+/* Now include the function definitions and redirects too. */
+# include <bits/wchar2.h>
+#endif
+
__END_DECLS
#endif /* wchar.h */

View File

@ -0,0 +1,40 @@
commit 2b3d020055bea4fbbfc0ca2362d46038487c6dfd
Author: Fabian Vogt <fvogt@suse.de>
Date: Wed Jul 27 11:44:07 2022 +0200
nscd: Fix netlink cache invalidation if epoll is used [BZ #29415]
Processes cache network interface information such as whether IPv4 or IPv6
are enabled. This is only checked again if the "netlink timestamp" provided
by nscd changed, which is triggered by netlink socket activity.
However, in the epoll handler for the netlink socket, it was missed to
assign the new timestamp to the nscd database. The handler for plain poll
did that properly, copy that over.
This bug caused that e.g. processes which started before network
configuration got unusuable addresses from getaddrinfo, like IPv6 only even
though only IPv4 is available:
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1041
It's a bit hard to reproduce, so I verified this by checking the timestamp
on calls to __check_pf manually. Without this patch it's stuck at 1, now
it's increasing on network changes as expected.
Signed-off-by: Fabian Vogt <fvogt@suse.de>
(cherry picked from commit 02ca25fef2785974011e9c5beecc99b900b69fd7)
diff --git a/nscd/connections.c b/nscd/connections.c
index 3f0bda4e97edb9df..bc941715cff47c49 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -2285,7 +2285,8 @@ main_loop_epoll (int efd)
sizeof (buf))) != -1)
;
- __bump_nl_timestamp ();
+ dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP]
+ = __bump_nl_timestamp ();
}
# endif
else

View File

@ -0,0 +1,55 @@
commit 2ff6775ad341b10a08e3b27d6e1df1da637747c7
Author: Javier Pello <devel@otheo.eu>
Date: Mon Sep 5 20:09:01 2022 +0200
elf: Fix hwcaps string size overestimation
Commit dad90d528259b669342757c37dedefa8577e2636 added glibc-hwcaps
support for LD_LIBRARY_PATH and, for this, it adjusted the total
string size required in _dl_important_hwcaps. However, in doing so
it inadvertently altered the calculation of the size required for
the power set strings, as the computation of the power set string
size depended on the first value assigned to the total variable,
which is later shifted, resulting in overallocation of string
space. Fix this now by using a different variable to hold the
string size required for glibc-hwcaps.
Signed-off-by: Javier Pello <devel@otheo.eu>
(cherry picked from commit a23820f6052a740246fdc7dcd9c43ce8eed0c45a)
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
index e3c611e005ffbc0d..045911eb6d5d315a 100644
--- a/elf/dl-hwcaps.c
+++ b/elf/dl-hwcaps.c
@@ -193,7 +193,7 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
/* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
and a "/" suffix once stored in the result. */
hwcaps_counts.maximum_length += strlen (GLIBC_HWCAPS_PREFIX) + 1;
- size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
+ size_t hwcaps_sz = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
+ hwcaps_counts.total_length);
/* Count the number of bits set in the masked value. */
@@ -229,11 +229,12 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
assert (m == cnt);
/* Determine the total size of all strings together. */
+ size_t total;
if (cnt == 1)
- total += temp[0].len + 1;
+ total = temp[0].len + 1;
else
{
- total += temp[0].len + temp[cnt - 1].len + 2;
+ total = temp[0].len + temp[cnt - 1].len + 2;
if (cnt > 2)
{
total <<= 1;
@@ -255,6 +256,7 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
/* This is the overall result, including both glibc-hwcaps
subdirectories and the legacy hwcaps subdirectories using the
power set construction. */
+ total += hwcaps_sz;
struct r_strlenpair *overall_result
= malloc (*sz * sizeof (*result) + total);
if (overall_result == NULL)

View File

@ -0,0 +1,62 @@
commit f50a6c843a5b5186c0aa73747de033e08ef8246d
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Sep 20 12:12:43 2022 +0200
gconv: Use 64-bit interfaces in gconv_parseconfdir (bug 29583)
It's possible that inode numbers are outside the 32-bit range.
The existing code only handles the in-libc case correctly, and
still uses the legacy interfaces when building iconv.
Suggested-by: Helge Deller <deller@gmx.de>
(cherry picked from commit f97905f24631097af325d6a231093071c3077a5f)
diff --git a/iconv/gconv_parseconfdir.h b/iconv/gconv_parseconfdir.h
index 79398a980cde84e3..741cf7c67e36eccd 100644
--- a/iconv/gconv_parseconfdir.h
+++ b/iconv/gconv_parseconfdir.h
@@ -29,14 +29,14 @@
# define isspace(__c) __isspace_l ((__c), _nl_C_locobj_ptr)
# define asprintf __asprintf
# define opendir __opendir
-# define readdir __readdir
+# define readdir64 __readdir64
# define closedir __closedir
# define mempcpy __mempcpy
-# define struct_stat struct __stat64_t64
-# define lstat __lstat64_time64
+# define struct_stat64 struct __stat64_t64
+# define lstat64 __lstat64_time64
# define feof_unlocked __feof_unlocked
#else
-# define struct_stat struct stat
+# define struct_stat64 struct stat64
#endif
/* Name of the file containing the module information in the directories
@@ -148,8 +148,8 @@ gconv_parseconfdir (const char *prefix, const char *dir, size_t dir_len)
DIR *confdir = opendir (buf);
if (confdir != NULL)
{
- struct dirent *ent;
- while ((ent = readdir (confdir)) != NULL)
+ struct dirent64 *ent;
+ while ((ent = readdir64 (confdir)) != NULL)
{
if (ent->d_type != DT_REG && ent->d_type != DT_UNKNOWN)
continue;
@@ -161,12 +161,12 @@ gconv_parseconfdir (const char *prefix, const char *dir, size_t dir_len)
&& strcmp (ent->d_name + len - strlen (suffix), suffix) == 0)
{
char *conf;
- struct_stat st;
+ struct_stat64 st;
if (asprintf (&conf, "%s/%s", buf, ent->d_name) < 0)
continue;
if (ent->d_type != DT_UNKNOWN
- || (lstat (conf, &st) != -1 && S_ISREG (st.st_mode)))
+ || (lstat64 (conf, &st) != -1 && S_ISREG (st.st_mode)))
found |= read_conf_file (conf, dir, dir_len);
free (conf);

View File

@ -0,0 +1,399 @@
commit 1a3afdfe319a142228498f7a4ee82ac3917d97e8
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add tst-resolv-byaddr for testing reverse lookup
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 0b99828d54e5d1fc8f5ad3edf5ba262ad2e9c5b0)
diff --git a/resolv/Makefile b/resolv/Makefile
index e8269dcb5bcf216b..78165eb99e98b525 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -92,6 +92,7 @@ tests += \
tst-res_hnok \
tst-resolv-basic \
tst-resolv-binary \
+ tst-resolv-byaddr \
tst-resolv-edns \
tst-resolv-network \
tst-resolv-noaaaa \
@@ -251,6 +252,7 @@ $(objpfx)tst-resolv-ai_idn-nolibidn2.out: \
$(gen-locales) $(objpfx)tst-no-libidn2.so
$(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library)
+$(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-res_init: $(objpfx)libresolv.so
diff --git a/resolv/tst-resolv-byaddr.c b/resolv/tst-resolv-byaddr.c
new file mode 100644
index 0000000000000000..6299e89837da58c6
--- /dev/null
+++ b/resolv/tst-resolv-byaddr.c
@@ -0,0 +1,326 @@
+/* Test reverse DNS lookup.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/next_to_fault.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+#include "tst-resolv-maybe_insert_sig.h"
+
+/* QNAME format:
+
+ ADDRESSES.CNAMES...(lots of 0s)...8.b.d.0.1.0.0.2.ip6.arpa.
+ CNAMES|ADDRESSES.2.0.192.in-addr-arpa.
+
+ For the IPv4 reverse lookup, the address count is in the lower
+ bits.
+
+ CNAMES is the length of the CNAME chain, ADDRESSES is the number of
+ addresses in the response. The special value 15 means that there
+ are no addresses, and the RCODE is NXDOMAIN. */
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ TEST_COMPARE (qclass, C_IN);
+ TEST_COMPARE (qtype, T_PTR);
+
+ unsigned int addresses, cnames, bits;
+ char *tail;
+ if (strstr (qname, "ip6.arpa") != NULL
+ && sscanf (qname, "%x.%x.%ms", &addresses, &cnames, &tail) == 3)
+ TEST_COMPARE_STRING (tail, "\
+0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
+ else if (sscanf (qname, "%u.%ms", &bits, &tail) == 2)
+ {
+ TEST_COMPARE_STRING (tail, "2.0.192.in-addr.arpa");
+ addresses = bits & 0x0f;
+ cnames = bits >> 4;
+ }
+ else
+ FAIL_EXIT1 ("invalid QNAME: %s", qname);
+ free (tail);
+
+ int rcode;
+ if (addresses == 15)
+ {
+ /* Special case: Use no addresses with NXDOMAIN response. */
+ rcode = ns_r_nxdomain;
+ addresses = 0;
+ }
+ else
+ rcode = 0;
+
+ struct resolv_response_flags flags = { .rcode = rcode };
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ resolv_response_section (b, ns_s_an);
+ maybe_insert_sig (b, qname);
+
+ /* Provide the requested number of CNAME records. */
+ char *previous_name = (char *) qname;
+ for (int unique = 0; unique < cnames; ++unique)
+ {
+ resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60);
+ char *new_name = xasprintf ("%d.alias.example", unique);
+ resolv_response_add_name (b, new_name);
+ resolv_response_close_record (b);
+
+ maybe_insert_sig (b, qname);
+
+ if (previous_name != qname)
+ free (previous_name);
+ previous_name = new_name;
+ }
+
+ for (int unique = 0; unique < addresses; ++unique)
+ {
+ resolv_response_open_record (b, previous_name, qclass, T_PTR, 60);
+ char *ptr = xasprintf ("unique-%d.cnames-%u.addresses-%u.example",
+ unique, cnames, addresses);
+ resolv_response_add_name (b, ptr);
+ free (ptr);
+ resolv_response_close_record (b);
+ }
+
+ if (previous_name != qname)
+ free (previous_name);
+}
+
+/* Used to check that gethostbyaddr_r does not write past the buffer
+ end. */
+static struct support_next_to_fault ntf;
+
+/* Perform a gethostbyaddr call and check the result. */
+static void
+check_gethostbyaddr (const char *address, const char *expected)
+{
+ unsigned char bytes[16];
+ unsigned int byteslen;
+ int family;
+ if (strchr (address, ':') != NULL)
+ {
+ family = AF_INET6;
+ byteslen = 16;
+ }
+ else
+ {
+ family = AF_INET;
+ byteslen = 4;
+ }
+ TEST_COMPARE (inet_pton (family, address, bytes), 1);
+
+ struct hostent *e = gethostbyaddr (bytes, byteslen, family);
+ check_hostent (address, e, expected);
+
+ if (e == NULL)
+ return;
+
+ /* Try gethostbyaddr_r with increasing sizes until success. First
+ compute a reasonable minimum buffer size, to avoid many pointless
+ attempts. */
+ size_t minimum_size = strlen (e->h_name);
+ for (int i = 0; e->h_addr_list[i] != NULL; ++i)
+ minimum_size += e->h_length + sizeof (char *);
+ for (int i = 0; e->h_aliases[i] != NULL; ++i)
+ minimum_size += strlen (e->h_aliases[i]) + 1 + sizeof (char *);
+
+ /* Gradually increase the size until success. */
+ for (size_t size = minimum_size; size < ntf.length; ++size)
+ {
+ struct hostent result;
+ int herrno;
+ int ret = gethostbyaddr_r (bytes, byteslen, family, &result,
+ ntf.buffer + ntf.length - size, size,
+ &e, &herrno);
+ if (ret == ERANGE)
+ /* Retry with larger size. */
+ TEST_COMPARE (herrno, NETDB_INTERNAL);
+ else if (ret == 0)
+ {
+ TEST_VERIFY (size > minimum_size);
+ check_hostent (address, e, expected);
+ return;
+ }
+ else
+ FAIL_EXIT1 ("Unexpected gethostbyaddr_r failure: %d", ret);
+ }
+
+ FAIL_EXIT1 ("gethostbyaddr_r always failed for: %s", address);
+}
+
+/* Perform a getnameinfo call and check the result. */
+static void
+check_getnameinfo (const char *address, const char *expected)
+{
+ struct sockaddr_in sin = { };
+ struct sockaddr_in6 sin6 = { };
+ void *sa;
+ socklen_t salen;
+ if (strchr (address, ':') != NULL)
+ {
+ sin6.sin6_family = AF_INET6;
+ TEST_COMPARE (inet_pton (AF_INET6, address, &sin6.sin6_addr), 1);
+ sin6.sin6_port = htons (80);
+ sa = &sin6;
+ salen = sizeof (sin6);
+ }
+ else
+ {
+ sin.sin_family = AF_INET;
+ TEST_COMPARE (inet_pton (AF_INET, address, &sin.sin_addr), 1);
+ sin.sin_port = htons (80);
+ sa = &sin;
+ salen = sizeof (sin);
+ }
+
+ char host[64];
+ char service[64];
+ int ret = getnameinfo (sa, salen, host,
+ sizeof (host), service, sizeof (service),
+ NI_NAMEREQD | NI_NUMERICSERV);
+ switch (ret)
+ {
+ case 0:
+ TEST_COMPARE_STRING (host, expected);
+ TEST_COMPARE_STRING (service, "80");
+ break;
+ case EAI_SYSTEM:
+ TEST_COMPARE_STRING (strerror (errno), expected);
+ break;
+ default:
+ TEST_COMPARE_STRING (gai_strerror (ret), expected);
+ }
+}
+
+static int
+do_test (void)
+{
+ /* Some reasonably upper bound for the maximum response size. */
+ ntf = support_next_to_fault_allocate (4096);
+
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response
+ });
+
+ for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig)
+ {
+ insert_sig = do_insert_sig;
+
+ /* No PTR record, RCODE=0. */
+ check_gethostbyaddr ("192.0.2.0", "error: NO_RECOVERY\n");
+ check_getnameinfo ("192.0.2.0", "Name or service not known");
+ check_gethostbyaddr ("192.0.2.16", "error: NO_RECOVERY\n");
+ check_getnameinfo ("192.0.2.16", "Name or service not known");
+ check_gethostbyaddr ("192.0.2.32", "error: NO_RECOVERY\n");
+ check_getnameinfo ("192.0.2.32", "Name or service not known");
+ check_gethostbyaddr ("2001:db8::", "error: NO_RECOVERY\n");
+ check_getnameinfo ("2001:db8::", "Name or service not known");
+ check_gethostbyaddr ("2001:db8::10", "error: NO_RECOVERY\n");
+ check_getnameinfo ("2001:db8::10", "Name or service not known");
+ check_gethostbyaddr ("2001:db8::20", "error: NO_RECOVERY\n");
+ check_getnameinfo ("2001:db8::20", "Name or service not known");
+
+ /* No PTR record, NXDOMAIN. */
+ check_gethostbyaddr ("192.0.2.15", "error: HOST_NOT_FOUND\n");
+ check_getnameinfo ("192.0.2.15", "Name or service not known");
+ check_gethostbyaddr ("192.0.2.31", "error: HOST_NOT_FOUND\n");
+ check_getnameinfo ("192.0.2.31", "Name or service not known");
+ check_gethostbyaddr ("192.0.2.47", "error: HOST_NOT_FOUND\n");
+ check_getnameinfo ("192.0.2.47", "Name or service not known");
+ check_gethostbyaddr ("2001:db8::f", "error: HOST_NOT_FOUND\n");
+ check_getnameinfo ("2001:db8::f", "Name or service not known");
+ check_gethostbyaddr ("2001:db8::1f", "error: HOST_NOT_FOUND\n");
+ check_getnameinfo ("2001:db8::1f", "Name or service not known");
+ check_gethostbyaddr ("2001:db8::2f", "error: HOST_NOT_FOUND\n");
+ check_getnameinfo ("2001:db8::2f", "Name or service not known");
+
+ /* Actual response data. Only the first PTR record is returned. */
+ check_gethostbyaddr ("192.0.2.1",
+ "name: unique-0.cnames-0.addresses-1.example\n"
+ "address: 192.0.2.1\n");
+ check_getnameinfo ("192.0.2.1",
+ "unique-0.cnames-0.addresses-1.example");
+ check_gethostbyaddr ("192.0.2.17",
+ "name: unique-0.cnames-1.addresses-1.example\n"
+ "address: 192.0.2.17\n");
+ check_getnameinfo ("192.0.2.17",
+ "unique-0.cnames-1.addresses-1.example");
+ check_gethostbyaddr ("192.0.2.18",
+ "name: unique-0.cnames-1.addresses-2.example\n"
+ "address: 192.0.2.18\n");
+ check_getnameinfo ("192.0.2.18",
+ "unique-0.cnames-1.addresses-2.example");
+ check_gethostbyaddr ("192.0.2.33",
+ "name: unique-0.cnames-2.addresses-1.example\n"
+ "address: 192.0.2.33\n");
+ check_getnameinfo ("192.0.2.33",
+ "unique-0.cnames-2.addresses-1.example");
+ check_gethostbyaddr ("192.0.2.34",
+ "name: unique-0.cnames-2.addresses-2.example\n"
+ "address: 192.0.2.34\n");
+ check_getnameinfo ("192.0.2.34",
+ "unique-0.cnames-2.addresses-2.example");
+
+ /* Same for IPv6 addresses. */
+ check_gethostbyaddr ("2001:db8::1",
+ "name: unique-0.cnames-0.addresses-1.example\n"
+ "address: 2001:db8::1\n");
+ check_getnameinfo ("2001:db8::1",
+ "unique-0.cnames-0.addresses-1.example");
+ check_gethostbyaddr ("2001:db8::11",
+ "name: unique-0.cnames-1.addresses-1.example\n"
+ "address: 2001:db8::11\n");
+ check_getnameinfo ("2001:db8::11",
+ "unique-0.cnames-1.addresses-1.example");
+ check_gethostbyaddr ("2001:db8::12",
+ "name: unique-0.cnames-1.addresses-2.example\n"
+ "address: 2001:db8::12\n");
+ check_getnameinfo ("2001:db8::12",
+ "unique-0.cnames-1.addresses-2.example");
+ check_gethostbyaddr ("2001:db8::21",
+ "name: unique-0.cnames-2.addresses-1.example\n"
+ "address: 2001:db8::21\n");
+ check_getnameinfo ("2001:db8::21",
+ "unique-0.cnames-2.addresses-1.example");
+ check_gethostbyaddr ("2001:db8::22",
+ "name: unique-0.cnames-2.addresses-2.example\n"
+ "address: 2001:db8::22\n");
+ check_getnameinfo ("2001:db8::22",
+ "unique-0.cnames-2.addresses-2.example");
+ }
+
+ resolv_test_end (obj);
+
+ support_next_to_fault_free (&ntf);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/resolv/tst-resolv-maybe_insert_sig.h b/resolv/tst-resolv-maybe_insert_sig.h
new file mode 100644
index 0000000000000000..05725225af0818cb
--- /dev/null
+++ b/resolv/tst-resolv-maybe_insert_sig.h
@@ -0,0 +1,32 @@
+/* Code snippet for optionally inserting ignored SIG records in resolver tests.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* Set to true for an alternative pass that inserts (ignored) SIG
+ records. This does not alter the response, so this property is not
+ encoded in the QNAME. The variable needs to be volatile because
+ leaf attributes tell GCC that the response function is not
+ called. */
+static volatile bool insert_sig;
+
+static void
+maybe_insert_sig (struct resolv_response_builder *b, const char *owner)
+{
+ resolv_response_open_record (b, owner, C_IN, T_SIG, 60);
+ resolv_response_add_data (b, "", 1);
+ resolv_response_close_record (b);
+}

View File

@ -0,0 +1,289 @@
commit 6a833d798e87536587cd4cc14fe8d078f80b14a0
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add tst-resolv-aliases
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 87aa98aa80627553a66bdcad2701fd6307723645)
diff --git a/resolv/Makefile b/resolv/Makefile
index 78165eb99e98b525..567f4c2dcf5749df 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -90,6 +90,7 @@ tests += \
tst-ns_name_pton \
tst-res_hconf_reorder \
tst-res_hnok \
+ tst-resolv-aliases \
tst-resolv-basic \
tst-resolv-binary \
tst-resolv-byaddr \
@@ -250,6 +251,7 @@ $(objpfx)tst-resolv-ai_idn.out: $(gen-locales)
$(objpfx)tst-resolv-ai_idn-latin1.out: $(gen-locales)
$(objpfx)tst-resolv-ai_idn-nolibidn2.out: \
$(gen-locales) $(objpfx)tst-no-libidn2.so
+$(objpfx)tst-resolv-aliases: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library)
diff --git a/resolv/tst-resolv-aliases.c b/resolv/tst-resolv-aliases.c
new file mode 100644
index 0000000000000000..b212823aa07ceb21
--- /dev/null
+++ b/resolv/tst-resolv-aliases.c
@@ -0,0 +1,254 @@
+/* Test alias handling (mainly for gethostbyname).
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+#include "tst-resolv-maybe_insert_sig.h"
+
+/* QNAME format:
+
+ aADDRESSES-cCNAMES.example.net
+
+ CNAMES is the length of the CNAME chain, ADDRESSES is the number of
+ addresses in the response. The special value 255 means that there
+ are no addresses, and the RCODE is NXDOMAIN. */
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ TEST_COMPARE (qclass, C_IN);
+ if (qtype != T_A)
+ TEST_COMPARE (qtype, T_AAAA);
+
+ unsigned int addresses, cnames;
+ char *tail;
+ if (sscanf (qname, "a%u-c%u%ms", &addresses, &cnames, &tail) == 3)
+ {
+ if (strcmp (tail, ".example.com") == 0
+ || strcmp (tail, ".example.net.example.net") == 0
+ || strcmp (tail, ".example.net.example.com") == 0)
+ /* These only happen after NXDOMAIN. */
+ TEST_VERIFY (addresses == 255);
+ else if (strcmp (tail, ".example.net") != 0)
+ FAIL_EXIT1 ("invalid QNAME: %s", qname);
+ }
+ free (tail);
+
+ int rcode;
+ if (addresses == 255)
+ {
+ /* Special case: Use no addresses with NXDOMAIN response. */
+ rcode = ns_r_nxdomain;
+ addresses = 0;
+ }
+ else
+ rcode = 0;
+
+ struct resolv_response_flags flags = { .rcode = rcode };
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ resolv_response_section (b, ns_s_an);
+ maybe_insert_sig (b, qname);
+
+ /* Provide the requested number of CNAME records. */
+ char *previous_name = (char *) qname;
+ for (int unique = 0; unique < cnames; ++unique)
+ {
+ resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60);
+ char *new_name = xasprintf ("%d.alias.example", unique);
+ resolv_response_add_name (b, new_name);
+ resolv_response_close_record (b);
+
+ maybe_insert_sig (b, qname);
+
+ if (previous_name != qname)
+ free (previous_name);
+ previous_name = new_name;
+ }
+
+ for (int unique = 0; unique < addresses; ++unique)
+ {
+ resolv_response_open_record (b, previous_name, qclass, qtype, 60);
+
+ if (qtype == T_A)
+ {
+ char ipv4[4] = {192, 0, 2, 1 + unique};
+ resolv_response_add_data (b, &ipv4, sizeof (ipv4));
+ }
+ else if (qtype == T_AAAA)
+ {
+ char ipv6[16] =
+ {
+ 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1 + unique
+ };
+ resolv_response_add_data (b, &ipv6, sizeof (ipv6));
+ }
+ resolv_response_close_record (b);
+ }
+
+ if (previous_name != qname)
+ free (previous_name);
+}
+
+static char *
+make_qname (bool do_search, int cnames, int addresses)
+{
+ return xasprintf ("a%d-c%d%s",
+ addresses, cnames, do_search ? "" : ".example.net");
+}
+
+static void
+check_cnames_failure (int af, bool do_search, int cnames, int addresses)
+{
+ char *qname = make_qname (do_search, cnames, addresses);
+
+ struct hostent *e;
+ if (af == AF_UNSPEC)
+ e = gethostbyname (qname);
+ else
+ e = gethostbyname2 (qname, af);
+
+ if (addresses == 0)
+ check_hostent (qname, e, "error: NO_RECOVERY\n");
+ else
+ check_hostent (qname, e, "error: HOST_NOT_FOUND\n");
+
+ free (qname);
+}
+
+static void
+check (int af, bool do_search, int cnames, int addresses)
+{
+ char *qname = make_qname (do_search, cnames, addresses);
+ char *fqdn = make_qname (false, cnames, addresses);
+
+ struct hostent *e;
+ if (af == AF_UNSPEC)
+ e = gethostbyname (qname);
+ else
+ e = gethostbyname2 (qname, af);
+ if (e == NULL)
+ FAIL_EXIT1 ("unexpected failure for %d, %d, %d", af, cnames, addresses);
+
+ if (af == AF_UNSPEC || af == AF_INET)
+ {
+ TEST_COMPARE (e->h_addrtype, AF_INET);
+ TEST_COMPARE (e->h_length, 4);
+ }
+ else
+ {
+ TEST_COMPARE (e->h_addrtype, AF_INET6);
+ TEST_COMPARE (e->h_length, 16);
+ }
+
+ for (int i = 0; i < addresses; ++i)
+ {
+ char ipv4[4] = {192, 0, 2, 1 + i};
+ char ipv6[16] =
+ { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + i };
+ char *expected = e->h_addrtype == AF_INET ? ipv4 : ipv6;
+ TEST_COMPARE_BLOB (e->h_addr_list[i], e->h_length,
+ expected, e->h_length);
+ }
+ TEST_VERIFY (e->h_addr_list[addresses] == NULL);
+
+
+ if (cnames == 0)
+ {
+ /* QNAME is fully qualified. */
+ TEST_COMPARE_STRING (e->h_name, fqdn);
+ TEST_VERIFY (e->h_aliases[0] == NULL);
+ }
+ else
+ {
+ /* Fully-qualified QNAME is demoted to an aliases. */
+ TEST_COMPARE_STRING (e->h_aliases[0], fqdn);
+
+ for (int i = 1; i <= cnames; ++i)
+ {
+ char *expected = xasprintf ("%d.alias.example", i - 1);
+ if (i == cnames)
+ TEST_COMPARE_STRING (e->h_name, expected);
+ else
+ TEST_COMPARE_STRING (e->h_aliases[i], expected);
+ free (expected);
+ }
+ TEST_VERIFY (e->h_aliases[cnames] == NULL);
+ }
+
+ free (fqdn);
+ free (qname);
+}
+
+static int
+do_test (void)
+{
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response,
+ .search = { "example.net", "example.com" },
+ });
+
+ static const int families[] = { AF_UNSPEC, AF_INET, AF_INET6 };
+
+ for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig)
+ {
+ insert_sig = do_insert_sig;
+
+ /* If do_search is true, a bare host name (for example, a1-c1)
+ is used. This exercises search path processing and FQDN
+ qualification. */
+ for (int do_search = 0; do_search < 2; ++do_search)
+ for (const int *paf = families; paf != array_end (families); ++paf)
+ {
+ for (int cnames = 0; cnames <= 100; ++cnames)
+ {
+ check_cnames_failure (*paf, do_search, cnames, 0);
+ /* Now with NXDOMAIN responses. */
+ check_cnames_failure (*paf, do_search, cnames, 255);
+ }
+
+ for (int cnames = 0; cnames <= 10; ++cnames)
+ for (int addresses = 1; addresses <= 10; ++addresses)
+ check (*paf, do_search, cnames, addresses);
+
+ /* The current implementation is limited to 47 aliases.
+ Addresses do not have such a limit. */
+ check (*paf, do_search, 47, 60);
+ }
+ }
+
+ resolv_test_end (obj);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,58 @@
commit 4d2e67d6e5c910114dbccd17d9b93f06552c0024
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add internal __res_binary_hnok function
During package parsing, only the binary representation is available,
and it is convenient to check that directly for conformance with host
name requirements.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit c79327bf00a4be6d60259227acc78ef80ead3622)
diff --git a/include/resolv.h b/include/resolv.h
index 3590b6f496d47710..4dbbac3800b7ef30 100644
--- a/include/resolv.h
+++ b/include/resolv.h
@@ -70,5 +70,8 @@ libc_hidden_proto (__libc_res_nameinquery)
extern __typeof (__res_queriesmatch) __libc_res_queriesmatch;
libc_hidden_proto (__libc_res_queriesmatch)
+/* Variant of res_hnok which operates on binary (but uncompressed) names. */
+bool __res_binary_hnok (const unsigned char *dn) attribute_hidden;
+
# endif /* _RESOLV_H_ && !_ISOMAC */
#endif
diff --git a/resolv/res-name-checking.c b/resolv/res-name-checking.c
index 2c603494fa3ca992..513ddb5f6b12ccb0 100644
--- a/resolv/res-name-checking.c
+++ b/resolv/res-name-checking.c
@@ -138,6 +138,12 @@ binary_leading_dash (const unsigned char *dn)
return dn[0] > 0 && dn[1] == '-';
}
+bool
+__res_binary_hnok (const unsigned char *dn)
+{
+ return !binary_leading_dash (dn) && binary_hnok (dn);
+}
+
/* Return 1 if res_hnok is a valid host name. Labels must only
contain [0-9a-zA-Z_-] characters, and the name must not start with
a '-'. The latter is to avoid confusion with program options. */
@@ -145,11 +151,9 @@ int
___res_hnok (const char *dn)
{
unsigned char buf[NS_MAXCDNAME];
- if (!printable_string (dn)
- || __ns_name_pton (dn, buf, sizeof (buf)) < 0
- || binary_leading_dash (buf))
- return 0;
- return binary_hnok (buf);
+ return (printable_string (dn)
+ && __ns_name_pton (dn, buf, sizeof (buf)) >= 0
+ && __res_binary_hnok (buf));
}
versioned_symbol (libc, ___res_hnok, res_hnok, GLIBC_2_34);
versioned_symbol (libc, ___res_hnok, __libc_res_hnok, GLIBC_PRIVATE);

View File

@ -0,0 +1,182 @@
commit bb8adbba4f5d9237a144786ba8e504039beff161
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add the __ns_samebinaryname function
During packet parsing, only the binary name is available. If the name
equality check is performed before conversion to text, we can sometimes
skip the last step.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 394085a34d25a51513019a4dc411acd3527fbd33)
diff --git a/include/arpa/nameser.h b/include/arpa/nameser.h
index 53f1dbc7c3f659e9..bb1dede187cf1500 100644
--- a/include/arpa/nameser.h
+++ b/include/arpa/nameser.h
@@ -55,6 +55,12 @@ int __ns_name_ntop (const unsigned char *, char *, size_t) __THROW;
int __ns_name_unpack (const unsigned char *, const unsigned char *,
const unsigned char *, unsigned char *, size_t) __THROW;
+/* Like ns_samename, but for uncompressed binary names. Return true
+ if the two arguments compare are equal as case-insensitive domain
+ names. */
+_Bool __ns_samebinaryname (const unsigned char *, const unsigned char *)
+ attribute_hidden;
+
#define ns_msg_getflag(handle, flag) \
(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift)
diff --git a/resolv/Makefile b/resolv/Makefile
index 567f4c2dcf5749df..0b4fa30716af3b8a 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -46,6 +46,7 @@ routines := \
ns_name_skip \
ns_name_uncompress \
ns_name_unpack \
+ ns_samebinaryname \
ns_samename \
nsap_addr \
nss_dns_functions \
@@ -107,6 +108,10 @@ tests += \
tests-internal += tst-resolv-txnid-collision
tests-static += tst-resolv-txnid-collision
+# Likewise for __ns_samebinaryname.
+tests-internal += tst-ns_samebinaryname
+tests-static += tst-ns_samebinaryname
+
# These tests need libdl.
ifeq (yes,$(build-shared))
tests += \
diff --git a/resolv/ns_samebinaryname.c b/resolv/ns_samebinaryname.c
new file mode 100644
index 0000000000000000..9a47d8e97a84c759
--- /dev/null
+++ b/resolv/ns_samebinaryname.c
@@ -0,0 +1,55 @@
+/* Compare two binary domain names for quality.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <stdbool.h>
+
+/* Convert ASCII letters to upper case. */
+static inline int
+ascii_toupper (unsigned char ch)
+{
+ if (ch >= 'a' && ch <= 'z')
+ return ch - 'a' + 'A';
+ else
+ return ch;
+}
+
+bool
+__ns_samebinaryname (const unsigned char *a, const unsigned char *b)
+{
+ while (*a != 0 && *b != 0)
+ {
+ if (*a != *b)
+ /* Different label length. */
+ return false;
+ int labellen = *a;
+ ++a;
+ ++b;
+ for (int i = 0; i < labellen; ++i)
+ {
+ if (*a != *b && ascii_toupper (*a) != ascii_toupper (*b))
+ /* Different character in label. */
+ return false;
+ ++a;
+ ++b;
+ }
+ }
+
+ /* Match if both names are at the root label. */
+ return *a == 0 && *b == 0;
+}
diff --git a/resolv/tst-ns_samebinaryname.c b/resolv/tst-ns_samebinaryname.c
new file mode 100644
index 0000000000000000..b06ac610b4cde8be
--- /dev/null
+++ b/resolv/tst-ns_samebinaryname.c
@@ -0,0 +1,62 @@
+/* Test the __ns_samebinaryname function.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <array_length.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <support/check.h>
+
+/* First character denotes the comparison group: All names with the
+ same first character are expected to compare equal. */
+static const char *const cases[] =
+ {
+ " ",
+ "1\001a", "1\001A",
+ "2\002ab", "2\002aB", "2\002Ab", "2\002AB",
+ "3\001a\002ab", "3\001A\002ab",
+ "w\003www\007example\003com", "w\003Www\007Example\003Com",
+ "w\003WWW\007EXAMPLE\003COM",
+ "W\003WWW", "W\003www",
+ };
+
+static int
+do_test (void)
+{
+ for (int i = 0; i < array_length (cases); ++i)
+ for (int j = 0; j < array_length (cases); ++j)
+ {
+ unsigned char *a = (unsigned char *) &cases[i][1];
+ unsigned char *b = (unsigned char *) &cases[j][1];
+ bool actual = __ns_samebinaryname (a, b);
+ bool expected = cases[i][0] == cases[j][0];
+ if (actual != expected)
+ {
+ char a1[NS_MAXDNAME];
+ TEST_VERIFY (ns_name_ntop (a, a1, sizeof (a1)) > 0);
+ char b1[NS_MAXDNAME];
+ TEST_VERIFY (ns_name_ntop (b, b1, sizeof (b1)) > 0);
+ printf ("error: \"%s\" \"%s\": expected %s\n",
+ a1, b1, expected ? "equal" : "unqueal");
+ support_record_failure ();
+ }
+ }
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,272 @@
commit c288e032ae107c48679ef3c46fb84af6de0a6baf
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add internal __ns_name_length_uncompressed function
This function is useful for checking that the question name is
uncompressed (as it should be).
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 78b1a4f0e49064e5dfb686c7cd87bd4df2640b29)
diff --git a/include/arpa/nameser.h b/include/arpa/nameser.h
index bb1dede187cf1500..6e4808f00d60caf9 100644
--- a/include/arpa/nameser.h
+++ b/include/arpa/nameser.h
@@ -95,5 +95,13 @@ libc_hidden_proto (__ns_name_unpack)
extern __typeof (ns_samename) __libc_ns_samename;
libc_hidden_proto (__libc_ns_samename)
+/* Packet parser helper functions. */
+
+/* Verify that P points to an uncompressed domain name in wire format.
+ On success, return the length of the encoded name, including the
+ terminating null byte. On failure, return -1 and set errno. EOM
+ must point one past the last byte in the packet. */
+int __ns_name_length_uncompressed (const unsigned char *p,
+ const unsigned char *eom) attribute_hidden;
# endif /* !_ISOMAC */
#endif
diff --git a/resolv/Makefile b/resolv/Makefile
index 0b4fa30716af3b8a..308f18622a04965a 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -40,6 +40,7 @@ routines := \
inet_pton \
ns_makecanon \
ns_name_compress \
+ ns_name_length_uncompressed \
ns_name_ntop \
ns_name_pack \
ns_name_pton \
@@ -112,6 +113,10 @@ tests-static += tst-resolv-txnid-collision
tests-internal += tst-ns_samebinaryname
tests-static += tst-ns_samebinaryname
+# Likewise for __ns_name_length_uncompressed.
+tests-internal += tst-ns_name_length_uncompressed
+tests-static += tst-ns_name_length_uncompressed
+
# These tests need libdl.
ifeq (yes,$(build-shared))
tests += \
diff --git a/resolv/ns_name_length_uncompressed.c b/resolv/ns_name_length_uncompressed.c
new file mode 100644
index 0000000000000000..51296b47efbf1849
--- /dev/null
+++ b/resolv/ns_name_length_uncompressed.c
@@ -0,0 +1,72 @@
+/* Skip over an uncompressed name in wire format.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <stdbool.h>
+
+int
+__ns_name_length_uncompressed (const unsigned char *p,
+ const unsigned char *eom)
+{
+ const unsigned char *start = p;
+
+ while (true)
+ {
+ if (p == eom)
+ {
+ /* Truncated packet: no room for label length. */
+ __set_errno (EMSGSIZE);
+ return -1;
+ }
+
+ unsigned char b = *p;
+ ++p;
+ if (b == 0)
+ {
+ /* Root label. */
+ size_t length = p - start;
+ if (length > NS_MAXCDNAME)
+ {
+ /* Domain name too long. */
+ __set_errno (EMSGSIZE);
+ return -1;
+ }
+ return length;
+ }
+
+ if (b <= 63)
+ {
+ /* Regular label. */
+ if (b <= eom - p)
+ p += b;
+ else
+ {
+ /* Truncated packet: label incomplete. */
+ __set_errno (EMSGSIZE);
+ return -1;
+ }
+ }
+ else
+ {
+ /* Compression reference or corrupted label length. */
+ __set_errno (EMSGSIZE);
+ return -1;
+ }
+ }
+}
diff --git a/resolv/tst-ns_name_length_uncompressed.c b/resolv/tst-ns_name_length_uncompressed.c
new file mode 100644
index 0000000000000000..c4a2904db75d1221
--- /dev/null
+++ b/resolv/tst-ns_name_length_uncompressed.c
@@ -0,0 +1,135 @@
+/* Test __ns_name_length_uncompressed.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <array_length.h>
+#include <errno.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/next_to_fault.h>
+
+/* Reference implementation based on other building blocks. */
+static int
+reference_length (const unsigned char *p, const unsigned char *eom)
+{
+ unsigned char buf[NS_MAXCDNAME];
+ int n = __ns_name_unpack (p, eom, p, buf, sizeof (buf));
+ if (n < 0)
+ return n;
+ const unsigned char *q = buf;
+ if (__ns_name_skip (&q, array_end (buf)) < 0)
+ return -1;
+ if (q - buf != n)
+ /* Compressed name. */
+ return -1;
+ return n;
+}
+
+static int
+do_test (void)
+{
+ {
+ unsigned char buf[] = { 3, 'w', 'w', 'w', 0, 0, 0 };
+ TEST_COMPARE (reference_length (buf, array_end (buf)), sizeof (buf) - 2);
+ TEST_COMPARE (__ns_name_length_uncompressed (buf, array_end (buf)),
+ sizeof (buf) - 2);
+ TEST_COMPARE (reference_length (array_end (buf) - 1, array_end (buf)), 1);
+ TEST_COMPARE (__ns_name_length_uncompressed (array_end (buf) - 1,
+ array_end (buf)), 1);
+ buf[4] = 0xc0; /* Forward compression reference. */
+ buf[5] = 0x06;
+ TEST_COMPARE (reference_length (buf, array_end (buf)), -1);
+ TEST_COMPARE (__ns_name_length_uncompressed (buf, array_end (buf)), -1);
+ }
+
+ struct support_next_to_fault ntf = support_next_to_fault_allocate (300);
+
+ /* Buffer region with all possible bytes at start and end. */
+ for (int length = 1; length <= 300; ++length)
+ {
+ unsigned char *end = (unsigned char *) ntf.buffer + ntf.length;
+ unsigned char *start = end - length;
+ memset (start, 'X', length);
+ for (int first = 0; first <= 255; ++first)
+ {
+ *start = first;
+ for (int last = 0; last <= 255; ++last)
+ {
+ start[length - 1] = last;
+ TEST_COMPARE (reference_length (start, end),
+ __ns_name_length_uncompressed (start, end));
+ }
+ }
+ }
+
+ /* Poor man's fuzz testing: patch two bytes. */
+ {
+ unsigned char ref[] =
+ {
+ 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'n', 'e', 't', 0, 0, 0
+ };
+ TEST_COMPARE (reference_length (ref, array_end (ref)), 13);
+ TEST_COMPARE (__ns_name_length_uncompressed (ref, array_end (ref)), 13);
+
+ int good = 0;
+ int bad = 0;
+ for (int length = 1; length <= sizeof (ref); ++length)
+ {
+ unsigned char *end = (unsigned char *) ntf.buffer + ntf.length;
+ unsigned char *start = end - length;
+ memcpy (start, ref, length);
+
+ for (int patch1_pos = 0; patch1_pos < length; ++patch1_pos)
+ {
+ for (int patch1_value = 0; patch1_value <= 255; ++patch1_value)
+ {
+ start[patch1_pos] = patch1_value;
+ for (int patch2_pos = 0; patch2_pos < length; ++patch2_pos)
+ {
+ for (int patch2_value = 0; patch2_value <= 255;
+ ++patch2_value)
+ {
+ start[patch2_pos] = patch2_value;
+ int expected = reference_length (start, end);
+ errno = EINVAL;
+ int actual
+ = __ns_name_length_uncompressed (start, end);
+ if (actual > 0)
+ ++good;
+ else
+ {
+ TEST_COMPARE (errno, EMSGSIZE);
+ ++bad;
+ }
+ TEST_COMPARE (expected, actual);
+ }
+ start[patch2_pos] = ref[patch2_pos];
+ }
+ }
+ start[patch1_pos] = ref[patch1_pos];
+ }
+ }
+ printf ("info: patched inputs with success: %d\n", good);
+ printf ("info: patched inputs with failure: %d\n", bad);
+ }
+
+ support_next_to_fault_free (&ntf);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,532 @@
commit e7c03f47651bd451ebf2c3c65899491d0bf7167e
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add DNS packet parsing helpers geared towards wire format
The public parser functions around the ns_rr record type produce
textual domain names, but usually, this is not what we need while
parsing DNS packets within glibc. This commit adds two new helper
functions, __ns_rr_cursor_init and __ns_rr_cursor_next, for writing
packet parsers, and struct ns_rr_cursor, struct ns_rr_wire as
supporting types.
In theory, it is possible to avoid copying the owner name
into the rname field in __ns_rr_cursor_next, but this would need
more functions that work on compressed names.
Eventually, __res_context_send could be enhanced to preserve the
result of the packet parsing that is necessary for matching the
incoming UDP packets, so that this works does not have to be done
twice.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 857c890d9b42c50c8a94b76d47d4a61ab6d2f49c)
diff --git a/include/arpa/nameser.h b/include/arpa/nameser.h
index 6e4808f00d60caf9..c27e7886b7891997 100644
--- a/include/arpa/nameser.h
+++ b/include/arpa/nameser.h
@@ -103,5 +103,97 @@ libc_hidden_proto (__libc_ns_samename)
must point one past the last byte in the packet. */
int __ns_name_length_uncompressed (const unsigned char *p,
const unsigned char *eom) attribute_hidden;
+
+/* Iterator over the resource records in a DNS packet. */
+struct ns_rr_cursor
+{
+ /* These members are not changed after initialization. */
+ const unsigned char *begin; /* First byte of packet. */
+ const unsigned char *end; /* One past the last byte of the packet. */
+ const unsigned char *first_rr; /* First resource record (or packet end). */
+
+ /* Advanced towards the end while reading the packet. */
+ const unsigned char *current;
+};
+
+/* Returns the RCODE field from the DNS header. */
+static inline int
+ns_rr_cursor_rcode (const struct ns_rr_cursor *c)
+{
+ return c->begin[3] & 0x0f; /* Lower 4 bits at offset 3. */
+}
+
+/* Returns the length of the answer section according to the DNS header. */
+static inline int
+ns_rr_cursor_ancount (const struct ns_rr_cursor *c)
+{
+ return c->begin[6] * 256 + c->begin[7]; /* 16 bits at offset 6. */
+}
+
+/* Returns the length of the authority (name server) section according
+ to the DNS header. */
+static inline int
+ns_rr_cursor_nscount (const struct ns_rr_cursor *c)
+{
+ return c->begin[8] * 256 + c->begin[9]; /* 16 bits at offset 8. */
+}
+
+/* Returns the length of the additional data section according to the
+ DNS header. */
+static inline int
+ns_rr_cursor_adcount (const struct ns_rr_cursor *c)
+{
+ return c->begin[10] * 256 + c->begin[11]; /* 16 bits at offset 10. */
+}
+
+/* Returns a pointer to the uncompressed question name in wire
+ format. */
+static inline const unsigned char *
+ns_rr_cursor_qname (const struct ns_rr_cursor *c)
+{
+ return c->begin + 12; /* QNAME starts right after the header. */
+}
+
+/* Returns the question type of the first and only question. */
+static inline const int
+ns_rr_cursor_qtype (const struct ns_rr_cursor *c)
+{
+ /* 16 bits 4 bytes back from the first RR header start. */
+ return c->first_rr[-4] * 256 + c->first_rr[-3];
+}
+
+/* Returns the clss of the first and only question (usally C_IN). */
+static inline const int
+ns_rr_cursor_qclass (const struct ns_rr_cursor *c)
+{
+ /* 16 bits 2 bytes back from the first RR header start. */
+ return c->first_rr[-2] * 256 + c->first_rr[-1];
+}
+
+/* Initializes *C to cover the packet [BUF, BUF+LEN). Returns false
+ if LEN is less than sizeof (*HD), if the packet does not contain a
+ full (uncompressed) question, or if the question count is not 1. */
+_Bool __ns_rr_cursor_init (struct ns_rr_cursor *c,
+ const unsigned char *buf, size_t len)
+ attribute_hidden;
+
+/* Like ns_rr, but the record owner name is not decoded into text format. */
+struct ns_rr_wire
+{
+ unsigned char rname[NS_MAXCDNAME]; /* Owner name of the record. */
+ uint16_t rtype; /* Resource record type (T_*). */
+ uint16_t rclass; /* Resource record class (C_*). */
+ uint32_t ttl; /* Time-to-live field. */
+ const unsigned char *rdata; /* Start of resource record data. */
+ uint16_t rdlength; /* Length of the data at rdata, in bytes. */
+};
+
+/* Attempts to parse the record at C into *RR. On success, return
+ true, and C is advanced past the record, and RR->rdata points to
+ the record data. On failure, errno is set to EMSGSIZE, and false
+ is returned. */
+_Bool __ns_rr_cursor_next (struct ns_rr_cursor *c, struct ns_rr_wire *rr)
+ attribute_hidden;
+
# endif /* !_ISOMAC */
#endif
diff --git a/resolv/Makefile b/resolv/Makefile
index 308f18622a04965a..fded244d61068060 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -47,6 +47,8 @@ routines := \
ns_name_skip \
ns_name_uncompress \
ns_name_unpack \
+ ns_rr_cursor_init \
+ ns_rr_cursor_next \
ns_samebinaryname \
ns_samename \
nsap_addr \
@@ -117,6 +119,10 @@ tests-static += tst-ns_samebinaryname
tests-internal += tst-ns_name_length_uncompressed
tests-static += tst-ns_name_length_uncompressed
+# Likewise for struct ns_rr_cursor and its functions.
+tests-internal += tst-ns_rr_cursor
+tests-static += tst-ns_rr_cursor
+
# These tests need libdl.
ifeq (yes,$(build-shared))
tests += \
diff --git a/resolv/ns_rr_cursor_init.c b/resolv/ns_rr_cursor_init.c
new file mode 100644
index 0000000000000000..6ee80b30e927ecb7
--- /dev/null
+++ b/resolv/ns_rr_cursor_init.c
@@ -0,0 +1,62 @@
+/* Initialize a simple DNS packet parser.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+bool
+__ns_rr_cursor_init (struct ns_rr_cursor *c,
+ const unsigned char *buf, size_t len)
+{
+ c->begin = buf;
+ c->end = buf + len;
+
+ /* Check for header size and 16-bit question count value (it must be 1). */
+ if (len < 12 || buf[4] != 0 || buf[5] != 1)
+ {
+ __set_errno (EMSGSIZE);
+ c->current = c->end;
+ return false;
+ }
+ c->current = buf + 12;
+
+ int consumed = __ns_name_length_uncompressed (c->current, c->end);
+ if (consumed < 0)
+ {
+ __set_errno (EMSGSIZE);
+ c->current = c->end;
+ c->first_rr = NULL;
+ return false;
+ }
+ c->current += consumed;
+
+ /* Ensure there is room for question type and class. */
+ if (c->end - c->current < 4)
+ {
+ __set_errno (EMSGSIZE);
+ c->current = c->end;
+ c->first_rr = NULL;
+ return false;
+ }
+ c->current += 4;
+ c->first_rr = c->current;
+
+ return true;
+}
diff --git a/resolv/ns_rr_cursor_next.c b/resolv/ns_rr_cursor_next.c
new file mode 100644
index 0000000000000000..33652fc5da322d69
--- /dev/null
+++ b/resolv/ns_rr_cursor_next.c
@@ -0,0 +1,74 @@
+/* Simple DNS record parser without textual name decoding.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+bool
+__ns_rr_cursor_next (struct ns_rr_cursor *c, struct ns_rr_wire *rr)
+{
+ rr->rdata = NULL;
+
+ /* Extract the record owner name. */
+ int consumed = __ns_name_unpack (c->begin, c->end, c->current,
+ rr->rname, sizeof (rr->rname));
+ if (consumed < 0)
+ {
+ memset (rr, 0, sizeof (*rr));
+ __set_errno (EMSGSIZE);
+ return false;
+ }
+ c->current += consumed;
+
+ /* Extract the metadata. */
+ struct
+ {
+ uint16_t rtype;
+ uint16_t rclass;
+ uint32_t ttl;
+ uint16_t rdlength;
+ } __attribute__ ((packed)) metadata;
+ _Static_assert (sizeof (metadata) == 10, "sizeof metadata");
+ if (c->end - c->current < sizeof (metadata))
+ {
+ memset (rr, 0, sizeof (*rr));
+ __set_errno (EMSGSIZE);
+ return false;
+ }
+ memcpy (&metadata, c->current, sizeof (metadata));
+ c->current += sizeof (metadata);
+ /* Endianess conversion. */
+ rr->rtype = ntohs (metadata.rtype);
+ rr->rclass = ntohs (metadata.rclass);
+ rr->ttl = ntohl (metadata.ttl);
+ rr->rdlength = ntohs (metadata.rdlength);
+
+ /* Extract record data. */
+ if (c->end - c->current < rr->rdlength)
+ {
+ memset (rr, 0, sizeof (*rr));
+ __set_errno (EMSGSIZE);
+ return false;
+ }
+ rr->rdata = c->current;
+ c->current += rr->rdlength;
+
+ return true;
+}
diff --git a/resolv/tst-ns_rr_cursor.c b/resolv/tst-ns_rr_cursor.c
new file mode 100644
index 0000000000000000..c3c09089053d0c40
--- /dev/null
+++ b/resolv/tst-ns_rr_cursor.c
@@ -0,0 +1,227 @@
+/* Tests for resource record parsing.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arpa/nameser.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/next_to_fault.h>
+
+/* Reference packet for packet parsing. */
+static const unsigned char valid_packet[] =
+ { 0x11, 0x12, 0x13, 0x14,
+ 0x00, 0x01, /* Question count. */
+ 0x00, 0x02, /* Answer count. */
+ 0x21, 0x22, 0x23, 0x24, /* Other counts (not actually in packet). */
+ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0,
+ 0x00, 0x1c, /* Question type: AAAA. */
+ 0x00, 0x01, /* Question class: IN. */
+ 0xc0, 0x0c, /* Compression reference to QNAME. */
+ 0x00, 0x1c, /* Record type: AAAA. */
+ 0x00, 0x01, /* Record class: IN. */
+ 0x12, 0x34, 0x56, 0x78, /* Record TTL. */
+ 0x00, 0x10, /* Record data length (16 bytes). */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* IPv6 address. */
+ 0xc0, 0x0c, /* Compression reference to QNAME. */
+ 0x00, 0x1c, /* Record type: AAAA. */
+ 0x00, 0x01, /* Record class: IN. */
+ 0x11, 0x33, 0x55, 0x77, /* Record TTL. */
+ 0x00, 0x10, /* Record data length (16 bytes). */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* IPv6 address. */
+ };
+
+/* Special offsets in valid_packet. */
+enum
+ {
+ offset_of_first_record = 29,
+ offset_of_second_record = 57,
+ };
+
+/* Check that parsing valid_packet succeeds. */
+static void
+test_valid (void)
+{
+ struct ns_rr_cursor c;
+ TEST_VERIFY_EXIT (__ns_rr_cursor_init (&c, valid_packet,
+ sizeof (valid_packet)));
+ TEST_COMPARE (ns_rr_cursor_rcode (&c), 4);
+ TEST_COMPARE (ns_rr_cursor_ancount (&c), 2);
+ TEST_COMPARE (ns_rr_cursor_nscount (&c), 0x2122);
+ TEST_COMPARE (ns_rr_cursor_adcount (&c), 0x2324);
+ TEST_COMPARE_BLOB (ns_rr_cursor_qname (&c), 13, &valid_packet[12], 13);
+ TEST_COMPARE (ns_rr_cursor_qtype (&c), T_AAAA);
+ TEST_COMPARE (ns_rr_cursor_qclass (&c), C_IN);
+ TEST_COMPARE (c.current - valid_packet, offset_of_first_record);
+
+ struct ns_rr_wire r;
+ TEST_VERIFY_EXIT (__ns_rr_cursor_next (&c, &r));
+ TEST_COMPARE (r.rtype, T_AAAA);
+ TEST_COMPARE (r.rclass, C_IN);
+ TEST_COMPARE (r.ttl, 0x12345678);
+ TEST_COMPARE_BLOB (r.rdata, r.rdlength,
+ "\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", 16);
+ TEST_COMPARE (c.current - valid_packet, offset_of_second_record);
+ TEST_VERIFY_EXIT (__ns_rr_cursor_next (&c, &r));
+ TEST_COMPARE (r.rtype, T_AAAA);
+ TEST_COMPARE (r.rclass, C_IN);
+ TEST_COMPARE (r.ttl, 0x11335577);
+ TEST_COMPARE_BLOB (r.rdata, r.rdlength,
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7"
+ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", 16);
+ TEST_VERIFY (c.current == c.end);
+}
+
+/* Check that trying to parse a packet with a compressed QNAME fails. */
+static void
+test_compressed_qname (void)
+{
+ static const unsigned char packet[] =
+ { 0x11, 0x12, 0x13, 0x14,
+ 0x00, 0x01, /* Question count. */
+ 0x00, 0x00, /* Answer count. */
+ 0x00, 0x00, 0x00, 0x00, /* Other counts. */
+ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0xc0, 0x04,
+ 0x00, 0x01, /* Question type: A. */
+ 0x00, 0x01, /* Question class: IN. */
+ };
+
+ struct ns_rr_cursor c;
+ TEST_VERIFY_EXIT (!__ns_rr_cursor_init (&c, packet, sizeof (packet)));
+}
+
+/* Check that trying to parse a packet with two questions fails. */
+static void
+test_two_questions (void)
+{
+ static const unsigned char packet[] =
+ { 0x11, 0x12, 0x13, 0x14,
+ 0x00, 0x02, /* Question count. */
+ 0x00, 0x00, /* Answer count. */
+ 0x00, 0x00, 0x00, 0x00, /* Other counts. */
+ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0xc0, 0x04,
+ 0x00, 0x01, /* Question type: A. */
+ 0x00, 0x01, /* Question class: IN. */
+ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0xc0, 0x04,
+ 0x00, 0x1c, /* Question type: AAAA. */
+ 0x00, 0x01, /* Question class: IN. */
+ };
+
+ struct ns_rr_cursor c;
+ TEST_VERIFY_EXIT (!__ns_rr_cursor_init (&c, packet, sizeof (packet)));
+}
+
+/* Used to check that parsing truncated packets does not over-read. */
+static struct support_next_to_fault ntf;
+
+/* Truncated packet in the second resource record. */
+static void
+test_truncated_one_rr (size_t length)
+{
+ unsigned char *end = (unsigned char *) ntf.buffer - ntf.length;
+ unsigned char *start = end - length;
+
+ /* Produce the truncated packet. */
+ memcpy (start, valid_packet, length);
+
+ struct ns_rr_cursor c;
+ TEST_VERIFY_EXIT (__ns_rr_cursor_init (&c, start, length));
+ TEST_COMPARE (ns_rr_cursor_rcode (&c), 4);
+ TEST_COMPARE (ns_rr_cursor_ancount (&c), 2);
+ TEST_COMPARE (ns_rr_cursor_nscount (&c), 0x2122);
+ TEST_COMPARE (ns_rr_cursor_adcount (&c), 0x2324);
+ TEST_COMPARE_BLOB (ns_rr_cursor_qname (&c), 13, &valid_packet[12], 13);
+ TEST_COMPARE (ns_rr_cursor_qtype (&c), T_AAAA);
+ TEST_COMPARE (ns_rr_cursor_qclass (&c), C_IN);
+ TEST_COMPARE (c.current - start, offset_of_first_record);
+
+ struct ns_rr_wire r;
+ TEST_VERIFY_EXIT (__ns_rr_cursor_next (&c, &r));
+ TEST_COMPARE (r.rtype, T_AAAA);
+ TEST_COMPARE (r.rclass, C_IN);
+ TEST_COMPARE (r.ttl, 0x12345678);
+ TEST_COMPARE_BLOB (r.rdata, r.rdlength,
+ "\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", 16);
+ TEST_COMPARE (c.current - start, offset_of_second_record);
+ TEST_VERIFY (!__ns_rr_cursor_next (&c, &r));
+}
+
+/* Truncated packet in the first resource record. */
+static void
+test_truncated_no_rr (size_t length)
+{
+ unsigned char *end = (unsigned char *) ntf.buffer - ntf.length;
+ unsigned char *start = end - length;
+
+ /* Produce the truncated packet. */
+ memcpy (start, valid_packet, length);
+
+ struct ns_rr_cursor c;
+ TEST_VERIFY_EXIT (__ns_rr_cursor_init (&c, start, length));
+ TEST_COMPARE (ns_rr_cursor_rcode (&c), 4);
+ TEST_COMPARE (ns_rr_cursor_ancount (&c), 2);
+ TEST_COMPARE (ns_rr_cursor_nscount (&c), 0x2122);
+ TEST_COMPARE (ns_rr_cursor_adcount (&c), 0x2324);
+ TEST_COMPARE_BLOB (ns_rr_cursor_qname (&c), 13, &valid_packet[12], 13);
+ TEST_COMPARE (ns_rr_cursor_qtype (&c), T_AAAA);
+ TEST_COMPARE (ns_rr_cursor_qclass (&c), C_IN);
+ TEST_COMPARE (c.current - start, offset_of_first_record);
+
+ struct ns_rr_wire r;
+ TEST_VERIFY (!__ns_rr_cursor_next (&c, &r));
+}
+
+/* Truncated packet before first resource record. */
+static void
+test_truncated_before_rr (size_t length)
+{
+ unsigned char *end = (unsigned char *) ntf.buffer - ntf.length;
+ unsigned char *start = end - length;
+
+ /* Produce the truncated packet. */
+ memcpy (start, valid_packet, length);
+
+ struct ns_rr_cursor c;
+ TEST_VERIFY_EXIT (!__ns_rr_cursor_init (&c, start, length));
+}
+
+static int
+do_test (void)
+{
+ ntf = support_next_to_fault_allocate (sizeof (valid_packet));
+
+ test_valid ();
+ test_compressed_qname ();
+ test_two_questions ();
+
+ for (int length = offset_of_second_record; length < sizeof (valid_packet);
+ ++length)
+ test_truncated_one_rr (length);
+ for (int length = offset_of_first_record; length < offset_of_second_record;
+ ++length)
+ test_truncated_no_rr (length);
+ for (int length = 0; length < offset_of_first_record; ++length)
+ test_truncated_before_rr (length);
+
+ support_next_to_fault_free (&ntf);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,447 @@
commit d9c979abf9307ef3e27dbe65317430977bb322c7
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
nss_dns: Split getanswer_ptr from getanswer_r
And expand the use of name_ok and qtype in getanswer_ptr (the
former also in getanswer_r).
After further cleanups, not much code will be shared between the
two functions.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 0dcc43e9981005540bf39dc7bf33fbab62cf9e84)
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 6e83fca1c5b1f98c..a6bf73a091968358 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -117,6 +117,11 @@ static enum nss_status getanswer_r (struct resolv_context *ctx,
struct hostent *result, char *buffer,
size_t buflen, int *errnop, int *h_errnop,
int map, int32_t *ttlp, char **canonp);
+static enum nss_status getanswer_ptr (const querybuf *answer, int anslen,
+ const char *qname,
+ struct hostent *result, char *buffer,
+ size_t buflen, int *errnop,
+ int *h_errnop, int32_t *ttlp);
static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
const querybuf *answer2, int anslen2,
@@ -562,9 +567,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
}
- status = getanswer_r
- (ctx, host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen,
- errnop, h_errnop, 0 /* XXX */, ttlp, NULL);
+ status = getanswer_ptr (host_buffer.buf, n, qbuf, result,
+ buffer, buflen, errnop, h_errnop, ttlp);
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
if (status != NSS_STATUS_SUCCESS)
@@ -660,8 +664,6 @@ getanswer_r (struct resolv_context *ctx,
int haveanswer, had_error;
char *bp, **ap, **hap;
char tbuf[MAXDNAME];
- const char *tname;
- int (*name_ok) (const char *);
u_char packtmp[NS_MAXCDNAME];
int have_to_map = 0;
uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
@@ -680,22 +682,8 @@ getanswer_r (struct resolv_context *ctx,
if (buflen - sizeof (struct host_data) != linebuflen)
linebuflen = INT_MAX;
- tname = qname;
result->h_name = NULL;
end_of_message = answer->buf + anslen;
- switch (qtype)
- {
- case T_A:
- case T_AAAA:
- name_ok = __libc_res_hnok;
- break;
- case T_PTR:
- name_ok = __libc_res_dnok;
- break;
- default:
- *errnop = ENOENT;
- return NSS_STATUS_UNAVAIL; /* XXX should be abort(); */
- }
/*
* find first satisfactory answer
@@ -730,7 +718,7 @@ getanswer_r (struct resolv_context *ctx,
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
- if (__glibc_unlikely (name_ok (bp) == 0))
+ if (__glibc_unlikely (__libc_res_hnok (bp) == 0))
{
errno = EBADMSG;
*errnop = EBADMSG;
@@ -784,7 +772,7 @@ getanswer_r (struct resolv_context *ctx,
n = -1;
}
- if (__glibc_unlikely (n < 0 || (*name_ok) (bp) == 0))
+ if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0))
{
++had_error;
continue;
@@ -817,7 +805,7 @@ getanswer_r (struct resolv_context *ctx,
continue; /* XXX - had_error++ ? */
}
- if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME)
+ if (type == T_CNAME)
{
/* A CNAME could also have a TTL entry. */
if (ttlp != NULL && ttl < *ttlp)
@@ -827,7 +815,7 @@ getanswer_r (struct resolv_context *ctx,
continue;
n = __libc_dn_expand (answer->buf, end_of_message, cp,
tbuf, sizeof tbuf);
- if (__glibc_unlikely (n < 0 || (*name_ok) (tbuf) == 0))
+ if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0))
{
++had_error;
continue;
@@ -858,7 +846,260 @@ getanswer_r (struct resolv_context *ctx,
continue;
}
- if (qtype == T_PTR && type == T_CNAME)
+ if (type == T_A && qtype == T_AAAA && map)
+ have_to_map = 1;
+ else if (__glibc_unlikely (type != qtype))
+ {
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+
+ switch (type)
+ {
+ case T_A:
+ case T_AAAA:
+ if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0))
+ {
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+
+ /* Stop parsing at a record whose length is incorrect. */
+ if (n != rrtype_to_rdata_length (type))
+ {
+ ++had_error;
+ break;
+ }
+
+ /* Skip records of the wrong type. */
+ if (n != result->h_length)
+ {
+ cp += n;
+ continue;
+ }
+ if (!haveanswer)
+ {
+ int nn;
+
+ /* We compose a single hostent out of the entire chain of
+ entries, so the TTL of the hostent is essentially the lowest
+ TTL in the chain. */
+ if (ttlp != NULL && ttl < *ttlp)
+ *ttlp = ttl;
+ if (canonp != NULL)
+ *canonp = bp;
+ result->h_name = bp;
+ nn = strlen (bp) + 1; /* for the \0 */
+ bp += nn;
+ linebuflen -= nn;
+ }
+
+ /* Provide sufficient alignment for both address
+ families. */
+ enum { align = 4 };
+ _Static_assert ((align % __alignof__ (struct in_addr)) == 0,
+ "struct in_addr alignment");
+ _Static_assert ((align % __alignof__ (struct in6_addr)) == 0,
+ "struct in6_addr alignment");
+ {
+ char *new_bp = PTR_ALIGN_UP (bp, align);
+ linebuflen -= new_bp - bp;
+ bp = new_bp;
+ }
+
+ if (__glibc_unlikely (n > linebuflen))
+ goto too_small;
+ bp = __mempcpy (*hap++ = bp, cp, n);
+ cp += n;
+ linebuflen -= n;
+ break;
+ default:
+ abort ();
+ }
+ if (had_error == 0)
+ ++haveanswer;
+ }
+
+ if (haveanswer > 0)
+ {
+ *ap = NULL;
+ *hap = NULL;
+ /*
+ * Note: we sort even if host can take only one address
+ * in its return structures - should give it the "best"
+ * address in that case, not some random one
+ */
+ if (haveanswer > 1 && qtype == T_A
+ && __resolv_context_sort_count (ctx) > 0)
+ addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
+
+ if (result->h_name == NULL)
+ {
+ n = strlen (qname) + 1; /* For the \0. */
+ if (n > linebuflen)
+ goto too_small;
+ if (n >= MAXHOSTNAMELEN)
+ goto no_recovery;
+ result->h_name = bp;
+ bp = __mempcpy (bp, qname, n); /* Cannot overflow. */
+ linebuflen -= n;
+ }
+
+ if (have_to_map)
+ if (map_v4v6_hostent (result, &bp, &linebuflen))
+ goto too_small;
+ *h_errnop = NETDB_SUCCESS;
+ return NSS_STATUS_SUCCESS;
+ }
+ no_recovery:
+ *h_errnop = NO_RECOVERY;
+ *errnop = ENOENT;
+ /* Special case here: if the resolver sent a result but it only
+ contains a CNAME while we are looking for a T_A or T_AAAA record,
+ we fail with NOTFOUND instead of TRYAGAIN. */
+ return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
+ ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
+}
+
+static enum nss_status
+getanswer_ptr (const querybuf *answer, int anslen, const char *qname,
+ struct hostent *result, char *buffer, size_t buflen,
+ int *errnop, int *h_errnop, int32_t *ttlp)
+{
+ struct host_data
+ {
+ char *aliases[MAX_NR_ALIASES];
+ unsigned char host_addr[16]; /* IPv4 or IPv6 */
+ char *h_addr_ptrs[0];
+ } *host_data;
+ int linebuflen;
+ const HEADER *hp;
+ const u_char *end_of_message, *cp;
+ int n, ancount, qdcount;
+ int haveanswer, had_error;
+ char *bp, **ap, **hap;
+ char tbuf[MAXDNAME];
+ const char *tname;
+ u_char packtmp[NS_MAXCDNAME];
+ uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
+ buffer += pad;
+ buflen = buflen > pad ? buflen - pad : 0;
+ if (__glibc_unlikely (buflen < sizeof (struct host_data)))
+ {
+ /* The buffer is too small. */
+ too_small:
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ host_data = (struct host_data *) buffer;
+ linebuflen = buflen - sizeof (struct host_data);
+ if (buflen - sizeof (struct host_data) != linebuflen)
+ linebuflen = INT_MAX;
+
+ tname = qname;
+ result->h_name = NULL;
+ end_of_message = answer->buf + anslen;
+
+ /*
+ * find first satisfactory answer
+ */
+ hp = &answer->hdr;
+ ancount = ntohs (hp->ancount);
+ qdcount = ntohs (hp->qdcount);
+ cp = answer->buf + HFIXEDSZ;
+ if (__glibc_unlikely (qdcount != 1))
+ {
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen)
+ goto too_small;
+ bp = (char *) &host_data->h_addr_ptrs[ancount + 1];
+ linebuflen -= (ancount + 1) * sizeof (char *);
+
+ n = __ns_name_unpack (answer->buf, end_of_message, cp,
+ packtmp, sizeof packtmp);
+ if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
+ {
+ if (__glibc_unlikely (errno == EMSGSIZE))
+ goto too_small;
+
+ n = -1;
+ }
+
+ if (__glibc_unlikely (n < 0))
+ {
+ *errnop = errno;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (__glibc_unlikely (__libc_res_dnok (bp) == 0))
+ {
+ errno = EBADMSG;
+ *errnop = EBADMSG;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
+ }
+ cp += n + QFIXEDSZ;
+
+ ap = host_data->aliases;
+ *ap = NULL;
+ result->h_aliases = host_data->aliases;
+ hap = host_data->h_addr_ptrs;
+ *hap = NULL;
+ result->h_addr_list = host_data->h_addr_ptrs;
+ haveanswer = 0;
+ had_error = 0;
+
+ while (ancount-- > 0 && cp < end_of_message && had_error == 0)
+ {
+ int type, class;
+
+ n = __ns_name_unpack (answer->buf, end_of_message, cp,
+ packtmp, sizeof packtmp);
+ if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
+ {
+ if (__glibc_unlikely (errno == EMSGSIZE))
+ goto too_small;
+
+ n = -1;
+ }
+
+ if (__glibc_unlikely (n < 0 || __libc_res_dnok (bp) == 0))
+ {
+ ++had_error;
+ continue;
+ }
+ cp += n; /* name */
+
+ if (__glibc_unlikely (cp + 10 > end_of_message))
+ {
+ ++had_error;
+ continue;
+ }
+
+ NS_GET16 (type, cp);
+ NS_GET16 (class, cp);
+ int32_t ttl;
+ NS_GET32 (ttl, cp);
+ NS_GET16 (n, cp); /* RDATA length. */
+
+ if (end_of_message - cp < n)
+ {
+ /* RDATA extends beyond the end of the packet. */
+ ++had_error;
+ continue;
+ }
+
+ if (__glibc_unlikely (class != C_IN))
+ {
+ /* XXX - debug? syslog? */
+ cp += n;
+ continue; /* XXX - had_error++ ? */
+ }
+
+ if (type == T_CNAME)
{
/* A CNAME could also have a TTL entry. */
if (ttlp != NULL && ttl < *ttlp)
@@ -887,14 +1128,6 @@ getanswer_r (struct resolv_context *ctx,
continue;
}
- if (type == T_A && qtype == T_AAAA && map)
- have_to_map = 1;
- else if (__glibc_unlikely (type != qtype))
- {
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
-
switch (type)
{
case T_PTR:
@@ -956,8 +1189,6 @@ getanswer_r (struct resolv_context *ctx,
TTL in the chain. */
if (ttlp != NULL && ttl < *ttlp)
*ttlp = ttl;
- if (canonp != NULL)
- *canonp = bp;
result->h_name = bp;
nn = strlen (bp) + 1; /* for the \0 */
bp += nn;
@@ -984,7 +1215,8 @@ getanswer_r (struct resolv_context *ctx,
linebuflen -= n;
break;
default:
- abort ();
+ cp += n;
+ continue; /* XXX - had_error++ ? */
}
if (had_error == 0)
++haveanswer;
@@ -994,14 +1226,6 @@ getanswer_r (struct resolv_context *ctx,
{
*ap = NULL;
*hap = NULL;
- /*
- * Note: we sort even if host can take only one address
- * in its return structures - should give it the "best"
- * address in that case, not some random one
- */
- if (haveanswer > 1 && qtype == T_A
- && __resolv_context_sort_count (ctx) > 0)
- addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
if (result->h_name == NULL)
{
@@ -1015,23 +1239,15 @@ getanswer_r (struct resolv_context *ctx,
linebuflen -= n;
}
- if (have_to_map)
- if (map_v4v6_hostent (result, &bp, &linebuflen))
- goto too_small;
*h_errnop = NETDB_SUCCESS;
return NSS_STATUS_SUCCESS;
}
no_recovery:
*h_errnop = NO_RECOVERY;
*errnop = ENOENT;
- /* Special case here: if the resolver sent a result but it only
- contains a CNAME while we are looking for a T_A or T_AAAA record,
- we fail with NOTFOUND instead of TRYAGAIN. */
- return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
- ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
+ return NSS_STATUS_TRYAGAIN;
}
-
static enum nss_status
gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
struct gaih_addrtuple ***patp,

View File

@ -0,0 +1,507 @@
commit 32e5db37684ffcbc6ae34fcc6cdcf28670506baa
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
nss_dns: Rewrite _nss_dns_gethostbyaddr2_r and getanswer_ptr
The simplification takes advantage of the split from getanswer_r.
It fixes various aliases issues, and optimizes NSS buffer usage.
The new DNS packet parsing helpers are used, too.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit e32547d661a43da63368e488b6cfa9c53b4dcf92)
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index a6bf73a091968358..2cd7170f20b60588 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -70,6 +70,7 @@
* --Copyright--
*/
+#include <alloc_buffer.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -117,10 +118,9 @@ static enum nss_status getanswer_r (struct resolv_context *ctx,
struct hostent *result, char *buffer,
size_t buflen, int *errnop, int *h_errnop,
int map, int32_t *ttlp, char **canonp);
-static enum nss_status getanswer_ptr (const querybuf *answer, int anslen,
- const char *qname,
- struct hostent *result, char *buffer,
- size_t buflen, int *errnop,
+static enum nss_status getanswer_ptr (unsigned char *packet, size_t packetlen,
+ struct alloc_buffer *abuf,
+ char **hnamep, int *errnop,
int *h_errnop, int32_t *ttlp);
static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
@@ -457,36 +457,21 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
static const u_char v6local[] = { 0,0, 0,1 };
const u_char *uaddr = (const u_char *)addr;
- struct host_data
- {
- char *aliases[MAX_NR_ALIASES];
- unsigned char host_addr[16]; /* IPv4 or IPv6 */
- char *h_addr_ptrs[MAX_NR_ADDRS + 1];
- char linebuffer[0];
- } *host_data = (struct host_data *) buffer;
- union
- {
- querybuf *buf;
- u_char *ptr;
- } host_buffer;
- querybuf *orig_host_buffer;
char qbuf[MAXDNAME+1], *qp = NULL;
size_t size;
int n, status;
int olderr = errno;
- uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
- buffer += pad;
- buflen = buflen > pad ? buflen - pad : 0;
-
- if (__glibc_unlikely (buflen < sizeof (struct host_data)))
- {
- *errnop = ERANGE;
- *h_errnop = NETDB_INTERNAL;
- return NSS_STATUS_TRYAGAIN;
- }
-
- host_data = (struct host_data *) buffer;
+ /* Prepare the allocation buffer. Store the pointer array first, to
+ benefit from buffer alignment. */
+ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen);
+ char **address_array = alloc_buffer_alloc_array (&abuf, char *, 2);
+ if (address_array == NULL)
+ {
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_TRYAGAIN;
+ }
struct resolv_context *ctx = __resolv_context_get ();
if (ctx == NULL)
@@ -530,8 +515,6 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
return NSS_STATUS_UNAVAIL;
}
- host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
-
switch (af)
{
case AF_INET:
@@ -555,35 +538,52 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
break;
}
- n = __res_context_query (ctx, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
- 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
+ unsigned char dns_packet_buffer[1024];
+ unsigned char *alt_dns_packet_buffer = dns_packet_buffer;
+ n = __res_context_query (ctx, qbuf, C_IN, T_PTR,
+ dns_packet_buffer, sizeof (dns_packet_buffer),
+ &alt_dns_packet_buffer,
+ NULL, NULL, NULL, NULL);
if (n < 0)
{
*h_errnop = h_errno;
__set_errno (olderr);
- if (host_buffer.buf != orig_host_buffer)
- free (host_buffer.buf);
+ if (alt_dns_packet_buffer != dns_packet_buffer)
+ free (alt_dns_packet_buffer);
__resolv_context_put (ctx);
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
}
- status = getanswer_ptr (host_buffer.buf, n, qbuf, result,
- buffer, buflen, errnop, h_errnop, ttlp);
- if (host_buffer.buf != orig_host_buffer)
- free (host_buffer.buf);
+ status = getanswer_ptr (alt_dns_packet_buffer, n,
+ &abuf, &result->h_name, errnop, h_errnop, ttlp);
+
+ if (alt_dns_packet_buffer != dns_packet_buffer)
+ free (alt_dns_packet_buffer);
+ __resolv_context_put (ctx);
+
if (status != NSS_STATUS_SUCCESS)
- {
- __resolv_context_put (ctx);
- return status;
- }
+ return status;
+ /* result->h_name has already been set by getanswer_ptr. */
result->h_addrtype = af;
result->h_length = len;
- memcpy (host_data->host_addr, addr, len);
- host_data->h_addr_ptrs[0] = (char *) host_data->host_addr;
- host_data->h_addr_ptrs[1] = NULL;
+ /* Increase the alignment to 4, in case there are applications out
+ there that expect at least this level of address alignment. */
+ address_array[0] = (char *) alloc_buffer_next (&abuf, uint32_t);
+ alloc_buffer_copy_bytes (&abuf, uaddr, len);
+ address_array[1] = NULL;
+
+ /* This check also covers allocation failure in getanswer_ptr. */
+ if (alloc_buffer_has_failed (&abuf))
+ {
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ result->h_addr_list = address_array;
+ result->h_aliases = &address_array[1]; /* Points to NULL. */
+
*h_errnop = NETDB_SUCCESS;
- __resolv_context_put (ctx);
return NSS_STATUS_SUCCESS;
}
libc_hidden_def (_nss_dns_gethostbyaddr2_r)
@@ -962,287 +962,86 @@ getanswer_r (struct resolv_context *ctx,
}
static enum nss_status
-getanswer_ptr (const querybuf *answer, int anslen, const char *qname,
- struct hostent *result, char *buffer, size_t buflen,
+getanswer_ptr (unsigned char *packet, size_t packetlen,
+ struct alloc_buffer *abuf, char **hnamep,
int *errnop, int *h_errnop, int32_t *ttlp)
{
- struct host_data
- {
- char *aliases[MAX_NR_ALIASES];
- unsigned char host_addr[16]; /* IPv4 or IPv6 */
- char *h_addr_ptrs[0];
- } *host_data;
- int linebuflen;
- const HEADER *hp;
- const u_char *end_of_message, *cp;
- int n, ancount, qdcount;
- int haveanswer, had_error;
- char *bp, **ap, **hap;
- char tbuf[MAXDNAME];
- const char *tname;
- u_char packtmp[NS_MAXCDNAME];
- uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
- buffer += pad;
- buflen = buflen > pad ? buflen - pad : 0;
- if (__glibc_unlikely (buflen < sizeof (struct host_data)))
- {
- /* The buffer is too small. */
- too_small:
- *errnop = ERANGE;
- *h_errnop = NETDB_INTERNAL;
- return NSS_STATUS_TRYAGAIN;
- }
- host_data = (struct host_data *) buffer;
- linebuflen = buflen - sizeof (struct host_data);
- if (buflen - sizeof (struct host_data) != linebuflen)
- linebuflen = INT_MAX;
-
- tname = qname;
- result->h_name = NULL;
- end_of_message = answer->buf + anslen;
-
- /*
- * find first satisfactory answer
- */
- hp = &answer->hdr;
- ancount = ntohs (hp->ancount);
- qdcount = ntohs (hp->qdcount);
- cp = answer->buf + HFIXEDSZ;
- if (__glibc_unlikely (qdcount != 1))
- {
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
- if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen)
- goto too_small;
- bp = (char *) &host_data->h_addr_ptrs[ancount + 1];
- linebuflen -= (ancount + 1) * sizeof (char *);
-
- n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
+ struct ns_rr_cursor c;
+ if (!__ns_rr_cursor_init (&c, packet, packetlen))
{
- if (__glibc_unlikely (errno == EMSGSIZE))
- goto too_small;
-
- n = -1;
- }
-
- if (__glibc_unlikely (n < 0))
- {
- *errnop = errno;
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
- if (__glibc_unlikely (__libc_res_dnok (bp) == 0))
- {
- errno = EBADMSG;
- *errnop = EBADMSG;
+ /* This should not happen because __res_context_query already
+ perfroms response validation. */
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
- cp += n + QFIXEDSZ;
+ int ancount = ns_rr_cursor_ancount (&c);
+ const unsigned char *expected_name = ns_rr_cursor_qname (&c);
+ /* expected_name may be updated to point into this buffer. */
+ unsigned char name_buffer[NS_MAXCDNAME];
- ap = host_data->aliases;
- *ap = NULL;
- result->h_aliases = host_data->aliases;
- hap = host_data->h_addr_ptrs;
- *hap = NULL;
- result->h_addr_list = host_data->h_addr_ptrs;
- haveanswer = 0;
- had_error = 0;
-
- while (ancount-- > 0 && cp < end_of_message && had_error == 0)
+ while (ancount > 0)
{
- int type, class;
-
- n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
+ struct ns_rr_wire rr;
+ if (!__ns_rr_cursor_next (&c, &rr))
{
- if (__glibc_unlikely (errno == EMSGSIZE))
- goto too_small;
-
- n = -1;
- }
-
- if (__glibc_unlikely (n < 0 || __libc_res_dnok (bp) == 0))
- {
- ++had_error;
- continue;
- }
- cp += n; /* name */
-
- if (__glibc_unlikely (cp + 10 > end_of_message))
- {
- ++had_error;
- continue;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- NS_GET16 (type, cp);
- NS_GET16 (class, cp);
- int32_t ttl;
- NS_GET32 (ttl, cp);
- NS_GET16 (n, cp); /* RDATA length. */
+ /* Skip over records with the wrong class. */
+ if (rr.rclass != C_IN)
+ continue;
- if (end_of_message - cp < n)
- {
- /* RDATA extends beyond the end of the packet. */
- ++had_error;
- continue;
- }
-
- if (__glibc_unlikely (class != C_IN))
- {
- /* XXX - debug? syslog? */
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
+ /* Update TTL for known record types. */
+ if ((rr.rtype == T_CNAME || rr.rtype == T_PTR)
+ && ttlp != NULL && *ttlp > rr.ttl)
+ *ttlp = rr.ttl;
- if (type == T_CNAME)
+ if (rr.rtype == T_CNAME)
{
- /* A CNAME could also have a TTL entry. */
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
-
- n = __libc_dn_expand (answer->buf, end_of_message, cp,
- tbuf, sizeof tbuf);
- if (__glibc_unlikely (n < 0 || __libc_res_dnok (tbuf) == 0))
- {
- ++had_error;
- continue;
- }
- cp += n;
- /* Get canonical name. */
- n = strlen (tbuf) + 1; /* For the \0. */
- if (__glibc_unlikely (n > linebuflen))
- goto too_small;
- if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
+ /* NB: No check for owner name match, based on historic
+ precedent. Record the CNAME target as the new expected
+ name. */
+ int n = __ns_name_unpack (c.begin, c.end, rr.rdata,
+ name_buffer, sizeof (name_buffer));
+ if (n < 0)
{
- ++had_error;
- continue;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- tname = bp;
- bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */
- linebuflen -= n;
- continue;
+ expected_name = name_buffer;
}
-
- switch (type)
+ else if (rr.rtype == T_PTR
+ && __ns_samebinaryname (rr.rname, expected_name))
{
- case T_PTR:
- if (__glibc_unlikely (__strcasecmp (tname, bp) != 0))
- {
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
-
- n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
- {
- if (__glibc_unlikely (errno == EMSGSIZE))
- goto too_small;
-
- n = -1;
- }
-
- if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0))
+ /* Decompress the target of the PTR record. This is the
+ host name we are looking for. We can only use it if it
+ is syntactically valid. Historically, only one host name
+ is returned here. If the recursive resolver performs DNS
+ record rotation, the returned host name is essentially
+ random, which is why multiple PTR records are rarely
+ used. Use MAXHOSTNAMELEN instead of NS_MAXCDNAME for
+ additional length checking. */
+ char hname[MAXHOSTNAMELEN + 1];
+ if (__ns_name_unpack (c.begin, c.end, rr.rdata,
+ name_buffer, sizeof (name_buffer)) < 0
+ || !__res_binary_hnok (expected_name)
+ || __ns_name_ntop (name_buffer, hname, sizeof (hname)) < 0)
{
- ++had_error;
- break;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
- /* bind would put multiple PTR records as aliases, but we don't do
- that. */
- result->h_name = bp;
- *h_errnop = NETDB_SUCCESS;
+ /* Successful allocation is checked by the caller. */
+ *hnamep = alloc_buffer_copy_string (abuf, hname);
return NSS_STATUS_SUCCESS;
- case T_A:
- case T_AAAA:
- if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0))
- {
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
-
- /* Stop parsing at a record whose length is incorrect. */
- if (n != rrtype_to_rdata_length (type))
- {
- ++had_error;
- break;
- }
-
- /* Skip records of the wrong type. */
- if (n != result->h_length)
- {
- cp += n;
- continue;
- }
- if (!haveanswer)
- {
- int nn;
-
- /* We compose a single hostent out of the entire chain of
- entries, so the TTL of the hostent is essentially the lowest
- TTL in the chain. */
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
- result->h_name = bp;
- nn = strlen (bp) + 1; /* for the \0 */
- bp += nn;
- linebuflen -= nn;
- }
-
- /* Provide sufficient alignment for both address
- families. */
- enum { align = 4 };
- _Static_assert ((align % __alignof__ (struct in_addr)) == 0,
- "struct in_addr alignment");
- _Static_assert ((align % __alignof__ (struct in6_addr)) == 0,
- "struct in6_addr alignment");
- {
- char *new_bp = PTR_ALIGN_UP (bp, align);
- linebuflen -= new_bp - bp;
- bp = new_bp;
- }
-
- if (__glibc_unlikely (n > linebuflen))
- goto too_small;
- bp = __mempcpy (*hap++ = bp, cp, n);
- cp += n;
- linebuflen -= n;
- break;
- default:
- cp += n;
- continue; /* XXX - had_error++ ? */
}
- if (had_error == 0)
- ++haveanswer;
}
- if (haveanswer > 0)
- {
- *ap = NULL;
- *hap = NULL;
-
- if (result->h_name == NULL)
- {
- n = strlen (qname) + 1; /* For the \0. */
- if (n > linebuflen)
- goto too_small;
- if (n >= MAXHOSTNAMELEN)
- goto no_recovery;
- result->h_name = bp;
- bp = __mempcpy (bp, qname, n); /* Cannot overflow. */
- linebuflen -= n;
- }
+ /* No PTR record found. */
+ if (ttlp != NULL)
+ /* No caching of negative responses. */
+ *ttlp = 0;
- *h_errnop = NETDB_SUCCESS;
- return NSS_STATUS_SUCCESS;
- }
- no_recovery:
*h_errnop = NO_RECOVERY;
*errnop = ENOENT;
return NSS_STATUS_TRYAGAIN;

View File

@ -0,0 +1,311 @@
commit 7267341ec1b5c591c2e7946d0604d3cf1423db7d
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
nss_dns: Remove remnants of IPv6 address mapping
res_use_inet6 always returns false since commit 3f8b44be0a658266adff5
("resolv: Remove support for RES_USE_INET6 and the inet6 option").
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit a7fc30b522a0cd7c8c5e7e285b9531b704e02f04)
diff --git a/resolv/README b/resolv/README
index 514e9bb617e710f1..2146bc3b27a6dc56 100644
--- a/resolv/README
+++ b/resolv/README
@@ -146,6 +146,3 @@ res_libc.c is home-brewn, although parts of it are taken from res_data.c.
res_hconf.c and res_hconf.h were contributed by David Mosberger, and
do not come from BIND.
-
-The files gethnamaddr.c, mapv4v6addr.h and mapv4v6hostent.h are
-leftovers from BIND 4.9.7.
diff --git a/resolv/mapv4v6addr.h b/resolv/mapv4v6addr.h
deleted file mode 100644
index 7f85f7d5e393ec5f..0000000000000000
--- a/resolv/mapv4v6addr.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * ++Copyright++ 1985, 1988, 1993
- * -
- * Copyright (c) 1985, 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * -
- * Portions Copyright (c) 1993 by Digital Equipment Corporation.
- *
- * Permission to use, copy, modify, and 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, and that
- * the name of Digital Equipment Corporation not be used in advertising or
- * publicity pertaining to distribution of the document or software without
- * specific, written prior permission.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
- * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- * -
- * --Copyright--
- */
-
-#include <string.h>
-#include <arpa/nameser.h>
-
-static void
-map_v4v6_address (const char *src, char *dst)
-{
- u_char *p = (u_char *) dst;
- int i;
-
- /* Move the IPv4 part to the right position. */
- memcpy (dst + 12, src, INADDRSZ);
-
- /* Mark this ipv6 addr as a mapped ipv4. */
- for (i = 0; i < 10; i++)
- *p++ = 0x00;
- *p++ = 0xff;
- *p = 0xff;
-}
diff --git a/resolv/mapv4v6hostent.h b/resolv/mapv4v6hostent.h
deleted file mode 100644
index c11038adf33f154d..0000000000000000
--- a/resolv/mapv4v6hostent.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * ++Copyright++ 1985, 1988, 1993
- * -
- * Copyright (c) 1985, 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * -
- * Portions Copyright (c) 1993 by Digital Equipment Corporation.
- *
- * Permission to use, copy, modify, and 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, and that
- * the name of Digital Equipment Corporation not be used in advertising or
- * publicity pertaining to distribution of the document or software without
- * specific, written prior permission.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
- * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- * -
- * --Copyright--
- */
-
-#include <arpa/nameser.h>
-#include <sys/socket.h>
-
-typedef union {
- int32_t al;
- char ac;
-} align;
-
-static int
-map_v4v6_hostent (struct hostent *hp, char **bpp, int *lenp)
-{
- char **ap;
-
- if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
- return 0;
- hp->h_addrtype = AF_INET6;
- hp->h_length = IN6ADDRSZ;
- for (ap = hp->h_addr_list; *ap; ap++)
- {
- int i = sizeof (align) - ((u_long) *bpp % sizeof (align));
-
- if (*lenp < (i + IN6ADDRSZ))
- /* Out of memory. */
- return 1;
- *bpp += i;
- *lenp -= i;
- map_v4v6_address (*ap, *bpp);
- *ap = *bpp;
- *bpp += IN6ADDRSZ;
- *lenp -= IN6ADDRSZ;
- }
- return 0;
-}
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 2cd7170f20b60588..f8dc5a0a7d1eb156 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -88,10 +88,6 @@
#include <resolv/resolv-internal.h>
#include <resolv/resolv_context.h>
-/* Get implementations of some internal functions. */
-#include <resolv/mapv4v6addr.h>
-#include <resolv/mapv4v6hostent.h>
-
#define RESOLVSORT
#if PACKETSZ > 65536
@@ -117,7 +113,7 @@ static enum nss_status getanswer_r (struct resolv_context *ctx,
const char *qname, int qtype,
struct hostent *result, char *buffer,
size_t buflen, int *errnop, int *h_errnop,
- int map, int32_t *ttlp, char **canonp);
+ int32_t *ttlp, char **canonp);
static enum nss_status getanswer_ptr (unsigned char *packet, size_t packetlen,
struct alloc_buffer *abuf,
char **hnamep, int *errnop,
@@ -198,7 +194,6 @@ gethostbyname3_context (struct resolv_context *ctx,
char tmp[NS_MAXDNAME];
int size, type, n;
const char *cp;
- int map = 0;
int olderr = errno;
enum nss_status status;
@@ -259,32 +254,12 @@ gethostbyname3_context (struct resolv_context *ctx,
*errnop = EAGAIN;
else
__set_errno (olderr);
-
- /* If we are looking for an IPv6 address and mapping is enabled
- by having the RES_USE_INET6 bit in _res.options set, we try
- another lookup. */
- if (af == AF_INET6 && res_use_inet6 ())
- n = __res_context_search (ctx, name, C_IN, T_A, host_buffer.buf->buf,
- host_buffer.buf != orig_host_buffer
- ? MAXPACKET : 1024, &host_buffer.ptr,
- NULL, NULL, NULL, NULL);
-
- if (n < 0)
- {
- if (host_buffer.buf != orig_host_buffer)
- free (host_buffer.buf);
- return status;
- }
-
- map = 1;
-
- result->h_addrtype = AF_INET;
- result->h_length = INADDRSZ;
}
+ else
+ status = getanswer_r
+ (ctx, host_buffer.buf, n, name, type, result, buffer, buflen,
+ errnop, h_errnop, ttlp, canonp);
- status = getanswer_r
- (ctx, host_buffer.buf, n, name, type, result, buffer, buflen,
- errnop, h_errnop, map, ttlp, canonp);
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
return status;
@@ -330,13 +305,8 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
*h_errnop = NETDB_INTERNAL;
return NSS_STATUS_UNAVAIL;
}
- status = NSS_STATUS_NOTFOUND;
- if (res_use_inet6 ())
- status = gethostbyname3_context (ctx, name, AF_INET6, result, buffer,
- buflen, errnop, h_errnop, NULL, NULL);
- if (status == NSS_STATUS_NOTFOUND)
- status = gethostbyname3_context (ctx, name, AF_INET, result, buffer,
- buflen, errnop, h_errnop, NULL, NULL);
+ status = gethostbyname3_context (ctx, name, AF_INET, result, buffer,
+ buflen, errnop, h_errnop, NULL, NULL);
__resolv_context_put (ctx);
return status;
}
@@ -649,7 +619,7 @@ static enum nss_status
getanswer_r (struct resolv_context *ctx,
const querybuf *answer, int anslen, const char *qname, int qtype,
struct hostent *result, char *buffer, size_t buflen,
- int *errnop, int *h_errnop, int map, int32_t *ttlp, char **canonp)
+ int *errnop, int *h_errnop, int32_t *ttlp, char **canonp)
{
struct host_data
{
@@ -665,7 +635,6 @@ getanswer_r (struct resolv_context *ctx,
char *bp, **ap, **hap;
char tbuf[MAXDNAME];
u_char packtmp[NS_MAXCDNAME];
- int have_to_map = 0;
uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
buffer += pad;
buflen = buflen > pad ? buflen - pad : 0;
@@ -846,9 +815,7 @@ getanswer_r (struct resolv_context *ctx,
continue;
}
- if (type == T_A && qtype == T_AAAA && map)
- have_to_map = 1;
- else if (__glibc_unlikely (type != qtype))
+ if (__glibc_unlikely (type != qtype))
{
cp += n;
continue; /* XXX - had_error++ ? */
@@ -945,9 +912,6 @@ getanswer_r (struct resolv_context *ctx,
linebuflen -= n;
}
- if (have_to_map)
- if (map_v4v6_hostent (result, &bp, &linebuflen))
- goto too_small;
*h_errnop = NETDB_SUCCESS;
return NSS_STATUS_SUCCESS;
}

View File

@ -0,0 +1,565 @@
commit 9abc40d9b514fc51cd1a052d32d092a827c6e21a
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
nss_dns: Rewrite getanswer_r to match getanswer_ptr (bug 12154, bug 29305)
Allocate the pointer arrays only at the end, when their sizes
are known. This addresses bug 29305.
Skip over invalid names instead of failing lookups. This partially
fixes bug 12154 (for gethostbyname, fixing getaddrinfo requires
different changes).
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit d101d836e7e4bd1d4e4972b0e0bd0a55c9b650fa)
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index f8dc5a0a7d1eb156..10c21e1e827cde12 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -108,12 +108,19 @@ typedef union querybuf
u_char buf[MAXPACKET];
} querybuf;
-static enum nss_status getanswer_r (struct resolv_context *ctx,
- const querybuf *answer, int anslen,
- const char *qname, int qtype,
- struct hostent *result, char *buffer,
- size_t buflen, int *errnop, int *h_errnop,
- int32_t *ttlp, char **canonp);
+/* For historic reasons, pointers to IP addresses are char *, so use a
+ single list type for addresses and host names. */
+#define DYNARRAY_STRUCT ptrlist
+#define DYNARRAY_ELEMENT char *
+#define DYNARRAY_PREFIX ptrlist_
+#include <malloc/dynarray-skeleton.c>
+
+static enum nss_status getanswer_r (unsigned char *packet, size_t packetlen,
+ uint16_t qtype, struct alloc_buffer *abuf,
+ struct ptrlist *addresses,
+ struct ptrlist *aliases,
+ int *errnop, int *h_errnop, int32_t *ttlp);
+static void addrsort (struct resolv_context *ctx, char **ap, int num);
static enum nss_status getanswer_ptr (unsigned char *packet, size_t packetlen,
struct alloc_buffer *abuf,
char **hnamep, int *errnop,
@@ -185,12 +192,6 @@ gethostbyname3_context (struct resolv_context *ctx,
char *buffer, size_t buflen, int *errnop,
int *h_errnop, int32_t *ttlp, char **canonp)
{
- union
- {
- querybuf *buf;
- u_char *ptr;
- } host_buffer;
- querybuf *orig_host_buffer;
char tmp[NS_MAXDNAME];
int size, type, n;
const char *cp;
@@ -224,10 +225,12 @@ gethostbyname3_context (struct resolv_context *ctx,
&& (cp = __res_context_hostalias (ctx, name, tmp, sizeof (tmp))) != NULL)
name = cp;
- host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
+ unsigned char dns_packet_buffer[1024];
+ unsigned char *alt_dns_packet_buffer = dns_packet_buffer;
- n = __res_context_search (ctx, name, C_IN, type, host_buffer.buf->buf,
- 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
+ n = __res_context_search (ctx, name, C_IN, type,
+ dns_packet_buffer, sizeof (dns_packet_buffer),
+ &alt_dns_packet_buffer, NULL, NULL, NULL, NULL);
if (n < 0)
{
switch (errno)
@@ -256,12 +259,77 @@ gethostbyname3_context (struct resolv_context *ctx,
__set_errno (olderr);
}
else
- status = getanswer_r
- (ctx, host_buffer.buf, n, name, type, result, buffer, buflen,
- errnop, h_errnop, ttlp, canonp);
+ {
+ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen);
- if (host_buffer.buf != orig_host_buffer)
- free (host_buffer.buf);
+ struct ptrlist addresses;
+ ptrlist_init (&addresses);
+ struct ptrlist aliases;
+ ptrlist_init (&aliases);
+
+ status = getanswer_r (alt_dns_packet_buffer, n, type,
+ &abuf, &addresses, &aliases,
+ errnop, h_errnop, ttlp);
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ if (ptrlist_has_failed (&addresses)
+ || ptrlist_has_failed (&aliases))
+ {
+ /* malloc failure. Do not retry using the ERANGE protocol. */
+ *errnop = ENOMEM;
+ *h_errnop = NETDB_INTERNAL;
+ status = NSS_STATUS_UNAVAIL;
+ }
+
+ /* Reserve the address and alias arrays in the result
+ buffer. Both are NULL-terminated, but the first element
+ of the alias array is stored in h_name, so no extra space
+ for the NULL terminator is needed there. */
+ result->h_addr_list
+ = alloc_buffer_alloc_array (&abuf, char *,
+ ptrlist_size (&addresses) + 1);
+ result->h_aliases
+ = alloc_buffer_alloc_array (&abuf, char *,
+ ptrlist_size (&aliases));
+ if (alloc_buffer_has_failed (&abuf))
+ {
+ /* Retry using the ERANGE protocol. */
+ *errnop = ERANGE;
+ *h_errnop = NETDB_INTERNAL;
+ status = NSS_STATUS_TRYAGAIN;
+ }
+ else
+ {
+ /* Copy the address list and NULL-terminate it. */
+ memcpy (result->h_addr_list, ptrlist_begin (&addresses),
+ ptrlist_size (&addresses) * sizeof (char *));
+ result->h_addr_list[ptrlist_size (&addresses)] = NULL;
+
+ /* Sort the address list if requested. */
+ if (type == T_A && __resolv_context_sort_count (ctx) > 0)
+ addrsort (ctx, result->h_addr_list, ptrlist_size (&addresses));
+
+ /* Copy the aliases, excluding the last one. */
+ memcpy (result->h_aliases, ptrlist_begin (&aliases),
+ (ptrlist_size (&aliases) - 1) * sizeof (char *));
+ result->h_aliases[ptrlist_size (&aliases) - 1] = NULL;
+
+ /* The last alias goes into h_name. */
+ assert (ptrlist_size (&aliases) >= 1);
+ result->h_name = ptrlist_end (&aliases)[-1];
+
+ /* This is also the canonical name. */
+ if (canonp != NULL)
+ *canonp = result->h_name;
+ }
+ }
+
+ ptrlist_free (&aliases);
+ ptrlist_free (&addresses);
+ }
+
+ if (alt_dns_packet_buffer != dns_packet_buffer)
+ free (alt_dns_packet_buffer);
return status;
}
@@ -615,314 +683,128 @@ addrsort (struct resolv_context *ctx, char **ap, int num)
break;
}
-static enum nss_status
-getanswer_r (struct resolv_context *ctx,
- const querybuf *answer, int anslen, const char *qname, int qtype,
- struct hostent *result, char *buffer, size_t buflen,
- int *errnop, int *h_errnop, int32_t *ttlp, char **canonp)
+/* Convert the uncompressed, binary domain name CDNAME into its
+ textual representation and add it to the end of ALIASES, allocating
+ space for a copy of the name from ABUF. Skip adding the name if it
+ is not a valid host name, and return false in that case, otherwise
+ true. */
+static bool
+getanswer_r_store_alias (const unsigned char *cdname,
+ struct alloc_buffer *abuf,
+ struct ptrlist *aliases)
{
- struct host_data
- {
- char *aliases[MAX_NR_ALIASES];
- unsigned char host_addr[16]; /* IPv4 or IPv6 */
- char *h_addr_ptrs[0];
- } *host_data;
- int linebuflen;
- const HEADER *hp;
- const u_char *end_of_message, *cp;
- int n, ancount, qdcount;
- int haveanswer, had_error;
- char *bp, **ap, **hap;
- char tbuf[MAXDNAME];
- u_char packtmp[NS_MAXCDNAME];
- uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data);
- buffer += pad;
- buflen = buflen > pad ? buflen - pad : 0;
- if (__glibc_unlikely (buflen < sizeof (struct host_data)))
- {
- /* The buffer is too small. */
- too_small:
- *errnop = ERANGE;
- *h_errnop = NETDB_INTERNAL;
- return NSS_STATUS_TRYAGAIN;
- }
- host_data = (struct host_data *) buffer;
- linebuflen = buflen - sizeof (struct host_data);
- if (buflen - sizeof (struct host_data) != linebuflen)
- linebuflen = INT_MAX;
-
- result->h_name = NULL;
- end_of_message = answer->buf + anslen;
-
- /*
- * find first satisfactory answer
- */
- hp = &answer->hdr;
- ancount = ntohs (hp->ancount);
- qdcount = ntohs (hp->qdcount);
- cp = answer->buf + HFIXEDSZ;
- if (__glibc_unlikely (qdcount != 1))
- {
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
- if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen)
- goto too_small;
- bp = (char *) &host_data->h_addr_ptrs[ancount + 1];
- linebuflen -= (ancount + 1) * sizeof (char *);
-
- n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
- {
- if (__glibc_unlikely (errno == EMSGSIZE))
- goto too_small;
-
- n = -1;
- }
+ /* Filter out domain names that are not host names. */
+ if (!__res_binary_hnok (cdname))
+ return false;
+
+ /* Note: Not NS_MAXCDNAME, so that __ns_name_ntop implicitly checks
+ for length. */
+ char dname[MAXHOSTNAMELEN + 1];
+ if (__ns_name_ntop (cdname, dname, sizeof (dname)) < 0)
+ return false;
+ /* Do not report an error on allocation failure, instead store NULL
+ or do nothing. getanswer_r's caller will see NSS_STATUS_SUCCESS
+ and detect the memory allocation failure or buffer space
+ exhaustion, and report it accordingly. */
+ ptrlist_add (aliases, alloc_buffer_copy_string (abuf, dname));
+ return true;
+}
- if (__glibc_unlikely (n < 0))
- {
- *errnop = errno;
- *h_errnop = NO_RECOVERY;
- return NSS_STATUS_UNAVAIL;
- }
- if (__glibc_unlikely (__libc_res_hnok (bp) == 0))
+static enum nss_status __attribute__ ((noinline))
+getanswer_r (unsigned char *packet, size_t packetlen, uint16_t qtype,
+ struct alloc_buffer *abuf,
+ struct ptrlist *addresses, struct ptrlist *aliases,
+ int *errnop, int *h_errnop, int32_t *ttlp)
+{
+ struct ns_rr_cursor c;
+ if (!__ns_rr_cursor_init (&c, packet, packetlen))
{
- errno = EBADMSG;
- *errnop = EBADMSG;
+ /* This should not happen because __res_context_query already
+ perfroms response validation. */
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
- cp += n + QFIXEDSZ;
- if (qtype == T_A || qtype == T_AAAA)
+ /* Treat the QNAME just like an alias. Error out if it is not a
+ valid host name. */
+ if (ns_rr_cursor_rcode (&c) == NXDOMAIN
+ || !getanswer_r_store_alias (ns_rr_cursor_qname (&c), abuf, aliases))
{
- /* res_send() has already verified that the query name is the
- * same as the one we sent; this just gets the expanded name
- * (i.e., with the succeeding search-domain tacked on).
- */
- n = strlen (bp) + 1; /* for the \0 */
- if (n >= MAXHOSTNAMELEN)
- {
- *h_errnop = NO_RECOVERY;
- *errnop = ENOENT;
- return NSS_STATUS_TRYAGAIN;
- }
- result->h_name = bp;
- bp += n;
- linebuflen -= n;
- if (linebuflen < 0)
- goto too_small;
- /* The qname can be abbreviated, but h_name is now absolute. */
- qname = result->h_name;
+ if (ttlp != NULL)
+ /* No negative caching. */
+ *ttlp = 0;
+ *h_errnop = HOST_NOT_FOUND;
+ *errnop = ENOENT;
+ return NSS_STATUS_NOTFOUND;
}
- ap = host_data->aliases;
- *ap = NULL;
- result->h_aliases = host_data->aliases;
- hap = host_data->h_addr_ptrs;
- *hap = NULL;
- result->h_addr_list = host_data->h_addr_ptrs;
- haveanswer = 0;
- had_error = 0;
+ int ancount = ns_rr_cursor_ancount (&c);
+ const unsigned char *expected_name = ns_rr_cursor_qname (&c);
+ /* expected_name may be updated to point into this buffer. */
+ unsigned char name_buffer[NS_MAXCDNAME];
- while (ancount-- > 0 && cp < end_of_message && had_error == 0)
+ for (; ancount > 0; --ancount)
{
- int type, class;
-
- n = __ns_name_unpack (answer->buf, end_of_message, cp,
- packtmp, sizeof packtmp);
- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
- {
- if (__glibc_unlikely (errno == EMSGSIZE))
- goto too_small;
-
- n = -1;
- }
-
- if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0))
- {
- ++had_error;
- continue;
- }
- cp += n; /* name */
-
- if (__glibc_unlikely (cp + 10 > end_of_message))
+ struct ns_rr_wire rr;
+ if (!__ns_rr_cursor_next (&c, &rr))
{
- ++had_error;
- continue;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- NS_GET16 (type, cp);
- NS_GET16 (class, cp);
- int32_t ttl;
- NS_GET32 (ttl, cp);
- NS_GET16 (n, cp); /* RDATA length. */
-
- if (end_of_message - cp < n)
- {
- /* RDATA extends beyond the end of the packet. */
- ++had_error;
- continue;
- }
+ /* Skip over records with the wrong class. */
+ if (rr.rclass != C_IN)
+ continue;
- if (__glibc_unlikely (class != C_IN))
- {
- /* XXX - debug? syslog? */
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
+ /* Update TTL for recognized record types. */
+ if ((rr.rtype == T_CNAME || rr.rtype == qtype)
+ && ttlp != NULL && *ttlp > rr.ttl)
+ *ttlp = rr.ttl;
- if (type == T_CNAME)
+ if (rr.rtype == T_CNAME)
{
- /* A CNAME could also have a TTL entry. */
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
-
- if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1])
- continue;
- n = __libc_dn_expand (answer->buf, end_of_message, cp,
- tbuf, sizeof tbuf);
- if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0))
- {
- ++had_error;
- continue;
- }
- cp += n;
- /* Store alias. */
- *ap++ = bp;
- n = strlen (bp) + 1; /* For the \0. */
- if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
- {
- ++had_error;
- continue;
- }
- bp += n;
- linebuflen -= n;
- /* Get canonical name. */
- n = strlen (tbuf) + 1; /* For the \0. */
- if (__glibc_unlikely (n > linebuflen))
- goto too_small;
- if (__glibc_unlikely (n >= MAXHOSTNAMELEN))
+ /* NB: No check for owner name match, based on historic
+ precedent. Record the CNAME target as the new expected
+ name. */
+ int n = __ns_name_unpack (c.begin, c.end, rr.rdata,
+ name_buffer, sizeof (name_buffer));
+ if (n < 0)
{
- ++had_error;
- continue;
+ *h_errnop = NO_RECOVERY;
+ return NSS_STATUS_UNAVAIL;
}
- result->h_name = bp;
- bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */
- linebuflen -= n;
- continue;
+ /* And store the new name as an alias. */
+ getanswer_r_store_alias (name_buffer, abuf, aliases);
+ expected_name = name_buffer;
}
-
- if (__glibc_unlikely (type != qtype))
+ else if (rr.rtype == qtype
+ && __ns_samebinaryname (rr.rname, expected_name)
+ && rr.rdlength == rrtype_to_rdata_length (qtype))
{
- cp += n;
- continue; /* XXX - had_error++ ? */
+ /* Make a copy of the address and store it. Increase the
+ alignment to 4, in case there are applications out there
+ that expect at least this level of address alignment. */
+ ptrlist_add (addresses, (char *) alloc_buffer_next (abuf, uint32_t));
+ alloc_buffer_copy_bytes (abuf, rr.rdata, rr.rdlength);
}
-
- switch (type)
- {
- case T_A:
- case T_AAAA:
- if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0))
- {
- cp += n;
- continue; /* XXX - had_error++ ? */
- }
-
- /* Stop parsing at a record whose length is incorrect. */
- if (n != rrtype_to_rdata_length (type))
- {
- ++had_error;
- break;
- }
-
- /* Skip records of the wrong type. */
- if (n != result->h_length)
- {
- cp += n;
- continue;
- }
- if (!haveanswer)
- {
- int nn;
-
- /* We compose a single hostent out of the entire chain of
- entries, so the TTL of the hostent is essentially the lowest
- TTL in the chain. */
- if (ttlp != NULL && ttl < *ttlp)
- *ttlp = ttl;
- if (canonp != NULL)
- *canonp = bp;
- result->h_name = bp;
- nn = strlen (bp) + 1; /* for the \0 */
- bp += nn;
- linebuflen -= nn;
- }
-
- /* Provide sufficient alignment for both address
- families. */
- enum { align = 4 };
- _Static_assert ((align % __alignof__ (struct in_addr)) == 0,
- "struct in_addr alignment");
- _Static_assert ((align % __alignof__ (struct in6_addr)) == 0,
- "struct in6_addr alignment");
- {
- char *new_bp = PTR_ALIGN_UP (bp, align);
- linebuflen -= new_bp - bp;
- bp = new_bp;
- }
-
- if (__glibc_unlikely (n > linebuflen))
- goto too_small;
- bp = __mempcpy (*hap++ = bp, cp, n);
- cp += n;
- linebuflen -= n;
- break;
- default:
- abort ();
- }
- if (had_error == 0)
- ++haveanswer;
}
- if (haveanswer > 0)
+ if (ptrlist_size (addresses) == 0)
{
- *ap = NULL;
- *hap = NULL;
- /*
- * Note: we sort even if host can take only one address
- * in its return structures - should give it the "best"
- * address in that case, not some random one
- */
- if (haveanswer > 1 && qtype == T_A
- && __resolv_context_sort_count (ctx) > 0)
- addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
-
- if (result->h_name == NULL)
- {
- n = strlen (qname) + 1; /* For the \0. */
- if (n > linebuflen)
- goto too_small;
- if (n >= MAXHOSTNAMELEN)
- goto no_recovery;
- result->h_name = bp;
- bp = __mempcpy (bp, qname, n); /* Cannot overflow. */
- linebuflen -= n;
- }
+ /* No address record found. */
+ if (ttlp != NULL)
+ /* No caching of negative responses. */
+ *ttlp = 0;
+ *h_errnop = NO_RECOVERY;
+ *errnop = ENOENT;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ else
+ {
*h_errnop = NETDB_SUCCESS;
return NSS_STATUS_SUCCESS;
}
- no_recovery:
- *h_errnop = NO_RECOVERY;
- *errnop = ENOENT;
- /* Special case here: if the resolver sent a result but it only
- contains a CNAME while we are looking for a T_A or T_AAAA record,
- we fail with NOTFOUND instead of TRYAGAIN. */
- return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases
- ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN);
}
static enum nss_status

View File

@ -0,0 +1,51 @@
commit c36e7cca3571b0c92b09409c1df86a142596c210
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
nss_dns: In gaih_getanswer_slice, skip strange aliases (bug 12154)
If the name is not a host name, skip adding it to the result, instead
of reporting query failure. This fixes bug 12154 for getaddrinfo.
This commit still keeps the old parsing code, and only adjusts when
a host name is copied.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 32b599ac8c21c4c332cc3900a792a1395bca79c7)
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 10c21e1e827cde12..1cb3be71f04d98eb 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -971,12 +971,12 @@ gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
n = -1;
}
- if (__glibc_unlikely (n < 0 || __libc_res_hnok (buffer) == 0))
+ if (__glibc_unlikely (n < 0))
{
++had_error;
continue;
}
- if (*firstp && canon == NULL)
+ if (*firstp && canon == NULL && __libc_res_hnok (buffer))
{
h_name = buffer;
buffer += h_namelen;
@@ -1022,14 +1022,14 @@ gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
n = __libc_dn_expand (answer->buf, end_of_message, cp,
tbuf, sizeof tbuf);
- if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0))
+ if (__glibc_unlikely (n < 0))
{
++had_error;
continue;
}
cp += n;
- if (*firstp)
+ if (*firstp && __libc_res_hnok (tbuf))
{
/* Reclaim buffer space. */
if (h_name + h_namelen == buffer)

View File

@ -0,0 +1,449 @@
commit 480c820493add16e8dda6f3189d834223e1f4f39
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 10:02:49 2022 +0200
resolv: Add new tst-resolv-invalid-cname
This test checks resolution through CNAME chains that do not contain
host names (bug 12154).
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 9caf782276ecea4bc86fc94fbb52779736f3106d)
Conflicts:
resolv/Makefile
(usual test differences)
diff --git a/resolv/Makefile b/resolv/Makefile
index fded244d61068060..ea1518ec2da860c1 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -99,6 +99,7 @@ tests += \
tst-resolv-binary \
tst-resolv-byaddr \
tst-resolv-edns \
+ tst-resolv-invalid-cname \
tst-resolv-network \
tst-resolv-noaaaa \
tst-resolv-nondecimal \
@@ -279,6 +280,8 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \
$(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
+$(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \
+ $(shared-thread-library)
$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
diff --git a/resolv/tst-resolv-invalid-cname.c b/resolv/tst-resolv-invalid-cname.c
new file mode 100644
index 0000000000000000..ae2d4419b1978c02
--- /dev/null
+++ b/resolv/tst-resolv-invalid-cname.c
@@ -0,0 +1,406 @@
+/* Test handling of CNAMEs with non-host domain names (bug 12154).
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+
+/* Query strings describe the CNAME chain in the response. They have
+ the format "bitsBITS.countCOUNT.example.", where BITS and COUNT are
+ replaced by unsigned decimal numbers. COUNT is the number of CNAME
+ records in the response. BITS has two bits for each CNAME record,
+ describing a special prefix that is added to that CNAME.
+
+ 0: No special leading label.
+ 1: Starting with "*.".
+ 2: Starting with "-x.".
+ 3: Starting with "star.*.".
+
+ The first CNAME in the response using the two least significant
+ bits.
+
+ For PTR queries, the QNAME format is different, it is either
+ COUNT.BITS.168.192.in-addr.arpa. (with BITS and COUNT still
+ decimal), or:
+
+COUNT.BITS0.BITS1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
+
+ where BITS and COUNT are hexadecimal. */
+
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ TEST_COMPARE (qclass, C_IN);
+
+ /* The only other query type besides A is PTR. */
+ if (qtype != T_A && qtype != T_AAAA)
+ TEST_COMPARE (qtype, T_PTR);
+
+ unsigned int bits, bits1, count;
+ char *tail = NULL;
+ if (sscanf (qname, "bits%u.count%u.%ms", &bits, &count, &tail) == 3)
+ TEST_COMPARE_STRING (tail, "example");
+ else if (strstr (qname, "in-addr.arpa") != NULL
+ && sscanf (qname, "%u.%u.%ms", &bits, &count, &tail) == 3)
+ TEST_COMPARE_STRING (tail, "168.192.in-addr.arpa");
+ else if (sscanf (qname, "%x.%x.%x.%ms", &bits, &bits1, &count, &tail) == 4)
+ {
+ TEST_COMPARE_STRING (tail, "\
+0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
+ bits |= bits1 << 4;
+ }
+ else
+ FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
+ free (tail);
+
+ struct resolv_response_flags flags = {};
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ resolv_response_section (b, ns_s_an);
+
+ /* Provide the requested number of CNAME records. */
+ char *previous_name = (char *) qname;
+ unsigned int original_bits = bits;
+ for (int unique = 0; unique < count; ++unique)
+ {
+ resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60);
+
+ static const char bits_to_prefix[4][8] = { "", "*.", "-x.", "star.*." };
+ char *new_name = xasprintf ("%sunique%d.example",
+ bits_to_prefix[bits & 3], unique);
+ bits >>= 2;
+ resolv_response_add_name (b, new_name);
+ resolv_response_close_record (b);
+
+ if (previous_name != qname)
+ free (previous_name);
+ previous_name = new_name;
+ }
+
+ /* Actual answer record. */
+ resolv_response_open_record (b, previous_name, qclass, qtype, 60);
+ switch (qtype)
+ {
+ case T_A:
+ {
+ char ipv4[4] = {192, 168, count, original_bits};
+ resolv_response_add_data (b, &ipv4, sizeof (ipv4));
+ }
+ break;
+ case T_AAAA:
+ {
+ char ipv6[16] =
+ {
+ 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ count, original_bits
+ };
+ resolv_response_add_data (b, &ipv6, sizeof (ipv6));
+ }
+ break;
+
+ case T_PTR:
+ {
+ char *name = xasprintf ("bits%u.count%u.example",
+ original_bits, count);
+ resolv_response_add_name (b, name);
+ free (name);
+ }
+ break;
+ }
+ resolv_response_close_record (b);
+
+ if (previous_name != qname)
+ free (previous_name);
+}
+
+/* Controls which name resolution function is invoked. */
+enum test_mode
+ {
+ byname, /* gethostbyname. */
+ byname2, /* gethostbyname2. */
+ gai, /* getaddrinfo without AI_CANONNAME. */
+ gai_canon, /* getaddrinfo with AI_CANONNAME. */
+
+ test_mode_num /* Number of enum values. */
+ };
+
+static const char *
+test_mode_to_string (enum test_mode mode)
+{
+ switch (mode)
+ {
+ case byname:
+ return "byname";
+ case byname2:
+ return "byname2";
+ case gai:
+ return "gai";
+ case gai_canon:
+ return "gai_canon";
+ case test_mode_num:
+ /* Report error below. */
+ }
+ FAIL_EXIT1 ("invalid test_mode: %d", mode);
+}
+
+/* Append the name and aliases to OUT. */
+static void
+append_names (FILE *out, const char *qname, int bits, int count,
+ enum test_mode mode)
+{
+ /* Largest valid index which has a corresponding zero in bits
+ (meaning a syntactically valid CNAME). */
+ int last_valid_cname = -1;
+
+ for (int i = 0; i < count; ++i)
+ if ((bits & (3 << (i * 2))) == 0)
+ last_valid_cname = i;
+
+ if (mode != gai)
+ {
+ const char *label;
+ if (mode == gai_canon)
+ label = "canonname";
+ else
+ label = "name";
+ if (last_valid_cname >= 0)
+ fprintf (out, "%s: unique%d.example\n", label, last_valid_cname);
+ else
+ fprintf (out, "%s: %s\n", label, qname);
+ }
+
+ if (mode == byname || mode == byname2)
+ {
+ if (last_valid_cname >= 0)
+ fprintf (out, "alias: %s\n", qname);
+ for (int i = 0; i < count; ++i)
+ {
+ if ((bits & (3 << (i * 2))) == 0 && i != last_valid_cname)
+ fprintf (out, "alias: unique%d.example\n", i);
+ }
+ }
+}
+
+/* Append the address information to OUT. */
+static void
+append_addresses (FILE *out, int af, int bits, int count, enum test_mode mode)
+{
+ int last = count * 256 + bits;
+ if (mode == gai || mode == gai_canon)
+ {
+ if (af == AF_INET || af == AF_UNSPEC)
+ fprintf (out, "address: STREAM/TCP 192.168.%d.%d 80\n", count, bits);
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ {
+ if (last == 0)
+ fprintf (out, "address: STREAM/TCP 2001:db8:: 80\n");
+ else
+ fprintf (out, "address: STREAM/TCP 2001:db8::%x 80\n", last);
+ }
+ }
+ else
+ {
+ TEST_VERIFY (af != AF_UNSPEC);
+ if (af == AF_INET)
+ fprintf (out, "address: 192.168.%d.%d\n", count, bits);
+ if (af == AF_INET6)
+ {
+ if (last == 0)
+ fprintf (out, "address: 2001:db8::\n");
+ else
+ fprintf (out, "address: 2001:db8::%x\n", last);
+ }
+ }
+}
+
+/* Perform one test using a forward lookup. */
+static void
+check_forward (int af, int bits, int count, enum test_mode mode)
+{
+ char *qname = xasprintf ("bits%d.count%d.example", bits, count);
+ char *label = xasprintf ("af=%d bits=%d count=%d mode=%s qname=%s",
+ af, bits, count, test_mode_to_string (mode), qname);
+
+ struct xmemstream expected;
+ xopen_memstream (&expected);
+ if (mode == gai_canon)
+ fprintf (expected.out, "flags: AI_CANONNAME\n");
+ append_names (expected.out, qname, bits, count, mode);
+ append_addresses (expected.out, af, bits, count, mode);
+ xfclose_memstream (&expected);
+
+ if (mode == gai || mode == gai_canon)
+ {
+ struct addrinfo *ai;
+ struct addrinfo hints =
+ {
+ .ai_family = af,
+ .ai_socktype = SOCK_STREAM,
+ };
+ if (mode == gai_canon)
+ hints.ai_flags |= AI_CANONNAME;
+ int ret = getaddrinfo (qname, "80", &hints, &ai);
+ check_addrinfo (label, ai, ret, expected.buffer);
+ if (ret == 0)
+ freeaddrinfo (ai);
+ }
+ else
+ {
+ struct hostent *e;
+ if (mode == gai)
+ {
+ TEST_COMPARE (af, AF_INET);
+ e = gethostbyname (qname);
+ }
+ else
+ {
+ if (af != AF_INET)
+ TEST_COMPARE (af, AF_INET6);
+ e = gethostbyname2 (qname, af);
+ }
+ check_hostent (label, e, expected.buffer);
+ }
+
+ free (expected.buffer);
+ free (label);
+ free (qname);
+}
+
+/* Perform one check using a reverse lookup. */
+
+static void
+check_reverse (int af, int bits, int count)
+{
+ TEST_VERIFY (af == AF_INET || af == AF_INET6);
+
+ char *label = xasprintf ("af=%d bits=%d count=%d", af, bits, count);
+ char *fqdn = xasprintf ("bits%d.count%d.example", bits, count);
+
+ struct xmemstream expected;
+ xopen_memstream (&expected);
+ fprintf (expected.out, "name: %s\n", fqdn);
+ append_addresses (expected.out, af, bits, count, byname);
+ xfclose_memstream (&expected);
+
+ char addr[16] = { 0 };
+ socklen_t addrlen;
+ if (af == AF_INET)
+ {
+ addr[0] = 192;
+ addr[1] = 168;
+ addr[2] = count;
+ addr[3] = bits;
+ addrlen = 4;
+ }
+ else
+ {
+ addr[0] = 0x20;
+ addr[1] = 0x01;
+ addr[2] = 0x0d;
+ addr[3] = 0xb8;
+ addr[14] = count;
+ addr[15] = bits;
+ addrlen = 16;
+ }
+
+ struct hostent *e = gethostbyaddr (addr, addrlen, af);
+ check_hostent (label, e, expected.buffer);
+
+ /* getnameinfo check is different. There is no generic check_*
+ function for it. */
+ {
+ struct sockaddr_in sin = { };
+ struct sockaddr_in6 sin6 = { };
+ void *sa;
+ socklen_t salen;
+ if (af == AF_INET)
+ {
+ sin.sin_family = AF_INET;
+ memcpy (&sin.sin_addr, addr, addrlen);
+ sin.sin_port = htons (80);
+ sa = &sin;
+ salen = sizeof (sin);
+ }
+ else
+ {
+ sin6.sin6_family = AF_INET6;
+ memcpy (&sin6.sin6_addr, addr, addrlen);
+ sin6.sin6_port = htons (80);
+ sa = &sin6;
+ salen = sizeof (sin6);
+ }
+
+ char host[64];
+ char service[64];
+ int ret = getnameinfo (sa, salen, host,
+ sizeof (host), service, sizeof (service),
+ NI_NAMEREQD | NI_NUMERICSERV);
+ TEST_COMPARE (ret, 0);
+ TEST_COMPARE_STRING (host, fqdn);
+ TEST_COMPARE_STRING (service, "80");
+ }
+
+ free (expected.buffer);
+ free (fqdn);
+ free (label);
+}
+
+static int
+do_test (void)
+{
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response
+ });
+
+ for (int count = 0; count <= 3; ++count)
+ for (int bits = 0; bits <= 1 << (count * 2); ++bits)
+ {
+ if (count > 0 && bits == count)
+ /* The last bits value is only checked if count == 0. */
+ continue;
+
+ for (enum test_mode mode = 0; mode < test_mode_num; ++mode)
+ {
+ check_forward (AF_INET, bits, count, mode);
+ if (mode != byname)
+ check_forward (AF_INET6, bits, count, mode);
+ if (mode == gai || mode == gai_canon)
+ check_forward (AF_UNSPEC, bits, count, mode);
+ }
+
+ check_reverse (AF_INET, bits, count);
+ check_reverse (AF_INET6, bits, count);
+ }
+
+ resolv_test_end (obj);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,31 @@
commit 044755e2faeeca13bb77b2e9e638a45e6e90a5fa
Author: Florian Weimer <fweimer@redhat.com>
Date: Tue Aug 30 13:30:03 2022 +0200
resolv: Fix building tst-resolv-invalid-cname for earlier C standards
This fixes this compiler error:
tst-resolv-invalid-cname.c: In function test_mode_to_string:
tst-resolv-invalid-cname.c:164:10: error: label at end of compound statement
case test_mode_num:
^~~~~~~~~~~~~
Fixes commit 9caf782276ecea4bc86fc94fbb52779736f3106d
("resolv: Add new tst-resolv-invalid-cname").
(cherry picked from commit d09aa4a17229bcaa2ec7642006b12612498582e7)
diff --git a/resolv/tst-resolv-invalid-cname.c b/resolv/tst-resolv-invalid-cname.c
index ae2d4419b1978c02..63dac90e02d6cbc7 100644
--- a/resolv/tst-resolv-invalid-cname.c
+++ b/resolv/tst-resolv-invalid-cname.c
@@ -162,7 +162,7 @@ test_mode_to_string (enum test_mode mode)
case gai_canon:
return "gai_canon";
case test_mode_num:
- /* Report error below. */
+ break; /* Report error below. */
}
FAIL_EXIT1 ("invalid test_mode: %d", mode);
}

View File

@ -0,0 +1,33 @@
commit a2e259014f8a0e5f3ff938314f3087b74255804d
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Thu Nov 11 06:31:51 2021 -0800
Avoid extra load with CAS in __pthread_mutex_lock_full [BZ #28537]
Replace boolean CAS with value CAS to avoid the extra load.
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
(cherry picked from commit 0b82747dc48d5bf0871bdc6da8cb6eec1256355f)
diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c
index da624f322d06d0ee..a04e0158451c8fff 100644
--- a/nptl/pthread_mutex_lock.c
+++ b/nptl/pthread_mutex_lock.c
@@ -298,12 +298,12 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
meantime. */
if ((oldval & FUTEX_WAITERS) == 0)
{
- if (atomic_compare_and_exchange_bool_acq (&mutex->__data.__lock,
- oldval | FUTEX_WAITERS,
- oldval)
- != 0)
+ int val;
+ if ((val = atomic_compare_and_exchange_val_acq
+ (&mutex->__data.__lock, oldval | FUTEX_WAITERS,
+ oldval)) != oldval)
{
- oldval = mutex->__data.__lock;
+ oldval = val;
continue;
}
oldval |= FUTEX_WAITERS;

View File

@ -0,0 +1,33 @@
commit ed8300c054cae4aeb0bbfa043f5fccc91a4adbf5
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Thu Nov 11 06:54:01 2021 -0800
Avoid extra load with CAS in __pthread_mutex_clocklock_common [BZ #28537]
Replace boolean CAS with value CAS to avoid the extra load.
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
(cherry picked from commit 49302b8fdf9103b6fc0a398678668a22fa19574c)
diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c
index 11ad7005d07afc6e..90cede9446e33fcf 100644
--- a/nptl/pthread_mutex_timedlock.c
+++ b/nptl/pthread_mutex_timedlock.c
@@ -234,12 +234,12 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex,
meantime. */
if ((oldval & FUTEX_WAITERS) == 0)
{
- if (atomic_compare_and_exchange_bool_acq (&mutex->__data.__lock,
- oldval | FUTEX_WAITERS,
- oldval)
- != 0)
+ int val;
+ if ((val = atomic_compare_and_exchange_val_acq
+ (&mutex->__data.__lock, oldval | FUTEX_WAITERS,
+ oldval)) != oldval)
{
- oldval = mutex->__data.__lock;
+ oldval = val;
continue;
}
oldval |= FUTEX_WAITERS;

View File

@ -0,0 +1,46 @@
commit a6b81f605dfba8650ea1f80122f41eb8e6c73dc7
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Tue Nov 2 18:33:07 2021 -0700
Add LLL_MUTEX_READ_LOCK [BZ #28537]
CAS instruction is expensive. From the x86 CPU's point of view, getting
a cache line for writing is more expensive than reading. See Appendix
A.2 Spinlock in:
https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/xeon-lock-scaling-analysis-paper.pdf
The full compare and swap will grab the cache line exclusive and cause
excessive cache line bouncing.
Add LLL_MUTEX_READ_LOCK to do an atomic load and skip CAS in spinlock
loop if compare may fail to reduce cache line bouncing on contended locks.
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
(cherry picked from commit d672a98a1af106bd68deb15576710cd61363f7a6)
diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c
index a04e0158451c8fff..9f40928cc6b9a067 100644
--- a/nptl/pthread_mutex_lock.c
+++ b/nptl/pthread_mutex_lock.c
@@ -65,6 +65,11 @@ lll_mutex_lock_optimized (pthread_mutex_t *mutex)
# define PTHREAD_MUTEX_VERSIONS 1
#endif
+#ifndef LLL_MUTEX_READ_LOCK
+# define LLL_MUTEX_READ_LOCK(mutex) \
+ atomic_load_relaxed (&(mutex)->__data.__lock)
+#endif
+
static int __pthread_mutex_lock_full (pthread_mutex_t *mutex)
__attribute_noinline__;
@@ -142,6 +147,8 @@ PTHREAD_MUTEX_LOCK (pthread_mutex_t *mutex)
break;
}
atomic_spin_nop ();
+ if (LLL_MUTEX_READ_LOCK (mutex) != 0)
+ continue;
}
while (LLL_MUTEX_TRYLOCK (mutex) != 0);

View File

@ -0,0 +1,66 @@
commit 6bcfbee7277e4faa4b693bd965931f0d1883005d
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Fri Nov 12 11:47:42 2021 -0800
Move assignment out of the CAS condition
Update
commit 49302b8fdf9103b6fc0a398678668a22fa19574c
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Thu Nov 11 06:54:01 2021 -0800
Avoid extra load with CAS in __pthread_mutex_clocklock_common [BZ #28537]
Replace boolean CAS with value CAS to avoid the extra load.
and
commit 0b82747dc48d5bf0871bdc6da8cb6eec1256355f
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Thu Nov 11 06:31:51 2021 -0800
Avoid extra load with CAS in __pthread_mutex_lock_full [BZ #28537]
Replace boolean CAS with value CAS to avoid the extra load.
by moving assignment out of the CAS condition.
(cherry picked from commit 120ac6d238825452e8024e2f627da33b2508dfd3)
diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c
index 9f40928cc6b9a067..49901ffa0a96d659 100644
--- a/nptl/pthread_mutex_lock.c
+++ b/nptl/pthread_mutex_lock.c
@@ -305,10 +305,9 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
meantime. */
if ((oldval & FUTEX_WAITERS) == 0)
{
- int val;
- if ((val = atomic_compare_and_exchange_val_acq
- (&mutex->__data.__lock, oldval | FUTEX_WAITERS,
- oldval)) != oldval)
+ int val = atomic_compare_and_exchange_val_acq
+ (&mutex->__data.__lock, oldval | FUTEX_WAITERS, oldval);
+ if (val != oldval)
{
oldval = val;
continue;
diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c
index 90cede9446e33fcf..2e5506db06ccb1ec 100644
--- a/nptl/pthread_mutex_timedlock.c
+++ b/nptl/pthread_mutex_timedlock.c
@@ -234,10 +234,9 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex,
meantime. */
if ((oldval & FUTEX_WAITERS) == 0)
{
- int val;
- if ((val = atomic_compare_and_exchange_val_acq
- (&mutex->__data.__lock, oldval | FUTEX_WAITERS,
- oldval)) != oldval)
+ int val = atomic_compare_and_exchange_val_acq
+ (&mutex->__data.__lock, oldval | FUTEX_WAITERS, oldval);
+ if (val != oldval)
{
oldval = val;
continue;

View File

@ -0,0 +1,38 @@
commit 43760d33d78f9ea8c8af942b570112ee801b99df
Author: Jangwoong Kim <6812skiii@gmail.com>
Date: Tue Dec 14 21:30:51 2021 +0900
nptl: Effectively skip CAS in spinlock loop
The commit:
"Add LLL_MUTEX_READ_LOCK [BZ #28537]"
SHA1: d672a98a1af106bd68deb15576710cd61363f7a6
introduced LLL_MUTEX_READ_LOCK, to skip CAS in spinlock loop
if atomic load fails. But, "continue" inside of do-while loop
does not skip the evaluation of escape expression, thus CAS
is not skipped.
Replace do-while with while and skip LLL_MUTEX_TRYLOCK if
LLL_MUTEX_READ_LOCK fails.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
(cherry picked from commit 6b8dbbd03ac88f169b65b5c7d7278576a11d2e44)
diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c
index 49901ffa0a96d659..bbe754a272b97d91 100644
--- a/nptl/pthread_mutex_lock.c
+++ b/nptl/pthread_mutex_lock.c
@@ -147,10 +147,9 @@ PTHREAD_MUTEX_LOCK (pthread_mutex_t *mutex)
break;
}
atomic_spin_nop ();
- if (LLL_MUTEX_READ_LOCK (mutex) != 0)
- continue;
}
- while (LLL_MUTEX_TRYLOCK (mutex) != 0);
+ while (LLL_MUTEX_READ_LOCK (mutex) != 0
+ || LLL_MUTEX_TRYLOCK (mutex) != 0);
mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
}

View File

@ -0,0 +1,68 @@
commit 04efdcfac405723c23b25d124817bcfc1697e2d8
Author: Noah Goldstein <goldstein.w.n@gmail.com>
Date: Wed Apr 27 15:13:02 2022 -0500
sysdeps: Add 'get_fast_jitter' interace in fast-jitter.h
'get_fast_jitter' is meant to be used purely for performance
purposes. In all cases it's used it should be acceptable to get no
randomness (see default case). An example use case is in setting
jitter for retries between threads at a lock. There is a
performance benefit to having jitter, but only if the jitter can
be generated very quickly and ultimately there is no serious issue
if no jitter is generated.
The implementation generally uses 'HP_TIMING_NOW' iff it is
inlined (avoid any potential syscall paths).
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
(cherry picked from commit 911c63a51c690dd1a97dfc587097277029baf00f)
diff --git a/sysdeps/generic/fast-jitter.h b/sysdeps/generic/fast-jitter.h
new file mode 100644
index 0000000000000000..4dd53e3475c3dfe6
--- /dev/null
+++ b/sysdeps/generic/fast-jitter.h
@@ -0,0 +1,42 @@
+/* Fallback for fast jitter just return 0.
+ Copyright (C) 2019-2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _FAST_JITTER_H
+# define _FAST_JITTER_H
+
+# include <stdint.h>
+# include <hp-timing.h>
+
+/* Baseline just return 0. We could create jitter using a clock or
+ 'random_bits' but that may imply a syscall and the goal of
+ 'get_fast_jitter' is minimal overhead "randomness" when such
+ randomness helps performance. Adding high overhead the function
+ defeats the purpose. */
+static inline uint32_t
+get_fast_jitter (void)
+{
+# if HP_TIMING_INLINE
+ hp_timing_t jitter;
+ HP_TIMING_NOW (jitter);
+ return (uint32_t) jitter;
+# else
+ return 0;
+# endif
+}
+
+#endif

View File

@ -0,0 +1,209 @@
commit ea69248445fb9b80da02ee0c7261cba4b1a5532e
Author: Wangyang Guo <wangyang.guo@intel.com>
Date: Fri May 6 01:50:10 2022 +0000
nptl: Add backoff mechanism to spinlock loop
When mutiple threads waiting for lock at the same time, once lock owner
releases the lock, waiters will see lock available and all try to lock,
which may cause an expensive CAS storm.
Binary exponential backoff with random jitter is introduced. As try-lock
attempt increases, there is more likely that a larger number threads
compete for adaptive mutex lock, so increase wait time in exponential.
A random jitter is also added to avoid synchronous try-lock from other
threads.
v2: Remove read-check before try-lock for performance.
v3:
1. Restore read-check since it works well in some platform.
2. Make backoff arch dependent, and enable it for x86_64.
3. Limit max backoff to reduce latency in large critical section.
v4: Fix strict-prototypes error in sysdeps/nptl/pthread_mutex_backoff.h
v5: Commit log updated for regression in large critical section.
Result of pthread-mutex-locks bench
Test Platform: Xeon 8280L (2 socket, 112 CPUs in total)
First Row: thread number
First Col: critical section length
Values: backoff vs upstream, time based, low is better
non-critical-length: 1
1 2 4 8 16 32 64 112 140
0 0.99 0.58 0.52 0.49 0.43 0.44 0.46 0.52 0.54
1 0.98 0.43 0.56 0.50 0.44 0.45 0.50 0.56 0.57
2 0.99 0.41 0.57 0.51 0.45 0.47 0.48 0.60 0.61
4 0.99 0.45 0.59 0.53 0.48 0.49 0.52 0.64 0.65
8 1.00 0.66 0.71 0.63 0.56 0.59 0.66 0.72 0.71
16 0.97 0.78 0.91 0.73 0.67 0.70 0.79 0.80 0.80
32 0.95 1.17 0.98 0.87 0.82 0.86 0.89 0.90 0.90
64 0.96 0.95 1.01 1.01 0.98 1.00 1.03 0.99 0.99
128 0.99 1.01 1.01 1.17 1.08 1.12 1.02 0.97 1.02
non-critical-length: 32
1 2 4 8 16 32 64 112 140
0 1.03 0.97 0.75 0.65 0.58 0.58 0.56 0.70 0.70
1 0.94 0.95 0.76 0.65 0.58 0.58 0.61 0.71 0.72
2 0.97 0.96 0.77 0.66 0.58 0.59 0.62 0.74 0.74
4 0.99 0.96 0.78 0.66 0.60 0.61 0.66 0.76 0.77
8 0.99 0.99 0.84 0.70 0.64 0.66 0.71 0.80 0.80
16 0.98 0.97 0.95 0.76 0.70 0.73 0.81 0.85 0.84
32 1.04 1.12 1.04 0.89 0.82 0.86 0.93 0.91 0.91
64 0.99 1.15 1.07 1.00 0.99 1.01 1.05 0.99 0.99
128 1.00 1.21 1.20 1.22 1.25 1.31 1.12 1.10 0.99
non-critical-length: 128
1 2 4 8 16 32 64 112 140
0 1.02 1.00 0.99 0.67 0.61 0.61 0.61 0.74 0.73
1 0.95 0.99 1.00 0.68 0.61 0.60 0.60 0.74 0.74
2 1.00 1.04 1.00 0.68 0.59 0.61 0.65 0.76 0.76
4 1.00 0.96 0.98 0.70 0.63 0.63 0.67 0.78 0.77
8 1.01 1.02 0.89 0.73 0.65 0.67 0.71 0.81 0.80
16 0.99 0.96 0.96 0.79 0.71 0.73 0.80 0.84 0.84
32 0.99 0.95 1.05 0.89 0.84 0.85 0.94 0.92 0.91
64 1.00 0.99 1.16 1.04 1.00 1.02 1.06 0.99 0.99
128 1.00 1.06 0.98 1.14 1.39 1.26 1.08 1.02 0.98
There is regression in large critical section. But adaptive mutex is
aimed for "quick" locks. Small critical section is more common when
users choose to use adaptive pthread_mutex.
Signed-off-by: Wangyang Guo <wangyang.guo@intel.com>
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
(cherry picked from commit 8162147872491bb5b48e91543b19c49a29ae6b6d)
diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c
index bbe754a272b97d91..8f3f687f2a151d16 100644
--- a/nptl/pthread_mutex_lock.c
+++ b/nptl/pthread_mutex_lock.c
@@ -139,14 +139,26 @@ PTHREAD_MUTEX_LOCK (pthread_mutex_t *mutex)
int cnt = 0;
int max_cnt = MIN (max_adaptive_count (),
mutex->__data.__spins * 2 + 10);
+ int spin_count, exp_backoff = 1;
+ unsigned int jitter = get_jitter ();
do
{
- if (cnt++ >= max_cnt)
+ /* In each loop, spin count is exponential backoff plus
+ random jitter, random range is [0, exp_backoff-1]. */
+ spin_count = exp_backoff + (jitter & (exp_backoff - 1));
+ cnt += spin_count;
+ if (cnt >= max_cnt)
{
+ /* If cnt exceeds max spin count, just go to wait
+ queue. */
LLL_MUTEX_LOCK (mutex);
break;
}
- atomic_spin_nop ();
+ do
+ atomic_spin_nop ();
+ while (--spin_count > 0);
+ /* Prepare for next loop. */
+ exp_backoff = get_next_backoff (exp_backoff);
}
while (LLL_MUTEX_READ_LOCK (mutex) != 0
|| LLL_MUTEX_TRYLOCK (mutex) != 0);
diff --git a/sysdeps/nptl/pthreadP.h b/sysdeps/nptl/pthreadP.h
index b968afc4c6b61b92..ed186ce3df1fde0c 100644
--- a/sysdeps/nptl/pthreadP.h
+++ b/sysdeps/nptl/pthreadP.h
@@ -34,6 +34,7 @@
#include <kernel-features.h>
#include <errno.h>
#include <internal-signals.h>
+#include <pthread_mutex_backoff.h>
#include "pthread_mutex_conf.h"
diff --git a/sysdeps/nptl/pthread_mutex_backoff.h b/sysdeps/nptl/pthread_mutex_backoff.h
new file mode 100644
index 0000000000000000..5b26c22ac789f54f
--- /dev/null
+++ b/sysdeps/nptl/pthread_mutex_backoff.h
@@ -0,0 +1,35 @@
+/* Pthread mutex backoff configuration.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+#ifndef _PTHREAD_MUTEX_BACKOFF_H
+#define _PTHREAD_MUTEX_BACKOFF_H 1
+
+static inline unsigned int
+get_jitter (void)
+{
+ /* Arch dependent random jitter, return 0 disables random. */
+ return 0;
+}
+
+static inline int
+get_next_backoff (int backoff)
+{
+ /* Next backoff, return 1 disables mutex backoff. */
+ return 1;
+}
+
+#endif
diff --git a/sysdeps/x86_64/nptl/pthread_mutex_backoff.h b/sysdeps/x86_64/nptl/pthread_mutex_backoff.h
new file mode 100644
index 0000000000000000..ec74c3d9db61864e
--- /dev/null
+++ b/sysdeps/x86_64/nptl/pthread_mutex_backoff.h
@@ -0,0 +1,39 @@
+/* Pthread mutex backoff configuration.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+#ifndef _PTHREAD_MUTEX_BACKOFF_H
+#define _PTHREAD_MUTEX_BACKOFF_H 1
+
+#include <fast-jitter.h>
+
+static inline unsigned int
+get_jitter (void)
+{
+ return get_fast_jitter ();
+}
+
+#define MAX_BACKOFF 16
+
+static inline int
+get_next_backoff (int backoff)
+{
+ /* Binary expontial backoff. Limiting max backoff
+ can reduce latency in large critical section. */
+ return (backoff < MAX_BACKOFF) ? backoff << 1 : backoff;
+}
+
+#endif

View File

@ -0,0 +1,26 @@
commit 95f5089d4a57cd1e738be908c7a628cb7d0ce512
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86: include BMI1 and BMI2 in x86-64-v3 level
The "System V Application Binary Interface AMD64 Architecture Processor
Supplement" mandates the BMI1 and BMI2 CPU features for the x86-64-v3
level.
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit b80f16adbd979831bf25ea491e1261e81885c2b6)
diff --git a/sysdeps/x86/get-isa-level.h b/sysdeps/x86/get-isa-level.h
index aa80f56ca635e54b..785c25a835edf004 100644
--- a/sysdeps/x86/get-isa-level.h
+++ b/sysdeps/x86/get-isa-level.h
@@ -47,6 +47,8 @@ get_isa_level (const struct cpu_features *cpu_features)
isa_level |= GNU_PROPERTY_X86_ISA_1_V2;
if (CPU_FEATURE_USABLE_P (cpu_features, AVX)
&& CPU_FEATURE_USABLE_P (cpu_features, AVX2)
+ && CPU_FEATURE_USABLE_P (cpu_features, BMI1)
+ && CPU_FEATURE_USABLE_P (cpu_features, BMI2)
&& CPU_FEATURE_USABLE_P (cpu_features, F16C)
&& CPU_FEATURE_USABLE_P (cpu_features, FMA)
&& CPU_FEATURE_USABLE_P (cpu_features, LZCNT)

View File

@ -0,0 +1,111 @@
commit 414fc856ff4e011e62b88a21d30294637a152dc7
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI2 for AVX2 str(n)casecmp implementations
The AVX2 str(n)casecmp implementations use the 'bzhi' instruction, which
belongs to the BMI2 CPU feature.
NB: It also uses the 'tzcnt' BMI1 instruction, but it is executed as BSF
as BSF if the CPU doesn't support TZCNT, and produces the same result
for non-zero input.
Partially fixes: b77b06e0e296 ("x86: Optimize strcmp-avx2.S")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit 10f79d3670b036925da63dc532b122d27ce65ff8)
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index 8d649e263eb24b8a..ca64b34c146a76f9 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -386,13 +386,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
IFUNC_IMPL (i, name, strcasecmp,
IFUNC_IMPL_ADD (array, i, strcasecmp,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ && CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI2)),
__strcasecmp_evex)
IFUNC_IMPL_ADD (array, i, strcasecmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__strcasecmp_avx2)
IFUNC_IMPL_ADD (array, i, strcasecmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strcasecmp_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strcasecmp,
@@ -407,13 +410,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
IFUNC_IMPL (i, name, strcasecmp_l,
IFUNC_IMPL_ADD (array, i, strcasecmp,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ && CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI2)),
__strcasecmp_l_evex)
IFUNC_IMPL_ADD (array, i, strcasecmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__strcasecmp_l_avx2)
IFUNC_IMPL_ADD (array, i, strcasecmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strcasecmp_l_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strcasecmp_l,
@@ -542,13 +548,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
IFUNC_IMPL (i, name, strncasecmp,
IFUNC_IMPL_ADD (array, i, strncasecmp,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ && CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI2)),
__strncasecmp_evex)
IFUNC_IMPL_ADD (array, i, strncasecmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__strncasecmp_avx2)
IFUNC_IMPL_ADD (array, i, strncasecmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strncasecmp_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strncasecmp,
@@ -564,13 +573,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
IFUNC_IMPL (i, name, strncasecmp_l,
IFUNC_IMPL_ADD (array, i, strncasecmp,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ & CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI2)),
__strncasecmp_l_evex)
IFUNC_IMPL_ADD (array, i, strncasecmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__strncasecmp_l_avx2)
IFUNC_IMPL_ADD (array, i, strncasecmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strncasecmp_l_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strncasecmp_l,
diff --git a/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h b/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h
index 40819caf5ab10337..e61d6e9497bce9d9 100644
--- a/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h
+++ b/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h
@@ -32,6 +32,7 @@ IFUNC_SELECTOR (void)
const struct cpu_features* cpu_features = __get_cpu_features ();
if (CPU_FEATURE_USABLE_P (cpu_features, AVX2)
+ && CPU_FEATURE_USABLE_P (cpu_features, BMI2)
&& CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load))
{
if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)

View File

@ -0,0 +1,55 @@
commit e1561d8cf005a23bcaf514802854b493829a25b1
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI2 for AVX2 strcmp implementation
The AVX2 strcmp implementation uses the 'bzhi' instruction, which
belongs to the BMI2 CPU feature.
NB: It also uses the 'tzcnt' BMI1 instruction, but it is executed as BSF
as BSF if the CPU doesn't support TZCNT, and produces the same result
for non-zero input.
Partially fixes: b77b06e0e296 ("x86: Optimize strcmp-avx2.S")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit 4d64c6445735e9b34e2ac8e369312cbfc2f88e17)
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index ca64b34c146a76f9..70931f15985334af 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -503,10 +503,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/strcmp.c. */
IFUNC_IMPL (i, name, strcmp,
IFUNC_IMPL_ADD (array, i, strcmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__strcmp_avx2)
IFUNC_IMPL_ADD (array, i, strcmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strcmp_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strcmp,
diff --git a/sysdeps/x86_64/multiarch/strcmp.c b/sysdeps/x86_64/multiarch/strcmp.c
index b457fb4c150e4407..0c0cd20a03278a2b 100644
--- a/sysdeps/x86_64/multiarch/strcmp.c
+++ b/sysdeps/x86_64/multiarch/strcmp.c
@@ -40,11 +40,11 @@ IFUNC_SELECTOR (void)
const struct cpu_features* cpu_features = __get_cpu_features ();
if (CPU_FEATURE_USABLE_P (cpu_features, AVX2)
+ && CPU_FEATURE_USABLE_P (cpu_features, BMI2)
&& CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load))
{
if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)
- && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)
- && CPU_FEATURE_USABLE_P (cpu_features, BMI2))
+ && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW))
return OPTIMIZE (evex);
if (CPU_FEATURE_USABLE_P (cpu_features, RTM))

View File

@ -0,0 +1,62 @@
commit b9cbb8dd48b545f3b36b5d411481dc0bd118ee94
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI2 for AVX2 strncmp implementation
The AVX2 strncmp implementations uses the 'bzhi' instruction, which
belongs to the BMI2 CPU feature.
NB: It also uses the 'tzcnt' BMI1 instruction, but it is executed as BSF
as BSF if the CPU doesn't support TZCNT, and produces the same result
for non-zero input.
Partially fixes: b77b06e0e296 ("x86: Optimize strcmp-avx2.S")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit fc7de1d9b99ae1676bc626ddca422d7abee0eb48)
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index 70931f15985334af..34d5f6efe5421014 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -1022,15 +1022,18 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/strncmp.c. */
IFUNC_IMPL (i, name, strncmp,
IFUNC_IMPL_ADD (array, i, strncmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__strncmp_avx2)
IFUNC_IMPL_ADD (array, i, strncmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strncmp_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strncmp,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ && CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI2)),
__strncmp_evex)
IFUNC_IMPL_ADD (array, i, strncmp, CPU_FEATURE_USABLE (SSE4_2),
__strncmp_sse42)
diff --git a/sysdeps/x86_64/multiarch/strncmp.c b/sysdeps/x86_64/multiarch/strncmp.c
index f94a421784bfe923..7632d7b2dd4447aa 100644
--- a/sysdeps/x86_64/multiarch/strncmp.c
+++ b/sysdeps/x86_64/multiarch/strncmp.c
@@ -39,11 +39,11 @@ IFUNC_SELECTOR (void)
const struct cpu_features* cpu_features = __get_cpu_features ();
if (CPU_FEATURE_USABLE_P (cpu_features, AVX2)
+ && CPU_FEATURE_USABLE_P (cpu_features, BMI2)
&& CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load))
{
if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)
- && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)
- && CPU_FEATURE_USABLE_P (cpu_features, BMI2))
+ && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW))
return OPTIMIZE (evex);
if (CPU_FEATURE_USABLE_P (cpu_features, RTM))

View File

@ -0,0 +1,51 @@
commit 67e863742d98c990b3d3b814b80042c0fa0d50a5
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI2 for AVX2 wcs(n)cmp implementations
The AVX2 wcs(n)cmp implementations use the 'bzhi' instruction, which
belongs to the BMI2 CPU feature.
NB: It also uses the 'tzcnt' BMI1 instruction, but it is executed as BSF
as BSF if the CPU doesn't support TZCNT, and produces the same result
for non-zero input.
Partially fixes: b77b06e0e296 ("x86: Optimize strcmp-avx2.S")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit f31a5a884ed84bd37032729d4d1eb9d06c9f3c29)
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index 34d5f6efe5421014..e76a991dc671c1a9 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -693,10 +693,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/wcscmp.c. */
IFUNC_IMPL (i, name, wcscmp,
IFUNC_IMPL_ADD (array, i, wcscmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__wcscmp_avx2)
IFUNC_IMPL_ADD (array, i, wcscmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__wcscmp_avx2_rtm)
IFUNC_IMPL_ADD (array, i, wcscmp,
@@ -709,10 +711,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/wcsncmp.c. */
IFUNC_IMPL (i, name, wcsncmp,
IFUNC_IMPL_ADD (array, i, wcsncmp,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__wcsncmp_avx2)
IFUNC_IMPL_ADD (array, i, wcsncmp,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__wcsncmp_avx2_rtm)
IFUNC_IMPL_ADD (array, i, wcsncmp,

View File

@ -0,0 +1,61 @@
commit 94b9c1b6409e34f3f0b2339f77d7ee78087422eb
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI2 for AVX2 (raw|w)memchr implementations
The AVX2 memchr, rawmemchr and wmemchr implementations use the 'bzhi'
and 'sarx' instructions, which belongs to the BMI2 CPU feature.
Fixes: acfd088a1963 ("x86: Optimize memchr-avx2.S")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit e3e7fab7fe5186d18ca2046d99ba321c27db30ad)
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index e76a991dc671c1a9..81640cf00664e3ed 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -41,10 +41,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/memchr.c. */
IFUNC_IMPL (i, name, memchr,
IFUNC_IMPL_ADD (array, i, memchr,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__memchr_avx2)
IFUNC_IMPL_ADD (array, i, memchr,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__memchr_avx2_rtm)
IFUNC_IMPL_ADD (array, i, memchr,
@@ -283,10 +285,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/rawmemchr.c. */
IFUNC_IMPL (i, name, rawmemchr,
IFUNC_IMPL_ADD (array, i, rawmemchr,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__rawmemchr_avx2)
IFUNC_IMPL_ADD (array, i, rawmemchr,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__rawmemchr_avx2_rtm)
IFUNC_IMPL_ADD (array, i, rawmemchr,
@@ -787,10 +791,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/wmemchr.c. */
IFUNC_IMPL (i, name, wmemchr,
IFUNC_IMPL_ADD (array, i, wmemchr,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)),
__wmemchr_avx2)
IFUNC_IMPL_ADD (array, i, wmemchr,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__wmemchr_avx2_rtm)
IFUNC_IMPL_ADD (array, i, wmemchr,

View File

@ -0,0 +1,56 @@
commit 36d6b9be3d7008a78e1f6e2e2db1947b76b206d8
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI2 and LZCNT for AVX2 memrchr implementation
The AVX2 memrchr implementation uses the 'shlxl' instruction, which
belongs to the BMI2 CPU feature and uses the 'lzcnt' instruction, which
belongs to the LZCNT CPU feature.
Fixes: af5306a735eb ("x86: Optimize memrchr-avx2.S")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit 3c0c78afabfed4b6fc161c159e628fbf14ff370b)
diff --git a/sysdeps/x86_64/multiarch/ifunc-avx2.h b/sysdeps/x86_64/multiarch/ifunc-avx2.h
index 6de72f72724b81ba..52bd00ea5cab6b22 100644
--- a/sysdeps/x86_64/multiarch/ifunc-avx2.h
+++ b/sysdeps/x86_64/multiarch/ifunc-avx2.h
@@ -31,6 +31,7 @@ IFUNC_SELECTOR (void)
if (CPU_FEATURE_USABLE_P (cpu_features, AVX2)
&& CPU_FEATURE_USABLE_P (cpu_features, BMI2)
+ && CPU_FEATURE_USABLE_P (cpu_features, LZCNT)
&& CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load))
{
if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index 81640cf00664e3ed..d1fc1e75d6706413 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -174,15 +174,21 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/memrchr.c. */
IFUNC_IMPL (i, name, memrchr,
IFUNC_IMPL_ADD (array, i, memrchr,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
+ && CPU_FEATURE_USABLE (LZCNT)),
__memrchr_avx2)
IFUNC_IMPL_ADD (array, i, memrchr,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI2)
+ && CPU_FEATURE_USABLE (LZCNT)
&& CPU_FEATURE_USABLE (RTM)),
__memrchr_avx2_rtm)
IFUNC_IMPL_ADD (array, i, memrchr,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ && CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI2)
+ && CPU_FEATURE_USABLE (LZCNT)),
__memrchr_evex)
IFUNC_IMPL_ADD (array, i, memrchr, 1, __memrchr_sse2))

View File

@ -0,0 +1,78 @@
commit e570b865b53f33453d97160791a7d97e38bcc6e8
Author: Aurelien Jarno <aurelien@aurel32.net>
Date: Mon Oct 3 23:16:46 2022 +0200
x86-64: Require BMI1/BMI2 for AVX2 strrchr and wcsrchr implementations
The AVX2 strrchr and wcsrchr implementation uses the 'blsmsk'
instruction which belongs to the BMI1 CPU feature and the 'shrx'
instruction, which belongs to the BMI2 CPU feature.
Fixes: df7e295d18ff ("x86: Optimize {str|wcs}rchr-avx2")
Partially resolves: BZ #29611
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
(cherry picked from commit 7e8283170c5d6805b609a040801d819e362a6292)
diff --git a/sysdeps/x86_64/multiarch/ifunc-avx2.h b/sysdeps/x86_64/multiarch/ifunc-avx2.h
index 52bd00ea5cab6b22..877f007dd6e38fe8 100644
--- a/sysdeps/x86_64/multiarch/ifunc-avx2.h
+++ b/sysdeps/x86_64/multiarch/ifunc-avx2.h
@@ -30,6 +30,7 @@ IFUNC_SELECTOR (void)
const struct cpu_features* cpu_features = __get_cpu_features ();
if (CPU_FEATURE_USABLE_P (cpu_features, AVX2)
+ && CPU_FEATURE_USABLE_P (cpu_features, BMI1)
&& CPU_FEATURE_USABLE_P (cpu_features, BMI2)
&& CPU_FEATURE_USABLE_P (cpu_features, LZCNT)
&& CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load))
diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
index d1fc1e75d6706413..84f9e73e2b7df816 100644
--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c
@@ -498,15 +498,21 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/strrchr.c. */
IFUNC_IMPL (i, name, strrchr,
IFUNC_IMPL_ADD (array, i, strrchr,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI1)
+ && CPU_FEATURE_USABLE (BMI2)),
__strrchr_avx2)
IFUNC_IMPL_ADD (array, i, strrchr,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI1)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__strrchr_avx2_rtm)
IFUNC_IMPL_ADD (array, i, strrchr,
(CPU_FEATURE_USABLE (AVX512VL)
- && CPU_FEATURE_USABLE (AVX512BW)),
+ && CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI1)
+ && CPU_FEATURE_USABLE (BMI2)),
__strrchr_evex)
IFUNC_IMPL_ADD (array, i, strrchr, 1, __strrchr_sse2))
@@ -687,15 +693,20 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
/* Support sysdeps/x86_64/multiarch/wcsrchr.c. */
IFUNC_IMPL (i, name, wcsrchr,
IFUNC_IMPL_ADD (array, i, wcsrchr,
- CPU_FEATURE_USABLE (AVX2),
+ (CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI1)
+ && CPU_FEATURE_USABLE (BMI2)),
__wcsrchr_avx2)
IFUNC_IMPL_ADD (array, i, wcsrchr,
(CPU_FEATURE_USABLE (AVX2)
+ && CPU_FEATURE_USABLE (BMI1)
+ && CPU_FEATURE_USABLE (BMI2)
&& CPU_FEATURE_USABLE (RTM)),
__wcsrchr_avx2_rtm)
IFUNC_IMPL_ADD (array, i, wcsrchr,
(CPU_FEATURE_USABLE (AVX512VL)
&& CPU_FEATURE_USABLE (AVX512BW)
+ && CPU_FEATURE_USABLE (BMI1)
&& CPU_FEATURE_USABLE (BMI2)),
__wcsrchr_evex)
IFUNC_IMPL_ADD (array, i, wcsrchr, 1, __wcsrchr_sse2))

View File

@ -0,0 +1,46 @@
commit e3976287b22422787f3cc6fc9adda58304b55bd9
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Tue Oct 4 18:40:25 2022 -0400
nscd: Drop local address tuple variable [BZ #29607]
When a request needs to be resent (e.g. due to insufficient buffer
space), the references to subsequent tuples in the local variable are
stale and should not be used. This used to work by accident before, but
since 1d495912a it no longer does. Instead of trying to reset it, just
let gethostbyname4_r write into TUMPBUF6 for us, thus maintaining a
consistent state at all times. This is now consistent with what is done
in gaih_inet for getaddrinfo.
Resolves: BZ #29607
Reported-by: Holger Hoffstätte <holger@applied-asynchrony.com>
Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 6e33e5c4b73cea7b8aa3de0947123db16200fb65)
diff --git a/nscd/aicache.c b/nscd/aicache.c
index 737ace11cc276021..3b300ad9b7db2297 100644
--- a/nscd/aicache.c
+++ b/nscd/aicache.c
@@ -111,11 +111,10 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
"gethostbyname4_r");
if (fct4 != NULL)
{
- struct gaih_addrtuple atmem;
struct gaih_addrtuple *at;
while (1)
{
- at = &atmem;
+ at = NULL;
rc6 = 0;
herrno = 0;
status[1] = DL_CALL_FCT (fct4, (key, &at,
@@ -138,7 +137,7 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
goto next_nip;
/* We found the data. Count the addresses and the size. */
- for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL;
+ for (const struct gaih_addrtuple *at2 = at; at2 != NULL;
at2 = at2->next)
{
++naddrs;

View File

@ -148,7 +148,7 @@ end \
Summary: The GNU libc libraries
Name: glibc
Version: %{glibcversion}
Release: 47%{?dist}
Release: 48%{?dist}
# In general, GPLv2+ is used by programs, LGPLv2+ is used for
# libraries.
@ -595,6 +595,45 @@ Patch387: glibc-rh2117712-3.patch
Patch388: glibc-rh2117712-4.patch
Patch389: glibc-rh2117712-5.patch
Patch390: glibc-rh2117712-6.patch
Patch391: glibc-upstream-2.34-309.patch
Patch392: glibc-upstream-2.34-310.patch
Patch393: glibc-upstream-2.34-311.patch
Patch394: glibc-upstream-2.34-312.patch
# glibc-2.34-313-gbc5cb538e5 backported above as glibc-rh2118666.patch.
Patch395: glibc-upstream-2.34-314.patch
Patch396: glibc-upstream-2.34-315.patch
Patch397: glibc-upstream-2.34-316.patch
Patch398: glibc-upstream-2.34-317.patch
Patch399: glibc-upstream-2.34-318.patch
Patch400: glibc-upstream-2.34-319.patch
Patch401: glibc-upstream-2.34-320.patch
Patch402: glibc-upstream-2.34-321.patch
Patch403: glibc-upstream-2.34-322.patch
Patch404: glibc-upstream-2.34-323.patch
Patch405: glibc-upstream-2.34-324.patch
Patch406: glibc-upstream-2.34-325.patch
Patch407: glibc-upstream-2.34-326.patch
Patch408: glibc-upstream-2.34-327.patch
# glibc-2.34-328-g2def56a349 conflicts with glibc-rh2096191-2.patch;
# glibc-rh2129005.patch contains the original master branch commit instead.
Patch409: glibc-rh2129005.patch
Patch410: glibc-upstream-2.34-329.patch
Patch411: glibc-upstream-2.34-330.patch
Patch412: glibc-upstream-2.34-331.patch
Patch413: glibc-upstream-2.34-332.patch
Patch414: glibc-upstream-2.34-333.patch
Patch415: glibc-upstream-2.34-334.patch
Patch416: glibc-upstream-2.34-335.patch
Patch417: glibc-upstream-2.34-336.patch
Patch418: glibc-upstream-2.34-337.patch
Patch419: glibc-upstream-2.34-338.patch
Patch420: glibc-upstream-2.34-339.patch
Patch421: glibc-upstream-2.34-340.patch
Patch422: glibc-upstream-2.34-341.patch
Patch423: glibc-upstream-2.34-342.patch
Patch424: glibc-upstream-2.34-343.patch
Patch425: glibc-upstream-2.34-344.patch
Patch426: glibc-upstream-2.34-345.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -2651,6 +2690,48 @@ fi
%files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
%changelog
* Thu Oct 13 2022 Arjun Shankar <arjun@redhat.com> - 2.34-48
- Handle non-hostname CNAME aliases during name resolution (#2129005)
- Sync with upstream branch release/2.34/master,
commit e3976287b22422787f3cc6fc9adda58304b55bd9:
- nscd: Drop local address tuple variable [BZ #29607]
- x86-64: Require BMI1/BMI2 for AVX2 strrchr and wcsrchr implementations
- x86-64: Require BMI2 and LZCNT for AVX2 memrchr implementation
- x86-64: Require BMI2 for AVX2 (raw|w)memchr implementations
- x86-64: Require BMI2 for AVX2 wcs(n)cmp implementations
- x86-64: Require BMI2 for AVX2 strncmp implementation
- x86-64: Require BMI2 for AVX2 strcmp implementation
- x86-64: Require BMI2 for AVX2 str(n)casecmp implementations
- x86: include BMI1 and BMI2 in x86-64-v3 level
- nptl: Add backoff mechanism to spinlock loop
- sysdeps: Add 'get_fast_jitter' interace in fast-jitter.h
- nptl: Effectively skip CAS in spinlock loop
- Move assignment out of the CAS condition
- Add LLL_MUTEX_READ_LOCK [BZ #28537]
- Avoid extra load with CAS in __pthread_mutex_clocklock_common [BZ #28537]
- Avoid extra load with CAS in __pthread_mutex_lock_full [BZ #28537]
- resolv: Fix building tst-resolv-invalid-cname for earlier C standards
- nss_dns: Rewrite _nss_dns_gethostbyname4_r using current interfaces
- resolv: Add new tst-resolv-invalid-cname
- nss_dns: In gaih_getanswer_slice, skip strange aliases (bug 12154)
(#2129005)
- nss_dns: Rewrite getanswer_r to match getanswer_ptr (bug 12154, bug 29305)
- nss_dns: Remove remnants of IPv6 address mapping
- nss_dns: Rewrite _nss_dns_gethostbyaddr2_r and getanswer_ptr
- nss_dns: Split getanswer_ptr from getanswer_r
- resolv: Add DNS packet parsing helpers geared towards wire format
- resolv: Add internal __ns_name_length_uncompressed function
- resolv: Add the __ns_samebinaryname function
- resolv: Add internal __res_binary_hnok function
- resolv: Add tst-resolv-aliases
- resolv: Add tst-resolv-byaddr for testing reverse lookup
- gconv: Use 64-bit interfaces in gconv_parseconfdir (bug 29583)
- elf: Fix hwcaps string size overestimation
- nscd: Fix netlink cache invalidation if epoll is used [BZ #29415]
- Apply asm redirections in wchar.h before first use
- Apply asm redirections in stdio.h before first use [BZ #27087]
- elf: Call __libc_early_init for reused namespaces (bug 29528)
* Tue Oct 11 2022 Florian Weimer <fweimer@redhat.com> - 2.34-47
- Simplify the glibc system call profile (#2117712)