commit e9f89fc74dc5e9d764e2793848fc85212051bfef Author: Tim Biermann <tbier@posteo.de> Date: Sat Oct 1 16:18:37 2022 +0200 [notify] glibc-32: upstream fixes for CVE-2022-39046 diff --git a/glibc-32/.signature b/glibc-32/.signature index d9170707..346961ce 100644 --- a/glibc-32/.signature +++ b/glibc-32/.signature @@ -1,8 +1,8 @@ untrusted comment: verify with /etc/ports/core.pub -RWRJc1FUaeVeqjo9bPJnyNtbs00H4I0jpMELqe2PP+oDIRcMcYWSUMgIXE/1f9tv6mDdScYxj2ZfG7y1zNzy7xFG0Hb21bcGcAM= -SHA256 (Pkgfile) = 5e08a30996fa14ee5d1e224e95bb393cf0d17944d6a37b78105d0400beeb523c +RWRJc1FUaeVeqo/yaq0daCyYMKbPUqcBeSMqGa3hu37jZ+RoLsJiclGtvbuNnEcVkZvlFgQvjramjaNCA/9u9K76uZKanzOApQw= +SHA256 (Pkgfile) = bf4f6b622c76910babef8944d76a95bc7ef5009cedd99aca864e698b44d09a10 SHA256 (.footprint) = f676700a19f936a1af944e81a516dbf182723d6ac244eadabd3fd19e9a01daa5 SHA256 (glibc-2.36.tar.xz) = 1c959fea240906226062cb4b1e7ebce71a9f0e3c0836c09e7e3423d434fcfe75 SHA256 (linux-5.15.55.tar.xz) = 1ef6bd508b6c3af3bef2d5b337e4477254dba284c79e329aa38f9763ae3bfdcc -SHA256 (glibc-2.36-1.patch) = 6245087ab20402836cbd32b52ba944d31aeafc961ba597bcec0fa3727db79eee +SHA256 (glibc-2.36-2.patch) = 4760e63dc0539952673bae08f3380608b53c74c9726e7f3137979f0f776d2a80 SHA256 (lib32.conf) = 2f174d2bcefe1c29327690514f34d6970fffdd54398320ca23a11b5f1e3c9b2d diff --git a/glibc-32/Pkgfile b/glibc-32/Pkgfile index 4b9baf9c..f6a5a858 100644 --- a/glibc-32/Pkgfile +++ b/glibc-32/Pkgfile @@ -4,12 +4,12 @@ name=glibc-32 version=2.36 -release=1 +release=2 _kernel_version=5.15.55 source=(https://ftp.gnu.org/gnu/glibc/glibc-$version.tar.xz https://www.kernel.org/pub/linux/kernel/v5.x/linux-$_kernel_version.tar.xz - glibc-$version-1.patch lib32.conf) + glibc-$version-2.patch lib32.conf) build() { # install kernel headers @@ -18,7 +18,7 @@ build() { make -C $SRC/linux-$_kernel_version INSTALL_HDR_PATH=$PKG/usr headers_install chown root:root $PKG/usr - patch -p1 -d $SRC/glibc-${version:0:4} -i $SRC/glibc-$version-1.patch + patch -p1 -d $SRC/glibc-${version:0:4} -i $SRC/glibc-$version-2.patch mkdir $SRC/build cd $SRC/build diff --git a/glibc-32/glibc-2.36-1.patch b/glibc-32/glibc-2.36-1.patch deleted file mode 100644 index 36bf0075..00000000 --- a/glibc-32/glibc-2.36-1.patch +++ /dev/null @@ -1,985 +0,0 @@ -diff --git a/NEWS b/NEWS -index f61e521fc8..ae30900bbc 100644 ---- a/NEWS -+++ b/NEWS -@@ -4,6 +4,16 @@ See the end for copying conditions. - - Please send GNU C library bug reports via <https://sourceware.org/bugzilla/> - using `glibc' in the "product" field. -+ -+Version 2.36.1 -+ -+The following bugs are resolved with this release: -+ -+ [28846] CMSG_NXTHDR may trigger -Wstrict-overflow warning -+ [29446] _dlopen now ignores dl_caller argument in static mode -+ [29485] Linux: Terminate subprocess on late failure in tst-pidfd -+ [29490] alpha: New __brk_call implementation is broken -+ - - Version 2.36 - -diff --git a/bits/socket.h b/bits/socket.h -index 2b99dea33b..aac8c49b00 100644 ---- a/bits/socket.h -+++ b/bits/socket.h -@@ -245,6 +245,12 @@ struct cmsghdr - + CMSG_ALIGN (sizeof (struct cmsghdr))) - #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) - -+/* Given a length, return the additional padding necessary such that -+ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ -+#define __CMSG_PADDING(len) ((sizeof (size_t) \ -+ - ((len) & (sizeof (size_t) - 1))) \ -+ & (sizeof (size_t) - 1)) -+ - extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, - struct cmsghdr *__cmsg) __THROW; - #ifdef __USE_EXTERN_INLINES -@@ -254,18 +260,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, - _EXTERN_INLINE struct cmsghdr * - __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) - { -+ /* We may safely assume that __cmsg lies between __mhdr->msg_control and -+ __mhdr->msg_controllen because the user is required to obtain the first -+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs -+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet -+ trust the value of __cmsg->cmsg_len and therefore do not use it in any -+ pointer arithmetic until we check its value. */ -+ -+ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; -+ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; -+ -+ size_t __size_needed = sizeof (struct cmsghdr) -+ + __CMSG_PADDING (__cmsg->cmsg_len); -+ -+ /* The current header is malformed, too small to be a full header. */ - if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) -- /* The kernel header does this so there may be a reason. */ - return (struct cmsghdr *) 0; - -+ /* There isn't enough space between __cmsg and the end of the buffer to -+ hold the current cmsg *and* the next one. */ -+ if (((size_t) -+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) -+ < __size_needed) -+ || ((size_t) -+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr -+ - __size_needed) -+ < __cmsg->cmsg_len)) -+ -+ return (struct cmsghdr *) 0; -+ -+ /* Now, we trust cmsg_len and can use it to find the next header. */ - __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg - + CMSG_ALIGN (__cmsg->cmsg_len)); -- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control -- + __mhdr->msg_controllen) -- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) -- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) -- /* No more entries. */ -- return (struct cmsghdr *) 0; - return __cmsg; - } - #endif /* Use `extern inline'. */ -diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c -index 2696dde4b1..9b07b4e132 100644 ---- a/dlfcn/dlopen.c -+++ b/dlfcn/dlopen.c -@@ -90,7 +90,7 @@ compat_symbol (libdl, ___dlopen, dlopen, GLIBC_2_1); - void * - __dlopen (const char *file, int mode, void *dl_caller) - { -- return dlopen_implementation (file, mode, RETURN_ADDRESS (0)); -+ return dlopen_implementation (file, mode, dl_caller); - } - - void * -diff --git a/elf/dl-cache.c b/elf/dl-cache.c -index 8bbf110d02..b97c17b3a9 100644 ---- a/elf/dl-cache.c -+++ b/elf/dl-cache.c -@@ -509,8 +509,9 @@ _dl_load_cache_lookup (const char *name) - we are accessing. Therefore we must make the copy of the - mapping data without using malloc. */ - char *temp; -- temp = alloca (strlen (best) + 1); -- strcpy (temp, best); -+ size_t best_len = strlen (best) + 1; -+ temp = alloca (best_len); -+ memcpy (temp, best, best_len); - return __strdup (temp); - } - -diff --git a/scripts/glibcextract.py b/scripts/glibcextract.py -index 43ab58ffe2..36d204c9b0 100644 ---- a/scripts/glibcextract.py -+++ b/scripts/glibcextract.py -@@ -17,6 +17,7 @@ - # License along with the GNU C Library; if not, see - # <https://www.gnu.org/licenses/>. - -+import collections - import os.path - import re - import subprocess -@@ -173,3 +174,21 @@ def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None, - if not allow_extra_2: - ret = 1 - return ret -+ -+CompileResult = collections.namedtuple("CompileResult", "returncode output") -+ -+def compile_c_snippet(snippet, cc, extra_cc_args=''): -+ """Compile and return whether the SNIPPET can be build with CC along -+ EXTRA_CC_ARGS compiler flags. Return a CompileResult with RETURNCODE -+ being 0 for success, or the failure value and the compiler output. -+ """ -+ with tempfile.TemporaryDirectory() as temp_dir: -+ c_file_name = os.path.join(temp_dir, 'test.c') -+ obj_file_name = os.path.join(temp_dir, 'test.o') -+ with open(c_file_name, 'w') as c_file: -+ c_file.write(snippet + '\n') -+ cmd = cc.split() + extra_cc_args.split() + ['-c', '-o', obj_file_name, -+ c_file_name] -+ r = subprocess.run(cmd, check=False, stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT) -+ return CompileResult(r.returncode, r.stdout) -diff --git a/socket/Makefile b/socket/Makefile -index 156eec6c85..2bde78387f 100644 ---- a/socket/Makefile -+++ b/socket/Makefile -@@ -34,6 +34,7 @@ routines := accept bind connect getpeername getsockname getsockopt \ - tests := \ - tst-accept4 \ - tst-sockopt \ -+ tst-cmsghdr \ - # tests - - tests-internal := \ -diff --git a/socket/tst-cmsghdr-skeleton.c b/socket/tst-cmsghdr-skeleton.c -new file mode 100644 -index 0000000000..4c6898569b ---- /dev/null -+++ b/socket/tst-cmsghdr-skeleton.c -@@ -0,0 +1,92 @@ -+/* Test ancillary data header creation. -+ 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/>. */ -+ -+/* We use the preprocessor to generate the function/macro tests instead of -+ using indirection because having all the macro expansions alongside -+ each other lets the compiler warn us about suspicious pointer -+ arithmetic across subsequent CMSG_{FIRST,NXT}HDR expansions. */ -+ -+#include <stdint.h> -+ -+#define RUN_TEST_CONCAT(suffix) run_test_##suffix -+#define RUN_TEST_FUNCNAME(suffix) RUN_TEST_CONCAT (suffix) -+ -+static void -+RUN_TEST_FUNCNAME (CMSG_NXTHDR_IMPL) (void) -+{ -+ struct msghdr m = {0}; -+ struct cmsghdr *cmsg; -+ char cmsgbuf[3 * CMSG_SPACE (sizeof (PAYLOAD))] = {0}; -+ -+ m.msg_control = cmsgbuf; -+ m.msg_controllen = sizeof (cmsgbuf); -+ -+ /* First header should point to the start of the buffer. */ -+ cmsg = CMSG_FIRSTHDR (&m); -+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); -+ -+ /* If the first header length consumes the entire buffer, there is no -+ space remaining for additional headers. */ -+ cmsg->cmsg_len = sizeof (cmsgbuf); -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg == NULL); -+ -+ /* The first header length is so big, using it would cause an overflow. */ -+ cmsg = CMSG_FIRSTHDR (&m); -+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); -+ cmsg->cmsg_len = SIZE_MAX; -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg == NULL); -+ -+ /* The first header leaves just enough space to hold another header. */ -+ cmsg = CMSG_FIRSTHDR (&m); -+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); -+ cmsg->cmsg_len = sizeof (cmsgbuf) - sizeof (struct cmsghdr); -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg != NULL); -+ -+ /* The first header leaves space but not enough for another header. */ -+ cmsg = CMSG_FIRSTHDR (&m); -+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); -+ cmsg->cmsg_len ++; -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg == NULL); -+ -+ /* The second header leaves just enough space to hold another header. */ -+ cmsg = CMSG_FIRSTHDR (&m); -+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); -+ cmsg->cmsg_len = CMSG_LEN (sizeof (PAYLOAD)); -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg != NULL); -+ cmsg->cmsg_len = sizeof (cmsgbuf) -+ - CMSG_SPACE (sizeof (PAYLOAD)) /* First header. */ -+ - sizeof (struct cmsghdr); -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg != NULL); -+ -+ /* The second header leaves space but not enough for another header. */ -+ cmsg = CMSG_FIRSTHDR (&m); -+ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg != NULL); -+ cmsg->cmsg_len ++; -+ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); -+ TEST_VERIFY_EXIT (cmsg == NULL); -+ -+ return; -+} -diff --git a/socket/tst-cmsghdr.c b/socket/tst-cmsghdr.c -new file mode 100644 -index 0000000000..68c96d3c9d ---- /dev/null -+++ b/socket/tst-cmsghdr.c -@@ -0,0 +1,56 @@ -+/* Test ancillary data header creation. -+ 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 <sys/socket.h> -+#include <gnu/lib-names.h> -+#include <support/xdlfcn.h> -+#include <support/check.h> -+ -+#define PAYLOAD "Hello, World!" -+ -+/* CMSG_NXTHDR is a macro that calls an inline function defined in -+ bits/socket.h. In case the function cannot be inlined, libc.so carries -+ a copy. Both versions need to be tested. */ -+ -+#define CMSG_NXTHDR_IMPL CMSG_NXTHDR -+#include "tst-cmsghdr-skeleton.c" -+#undef CMSG_NXTHDR_IMPL -+ -+static struct cmsghdr * (* cmsg_nxthdr) (struct msghdr *, struct cmsghdr *); -+ -+#define CMSG_NXTHDR_IMPL cmsg_nxthdr -+#include "tst-cmsghdr-skeleton.c" -+#undef CMSG_NXTHDR_IMPL -+ -+static int -+do_test (void) -+{ -+ static void *handle; -+ -+ run_test_CMSG_NXTHDR (); -+ -+ handle = xdlopen (LIBC_SO, RTLD_LAZY); -+ cmsg_nxthdr = (struct cmsghdr * (*) (struct msghdr *, struct cmsghdr *)) -+ xdlsym (handle, "__cmsg_nxthdr"); -+ -+ run_test_cmsg_nxthdr (); -+ -+ return 0; -+} -+ -+#include <support/test-driver.c> -diff --git a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h -index 5b35ea81ec..70fce4fb27 100644 ---- a/sysdeps/mach/hurd/bits/socket.h -+++ b/sysdeps/mach/hurd/bits/socket.h -@@ -249,6 +249,12 @@ struct cmsghdr - + CMSG_ALIGN (sizeof (struct cmsghdr))) - #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) - -+/* Given a length, return the additional padding necessary such that -+ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ -+#define __CMSG_PADDING(len) ((sizeof (size_t) \ -+ - ((len) & (sizeof (size_t) - 1))) \ -+ & (sizeof (size_t) - 1)) -+ - extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, - struct cmsghdr *__cmsg) __THROW; - #ifdef __USE_EXTERN_INLINES -@@ -258,18 +264,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, - _EXTERN_INLINE struct cmsghdr * - __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) - { -+ /* We may safely assume that __cmsg lies between __mhdr->msg_control and -+ __mhdr->msg_controllen because the user is required to obtain the first -+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs -+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet -+ trust the value of __cmsg->cmsg_len and therefore do not use it in any -+ pointer arithmetic until we check its value. */ -+ -+ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; -+ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; -+ -+ size_t __size_needed = sizeof (struct cmsghdr) -+ + __CMSG_PADDING (__cmsg->cmsg_len); -+ -+ /* The current header is malformed, too small to be a full header. */ - if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) -- /* The kernel header does this so there may be a reason. */ - return (struct cmsghdr *) 0; - -+ /* There isn't enough space between __cmsg and the end of the buffer to -+ hold the current cmsg *and* the next one. */ -+ if (((size_t) -+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) -+ < __size_needed) -+ || ((size_t) -+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr -+ - __size_needed) -+ < __cmsg->cmsg_len)) -+ -+ return (struct cmsghdr *) 0; -+ -+ /* Now, we trust cmsg_len and can use it to find the next header. */ - __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg - + CMSG_ALIGN (__cmsg->cmsg_len)); -- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control -- + __mhdr->msg_controllen) -- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) -- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) -- /* No more entries. */ -- return (struct cmsghdr *) 0; - return __cmsg; - } - #endif /* Use `extern inline'. */ -diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile -index a139a16532..3ceda9fdbf 100644 ---- a/sysdeps/unix/sysv/linux/Makefile -+++ b/sysdeps/unix/sysv/linux/Makefile -@@ -265,6 +265,14 @@ $(objpfx)tst-mount-consts.out: ../sysdeps/unix/sysv/linux/tst-mount-consts.py - < /dev/null > $@ 2>&1; $(evaluate-test) - $(objpfx)tst-mount-consts.out: $(sysdeps-linux-python-deps) - -+tests-special += $(objpfx)tst-mount-compile.out -+$(objpfx)tst-mount-compile.out: ../sysdeps/unix/sysv/linux/tst-mount-compile.py -+ $(sysdeps-linux-python) \ -+ ../sysdeps/unix/sysv/linux/tst-mount-compile.py \ -+ $(sysdeps-linux-python-cc) \ -+ < /dev/null > $@ 2>&1; $(evaluate-test) -+$(objpfx)tst-mount-compile.out: $(sysdeps-linux-python-deps) -+ - tst-rseq-disable-ENV = GLIBC_TUNABLES=glibc.pthread.rseq=0 - - endif # $(subdir) == misc -diff --git a/sysdeps/unix/sysv/linux/alpha/brk_call.h b/sysdeps/unix/sysv/linux/alpha/brk_call.h -index b8088cf13f..0b851b6c86 100644 ---- a/sysdeps/unix/sysv/linux/alpha/brk_call.h -+++ b/sysdeps/unix/sysv/linux/alpha/brk_call.h -@@ -21,8 +21,7 @@ __brk_call (void *addr) - { - unsigned long int result = INTERNAL_SYSCALL_CALL (brk, addr); - if (result == -ENOMEM) -- /* Mimic the default error reporting behavior. */ -- return addr; -- else -- return (void *) result; -+ /* Mimic the generic error reporting behavior. */ -+ result = INTERNAL_SYSCALL_CALL (brk, 0); -+ return (void *) result; - } -diff --git a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h -index 4f1f810ea1..539b8d7716 100644 ---- a/sysdeps/unix/sysv/linux/bits/socket.h -+++ b/sysdeps/unix/sysv/linux/bits/socket.h -@@ -307,6 +307,12 @@ struct cmsghdr - + CMSG_ALIGN (sizeof (struct cmsghdr))) - #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) - -+/* Given a length, return the additional padding necessary such that -+ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ -+#define __CMSG_PADDING(len) ((sizeof (size_t) \ -+ - ((len) & (sizeof (size_t) - 1))) \ -+ & (sizeof (size_t) - 1)) -+ - extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, - struct cmsghdr *__cmsg) __THROW; - #ifdef __USE_EXTERN_INLINES -@@ -316,18 +322,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, - _EXTERN_INLINE struct cmsghdr * - __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) - { -+ /* We may safely assume that __cmsg lies between __mhdr->msg_control and -+ __mhdr->msg_controllen because the user is required to obtain the first -+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs -+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet -+ trust the value of __cmsg->cmsg_len and therefore do not use it in any -+ pointer arithmetic until we check its value. */ -+ -+ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; -+ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; -+ -+ size_t __size_needed = sizeof (struct cmsghdr) -+ + __CMSG_PADDING (__cmsg->cmsg_len); -+ -+ /* The current header is malformed, too small to be a full header. */ - if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) -- /* The kernel header does this so there may be a reason. */ - return (struct cmsghdr *) 0; - -+ /* There isn't enough space between __cmsg and the end of the buffer to -+ hold the current cmsg *and* the next one. */ -+ if (((size_t) -+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) -+ < __size_needed) -+ || ((size_t) -+ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr -+ - __size_needed) -+ < __cmsg->cmsg_len)) -+ -+ return (struct cmsghdr *) 0; -+ -+ /* Now, we trust cmsg_len and can use it to find the next header. */ - __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg - + CMSG_ALIGN (__cmsg->cmsg_len)); -- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control -- + __mhdr->msg_controllen) -- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) -- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) -- /* No more entries. */ -- return (struct cmsghdr *) 0; - return __cmsg; - } - #endif /* Use `extern inline'. */ -diff --git a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c -index 15b7a3a925..24f72b797a 100644 ---- a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c -+++ b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c -@@ -23,18 +23,38 @@ - struct cmsghdr * - __cmsg_nxthdr (struct msghdr *mhdr, struct cmsghdr *cmsg) - { -+ /* We may safely assume that cmsg lies between mhdr->msg_control and -+ mhdr->msg_controllen because the user is required to obtain the first -+ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs -+ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet -+ trust the value of cmsg->cmsg_len and therefore do not use it in any -+ pointer arithmetic until we check its value. */ -+ -+ unsigned char * msg_control_ptr = (unsigned char *) mhdr->msg_control; -+ unsigned char * cmsg_ptr = (unsigned char *) cmsg; -+ -+ size_t size_needed = sizeof (struct cmsghdr) -+ + __CMSG_PADDING (cmsg->cmsg_len); -+ -+ /* The current header is malformed, too small to be a full header. */ - if ((size_t) cmsg->cmsg_len < sizeof (struct cmsghdr)) -- /* The kernel header does this so there may be a reason. */ -- return NULL; -+ return (struct cmsghdr *) 0; -+ -+ /* There isn't enough space between cmsg and the end of the buffer to -+ hold the current cmsg *and* the next one. */ -+ if (((size_t) -+ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr) -+ < size_needed) -+ || ((size_t) -+ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr -+ - size_needed) -+ < cmsg->cmsg_len)) -+ -+ return (struct cmsghdr *) 0; - -+ /* Now, we trust cmsg_len and can use it to find the next header. */ - cmsg = (struct cmsghdr *) ((unsigned char *) cmsg - + CMSG_ALIGN (cmsg->cmsg_len)); -- if ((unsigned char *) (cmsg + 1) > ((unsigned char *) mhdr->msg_control -- + mhdr->msg_controllen) -- || ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len) -- > ((unsigned char *) mhdr->msg_control + mhdr->msg_controllen))) -- /* No more entries. */ -- return NULL; - return cmsg; - } - libc_hidden_def (__cmsg_nxthdr) -diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h -index bf4be80f8d..202520ee25 100644 ---- a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h -+++ b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h -@@ -122,6 +122,7 @@ - #define __NR_mbind 235 - #define __NR_membarrier 283 - #define __NR_memfd_create 279 -+#define __NR_memfd_secret 447 - #define __NR_migrate_pages 238 - #define __NR_mincore 232 - #define __NR_mkdirat 34 -diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h -index d656aedcc2..4e65f337d4 100644 ---- a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h -+++ b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h -@@ -127,6 +127,7 @@ - #define __NR_mbind 235 - #define __NR_membarrier 283 - #define __NR_memfd_create 279 -+#define __NR_memfd_secret 447 - #define __NR_migrate_pages 238 - #define __NR_mincore 232 - #define __NR_mkdirat 34 -diff --git a/sysdeps/unix/sysv/linux/sys/mount.h b/sysdeps/unix/sysv/linux/sys/mount.h -index f965986ba8..19841d0738 100644 ---- a/sysdeps/unix/sysv/linux/sys/mount.h -+++ b/sysdeps/unix/sysv/linux/sys/mount.h -@@ -27,77 +27,113 @@ - #include <stddef.h> - #include <sys/ioctl.h> - --#define BLOCK_SIZE 1024 -+#ifdef __has_include -+# if __has_include ("linux/mount.h") -+# include "linux/mount.h" -+# endif -+#endif -+ -+ - #define BLOCK_SIZE_BITS 10 -+#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS) - - - /* These are the fs-independent mount-flags: up to 16 flags are - supported */ - enum - { -+#undef MS_RDONLY - MS_RDONLY = 1, /* Mount read-only. */ - #define MS_RDONLY MS_RDONLY -+#undef MS_NOSUID - MS_NOSUID = 2, /* Ignore suid and sgid bits. */ - #define MS_NOSUID MS_NOSUID -+#undef MS_NODEV - MS_NODEV = 4, /* Disallow access to device special files. */ - #define MS_NODEV MS_NODEV -+#undef MS_NOEXEC - MS_NOEXEC = 8, /* Disallow program execution. */ - #define MS_NOEXEC MS_NOEXEC -+#undef MS_SYNCHRONOUS - MS_SYNCHRONOUS = 16, /* Writes are synced at once. */ - #define MS_SYNCHRONOUS MS_SYNCHRONOUS -+#undef MS_REMOUNT - MS_REMOUNT = 32, /* Alter flags of a mounted FS. */ - #define MS_REMOUNT MS_REMOUNT -+#undef MS_MANDLOCK - MS_MANDLOCK = 64, /* Allow mandatory locks on an FS. */ - #define MS_MANDLOCK MS_MANDLOCK -+#undef MS_DIRSYNC - MS_DIRSYNC = 128, /* Directory modifications are synchronous. */ - #define MS_DIRSYNC MS_DIRSYNC -+#undef MS_NOSYMFOLLOW - MS_NOSYMFOLLOW = 256, /* Do not follow symlinks. */ - #define MS_NOSYMFOLLOW MS_NOSYMFOLLOW -+#undef MS_NOATIME - MS_NOATIME = 1024, /* Do not update access times. */ - #define MS_NOATIME MS_NOATIME -+#undef MS_NODIRATIME - MS_NODIRATIME = 2048, /* Do not update directory access times. */ - #define MS_NODIRATIME MS_NODIRATIME -+#undef MS_BIND - MS_BIND = 4096, /* Bind directory at different place. */ - #define MS_BIND MS_BIND -+#undef MS_MOVE - MS_MOVE = 8192, - #define MS_MOVE MS_MOVE -+#undef MS_REC - MS_REC = 16384, - #define MS_REC MS_REC -+#undef MS_SILENT - MS_SILENT = 32768, - #define MS_SILENT MS_SILENT -+#undef MS_POSIXACL - MS_POSIXACL = 1 << 16, /* VFS does not apply the umask. */ - #define MS_POSIXACL MS_POSIXACL -+#undef MS_UNBINDABLE - MS_UNBINDABLE = 1 << 17, /* Change to unbindable. */ - #define MS_UNBINDABLE MS_UNBINDABLE -+#undef MS_PRIVATE - MS_PRIVATE = 1 << 18, /* Change to private. */ - #define MS_PRIVATE MS_PRIVATE -+#undef MS_SLAVE - MS_SLAVE = 1 << 19, /* Change to slave. */ - #define MS_SLAVE MS_SLAVE -+#undef MS_SHARED - MS_SHARED = 1 << 20, /* Change to shared. */ - #define MS_SHARED MS_SHARED -+#undef MS_RELATIME - MS_RELATIME = 1 << 21, /* Update atime relative to mtime/ctime. */ - #define MS_RELATIME MS_RELATIME -+#undef MS_KERNMOUNT - MS_KERNMOUNT = 1 << 22, /* This is a kern_mount call. */ - #define MS_KERNMOUNT MS_KERNMOUNT -+#undef MS_I_VERSION - MS_I_VERSION = 1 << 23, /* Update inode I_version field. */ - #define MS_I_VERSION MS_I_VERSION -+#undef MS_STRICTATIME - MS_STRICTATIME = 1 << 24, /* Always perform atime updates. */ - #define MS_STRICTATIME MS_STRICTATIME -+#undef MS_LAZYTIME - MS_LAZYTIME = 1 << 25, /* Update the on-disk [acm]times lazily. */ - #define MS_LAZYTIME MS_LAZYTIME -+#undef MS_ACTIVE - MS_ACTIVE = 1 << 30, - #define MS_ACTIVE MS_ACTIVE -+#undef MS_NOUSER - MS_NOUSER = 1 << 31 - #define MS_NOUSER MS_NOUSER - }; - - /* Flags that can be altered by MS_REMOUNT */ -+#undef MS_RMT_MASK - #define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION \ - |MS_LAZYTIME) - - - /* Magic mount flag number. Has to be or-ed to the flag values. */ - -+#undef MS_MGC_VAL - #define MS_MGC_VAL 0xc0ed0000 /* Magic flag number to indicate "new" flags */ - #define MS_MGC_MSK 0xffff0000 /* Magic flag number mask */ - -@@ -106,20 +142,35 @@ enum - is probably as bad and I don't want to create yet another include - file. */ - -+#undef BLKROSET - #define BLKROSET _IO(0x12, 93) /* Set device read-only (0 = read-write). */ -+#undef BLKROGET - #define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */ -+#undef BLKRRPART - #define BLKRRPART _IO(0x12, 95) /* Re-read partition table. */ -+#undef BLKGETSIZE - #define BLKGETSIZE _IO(0x12, 96) /* Return device size. */ -+#undef BLKFLSBUF - #define BLKFLSBUF _IO(0x12, 97) /* Flush buffer cache. */ -+#undef BLKRASET - #define BLKRASET _IO(0x12, 98) /* Set read ahead for block device. */ -+#undef BLKRAGET - #define BLKRAGET _IO(0x12, 99) /* Get current read ahead setting. */ -+#undef BLKFRASET - #define BLKFRASET _IO(0x12,100) /* Set filesystem read-ahead. */ -+#undef BLKFRAGET - #define BLKFRAGET _IO(0x12,101) /* Get filesystem read-ahead. */ -+#undef BLKSECTSET - #define BLKSECTSET _IO(0x12,102) /* Set max sectors per request. */ -+#undef BLKSECTGET - #define BLKSECTGET _IO(0x12,103) /* Get max sectors per request. */ -+#undef BLKSSZGET - #define BLKSSZGET _IO(0x12,104) /* Get block device sector size. */ -+#undef BLKBSZGET - #define BLKBSZGET _IOR(0x12,112,size_t) -+#undef BLKBSZSET - #define BLKBSZSET _IOW(0x12,113,size_t) -+#undef BLKGETSIZE64 - #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size. */ - - -@@ -137,9 +188,6 @@ enum - }; - - --/* fsopen flags. */ --#define FSOPEN_CLOEXEC 0x00000001 -- - /* fsmount flags. */ - #define FSMOUNT_CLOEXEC 0x00000001 - -@@ -157,6 +205,7 @@ enum - #define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks. */ - - -+#ifndef MOUNT_ATTR_SIZE_VER0 - /* For mount_setattr. */ - struct mount_attr - { -@@ -165,6 +214,7 @@ struct mount_attr - uint64_t propagation; - uint64_t userns_fd; - }; -+#endif - - #define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */ - -@@ -185,26 +235,31 @@ struct mount_attr - #define FSPICK_EMPTY_PATH 0x00000008 - - -+#ifndef FSOPEN_CLOEXEC - /* The type of fsconfig call made. */ - enum fsconfig_command - { - FSCONFIG_SET_FLAG = 0, /* Set parameter, supplying no value */ --#define FSCONFIG_SET_FLAG FSCONFIG_SET_FLAG -+# define FSCONFIG_SET_FLAG FSCONFIG_SET_FLAG - FSCONFIG_SET_STRING = 1, /* Set parameter, supplying a string value */ --#define FSCONFIG_SET_STRING FSCONFIG_SET_STRING -+# define FSCONFIG_SET_STRING FSCONFIG_SET_STRING - FSCONFIG_SET_BINARY = 2, /* Set parameter, supplying a binary blob value */ --#define FSCONFIG_SET_BINARY FSCONFIG_SET_BINARY -+# define FSCONFIG_SET_BINARY FSCONFIG_SET_BINARY - FSCONFIG_SET_PATH = 3, /* Set parameter, supplying an object by path */ --#define FSCONFIG_SET_PATH FSCONFIG_SET_PATH -+# define FSCONFIG_SET_PATH FSCONFIG_SET_PATH - FSCONFIG_SET_PATH_EMPTY = 4, /* Set parameter, supplying an object by (empty) path */ --#define FSCONFIG_SET_PATH_EMPTY FSCONFIG_SET_PATH_EMPTY -+# define FSCONFIG_SET_PATH_EMPTY FSCONFIG_SET_PATH_EMPTY - FSCONFIG_SET_FD = 5, /* Set parameter, supplying an object by fd */ --#define FSCONFIG_SET_FD FSCONFIG_SET_FD -+# define FSCONFIG_SET_FD FSCONFIG_SET_FD - FSCONFIG_CMD_CREATE = 6, /* Invoke superblock creation */ --#define FSCONFIG_CMD_CREATE FSCONFIG_CMD_CREATE -+# define FSCONFIG_CMD_CREATE FSCONFIG_CMD_CREATE - FSCONFIG_CMD_RECONFIGURE = 7, /* Invoke superblock reconfiguration */ --#define FSCONFIG_CMD_RECONFIGURE FSCONFIG_CMD_RECONFIGURE -+# define FSCONFIG_CMD_RECONFIGURE FSCONFIG_CMD_RECONFIGURE - }; -+#endif -+ -+/* fsopen flags. */ -+#define FSOPEN_CLOEXEC 0x00000001 - - /* open_tree flags. */ - #define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */ -diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list -index 6c7b2f7011..028ad3107a 100644 ---- a/sysdeps/unix/sysv/linux/syscall-names.list -+++ b/sysdeps/unix/sysv/linux/syscall-names.list -@@ -21,8 +21,8 @@ - # This file can list all potential system calls. The names are only - # used if the installed kernel headers also provide them. - --# The list of system calls is current as of Linux 5.18. --kernel 5.18 -+# The list of system calls is current as of Linux 5.19. -+kernel 5.19 - - FAST_atomic_update - FAST_cmpxchg -diff --git a/sysdeps/unix/sysv/linux/tst-mount-compile.py b/sysdeps/unix/sysv/linux/tst-mount-compile.py -new file mode 100755 -index 0000000000..0ec74d4e0b ---- /dev/null -+++ b/sysdeps/unix/sysv/linux/tst-mount-compile.py -@@ -0,0 +1,66 @@ -+#!/usr/bin/python3 -+# Check if glibc provided sys/mount.h can be used along related kernel -+# headers. -+# 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/>. -+ -+import argparse -+import sys -+ -+import glibcextract -+ -+ -+def main(): -+ """The main entry point.""" -+ parser = argparse.ArgumentParser( -+ description='Check if glibc provided sys/mount.h can be ' -+ ' used along related kernel headers.') -+ parser.add_argument('--cc', metavar='CC', -+ help='C compiler (including options) to use') -+ args = parser.parse_args() -+ -+ if glibcextract.compile_c_snippet( -+ '#include <linux/mount.h>', -+ args.cc).returncode != 0: -+ sys.exit (77) -+ -+ def check(testname, snippet): -+ # Add -Werror to catch macro redefinitions and _ISOMAC to avoid -+ # internal glibc definitions. -+ r = glibcextract.compile_c_snippet(snippet, args.cc, -+ '-Werror -D_ISOMAC') -+ if r.returncode != 0: -+ print('error: test {}:\n{}'.format(testname, r.output.decode())) -+ return r.returncode -+ -+ status = max( -+ check("sys/mount.h + linux/mount.h", -+ "#include <sys/mount.h>\n" -+ "#include <linux/mount.h>"), -+ check("sys/mount.h + linux/fs.h", -+ "#include <sys/mount.h>\n" -+ "#include <linux/fs.h>"), -+ check("linux/mount.h + sys/mount.h", -+ "#include <linux/mount.h>\n" -+ "#include <sys/mount.h>"), -+ check("linux/fs.h + sys/mount.h", -+ "#include <linux/fs.h>\n" -+ "#include <sys/mount.h>")) -+ sys.exit(status) -+ -+if __name__ == '__main__': -+ main() -diff --git a/sysdeps/unix/sysv/linux/tst-mount-consts.py b/sysdeps/unix/sysv/linux/tst-mount-consts.py -index a62f803123..be2ef2daf1 100755 ---- a/sysdeps/unix/sysv/linux/tst-mount-consts.py -+++ b/sysdeps/unix/sysv/linux/tst-mount-consts.py -@@ -33,6 +33,11 @@ def main(): - help='C compiler (including options) to use') - args = parser.parse_args() - -+ if glibcextract.compile_c_snippet( -+ '#include <linux/mount.h>', -+ args.cc).returncode != 0: -+ sys.exit (77) -+ - linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) - # Constants in glibc were updated to match Linux v5.16. When glibc - # constants are updated this value should be updated to match the -diff --git a/sysdeps/unix/sysv/linux/tst-pidfd-consts.py b/sysdeps/unix/sysv/linux/tst-pidfd-consts.py -index 90cbb9be64..d732173abd 100644 ---- a/sysdeps/unix/sysv/linux/tst-pidfd-consts.py -+++ b/sysdeps/unix/sysv/linux/tst-pidfd-consts.py -@@ -33,11 +33,13 @@ def main(): - help='C compiler (including options) to use') - args = parser.parse_args() - -- linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) -- # Linux started to provide pidfd.h with 5.10. -- if linux_version_headers < (5, 10): -+ if glibcextract.compile_c_snippet( -+ '#include <linux/pidfd.h>', -+ args.cc).returncode != 0: - sys.exit (77) -- linux_version_glibc = (5, 18) -+ -+ linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) -+ linux_version_glibc = (5, 19) - sys.exit(glibcextract.compare_macro_consts( - '#include <sys/pidfd.h>\n', - '#include <asm/fcntl.h>\n' -diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c -index 037af22290..5711d1c312 100644 ---- a/sysdeps/unix/sysv/linux/tst-pidfd.c -+++ b/sysdeps/unix/sysv/linux/tst-pidfd.c -@@ -147,8 +147,11 @@ do_test (void) - may be denied if the process doesn't have CAP_SYS_PTRACE or - if a LSM security_ptrace_access_check denies access. */ - if (fd == -1 && errno == EPERM) -- FAIL_UNSUPPORTED ("don't have permission to use pidfd_getfd on pidfd, " -- "skipping test"); -+ { -+ TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), 0); -+ FAIL_UNSUPPORTED ("don't have permission to use pidfd_getfd on pidfd, " -+ "skipping test"); -+ } - TEST_VERIFY (fd > 0); - - char *path = xasprintf ("/proc/%d/fd/%d", pid, remote_fd); -diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile -index e6b9e8743a..3d19d5556f 100644 ---- a/wcsmbs/Makefile -+++ b/wcsmbs/Makefile -@@ -73,6 +73,8 @@ $(objpfx)tst-wcstol-locale.out: $(gen-locales) - $(objpfx)tst-wcstod-nan-locale.out: $(gen-locales) - $(objpfx)tst-c16-surrogate.out: $(gen-locales) - $(objpfx)tst-c32-state.out: $(gen-locales) -+$(objpfx)test-c8rtomb.out: $(gen-locales) -+$(objpfx)test-mbrtoc8.out: $(gen-locales) - endif - - $(objpfx)tst-wcstod-round: $(libm) -diff --git a/wcsmbs/uchar.h b/wcsmbs/uchar.h -index c37e8619a0..5f7139f279 100644 ---- a/wcsmbs/uchar.h -+++ b/wcsmbs/uchar.h -@@ -34,8 +34,16 @@ - /* Declare the C2x char8_t typedef in C2x modes, but only if the C++ - __cpp_char8_t feature test macro is not defined. */ - #if __GLIBC_USE (ISOC2X) && !defined __cpp_char8_t -+#if __GNUC_PREREQ (10, 0) && defined __cplusplus -+/* Suppress the diagnostic regarding char8_t being a keyword in C++20. */ -+# pragma GCC diagnostic push -+# pragma GCC diagnostic ignored "-Wc++20-compat" -+#endif - /* Define the 8-bit character type. */ - typedef unsigned char char8_t; -+#if __GNUC_PREREQ (10, 0) && defined __cplusplus -+# pragma GCC diagnostic pop -+#endif - #endif - - #ifndef __USE_ISOCXX11 -diff -pruN glibc-2.32.orig/sysdeps/unix/sysv/linux/x86_64/64/configure glibc-2.32/sysdeps/unix/sysv/linux/x86_64/64/configure ---- glibc-2.32.orig/sysdeps/unix/sysv/linux/x86_64/64/configure 2021-09-18 21:02:32.741186019 +1000 -+++ glibc-2.32/sysdeps/unix/sysv/linux/x86_64/64/configure 2021-09-18 21:03:05.314302356 +1000 -@@ -4,10 +4,10 @@ - test -n "$libc_cv_slibdir" || - case "$prefix" in - /usr | /usr/) -- libc_cv_slibdir='/lib64' -- libc_cv_rtlddir='/lib64' -+ libc_cv_slibdir='/lib' -+ libc_cv_rtlddir='/lib' - if test "$libdir" = '${exec_prefix}/lib'; then -- libdir='${exec_prefix}/lib64'; -+ libdir='${exec_prefix}/lib'; - # Locale data can be shared between 32-bit and 64-bit libraries. - libc_cv_complocaledir='${exec_prefix}/lib/locale' - fi -diff -pruN glibc-2.32.orig/sysdeps/unix/sysv/linux/x86_64/ldconfig.h glibc-2.32/sysdeps/unix/sysv/linux/x86_64/ldconfig.h ---- glibc-2.32.orig/sysdeps/unix/sysv/linux/x86_64/ldconfig.h 2021-09-18 21:02:32.742186053 +1000 -+++ glibc-2.32/sysdeps/unix/sysv/linux/x86_64/ldconfig.h 2021-09-18 21:03:05.314302356 +1000 -@@ -18,9 +18,9 @@ - #include <sysdeps/generic/ldconfig.h> - - #define SYSDEP_KNOWN_INTERPRETER_NAMES \ -- { "/lib/ld-linux.so.2", FLAG_ELF_LIBC6 }, \ -+ { "/lib32/ld-linux.so.2", FLAG_ELF_LIBC6 }, \ - { "/libx32/ld-linux-x32.so.2", FLAG_ELF_LIBC6 }, \ -- { "/lib64/ld-linux-x86-64.so.2", FLAG_ELF_LIBC6 }, -+ { "/lib/ld-linux-x86-64.so.2", FLAG_ELF_LIBC6 }, - #define SYSDEP_KNOWN_LIBRARY_NAMES \ - { "libc.so.6", FLAG_ELF_LIBC6 }, \ - { "libm.so.6", FLAG_ELF_LIBC6 }, diff --git a/glibc-32/glibc-2.36-2.patch b/glibc-32/glibc-2.36-2.patch new file mode 100644 index 00000000..b810e453 --- /dev/null +++ b/glibc-32/glibc-2.36-2.patch @@ -0,0 +1,6002 @@ +diff --git a/NEWS b/NEWS +index f61e521fc8..91bcfeb7a6 100644 +--- a/NEWS ++++ b/NEWS +@@ -5,6 +5,31 @@ See the end for copying conditions. + Please send GNU C library bug reports via <https://sourceware.org/bugzilla/> + using `glibc' in the "product" field. + ++Version 2.36.1 ++ ++Security related changes: ++ ++ CVE-2022-39046: When the syslog function is passed a crafted input ++ string larger than 1024 bytes, it reads uninitialized memory from the ++ heap and prints it to the target log file, potentially revealing a ++ portion of the contents of the heap. ++ ++The following bugs are resolved with this release: ++ ++ [12154] Do not fail DNS resolution for CNAMEs which are not host names ++ [28846] CMSG_NXTHDR may trigger -Wstrict-overflow warning ++ [29305] Conserve NSS buffer space during DNS packet parsing ++ [29415] nscd: Fix netlink cache invalidation if epoll is used ++ [28937] New DSO dependency sorter does not put new map first if in a cycle ++ [29446] _dlopen now ignores dl_caller argument in static mode ++ [29485] Linux: Terminate subprocess on late failure in tst-pidfd ++ [29490] alpha: New __brk_call implementation is broken ++ [29528] elf: Call __libc_early_init for reused namespaces ++ [29537] libc: [2.34 regression]: Alignment issue on m68k when using ++ [29539] libc: LD_TRACE_LOADED_OBJECTS changed how vDSO library are ++ [29583] Use 64-bit interfaces in gconv_parseconfdir ++ [29638] libc: stdlib: arc4random fallback is never used ++ + Version 2.36 + + Major new features: +diff --git a/bits/socket.h b/bits/socket.h +index 2b99dea33b..aac8c49b00 100644 +--- a/bits/socket.h ++++ b/bits/socket.h +@@ -245,6 +245,12 @@ struct cmsghdr + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -254,18 +260,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c +index 2696dde4b1..9b07b4e132 100644 +--- a/dlfcn/dlopen.c ++++ b/dlfcn/dlopen.c +@@ -90,7 +90,7 @@ compat_symbol (libdl, ___dlopen, dlopen, GLIBC_2_1); + void * + __dlopen (const char *file, int mode, void *dl_caller) + { +- return dlopen_implementation (file, mode, RETURN_ADDRESS (0)); ++ return dlopen_implementation (file, mode, dl_caller); + } + + void * +diff --git a/elf/Makefile b/elf/Makefile +index fd77d0c7c8..72178d33ff 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -374,6 +374,8 @@ tests += \ + tst-align \ + tst-align2 \ + tst-align3 \ ++ tst-audit-tlsdesc \ ++ tst-audit-tlsdesc-dlopen \ + tst-audit1 \ + tst-audit2 \ + tst-audit8 \ +@@ -408,6 +410,7 @@ tests += \ + tst-dlmopen4 \ + tst-dlmopen-dlerror \ + tst-dlmopen-gethostbyname \ ++ tst-dlmopen-twice \ + tst-dlopenfail \ + tst-dlopenfail-2 \ + tst-dlopenrpath \ +@@ -765,6 +768,8 @@ modules-names += \ + tst-alignmod3 \ + tst-array2dep \ + tst-array5dep \ ++ tst-audit-tlsdesc-mod1 \ ++ tst-audit-tlsdesc-mod2 \ + tst-audit11mod1 \ + tst-audit11mod2 \ + tst-audit12mod1 \ +@@ -798,6 +803,7 @@ modules-names += \ + tst-auditmanymod7 \ + tst-auditmanymod8 \ + tst-auditmanymod9 \ ++ tst-auditmod-tlsdesc \ + tst-auditmod1 \ + tst-auditmod9a \ + tst-auditmod9b \ +@@ -834,6 +840,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 \ +@@ -990,23 +998,8 @@ modules-names += tst-gnu2-tls1mod + $(objpfx)tst-gnu2-tls1: $(objpfx)tst-gnu2-tls1mod.so + tst-gnu2-tls1mod.so-no-z-defs = yes + CFLAGS-tst-gnu2-tls1mod.c += -mtls-dialect=gnu2 ++endif # $(have-mtls-dialect-gnu2) + +-tests += tst-audit-tlsdesc tst-audit-tlsdesc-dlopen +-modules-names += tst-audit-tlsdesc-mod1 tst-audit-tlsdesc-mod2 tst-auditmod-tlsdesc +-$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \ +- $(objpfx)tst-audit-tlsdesc-mod2.so \ +- $(shared-thread-library) +-CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2 +-CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2 +-$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) +-$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \ +- $(objpfx)tst-audit-tlsdesc-mod2.so +-$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so +-$(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 +-endif + ifeq (yes,$(have-protected-data)) + modules-names += tst-protected1moda tst-protected1modb + tests += tst-protected1a tst-protected1b +@@ -2967,3 +2960,25 @@ $(objpfx)tst-tls-allocation-failure-static-patched.out: \ + grep -q '^Fatal glibc error: Cannot allocate TLS block$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) ++ ++$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \ ++ $(objpfx)tst-audit-tlsdesc-mod2.so \ ++ $(shared-thread-library) ++ifeq (yes,$(have-mtls-dialect-gnu2)) ++# The test is valid for all TLS types, but we want to exercise GNU2 ++# TLS if possible. ++CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2 ++CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2 ++endif ++$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) ++$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \ ++ $(objpfx)tst-audit-tlsdesc-mod2.so ++$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so ++$(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-cache.c b/elf/dl-cache.c +index 8bbf110d02..b97c17b3a9 100644 +--- a/elf/dl-cache.c ++++ b/elf/dl-cache.c +@@ -509,8 +509,9 @@ _dl_load_cache_lookup (const char *name) + we are accessing. Therefore we must make the copy of the + mapping data without using malloc. */ + char *temp; +- temp = alloca (strlen (best) + 1); +- strcpy (temp, best); ++ size_t best_len = strlen (best) + 1; ++ temp = alloca (best_len); ++ memcpy (temp, best, best_len); + return __strdup (temp); + } + +diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c +index 6f161f6ad5..92eb53790e 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) +diff --git a/elf/dl-open.c b/elf/dl-open.c +index a23e65926b..46e8066fd8 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -844,11 +844,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_update (nsid)->r_state = RT_CONSISTENT; + } +diff --git a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c +index 96638d7ed1..3e2a6a584e 100644 +--- a/elf/dl-sort-maps.c ++++ b/elf/dl-sort-maps.c +@@ -27,12 +27,12 @@ + If FOR_FINI is true, this is called for finishing an object. */ + static void + _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps, +- unsigned int skip, bool for_fini) ++ bool force_first, bool for_fini) + { + /* Allows caller to do the common optimization of skipping the first map, + usually the main binary. */ +- maps += skip; +- nmaps -= skip; ++ maps += force_first; ++ nmaps -= force_first; + + /* A list of one element need not be sorted. */ + if (nmaps <= 1) +@@ -182,8 +182,9 @@ dfs_traversal (struct link_map ***rpo, struct link_map *map, + + static void + _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps, +- unsigned int skip __attribute__ ((unused)), bool for_fini) ++ bool force_first, bool for_fini) + { ++ struct link_map *first_map = maps[0]; + for (int i = nmaps - 1; i >= 0; i--) + maps[i]->l_visited = 0; + +@@ -208,14 +209,6 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps, + Adjusting the order so that maps[0] is last traversed naturally avoids + this problem. + +- Further, the old "optimization" of skipping the main object at maps[0] +- from the call-site (i.e. _dl_sort_maps(maps+1,nmaps-1)) is in general +- no longer valid, since traversing along object dependency-links +- may "find" the main object even when it is not included in the initial +- order (e.g. a dlopen()'ed shared object can have circular dependencies +- linked back to itself). In such a case, traversing N-1 objects will +- create a N-object result, and raise problems. +- + To summarize, just passing in the full list, and iterating from back + to front makes things much more straightforward. */ + +@@ -274,6 +267,27 @@ _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps, + } + + memcpy (maps, rpo, sizeof (struct link_map *) * nmaps); ++ ++ /* Skipping the first object at maps[0] is not valid in general, ++ since traversing along object dependency-links may "find" that ++ first object even when it is not included in the initial order ++ (e.g., a dlopen'ed shared object can have circular dependencies ++ linked back to itself). In such a case, traversing N-1 objects ++ will create a N-object result, and raise problems. Instead, ++ force the object back into first place after sorting. This naive ++ approach may introduce further dependency ordering violations ++ compared to rotating the cycle until the first map is again in ++ the first position, but as there is a cycle, at least one ++ violation is already present. */ ++ if (force_first && maps[0] != first_map) ++ { ++ int i; ++ for (i = 0; maps[i] != first_map; ++i) ++ ; ++ assert (i < nmaps); ++ memmove (&maps[1], maps, i * sizeof (maps[0])); ++ maps[0] = first_map; ++ } + } + + void +@@ -286,7 +300,7 @@ _dl_sort_maps_init (void) + + void + _dl_sort_maps (struct link_map **maps, unsigned int nmaps, +- unsigned int skip, bool for_fini) ++ bool force_first, bool for_fini) + { + /* It can be tempting to use a static function pointer to store and call + the current selected sorting algorithm routine, but experimentation +@@ -296,9 +310,9 @@ _dl_sort_maps (struct link_map **maps, unsigned int nmaps, + input cases. A simple if-case with direct function calls appears to + be the fastest. */ + if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original)) +- _dl_sort_maps_original (maps, nmaps, skip, for_fini); ++ _dl_sort_maps_original (maps, nmaps, force_first, for_fini); + else +- _dl_sort_maps_dfs (maps, nmaps, skip, for_fini); ++ _dl_sort_maps_dfs (maps, nmaps, force_first, for_fini); + } + + #endif /* HAVE_TUNABLES. */ +diff --git a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def +index 5f7f18ef27..4bf9052db1 100644 +--- a/elf/dso-sort-tests-1.def ++++ b/elf/dso-sort-tests-1.def +@@ -64,3 +64,10 @@ output: b>a>{}<a<b + tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c + output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<a<c<d<g<f<b<e];} + output(glibc.rtld.dynamic_sort=2): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[<g<f<a<b<c<d<e];} ++ ++# Test that even in the presence of dependency loops involving dlopen'ed ++# object, that object is initialized last (and not unloaded prematurely). ++# Final destructor order is indeterminate due to the cycle. ++tst-bz28937: {+a;+b;-b;+c;%c};a->a1;a->a2;a2->a;b->b1;c->a1;c=>a1 ++output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a<a2<c<a1 ++output(glibc.rtld.dynamic_sort=2): {+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<a2<a<c<a1 +diff --git a/elf/rtld.c b/elf/rtld.c +index cbbaf4a331..3e771a93d8 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2122,6 +2122,12 @@ dl_main (const ElfW(Phdr) *phdr, + if (l->l_faked) + /* The library was not found. */ + _dl_printf ("\t%s => not found\n", l->l_libname->name); ++ else if (strcmp (l->l_libname->name, l->l_name) == 0) ++ /* Print vDSO like libraries without duplicate name. Some ++ consumers depend of this format. */ ++ _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name, ++ (int) sizeof l->l_map_start * 2, ++ (size_t) l->l_map_start); + else + _dl_printf ("\t%s => %s (0x%0*Zx)\n", + DSO_FILENAME (l->l_libname->name), +diff --git a/elf/tst-dlmopen-twice-mod1.c b/elf/tst-dlmopen-twice-mod1.c +new file mode 100644 +index 0000000000..0eaf04948c +--- /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 0000000000..40c6c01f96 +--- /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 0000000000..449f3c8fa9 +--- /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> +diff --git a/iconv/gconv_parseconfdir.h b/iconv/gconv_parseconfdir.h +index debb96b322..b72933b526 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); +diff --git a/include/arpa/nameser.h b/include/arpa/nameser.h +index 53f1dbc7c3..c27e7886b7 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) + +@@ -89,5 +95,105 @@ 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; ++ ++/* 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/include/bits/wchar2-decl.h b/include/bits/wchar2-decl.h +new file mode 100644 +index 0000000000..00b1b93342 +--- /dev/null ++++ b/include/bits/wchar2-decl.h +@@ -0,0 +1 @@ ++#include <wcsmbs/bits/wchar2-decl.h> +diff --git a/include/resolv.h b/include/resolv.h +index 3590b6f496..4dbbac3800 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/misc/syslog.c b/misc/syslog.c +index 554089bfc4..f67d4b58a4 100644 +--- a/misc/syslog.c ++++ b/misc/syslog.c +@@ -167,7 +167,7 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap, + _nl_C_locobj_ptr); + + #define SYSLOG_HEADER(__pri, __timestamp, __msgoff, pid) \ +- "<%d>%s %n%s%s%.0d%s: ", \ ++ "<%d>%s%n%s%s%.0d%s: ", \ + __pri, __timestamp, __msgoff, \ + LogTag == NULL ? __progname : LogTag, \ + "[" + (pid == 0), pid, "]" + (pid == 0) +@@ -193,28 +193,32 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap, + int vl = __vsnprintf_internal (bufs + l, sizeof bufs - l, fmt, apc, + mode_flags); + if (0 <= vl && vl < sizeof bufs - l) +- { +- buf = bufs; +- bufsize = l + vl; +- } ++ buf = bufs; ++ bufsize = l + vl; + + va_end (apc); + } + + if (buf == NULL) + { +- buf = malloc (l * sizeof (char)); ++ buf = malloc ((bufsize + 1) * sizeof (char)); + if (buf != NULL) + { + /* Tell the cancellation handler to free this buffer. */ + clarg.buf = buf; + + if (has_ts) +- __snprintf (bufs, sizeof bufs, ++ __snprintf (buf, l + 1, + SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); + else +- __snprintf (bufs, sizeof bufs, ++ __snprintf (buf, l + 1, + SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); ++ ++ va_list apc; ++ va_copy (apc, ap); ++ __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc, ++ mode_flags); ++ va_end (apc); + } + else + { +diff --git a/misc/tst-syslog.c b/misc/tst-syslog.c +index e550d15796..3560b518a2 100644 +--- a/misc/tst-syslog.c ++++ b/misc/tst-syslog.c +@@ -68,21 +68,19 @@ static const int priorities[] = + LOG_DEBUG + }; + +-enum +- { +- ident_length = 64, +- msg_length = 64 +- }; ++#define IDENT_LENGTH 64 ++#define MSG_LENGTH 1024 + + #define SYSLOG_MSG_BASE "syslog_message" + #define OPENLOG_IDENT "openlog_ident" ++static char large_message[MSG_LENGTH]; + + struct msg_t + { + int priority; + int facility; +- char ident[ident_length]; +- char msg[msg_length]; ++ char ident[IDENT_LENGTH]; ++ char msg[MSG_LENGTH]; + pid_t pid; + }; + +@@ -147,6 +145,37 @@ check_syslog_message (const struct msg_t *msg, int msgnum, int options, + return true; + } + ++static void ++send_syslog_large (int options) ++{ ++ int facility = LOG_USER; ++ int priority = LOG_INFO; ++ ++ syslog (facility | priority, "%s %d %d", large_message, facility, ++ priority); ++} ++ ++static void ++send_vsyslog_large (int options) ++{ ++ int facility = LOG_USER; ++ int priority = LOG_INFO; ++ ++ call_vsyslog (facility | priority, "%s %d %d", large_message, facility, ++ priority); ++} ++ ++static bool ++check_syslog_message_large (const struct msg_t *msg, int msgnum, int options, ++ pid_t pid) ++{ ++ TEST_COMPARE (msg->facility, LOG_USER); ++ TEST_COMPARE (msg->priority, LOG_INFO); ++ TEST_COMPARE_STRING (msg->msg, large_message); ++ ++ return false; ++} ++ + static void + send_openlog (int options) + { +@@ -179,6 +208,17 @@ send_openlog (int options) + closelog (); + } + ++static void ++send_openlog_large (int options) ++{ ++ /* Define a non-default IDENT and a not default facility. */ ++ openlog (OPENLOG_IDENT, options, LOG_LOCAL0); ++ ++ syslog (LOG_INFO, "%s %d %d", large_message, LOG_LOCAL0, LOG_INFO); ++ ++ closelog (); ++} ++ + static bool + check_openlog_message (const struct msg_t *msg, int msgnum, + int options, pid_t pid) +@@ -189,7 +229,7 @@ check_openlog_message (const struct msg_t *msg, int msgnum, + int expected_priority = priorities[msgnum % array_length (priorities)]; + TEST_COMPARE (msg->priority, expected_priority); + +- char expected_ident[ident_length]; ++ char expected_ident[IDENT_LENGTH]; + snprintf (expected_ident, sizeof (expected_ident), "%s%s%.0d%s:", + OPENLOG_IDENT, + options & LOG_PID ? "[" : "", +@@ -211,17 +251,43 @@ check_openlog_message (const struct msg_t *msg, int msgnum, + return true; + } + ++static bool ++check_openlog_message_large (const struct msg_t *msg, int msgnum, ++ int options, pid_t pid) ++{ ++ char expected_ident[IDENT_LENGTH]; ++ snprintf (expected_ident, sizeof (expected_ident), "%s%s%.0d%s:", ++ OPENLOG_IDENT, ++ options & LOG_PID ? "[" : "", ++ options & LOG_PID ? pid : 0, ++ options & LOG_PID ? "]" : ""); ++ ++ TEST_COMPARE_STRING (msg->ident, expected_ident); ++ TEST_COMPARE_STRING (msg->msg, large_message); ++ TEST_COMPARE (msg->priority, LOG_INFO); ++ TEST_COMPARE (msg->facility, LOG_LOCAL0); ++ ++ return false; ++} ++ + static struct msg_t + parse_syslog_msg (const char *msg) + { + struct msg_t r = { .pid = -1 }; + int number; ++ int wsb, wsa; ++ ++#define STRINPUT(size) XSTRINPUT(size) ++#define XSTRINPUT(size) "%" # size "s" + + /* The message in the form: +- <179>Apr 8 14:51:19 tst-syslog: syslog message 176 3 */ +- int n = sscanf (msg, "<%3d>%*s %*d %*d:%*d:%*d %32s %64s %*d %*d", +- &number, r.ident, r.msg); ++ <179>Apr 8 14:51:19 tst-syslog: message 176 3 */ ++ int n = sscanf (msg, "<%3d>%*s %*d %*d:%*d:%*d%n %n" STRINPUT(IDENT_LENGTH) ++ " " STRINPUT(MSG_LENGTH) " %*d %*d", ++ &number, &wsb, &wsa, r.ident, r.msg); + TEST_COMPARE (n, 3); ++ /* It should only one space between timestamp and message. */ ++ TEST_COMPARE (wsa - wsb, 1); + + r.facility = number & LOG_FACMASK; + r.priority = number & LOG_PRIMASK; +@@ -246,7 +312,7 @@ parse_syslog_console (const char *msg) + + /* The message in the form: + openlog_ident: syslog_message 128 0 */ +- int n = sscanf (msg, "%32s %64s %d %d", ++ int n = sscanf (msg, STRINPUT(IDENT_LENGTH) " " STRINPUT(MSG_LENGTH) " %d %d", + r.ident, r.msg, &facility, &priority); + TEST_COMPARE (n, 4); + +@@ -281,7 +347,7 @@ check_syslog_udp (void (*syslog_send)(int), int options, + int msgnum = 0; + while (1) + { +- char buf[512]; ++ char buf[2048]; + size_t l = xrecvfrom (server_udp, buf, sizeof (buf), 0, + (struct sockaddr *) &addr, &addrlen); + buf[l] = '\0'; +@@ -325,7 +391,7 @@ check_syslog_tcp (void (*syslog_send)(int), int options, + + int client_tcp = xaccept (server_tcp, NULL, NULL); + +- char buf[512], *rb = buf; ++ char buf[2048], *rb = buf; + size_t rbl = sizeof (buf); + size_t prl = 0; /* Track the size of the partial record. */ + int msgnum = 0; +@@ -393,20 +459,34 @@ check_syslog_console_read (FILE *fp) + } + + static void +-check_syslog_console (void) ++check_syslog_console_read_large (FILE *fp) ++{ ++ char buf[2048]; ++ TEST_VERIFY (fgets (buf, sizeof (buf), fp) != NULL); ++ struct msg_t msg = parse_syslog_console (buf); ++ ++ TEST_COMPARE_STRING (msg.ident, OPENLOG_IDENT ":"); ++ TEST_COMPARE_STRING (msg.msg, large_message); ++ TEST_COMPARE (msg.priority, LOG_INFO); ++ TEST_COMPARE (msg.facility, LOG_LOCAL0); ++} ++ ++static void ++check_syslog_console (void (*syslog_send)(int), ++ void (*syslog_check)(FILE *fp)) + { + xmkfifo (_PATH_CONSOLE, 0666); + + pid_t sender_pid = xfork (); + if (sender_pid == 0) + { +- send_openlog (LOG_CONS); ++ syslog_send (LOG_CONS); + _exit (0); + } + + { + FILE *fp = xfopen (_PATH_CONSOLE, "r+"); +- check_syslog_console_read (fp); ++ syslog_check (fp); + xfclose (fp); + } + +@@ -425,16 +505,28 @@ send_openlog_callback (void *clousure) + } + + static void +-check_syslog_perror (void) ++send_openlog_callback_large (void *clousure) ++{ ++ int options = *(int *) clousure; ++ send_openlog_large (options); ++} ++ ++static void ++check_syslog_perror (bool large) + { + struct support_capture_subprocess result; +- result = support_capture_subprocess (send_openlog_callback, ++ result = support_capture_subprocess (large ++ ? send_openlog_callback_large ++ : send_openlog_callback, + &(int){LOG_PERROR}); + + FILE *mfp = fmemopen (result.err.buffer, result.err.length, "r"); + if (mfp == NULL) + FAIL_EXIT1 ("fmemopen: %m"); +- check_syslog_console_read (mfp); ++ if (large) ++ check_syslog_console_read_large (mfp); ++ else ++ check_syslog_console_read (mfp); + xfclose (mfp); + + support_capture_subprocess_check (&result, "tst-openlog-child", 0, +@@ -462,10 +554,31 @@ do_test (void) + check_syslog_tcp (send_openlog, LOG_PID, check_openlog_message); + + /* Check the LOG_CONS option. */ +- check_syslog_console (); ++ check_syslog_console (send_openlog, check_syslog_console_read); + + /* Check the LOG_PERROR option. */ +- check_syslog_perror (); ++ check_syslog_perror (false); ++ ++ /* Similar tests as before, but with a large message to trigger the ++ syslog path that uses dynamically allocated memory. */ ++ memset (large_message, 'a', sizeof large_message - 1); ++ large_message[sizeof large_message - 1] = '\0'; ++ ++ check_syslog_udp (send_syslog_large, 0, check_syslog_message_large); ++ check_syslog_tcp (send_syslog_large, 0, check_syslog_message_large); ++ ++ check_syslog_udp (send_vsyslog_large, 0, check_syslog_message_large); ++ check_syslog_tcp (send_vsyslog_large, 0, check_syslog_message_large); ++ ++ check_syslog_udp (send_openlog_large, 0, check_openlog_message_large); ++ check_syslog_tcp (send_openlog_large, 0, check_openlog_message_large); ++ ++ check_syslog_udp (send_openlog_large, LOG_PID, check_openlog_message_large); ++ check_syslog_tcp (send_openlog_large, LOG_PID, check_openlog_message_large); ++ ++ check_syslog_console (send_openlog_large, check_syslog_console_read_large); ++ ++ check_syslog_perror (true); + + return 0; + } +diff --git a/nscd/connections.c b/nscd/connections.c +index 61d1674eb4..531d2e83df 100644 +--- a/nscd/connections.c ++++ b/nscd/connections.c +@@ -2284,7 +2284,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 +diff --git a/resolv/Makefile b/resolv/Makefile +index 5b15321f9b..f8a92c6cff 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -40,12 +40,16 @@ routines := \ + inet_pton \ + ns_makecanon \ + ns_name_compress \ ++ ns_name_length_uncompressed \ + ns_name_ntop \ + ns_name_pack \ + ns_name_pton \ + ns_name_skip \ + ns_name_uncompress \ + ns_name_unpack \ ++ ns_rr_cursor_init \ ++ ns_rr_cursor_next \ ++ ns_samebinaryname \ + ns_samename \ + nsap_addr \ + nss_dns_functions \ +@@ -89,9 +93,12 @@ tests += \ + tst-ns_name_pton \ + tst-res_hconf_reorder \ + tst-res_hnok \ ++ tst-resolv-aliases \ + tst-resolv-basic \ + tst-resolv-binary \ ++ tst-resolv-byaddr \ + tst-resolv-edns \ ++ tst-resolv-invalid-cname \ + tst-resolv-network \ + tst-resolv-noaaaa \ + tst-resolv-nondecimal \ +@@ -104,6 +111,18 @@ 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 ++ ++# Likewise for __ns_name_length_uncompressed. ++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 += \ +@@ -258,8 +277,10 @@ $(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) + $(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 +@@ -267,6 +288,8 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \ + $(shared-thread-library) + $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \ + $(shared-thread-library) ++$(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-noaaaa: $(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) +diff --git a/resolv/README b/resolv/README +index 514e9bb617..2146bc3b27 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 7f85f7d5e3..0000000000 +--- 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 c11038adf3..0000000000 +--- 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/ns_name_length_uncompressed.c b/resolv/ns_name_length_uncompressed.c +new file mode 100644 +index 0000000000..51296b47ef +--- /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/ns_rr_cursor_init.c b/resolv/ns_rr_cursor_init.c +new file mode 100644 +index 0000000000..6ee80b30e9 +--- /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 0000000000..33652fc5da +--- /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/ns_samebinaryname.c b/resolv/ns_samebinaryname.c +new file mode 100644 +index 0000000000..9a47d8e97a +--- /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/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c +index 544cffbecd..9fa81f23c8 100644 +--- a/resolv/nss_dns/dns-host.c ++++ b/resolv/nss_dns/dns-host.c +@@ -69,6 +69,7 @@ + * --Copyright-- + */ + ++#include <alloc_buffer.h> + #include <assert.h> + #include <ctype.h> + #include <errno.h> +@@ -86,10 +87,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 +@@ -103,32 +100,36 @@ + #endif + #define MAXHOSTNAMELEN 256 + +-/* We need this time later. */ +-typedef union querybuf +-{ +- HEADER hdr; +- 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, +- int map, int32_t *ttlp, char **canonp); +- +-static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1, +- const querybuf *answer2, int anslen2, +- const char *qname, ++/* 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, ++ int *h_errnop, int32_t *ttlp); ++ ++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); + +@@ -183,16 +184,9 @@ 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; +- int map = 0; + int olderr = errno; + enum nss_status status; + +@@ -223,10 +217,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) +@@ -253,34 +249,79 @@ gethostbyname3_context (struct resolv_context *ctx, + *errnop = EAGAIN; + else + __set_errno (olderr); ++ } ++ else ++ { ++ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen); + +- /* 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); ++ struct ptrlist addresses; ++ ptrlist_init (&addresses); ++ struct ptrlist aliases; ++ ptrlist_init (&aliases); + +- if (n < 0) ++ status = getanswer_r (alt_dns_packet_buffer, n, type, ++ &abuf, &addresses, &aliases, ++ errnop, h_errnop, ttlp); ++ if (status == NSS_STATUS_SUCCESS) + { +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); +- return status; +- } ++ 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; ++ } + +- map = 1; ++ /* 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)); + +- result->h_addrtype = AF_INET; +- result->h_length = INADDRSZ; ++ /* 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); + } + +- 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); ++ if (alt_dns_packet_buffer != dns_packet_buffer) ++ free (alt_dns_packet_buffer); + return status; + } + +@@ -324,13 +365,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; + } +@@ -365,17 +401,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; +@@ -384,22 +416,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) + { +@@ -430,12 +461,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; +@@ -451,36 +490,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) +@@ -524,8 +548,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: +@@ -549,36 +571,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_r +- (ctx, host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen, +- errnop, h_errnop, 0 /* XXX */, ttlp, NULL); +- 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) +@@ -640,650 +678,362 @@ 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, int map, 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]; +- 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); +- 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; +- 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(); */ +- } ++ /* 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; ++} + +- /* +- * find first satisfactory answer +- */ +- hp = &answer->hdr; +- ancount = ntohs (hp->ancount); +- qdcount = ntohs (hp->qdcount); +- cp = answer->buf + HFIXEDSZ; +- if (__glibc_unlikely (qdcount != 1)) ++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)) + { ++ /* This should not happen because __res_context_query already ++ perfroms response validation. */ + *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)) ++ /* 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)) + { +- *errnop = errno; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; +- } +- if (__glibc_unlikely (name_ok (bp) == 0)) +- { +- errno = EBADMSG; +- *errnop = EBADMSG; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; ++ if (ttlp != NULL) ++ /* No negative caching. */ ++ *ttlp = 0; ++ *h_errnop = HOST_NOT_FOUND; ++ *errnop = ENOENT; ++ return NSS_STATUS_NOTFOUND; + } +- cp += n + QFIXEDSZ; + +- if (qtype == T_A || qtype == T_AAAA) ++ 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]; ++ ++ for (; ancount > 0; --ancount) + { +- /* 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) ++ struct ns_rr_wire rr; ++ if (!__ns_rr_cursor_next (&c, &rr)) + { + *h_errnop = NO_RECOVERY; +- *errnop = ENOENT; +- return NSS_STATUS_TRYAGAIN; ++ return NSS_STATUS_UNAVAIL; + } +- 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; +- } + +- 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; ++ /* Skip over records with the wrong class. */ ++ if (rr.rclass != C_IN) ++ continue; + +- while (ancount-- > 0 && cp < end_of_message && had_error == 0) +- { +- int type, class; ++ /* Update TTL for recognized record types. */ ++ if ((rr.rtype == T_CNAME || rr.rtype == qtype) ++ && ttlp != NULL && *ttlp > rr.ttl) ++ *ttlp = rr.ttl; + +- n = __ns_name_unpack (answer->buf, end_of_message, cp, +- packtmp, sizeof packtmp); +- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) ++ if (rr.rtype == T_CNAME) + { +- if (__glibc_unlikely (errno == EMSGSIZE)) +- goto too_small; +- +- n = -1; ++ /* 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) ++ { ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; ++ } ++ /* And store the new name as an alias. */ ++ getanswer_r_store_alias (name_buffer, abuf, aliases); ++ expected_name = name_buffer; + } +- +- if (__glibc_unlikely (n < 0 || (*name_ok) (bp) == 0)) ++ else if (rr.rtype == qtype ++ && __ns_samebinaryname (rr.rname, expected_name) ++ && rr.rdlength == rrtype_to_rdata_length (qtype)) + { +- ++had_error; +- continue; ++ /* 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); + } +- cp += n; /* name */ ++ } + +- if (__glibc_unlikely (cp + 10 > end_of_message)) +- { +- ++had_error; +- continue; +- } ++ if (ptrlist_size (addresses) == 0) ++ { ++ /* No address record found. */ ++ if (ttlp != NULL) ++ /* No caching of negative responses. */ ++ *ttlp = 0; + +- NS_GET16 (type, cp); +- NS_GET16 (class, cp); +- int32_t ttl; +- NS_GET32 (ttl, cp); +- NS_GET16 (n, cp); /* RDATA length. */ ++ *h_errnop = NO_RECOVERY; ++ *errnop = ENOENT; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ else ++ { ++ *h_errnop = NETDB_SUCCESS; ++ return NSS_STATUS_SUCCESS; ++ } ++} + +- if (end_of_message - cp < n) +- { +- /* RDATA extends beyond the end of the packet. */ +- ++had_error; +- continue; +- } ++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) ++{ ++ struct ns_rr_cursor c; ++ if (!__ns_rr_cursor_init (&c, packet, packetlen)) ++ { ++ /* This should not happen because __res_context_query already ++ perfroms response validation. */ ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; ++ } ++ 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]; + +- if (__glibc_unlikely (class != C_IN)) ++ while (ancount > 0) ++ { ++ struct ns_rr_wire rr; ++ if (!__ns_rr_cursor_next (&c, &rr)) + { +- /* XXX - debug? syslog? */ +- cp += n; +- continue; /* XXX - had_error++ ? */ ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } + +- if ((qtype == T_A || qtype == T_AAAA) && type == 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 || (*name_ok) (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)) +- { +- ++had_error; +- continue; +- } +- result->h_name = bp; +- bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */ +- linebuflen -= n; +- continue; +- } ++ /* Skip over records with the wrong class. */ ++ if (rr.rclass != C_IN) ++ continue; + +- if (qtype == T_PTR && type == T_CNAME) +- { +- /* A CNAME could also have a TTL entry. */ +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; ++ /* Update TTL for known record types. */ ++ if ((rr.rtype == T_CNAME || rr.rtype == T_PTR) ++ && ttlp != NULL && *ttlp > rr.ttl) ++ *ttlp = rr.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)) ++ if (rr.rtype == T_CNAME) ++ { ++ /* 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; + } +- +- if (type == T_A && qtype == T_AAAA && map) +- have_to_map = 1; +- else if (__glibc_unlikely (type != qtype)) ++ else if (rr.rtype == T_PTR ++ && __ns_samebinaryname (rr.rname, expected_name)) + { +- cp += n; +- continue; /* XXX - had_error++ ? */ +- } +- +- switch (type) +- { +- case T_PTR: +- if (__glibc_unlikely (__strcasecmp (tname, 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) + { +- cp += n; +- continue; /* XXX - had_error++ ? */ ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } +- +- 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; +- break; +- } +- 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; +- 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; +- } ++ /* No PTR record found. */ ++ if (ttlp != NULL) ++ /* No caching of negative responses. */ ++ *ttlp = 0; + +- 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; + } + +- ++/* 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)) ++ struct ns_rr_cursor c; ++ if (!__ns_rr_cursor_init (&c, packet, packetlen)) + { +- *errnop = errno; ++ /* This should not happen because __res_context_query already ++ perfroms response validation. */ + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } +- if (__glibc_unlikely (__libc_res_hnok (buffer) == 0)) +- { +- errno = EBADMSG; +- *errnop = EBADMSG; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; +- } +- cp += n + QFIXEDSZ; +- +- int haveanswer = 0; +- int had_error = 0; +- char *canon = NULL; +- char *h_name = NULL; +- int h_namelen = 0; +- +- if (ancount == 0) ++ 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]; ++ ++ /* 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 || !__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) ++ 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_hnok (buffer) == 0)) +- { +- ++had_error; +- continue; +- } +- if (*firstp && canon == NULL) +- { +- h_name = buffer; +- buffer += h_namelen; +- buflen -= h_namelen; +- } +- +- cp += n; /* name */ +- +- if (__glibc_unlikely (cp + 10 > end_of_message)) +- { +- ++had_error; +- continue; ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } + +- 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. */ ++ /* Update TTL for known record types. */ ++ if ((rr.rtype == T_CNAME || rr.rtype == qtype) ++ && ttlp != NULL && *ttlp > rr.ttl) ++ *ttlp = rr.ttl; + +- if (end_of_message - cp < n) ++ if (rr.rtype == T_CNAME) + { +- /* RDATA extends beyond the end of the packet. */ +- ++had_error; +- continue; +- } +- +- if (class != C_IN) +- { +- cp += n; +- continue; +- } +- +- if (type == 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 || __libc_res_hnok (tbuf) == 0)) ++ /* 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; +- } +- cp += n; +- +- if (*firstp) +- { +- /* 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 +@@ -1295,7 +1045,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. +@@ -1375,36 +1128,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; +@@ -1412,18 +1159,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; + } +diff --git a/resolv/res-name-checking.c b/resolv/res-name-checking.c +index 07a412d8ff..213edceaf3 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); +diff --git a/resolv/tst-ns_name_length_uncompressed.c b/resolv/tst-ns_name_length_uncompressed.c +new file mode 100644 +index 0000000000..c4a2904db7 +--- /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> +diff --git a/resolv/tst-ns_rr_cursor.c b/resolv/tst-ns_rr_cursor.c +new file mode 100644 +index 0000000000..c3c0908905 +--- /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> +diff --git a/resolv/tst-ns_samebinaryname.c b/resolv/tst-ns_samebinaryname.c +new file mode 100644 +index 0000000000..b06ac610b4 +--- /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> +diff --git a/resolv/tst-resolv-aliases.c b/resolv/tst-resolv-aliases.c +new file mode 100644 +index 0000000000..b212823aa0 +--- /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> +diff --git a/resolv/tst-resolv-byaddr.c b/resolv/tst-resolv-byaddr.c +new file mode 100644 +index 0000000000..6299e89837 +--- /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-invalid-cname.c b/resolv/tst-resolv-invalid-cname.c +new file mode 100644 +index 0000000000..63dac90e02 +--- /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: ++ break; /* 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> +diff --git a/resolv/tst-resolv-maybe_insert_sig.h b/resolv/tst-resolv-maybe_insert_sig.h +new file mode 100644 +index 0000000000..05725225af +--- /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); ++} +diff --git a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py +index 2dd6bfda18..b87cf2f809 100644 +--- a/scripts/dso-ordering-test.py ++++ b/scripts/dso-ordering-test.py +@@ -707,13 +707,12 @@ def process_testcase(t): + "\t$(compile.c) $(OUTPUT_OPTION)\n") + makefile.write (rule) + +- not_depended_objs = find_objs_not_depended_on(test_descr) +- if not_depended_objs: +- depstr = "" +- for dep in not_depended_objs: +- depstr += (" $(objpfx)" + test_subdir + "/" +- + test_name + "-" + dep + ".so") +- makefile.write("$(objpfx)%s.out:%s\n" % (base_test_name, depstr)) ++ # Ensure that all shared objects are built before running the ++ # test, whether there link-time dependencies or not. ++ depobjs = ["$(objpfx){}/{}-{}.so".format(test_subdir, test_name, dep) ++ for dep in test_descr.objs] ++ makefile.write("$(objpfx){}.out: {}\n".format( ++ base_test_name, " ".join(depobjs))) + + # Add main executable to test-srcs + makefile.write("test-srcs += %s/%s\n" % (test_subdir, test_name)) +diff --git a/scripts/glibcextract.py b/scripts/glibcextract.py +index 43ab58ffe2..36d204c9b0 100644 +--- a/scripts/glibcextract.py ++++ b/scripts/glibcextract.py +@@ -17,6 +17,7 @@ + # License along with the GNU C Library; if not, see + # <https://www.gnu.org/licenses/>. + ++import collections + import os.path + import re + import subprocess +@@ -173,3 +174,21 @@ def compare_macro_consts(source_1, source_2, cc, macro_re, exclude_re=None, + if not allow_extra_2: + ret = 1 + return ret ++ ++CompileResult = collections.namedtuple("CompileResult", "returncode output") ++ ++def compile_c_snippet(snippet, cc, extra_cc_args=''): ++ """Compile and return whether the SNIPPET can be build with CC along ++ EXTRA_CC_ARGS compiler flags. Return a CompileResult with RETURNCODE ++ being 0 for success, or the failure value and the compiler output. ++ """ ++ with tempfile.TemporaryDirectory() as temp_dir: ++ c_file_name = os.path.join(temp_dir, 'test.c') ++ obj_file_name = os.path.join(temp_dir, 'test.o') ++ with open(c_file_name, 'w') as c_file: ++ c_file.write(snippet + '\n') ++ cmd = cc.split() + extra_cc_args.split() + ['-c', '-o', obj_file_name, ++ c_file_name] ++ r = subprocess.run(cmd, check=False, stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT) ++ return CompileResult(r.returncode, r.stdout) +diff --git a/socket/Makefile b/socket/Makefile +index 156eec6c85..2bde78387f 100644 +--- a/socket/Makefile ++++ b/socket/Makefile +@@ -34,6 +34,7 @@ routines := accept bind connect getpeername getsockname getsockopt \ + tests := \ + tst-accept4 \ + tst-sockopt \ ++ tst-cmsghdr \ + # tests + + tests-internal := \ +diff --git a/socket/tst-cmsghdr-skeleton.c b/socket/tst-cmsghdr-skeleton.c +new file mode 100644 +index 0000000000..4c6898569b +--- /dev/null ++++ b/socket/tst-cmsghdr-skeleton.c +@@ -0,0 +1,92 @@ ++/* Test ancillary data header creation. ++ 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/>. */ ++ ++/* We use the preprocessor to generate the function/macro tests instead of ++ using indirection because having all the macro expansions alongside ++ each other lets the compiler warn us about suspicious pointer ++ arithmetic across subsequent CMSG_{FIRST,NXT}HDR expansions. */ ++ ++#include <stdint.h> ++ ++#define RUN_TEST_CONCAT(suffix) run_test_##suffix ++#define RUN_TEST_FUNCNAME(suffix) RUN_TEST_CONCAT (suffix) ++ ++static void ++RUN_TEST_FUNCNAME (CMSG_NXTHDR_IMPL) (void) ++{ ++ struct msghdr m = {0}; ++ struct cmsghdr *cmsg; ++ char cmsgbuf[3 * CMSG_SPACE (sizeof (PAYLOAD))] = {0}; ++ ++ m.msg_control = cmsgbuf; ++ m.msg_controllen = sizeof (cmsgbuf); ++ ++ /* First header should point to the start of the buffer. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ ++ /* If the first header length consumes the entire buffer, there is no ++ space remaining for additional headers. */ ++ cmsg->cmsg_len = sizeof (cmsgbuf); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The first header length is so big, using it would cause an overflow. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = SIZE_MAX; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The first header leaves just enough space to hold another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = sizeof (cmsgbuf) - sizeof (struct cmsghdr); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ ++ /* The first header leaves space but not enough for another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len ++; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The second header leaves just enough space to hold another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = CMSG_LEN (sizeof (PAYLOAD)); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ cmsg->cmsg_len = sizeof (cmsgbuf) ++ - CMSG_SPACE (sizeof (PAYLOAD)) /* First header. */ ++ - sizeof (struct cmsghdr); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ ++ /* The second header leaves space but not enough for another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ cmsg->cmsg_len ++; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ return; ++} +diff --git a/socket/tst-cmsghdr.c b/socket/tst-cmsghdr.c +new file mode 100644 +index 0000000000..68c96d3c9d +--- /dev/null ++++ b/socket/tst-cmsghdr.c +@@ -0,0 +1,56 @@ ++/* Test ancillary data header creation. ++ 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 <sys/socket.h> ++#include <gnu/lib-names.h> ++#include <support/xdlfcn.h> ++#include <support/check.h> ++ ++#define PAYLOAD "Hello, World!" ++ ++/* CMSG_NXTHDR is a macro that calls an inline function defined in ++ bits/socket.h. In case the function cannot be inlined, libc.so carries ++ a copy. Both versions need to be tested. */ ++ ++#define CMSG_NXTHDR_IMPL CMSG_NXTHDR ++#include "tst-cmsghdr-skeleton.c" ++#undef CMSG_NXTHDR_IMPL ++ ++static struct cmsghdr * (* cmsg_nxthdr) (struct msghdr *, struct cmsghdr *); ++ ++#define CMSG_NXTHDR_IMPL cmsg_nxthdr ++#include "tst-cmsghdr-skeleton.c" ++#undef CMSG_NXTHDR_IMPL ++ ++static int ++do_test (void) ++{ ++ static void *handle; ++ ++ run_test_CMSG_NXTHDR (); ++ ++ handle = xdlopen (LIBC_SO, RTLD_LAZY); ++ cmsg_nxthdr = (struct cmsghdr * (*) (struct msghdr *, struct cmsghdr *)) ++ xdlsym (handle, "__cmsg_nxthdr"); ++ ++ run_test_cmsg_nxthdr (); ++ ++ return 0; ++} ++ ++#include <support/test-driver.c> +diff --git a/stdlib/arc4random.c b/stdlib/arc4random.c +index e417ef624d..960a38f295 100644 +--- a/stdlib/arc4random.c ++++ b/stdlib/arc4random.c +@@ -34,7 +34,7 @@ void + __arc4random_buf (void *p, size_t n) + { + static int seen_initialized; +- size_t l; ++ ssize_t l; + int fd; + + if (n == 0) +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 050a3032de..6b256b8388 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -1048,9 +1048,11 @@ extern void _dl_init (struct link_map *main_map, int argc, char **argv, + initializer functions have completed. */ + extern void _dl_fini (void) attribute_hidden; + +-/* Sort array MAPS according to dependencies of the contained objects. */ ++/* Sort array MAPS according to dependencies of the contained objects. ++ If FORCE_FIRST, MAPS[0] keeps its place even if the dependencies ++ say otherwise. */ + extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps, +- unsigned int skip, bool for_fini) attribute_hidden; ++ bool force_first, bool for_fini) attribute_hidden; + + /* The dynamic linker calls this function before and having changing + any shared object mappings. The `r_state' member of `struct r_debug' +diff --git a/sysdeps/generic/libc-lock-arch.h b/sysdeps/generic/libc-lock-arch.h +new file mode 100644 +index 0000000000..4713b30a8a +--- /dev/null ++++ b/sysdeps/generic/libc-lock-arch.h +@@ -0,0 +1,25 @@ ++/* Private libc-internal arch-specific definitions. Generic version. ++ 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; see the file COPYING.LIB. If ++ not, see <https://www.gnu.org/licenses/>. */ ++ ++#ifndef _LIBC_LOCK_ARCH_H ++#define _LIBC_LOCK_ARCH_H ++ ++/* The default definition uses the natural alignment from the lock type. */ ++#define __LIBC_LOCK_ALIGNMENT ++ ++#endif +diff --git a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h +index 5b35ea81ec..70fce4fb27 100644 +--- a/sysdeps/mach/hurd/bits/socket.h ++++ b/sysdeps/mach/hurd/bits/socket.h +@@ -249,6 +249,12 @@ struct cmsghdr + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -258,18 +264,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff --git a/sysdeps/nptl/libc-lock.h b/sysdeps/nptl/libc-lock.h +index 5af476c48b..63b3f3d75c 100644 +--- a/sysdeps/nptl/libc-lock.h ++++ b/sysdeps/nptl/libc-lock.h +@@ -22,6 +22,7 @@ + #include <pthread.h> + #define __need_NULL + #include <stddef.h> ++#include <libc-lock-arch.h> + + + /* Mutex type. */ +@@ -29,7 +30,12 @@ + # if (!IS_IN (libc) && !IS_IN (libpthread)) || !defined _LIBC + typedef struct { pthread_mutex_t mutex; } __libc_lock_recursive_t; + # else +-typedef struct { int lock; int cnt; void *owner; } __libc_lock_recursive_t; ++typedef struct ++{ ++ int lock __LIBC_LOCK_ALIGNMENT; ++ int cnt; ++ void *owner; ++} __libc_lock_recursive_t; + # endif + #else + typedef struct __libc_lock_recursive_opaque__ __libc_lock_recursive_t; +diff --git a/sysdeps/nptl/libc-lockP.h b/sysdeps/nptl/libc-lockP.h +index d3a6837fd2..425f514c5c 100644 +--- a/sysdeps/nptl/libc-lockP.h ++++ b/sysdeps/nptl/libc-lockP.h +@@ -32,9 +32,10 @@ + ld.so might be used on old kernels with a different libc.so. */ + #include <lowlevellock.h> + #include <tls.h> ++#include <libc-lock-arch.h> + + /* Mutex type. */ +-typedef int __libc_lock_t; ++typedef int __libc_lock_t __LIBC_LOCK_ALIGNMENT; + typedef struct { pthread_mutex_t mutex; } __rtld_lock_recursive_t; + typedef pthread_rwlock_t __libc_rwlock_t; + +diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +index bcff909b2f..5cda9bb072 100644 +--- a/sysdeps/posix/getaddrinfo.c ++++ b/sysdeps/posix/getaddrinfo.c +@@ -540,11 +540,11 @@ get_nscd_addresses (const char *name, const struct addrinfo *req, + at[count].addr[2] = htonl (0xffff); + } + else if (req->ai_family == AF_UNSPEC +- || air->family[count] == req->ai_family) ++ || air->family[i] == req->ai_family) + { +- at[count].family = air->family[count]; ++ at[count].family = air->family[i]; + memcpy (at[count].addr, addrs, size); +- if (air->family[count] == AF_INET6) ++ if (air->family[i] == AF_INET6) + res->got_ipv6 = true; + } + at[count].next = at + count + 1; +diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +index a139a16532..3ceda9fdbf 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -265,6 +265,14 @@ $(objpfx)tst-mount-consts.out: ../sysdeps/unix/sysv/linux/tst-mount-consts.py + < /dev/null > $@ 2>&1; $(evaluate-test) + $(objpfx)tst-mount-consts.out: $(sysdeps-linux-python-deps) + ++tests-special += $(objpfx)tst-mount-compile.out ++$(objpfx)tst-mount-compile.out: ../sysdeps/unix/sysv/linux/tst-mount-compile.py ++ $(sysdeps-linux-python) \ ++ ../sysdeps/unix/sysv/linux/tst-mount-compile.py \ ++ $(sysdeps-linux-python-cc) \ ++ < /dev/null > $@ 2>&1; $(evaluate-test) ++$(objpfx)tst-mount-compile.out: $(sysdeps-linux-python-deps) ++ + tst-rseq-disable-ENV = GLIBC_TUNABLES=glibc.pthread.rseq=0 + + endif # $(subdir) == misc +diff --git a/sysdeps/unix/sysv/linux/alpha/brk_call.h b/sysdeps/unix/sysv/linux/alpha/brk_call.h +index b8088cf13f..0b851b6c86 100644 +--- a/sysdeps/unix/sysv/linux/alpha/brk_call.h ++++ b/sysdeps/unix/sysv/linux/alpha/brk_call.h +@@ -21,8 +21,7 @@ __brk_call (void *addr) + { + unsigned long int result = INTERNAL_SYSCALL_CALL (brk, addr); + if (result == -ENOMEM) +- /* Mimic the default error reporting behavior. */ +- return addr; +- else +- return (void *) result; ++ /* Mimic the generic error reporting behavior. */ ++ result = INTERNAL_SYSCALL_CALL (brk, 0); ++ return (void *) result; + } +diff --git a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h +index 4f1f810ea1..539b8d7716 100644 +--- a/sysdeps/unix/sysv/linux/bits/socket.h ++++ b/sysdeps/unix/sysv/linux/bits/socket.h +@@ -307,6 +307,12 @@ struct cmsghdr + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -316,18 +322,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff --git a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c +index 15b7a3a925..24f72b797a 100644 +--- a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c ++++ b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c +@@ -23,18 +23,38 @@ + struct cmsghdr * + __cmsg_nxthdr (struct msghdr *mhdr, struct cmsghdr *cmsg) + { ++ /* We may safely assume that cmsg lies between mhdr->msg_control and ++ mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * msg_control_ptr = (unsigned char *) mhdr->msg_control; ++ unsigned char * cmsg_ptr = (unsigned char *) cmsg; ++ ++ size_t size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ +- return NULL; ++ return (struct cmsghdr *) 0; ++ ++ /* There isn't enough space between cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr) ++ < size_needed) ++ || ((size_t) ++ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr ++ - size_needed) ++ < cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; + ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + cmsg = (struct cmsghdr *) ((unsigned char *) cmsg + + CMSG_ALIGN (cmsg->cmsg_len)); +- if ((unsigned char *) (cmsg + 1) > ((unsigned char *) mhdr->msg_control +- + mhdr->msg_controllen) +- || ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len) +- > ((unsigned char *) mhdr->msg_control + mhdr->msg_controllen))) +- /* No more entries. */ +- return NULL; + return cmsg; + } + libc_hidden_def (__cmsg_nxthdr) +diff --git a/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h b/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h +new file mode 100644 +index 0000000000..1844bbaf6f +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h +@@ -0,0 +1,25 @@ ++/* Private libc-internal arch-specific definitions. m68k version. ++ 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; see the file COPYING.LIB. If ++ not, see <https://www.gnu.org/licenses/>. */ ++ ++#ifndef _LIBC_LOCK_ARCH_H ++#define _LIBC_LOCK_ARCH_H ++ ++/* Linux enforces 4-bytes alignment on futex inputs. */ ++#define __LIBC_LOCK_ALIGNMENT __attribute__ ((__aligned__ (4))) ++ ++#endif +diff --git a/sysdeps/unix/sysv/linux/not-cancel.h b/sysdeps/unix/sysv/linux/not-cancel.h +index a263d294b1..cf35c8bfc9 100644 +--- a/sysdeps/unix/sysv/linux/not-cancel.h ++++ b/sysdeps/unix/sysv/linux/not-cancel.h +@@ -68,7 +68,7 @@ __writev_nocancel_nostatus (int fd, const struct iovec *iov, int iovcnt) + INTERNAL_SYSCALL_CALL (writev, fd, iov, iovcnt); + } + +-static inline int ++static inline ssize_t + __getrandom_nocancel (void *buf, size_t buflen, unsigned int flags) + { + return INLINE_SYSCALL_CALL (getrandom, buf, buflen, flags); +diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h +index bf4be80f8d..202520ee25 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h +@@ -122,6 +122,7 @@ + #define __NR_mbind 235 + #define __NR_membarrier 283 + #define __NR_memfd_create 279 ++#define __NR_memfd_secret 447 + #define __NR_migrate_pages 238 + #define __NR_mincore 232 + #define __NR_mkdirat 34 +diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h +index d656aedcc2..4e65f337d4 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h +@@ -127,6 +127,7 @@ + #define __NR_mbind 235 + #define __NR_membarrier 283 + #define __NR_memfd_create 279 ++#define __NR_memfd_secret 447 + #define __NR_migrate_pages 238 + #define __NR_mincore 232 + #define __NR_mkdirat 34 +diff --git a/sysdeps/unix/sysv/linux/sys/mount.h b/sysdeps/unix/sysv/linux/sys/mount.h +index f965986ba8..19841d0738 100644 +--- a/sysdeps/unix/sysv/linux/sys/mount.h ++++ b/sysdeps/unix/sysv/linux/sys/mount.h +@@ -27,77 +27,113 @@ + #include <stddef.h> + #include <sys/ioctl.h> + +-#define BLOCK_SIZE 1024 ++#ifdef __has_include ++# if __has_include ("linux/mount.h") ++# include "linux/mount.h" ++# endif ++#endif ++ ++ + #define BLOCK_SIZE_BITS 10 ++#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS) + + + /* These are the fs-independent mount-flags: up to 16 flags are + supported */ + enum + { ++#undef MS_RDONLY + MS_RDONLY = 1, /* Mount read-only. */ + #define MS_RDONLY MS_RDONLY ++#undef MS_NOSUID + MS_NOSUID = 2, /* Ignore suid and sgid bits. */ + #define MS_NOSUID MS_NOSUID ++#undef MS_NODEV + MS_NODEV = 4, /* Disallow access to device special files. */ + #define MS_NODEV MS_NODEV ++#undef MS_NOEXEC + MS_NOEXEC = 8, /* Disallow program execution. */ + #define MS_NOEXEC MS_NOEXEC ++#undef MS_SYNCHRONOUS + MS_SYNCHRONOUS = 16, /* Writes are synced at once. */ + #define MS_SYNCHRONOUS MS_SYNCHRONOUS ++#undef MS_REMOUNT + MS_REMOUNT = 32, /* Alter flags of a mounted FS. */ + #define MS_REMOUNT MS_REMOUNT ++#undef MS_MANDLOCK + MS_MANDLOCK = 64, /* Allow mandatory locks on an FS. */ + #define MS_MANDLOCK MS_MANDLOCK ++#undef MS_DIRSYNC + MS_DIRSYNC = 128, /* Directory modifications are synchronous. */ + #define MS_DIRSYNC MS_DIRSYNC ++#undef MS_NOSYMFOLLOW + MS_NOSYMFOLLOW = 256, /* Do not follow symlinks. */ + #define MS_NOSYMFOLLOW MS_NOSYMFOLLOW ++#undef MS_NOATIME + MS_NOATIME = 1024, /* Do not update access times. */ + #define MS_NOATIME MS_NOATIME ++#undef MS_NODIRATIME + MS_NODIRATIME = 2048, /* Do not update directory access times. */ + #define MS_NODIRATIME MS_NODIRATIME ++#undef MS_BIND + MS_BIND = 4096, /* Bind directory at different place. */ + #define MS_BIND MS_BIND ++#undef MS_MOVE + MS_MOVE = 8192, + #define MS_MOVE MS_MOVE ++#undef MS_REC + MS_REC = 16384, + #define MS_REC MS_REC ++#undef MS_SILENT + MS_SILENT = 32768, + #define MS_SILENT MS_SILENT ++#undef MS_POSIXACL + MS_POSIXACL = 1 << 16, /* VFS does not apply the umask. */ + #define MS_POSIXACL MS_POSIXACL ++#undef MS_UNBINDABLE + MS_UNBINDABLE = 1 << 17, /* Change to unbindable. */ + #define MS_UNBINDABLE MS_UNBINDABLE ++#undef MS_PRIVATE + MS_PRIVATE = 1 << 18, /* Change to private. */ + #define MS_PRIVATE MS_PRIVATE ++#undef MS_SLAVE + MS_SLAVE = 1 << 19, /* Change to slave. */ + #define MS_SLAVE MS_SLAVE ++#undef MS_SHARED + MS_SHARED = 1 << 20, /* Change to shared. */ + #define MS_SHARED MS_SHARED ++#undef MS_RELATIME + MS_RELATIME = 1 << 21, /* Update atime relative to mtime/ctime. */ + #define MS_RELATIME MS_RELATIME ++#undef MS_KERNMOUNT + MS_KERNMOUNT = 1 << 22, /* This is a kern_mount call. */ + #define MS_KERNMOUNT MS_KERNMOUNT ++#undef MS_I_VERSION + MS_I_VERSION = 1 << 23, /* Update inode I_version field. */ + #define MS_I_VERSION MS_I_VERSION ++#undef MS_STRICTATIME + MS_STRICTATIME = 1 << 24, /* Always perform atime updates. */ + #define MS_STRICTATIME MS_STRICTATIME ++#undef MS_LAZYTIME + MS_LAZYTIME = 1 << 25, /* Update the on-disk [acm]times lazily. */ + #define MS_LAZYTIME MS_LAZYTIME ++#undef MS_ACTIVE + MS_ACTIVE = 1 << 30, + #define MS_ACTIVE MS_ACTIVE ++#undef MS_NOUSER + MS_NOUSER = 1 << 31 + #define MS_NOUSER MS_NOUSER + }; + + /* Flags that can be altered by MS_REMOUNT */ ++#undef MS_RMT_MASK + #define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION \ + |MS_LAZYTIME) + + + /* Magic mount flag number. Has to be or-ed to the flag values. */ + ++#undef MS_MGC_VAL + #define MS_MGC_VAL 0xc0ed0000 /* Magic flag number to indicate "new" flags */ + #define MS_MGC_MSK 0xffff0000 /* Magic flag number mask */ + +@@ -106,20 +142,35 @@ enum + is probably as bad and I don't want to create yet another include + file. */ + ++#undef BLKROSET + #define BLKROSET _IO(0x12, 93) /* Set device read-only (0 = read-write). */ ++#undef BLKROGET + #define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */ ++#undef BLKRRPART + #define BLKRRPART _IO(0x12, 95) /* Re-read partition table. */ ++#undef BLKGETSIZE + #define BLKGETSIZE _IO(0x12, 96) /* Return device size. */ ++#undef BLKFLSBUF + #define BLKFLSBUF _IO(0x12, 97) /* Flush buffer cache. */ ++#undef BLKRASET + #define BLKRASET _IO(0x12, 98) /* Set read ahead for block device. */ ++#undef BLKRAGET + #define BLKRAGET _IO(0x12, 99) /* Get current read ahead setting. */ ++#undef BLKFRASET + #define BLKFRASET _IO(0x12,100) /* Set filesystem read-ahead. */ ++#undef BLKFRAGET + #define BLKFRAGET _IO(0x12,101) /* Get filesystem read-ahead. */ ++#undef BLKSECTSET + #define BLKSECTSET _IO(0x12,102) /* Set max sectors per request. */ ++#undef BLKSECTGET + #define BLKSECTGET _IO(0x12,103) /* Get max sectors per request. */ ++#undef BLKSSZGET + #define BLKSSZGET _IO(0x12,104) /* Get block device sector size. */ ++#undef BLKBSZGET + #define BLKBSZGET _IOR(0x12,112,size_t) ++#undef BLKBSZSET + #define BLKBSZSET _IOW(0x12,113,size_t) ++#undef BLKGETSIZE64 + #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size. */ + + +@@ -137,9 +188,6 @@ enum + }; + + +-/* fsopen flags. */ +-#define FSOPEN_CLOEXEC 0x00000001 +- + /* fsmount flags. */ + #define FSMOUNT_CLOEXEC 0x00000001 + +@@ -157,6 +205,7 @@ enum + #define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks. */ + + ++#ifndef MOUNT_ATTR_SIZE_VER0 + /* For mount_setattr. */ + struct mount_attr + { +@@ -165,6 +214,7 @@ struct mount_attr + uint64_t propagation; + uint64_t userns_fd; + }; ++#endif + + #define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */ + +@@ -185,26 +235,31 @@ struct mount_attr + #define FSPICK_EMPTY_PATH 0x00000008 + + ++#ifndef FSOPEN_CLOEXEC + /* The type of fsconfig call made. */ + enum fsconfig_command + { + FSCONFIG_SET_FLAG = 0, /* Set parameter, supplying no value */ +-#define FSCONFIG_SET_FLAG FSCONFIG_SET_FLAG ++# define FSCONFIG_SET_FLAG FSCONFIG_SET_FLAG + FSCONFIG_SET_STRING = 1, /* Set parameter, supplying a string value */ +-#define FSCONFIG_SET_STRING FSCONFIG_SET_STRING ++# define FSCONFIG_SET_STRING FSCONFIG_SET_STRING + FSCONFIG_SET_BINARY = 2, /* Set parameter, supplying a binary blob value */ +-#define FSCONFIG_SET_BINARY FSCONFIG_SET_BINARY ++# define FSCONFIG_SET_BINARY FSCONFIG_SET_BINARY + FSCONFIG_SET_PATH = 3, /* Set parameter, supplying an object by path */ +-#define FSCONFIG_SET_PATH FSCONFIG_SET_PATH ++# define FSCONFIG_SET_PATH FSCONFIG_SET_PATH + FSCONFIG_SET_PATH_EMPTY = 4, /* Set parameter, supplying an object by (empty) path */ +-#define FSCONFIG_SET_PATH_EMPTY FSCONFIG_SET_PATH_EMPTY ++# define FSCONFIG_SET_PATH_EMPTY FSCONFIG_SET_PATH_EMPTY + FSCONFIG_SET_FD = 5, /* Set parameter, supplying an object by fd */ +-#define FSCONFIG_SET_FD FSCONFIG_SET_FD ++# define FSCONFIG_SET_FD FSCONFIG_SET_FD + FSCONFIG_CMD_CREATE = 6, /* Invoke superblock creation */ +-#define FSCONFIG_CMD_CREATE FSCONFIG_CMD_CREATE ++# define FSCONFIG_CMD_CREATE FSCONFIG_CMD_CREATE + FSCONFIG_CMD_RECONFIGURE = 7, /* Invoke superblock reconfiguration */ +-#define FSCONFIG_CMD_RECONFIGURE FSCONFIG_CMD_RECONFIGURE ++# define FSCONFIG_CMD_RECONFIGURE FSCONFIG_CMD_RECONFIGURE + }; ++#endif ++ ++/* fsopen flags. */ ++#define FSOPEN_CLOEXEC 0x00000001 + + /* open_tree flags. */ + #define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */ +diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list +index 6c7b2f7011..028ad3107a 100644 +--- a/sysdeps/unix/sysv/linux/syscall-names.list ++++ b/sysdeps/unix/sysv/linux/syscall-names.list +@@ -21,8 +21,8 @@ + # This file can list all potential system calls. The names are only + # used if the installed kernel headers also provide them. + +-# The list of system calls is current as of Linux 5.18. +-kernel 5.18 ++# The list of system calls is current as of Linux 5.19. ++kernel 5.19 + + FAST_atomic_update + FAST_cmpxchg +diff --git a/sysdeps/unix/sysv/linux/tst-mount-compile.py b/sysdeps/unix/sysv/linux/tst-mount-compile.py +new file mode 100755 +index 0000000000..0ec74d4e0b +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-mount-compile.py +@@ -0,0 +1,66 @@ ++#!/usr/bin/python3 ++# Check if glibc provided sys/mount.h can be used along related kernel ++# headers. ++# 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/>. ++ ++import argparse ++import sys ++ ++import glibcextract ++ ++ ++def main(): ++ """The main entry point.""" ++ parser = argparse.ArgumentParser( ++ description='Check if glibc provided sys/mount.h can be ' ++ ' used along related kernel headers.') ++ parser.add_argument('--cc', metavar='CC', ++ help='C compiler (including options) to use') ++ args = parser.parse_args() ++ ++ if glibcextract.compile_c_snippet( ++ '#include <linux/mount.h>', ++ args.cc).returncode != 0: ++ sys.exit (77) ++ ++ def check(testname, snippet): ++ # Add -Werror to catch macro redefinitions and _ISOMAC to avoid ++ # internal glibc definitions. ++ r = glibcextract.compile_c_snippet(snippet, args.cc, ++ '-Werror -D_ISOMAC') ++ if r.returncode != 0: ++ print('error: test {}:\n{}'.format(testname, r.output.decode())) ++ return r.returncode ++ ++ status = max( ++ check("sys/mount.h + linux/mount.h", ++ "#include <sys/mount.h>\n" ++ "#include <linux/mount.h>"), ++ check("sys/mount.h + linux/fs.h", ++ "#include <sys/mount.h>\n" ++ "#include <linux/fs.h>"), ++ check("linux/mount.h + sys/mount.h", ++ "#include <linux/mount.h>\n" ++ "#include <sys/mount.h>"), ++ check("linux/fs.h + sys/mount.h", ++ "#include <linux/fs.h>\n" ++ "#include <sys/mount.h>")) ++ sys.exit(status) ++ ++if __name__ == '__main__': ++ main() +diff --git a/sysdeps/unix/sysv/linux/tst-mount-consts.py b/sysdeps/unix/sysv/linux/tst-mount-consts.py +index a62f803123..be2ef2daf1 100755 +--- a/sysdeps/unix/sysv/linux/tst-mount-consts.py ++++ b/sysdeps/unix/sysv/linux/tst-mount-consts.py +@@ -33,6 +33,11 @@ def main(): + help='C compiler (including options) to use') + args = parser.parse_args() + ++ if glibcextract.compile_c_snippet( ++ '#include <linux/mount.h>', ++ args.cc).returncode != 0: ++ sys.exit (77) ++ + linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) + # Constants in glibc were updated to match Linux v5.16. When glibc + # constants are updated this value should be updated to match the +diff --git a/sysdeps/unix/sysv/linux/tst-pidfd-consts.py b/sysdeps/unix/sysv/linux/tst-pidfd-consts.py +index 90cbb9be64..d732173abd 100644 +--- a/sysdeps/unix/sysv/linux/tst-pidfd-consts.py ++++ b/sysdeps/unix/sysv/linux/tst-pidfd-consts.py +@@ -33,11 +33,13 @@ def main(): + help='C compiler (including options) to use') + args = parser.parse_args() + +- linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) +- # Linux started to provide pidfd.h with 5.10. +- if linux_version_headers < (5, 10): ++ if glibcextract.compile_c_snippet( ++ '#include <linux/pidfd.h>', ++ args.cc).returncode != 0: + sys.exit (77) +- linux_version_glibc = (5, 18) ++ ++ linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) ++ linux_version_glibc = (5, 19) + sys.exit(glibcextract.compare_macro_consts( + '#include <sys/pidfd.h>\n', + '#include <asm/fcntl.h>\n' +diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c +index 037af22290..5711d1c312 100644 +--- a/sysdeps/unix/sysv/linux/tst-pidfd.c ++++ b/sysdeps/unix/sysv/linux/tst-pidfd.c +@@ -147,8 +147,11 @@ do_test (void) + may be denied if the process doesn't have CAP_SYS_PTRACE or + if a LSM security_ptrace_access_check denies access. */ + if (fd == -1 && errno == EPERM) +- FAIL_UNSUPPORTED ("don't have permission to use pidfd_getfd on pidfd, " +- "skipping test"); ++ { ++ TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), 0); ++ FAIL_UNSUPPORTED ("don't have permission to use pidfd_getfd on pidfd, " ++ "skipping test"); ++ } + TEST_VERIFY (fd > 0); + + char *path = xasprintf ("/proc/%d/fd/%d", pid, remote_fd); +diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile +index e6b9e8743a..4af102a3f6 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 \ +@@ -73,6 +74,8 @@ $(objpfx)tst-wcstol-locale.out: $(gen-locales) + $(objpfx)tst-wcstod-nan-locale.out: $(gen-locales) + $(objpfx)tst-c16-surrogate.out: $(gen-locales) + $(objpfx)tst-c32-state.out: $(gen-locales) ++$(objpfx)test-c8rtomb.out: $(gen-locales) ++$(objpfx)test-mbrtoc8.out: $(gen-locales) + endif + + $(objpfx)tst-wcstod-round: $(libm) +diff --git a/wcsmbs/bits/wchar2-decl.h b/wcsmbs/bits/wchar2-decl.h +new file mode 100644 +index 0000000000..8e1735c33b +--- /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 0e017f458b..3f110efe57 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/uchar.h b/wcsmbs/uchar.h +index c37e8619a0..5f7139f279 100644 +--- a/wcsmbs/uchar.h ++++ b/wcsmbs/uchar.h +@@ -34,8 +34,16 @@ + /* Declare the C2x char8_t typedef in C2x modes, but only if the C++ + __cpp_char8_t feature test macro is not defined. */ + #if __GLIBC_USE (ISOC2X) && !defined __cpp_char8_t ++#if __GNUC_PREREQ (10, 0) && defined __cplusplus ++/* Suppress the diagnostic regarding char8_t being a keyword in C++20. */ ++# pragma GCC diagnostic push ++# pragma GCC diagnostic ignored "-Wc++20-compat" ++#endif + /* Define the 8-bit character type. */ + typedef unsigned char char8_t; ++#if __GNUC_PREREQ (10, 0) && defined __cplusplus ++# pragma GCC diagnostic pop ++#endif + #endif + + #ifndef __USE_ISOCXX11 +diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h +index 5d6a40853d..c1321c7518 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 */