diff --git a/Makeconfig b/Makeconfig index ba70321af1..9dd058e04b 100644 --- a/Makeconfig +++ b/Makeconfig @@ -43,6 +43,22 @@ else $(error objdir must be defined by the build-directory Makefile) endif +# Did we request 'make -s' run? "yes" or "no". +# Starting from make-4.4 MAKEFLAGS now contains long +# options like '--shuffle'. To detect presence of 's' +# we pick first word with short options. Long options +# are guaranteed to come after whitespace. We use '-' +# prefix to always have a word before long options +# even if no short options were passed. +# Typical MAKEFLAGS values to watch for: +# "rs --shuffle=42" (silent) +# " --shuffle" (not silent) +ifeq ($(findstring s, $(firstword -$(MAKEFLAGS))),) +silent-make := no +else +silent-make := yes +endif + # Root of the sysdeps tree. sysdep_dir := $(..)sysdeps export sysdep_dir := $(sysdep_dir) @@ -868,7 +884,7 @@ endif # Use 64 bit time_t support for installed programs installed-modules = nonlib nscd lddlibc4 ldconfig locale_programs \ iconvprogs libnss_files libnss_compat libnss_db libnss_hesiod \ - libutil libpcprofile libSegFault + libutil libpcprofile libSegFault libnsl +extra-time-flags = $(if $(filter $(installed-modules),\ $(in-module)),-D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64) @@ -917,7 +933,7 @@ endif # umpteen zillion filenames along with it (we use `...' instead) # but we don't want this echoing done when the user has said # he doesn't want to see commands echoed by using -s. -ifneq "$(findstring s,$(MAKEFLAGS))" "" # if -s +ifeq ($(silent-make),yes) # if -s +cmdecho := echo >/dev/null else # not -s +cmdecho := echo diff --git a/Makerules b/Makerules index d1e139d03c..09c0cf8357 100644 --- a/Makerules +++ b/Makerules @@ -794,7 +794,7 @@ endif # Maximize efficiency by minimizing the number of rules. .SUFFIXES: # Clear the suffix list. We don't use suffix rules. # Don't define any builtin rules. -MAKEFLAGS := $(MAKEFLAGS)r +MAKEFLAGS := $(MAKEFLAGS) -r # Generic rule for making directories. %/: @@ -811,7 +811,7 @@ MAKEFLAGS := $(MAKEFLAGS)r .PRECIOUS: $(foreach l,$(libtypes),$(patsubst %,$(common-objpfx)$l,c)) # Use the verbose option of ar and tar when not running silently. -ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s +ifeq ($(silent-make),no) # if not -s verbose := v else # -s verbose := diff --git a/NEWS b/NEWS index f61e521fc8..0f0ebce3f0 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,94 @@ See the end for copying conditions. Please send GNU C library bug reports via using `glibc' in the "product" field. +Version 2.36.1 + +Major new features: + +* The getent tool now supports the --no-addrconfig option. The output of + getent with --no-addrconfig may contain addresses of families not + configured on the current host i.e. as-if you had not passed + AI_ADDRCONFIG to getaddrinfo calls. + +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. + + CVE-2023-4527: If the system is configured in no-aaaa mode via + /etc/resolv.conf, getaddrinfo is called for the AF_UNSPEC address + family, and a DNS response is received over TCP that is larger than + 2048 bytes, getaddrinfo may potentially disclose stack contents via + the returned address data, or crash. + + CVE-2023-4806: When an NSS plugin only implements the + _gethostbyname2_r and _getcanonname_r callbacks, getaddrinfo could use + memory that was freed during buffer resizing, potentially causing a + crash or read or write to arbitrary memory. + + CVE-2023-5156: The fix for CVE-2023-4806 introduced a memory leak when + an application calls getaddrinfo for AF_INET6 with AI_CANONNAME, + AI_ALL and AI_V4MAPPED flags set. + + CVE-2023-4911: If a tunable of the form NAME=NAME=VAL is passed in the + environment of a setuid program and NAME is valid, it may result in a + buffer overflow, which could be exploited to achieve escalated + privileges. This flaw was introduced in glibc 2.34. + +The following bugs are resolved with this release: + + [12154] Do not fail DNS resolution for CNAMEs which are not host names + [20975] Deferred cancellation triggers in __check_pf and looses lock leading to deadlock + [24816] Fix tst-nss-files-hosts-long on single-stack hosts + [27576] gmon: improve mcount overflow handling + [28846] CMSG_NXTHDR may trigger -Wstrict-overflow warning + [29039] Corrupt DTV after reuse of a TLS module ID following dlclose with unused TLS + [29444] gmon: Fix allocated buffer overflow (bug 29444) + [29864] libc: __libc_start_main() should obtain program headers + address (_dl_phdr) from the auxv, not the ELF header. + [29305] Conserve NSS buffer space during DNS packet parsing + [29402] nscd: nscd: No such file or directory + [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 + [29463] math/test-float128-y1 fails on x86_64 + [29488] test-ibm128-llround fails on ppc64el when built with gcc-12 and -O2 + or higher + [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 + [29576] build: librtld.os: in function `_dl_start_profile': + (.text+0x9444): undefined reference to `strcpy' + [29583] Use 64-bit interfaces in gconv_parseconfdir + [29600] Do not completely clear reused namespace in dlmopen + [29607] nscd repeatably crashes calling __strlen_avx2 when hosts cache is + enabled + [29638] libc: stdlib: arc4random fallback is never used + [29657] libc: Incorrect struct stat for 64-bit time on linux/generic + platforms + [29730] broken y2038 support in fstatat on MIPS N64 + [29771] Restore IPC_64 support in sysvipc *ctl functions + [29776] elf/tst-tlsopt-powerpc fails when compiled with -mcpu=power10 + [29951] time: Set daylight to 1 for matching DST/offset change + [30053] time: strftime %s returns -1 after 2038 on 32 bits systems + [30101] gmon: fix memory corruption issues + [30151] gshadow: Matching sgetsgent, sgetsgent_r ERANGE handling + [30163] posix: Fix system blocks SIGCHLD erroneously + [30305] x86_64: Fix asm constraints in feraiseexcept + [30477] libc: [RISCV]: time64 does not work on riscv32 + [30515] _dl_find_object incorrectly returns 1 during early startup + [30745] Slight bug in cache info codes for x86 + [30804] F_GETLK, F_SETLK, and F_SETLKW value change for powerpc64 with + -D_FILE_OFFSET_BITS=64 + [30842] Stack read overflow in getaddrinfo in no-aaaa mode (CVE-2023-4527) + [30843] potential use-after-free in getcanonname (CVE-2023-4806) + [31184] FAIL: elf/tst-tlsgap + [31185] Incorrect thread point access in _dl_tlsdesc_undefweak and _dl_tlsdesc_dynamic + 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/csu/libc-start.c b/csu/libc-start.c index 543560f36c..bfeee6d851 100644 --- a/csu/libc-start.c +++ b/csu/libc-start.c @@ -262,28 +262,7 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), } # endif _dl_aux_init (auxvec); - if (GL(dl_phdr) == NULL) # endif - { - /* Starting from binutils-2.23, the linker will define the - magic symbol __ehdr_start to point to our own ELF header - if it is visible in a segment that also includes the phdrs. - So we can set up _dl_phdr and _dl_phnum even without any - information from auxv. */ - - extern const ElfW(Ehdr) __ehdr_start -# if BUILD_PIE_DEFAULT - __attribute__ ((visibility ("hidden"))); -# else - __attribute__ ((weak, visibility ("hidden"))); - if (&__ehdr_start != NULL) -# endif - { - assert (__ehdr_start.e_phentsize == sizeof *GL(dl_phdr)); - GL(dl_phdr) = (const void *) &__ehdr_start + __ehdr_start.e_phoff; - GL(dl_phnum) = __ehdr_start.e_phnum; - } - } __tunables_init (__environ); diff --git a/csu/libc-tls.c b/csu/libc-tls.c index 0a216c5502..7fdf7cd7a8 100644 --- a/csu/libc-tls.c +++ b/csu/libc-tls.c @@ -118,19 +118,18 @@ __libc_setup_tls (void) __tls_pre_init_tp (); /* Look through the TLS segment if there is any. */ - if (_dl_phdr != NULL) - for (phdr = _dl_phdr; phdr < &_dl_phdr[_dl_phnum]; ++phdr) - if (phdr->p_type == PT_TLS) - { - /* Remember the values we need. */ - memsz = phdr->p_memsz; - filesz = phdr->p_filesz; - initimage = (void *) phdr->p_vaddr + main_map->l_addr; - align = phdr->p_align; - if (phdr->p_align > max_align) - max_align = phdr->p_align; - break; - } + for (phdr = _dl_phdr; phdr < &_dl_phdr[_dl_phnum]; ++phdr) + if (phdr->p_type == PT_TLS) + { + /* Remember the values we need. */ + memsz = phdr->p_memsz; + filesz = phdr->p_filesz; + initimage = (void *) phdr->p_vaddr + main_map->l_addr; + align = phdr->p_align; + if (phdr->p_align > max_align) + max_align = phdr->p_align; + break; + } /* Calculate the size of the static TLS surplus, with 0 auditors. */ _dl_tls_static_surplus_init (0); 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..30c9af1de9 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -53,6 +53,7 @@ routines = \ # profiled libraries. dl-routines = \ dl-call-libc-early-init \ + dl-call_fini \ dl-close \ dl-debug \ dl-debug-symbols \ @@ -374,6 +375,8 @@ tests += \ tst-align \ tst-align2 \ tst-align3 \ + tst-audit-tlsdesc \ + tst-audit-tlsdesc-dlopen \ tst-audit1 \ tst-audit2 \ tst-audit8 \ @@ -408,6 +411,7 @@ tests += \ tst-dlmopen4 \ tst-dlmopen-dlerror \ tst-dlmopen-gethostbyname \ + tst-dlmopen-twice \ tst-dlopenfail \ tst-dlopenfail-2 \ tst-dlopenrpath \ @@ -631,6 +635,7 @@ ifeq ($(run-built-tests),yes) tests-special += \ $(objpfx)noload-mem.out \ $(objpfx)tst-ldconfig-X.out \ + $(objpfx)tst-ldconfig-p.out \ $(objpfx)tst-leaks1-mem.out \ $(objpfx)tst-rtld-help.out \ # tests-special @@ -765,6 +770,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 +805,7 @@ modules-names += \ tst-auditmanymod7 \ tst-auditmanymod8 \ tst-auditmanymod9 \ + tst-auditmod-tlsdesc \ tst-auditmod1 \ tst-auditmod9a \ tst-auditmod9b \ @@ -834,6 +842,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 +1000,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 @@ -2410,6 +2405,11 @@ $(objpfx)tst-ldconfig-X.out : tst-ldconfig-X.sh $(objpfx)ldconfig '$(run-program-env)' > $@; \ $(evaluate-test) +$(objpfx)tst-ldconfig-p.out : tst-ldconfig-p.sh $(objpfx)ldconfig + $(SHELL) $< '$(common-objpfx)' '$(test-wrapper-env)' \ + '$(run-program-env)' > $@; \ + $(evaluate-test) + # Test static linking of all the libraries we can possibly link # together. Note that in some configurations this may be less than the # complete list of libraries we build but we try to maxmimize this list. @@ -2967,3 +2967,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-call_fini.c b/elf/dl-call_fini.c new file mode 100644 index 0000000000..9e7ba10fa2 --- /dev/null +++ b/elf/dl-call_fini.c @@ -0,0 +1,50 @@ +/* Invoke DT_FINI and DT_FINI_ARRAY callbacks. + Copyright (C) 1996-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 + . */ + +#include +#include + +void +_dl_call_fini (void *closure_map) +{ + struct link_map *map = closure_map; + + /* When debugging print a message first. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS)) + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", map->l_name, map->l_ns); + + /* Make sure nothing happens if we are called twice. */ + map->l_init_called = 0; + + ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY]; + if (fini_array != NULL) + { + ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr + + fini_array->d_un.d_ptr); + size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val + / sizeof (ElfW(Addr))); + + while (sz-- > 0) + ((fini_t) array[sz]) (); + } + + /* Next try the old-style destructor. */ + ElfW(Dyn) *fini = map->l_info[DT_FINI]; + if (fini != NULL) + DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr)); +} diff --git a/elf/dl-close.c b/elf/dl-close.c index bcd6e206e9..14deca2e2b 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -36,11 +36,6 @@ #include - -/* Type of the constructor functions. */ -typedef void (*fini_t) (void); - - /* Special l_idx value used to indicate which objects remain loaded. */ #define IDX_STILL_USED -1 @@ -110,31 +105,6 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, return false; } -/* Invoke dstructors for CLOSURE (a struct link_map *). Called with - exception handling temporarily disabled, to make errors fatal. */ -static void -call_destructors (void *closure) -{ - struct link_map *map = closure; - - if (map->l_info[DT_FINI_ARRAY] != NULL) - { - ElfW(Addr) *array = - (ElfW(Addr) *) (map->l_addr - + map->l_info[DT_FINI_ARRAY]->d_un.d_ptr); - unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))); - - while (sz-- > 0) - ((fini_t) array[sz]) (); - } - - /* Next try the old-style destructor. */ - if (map->l_info[DT_FINI] != NULL) - DL_CALL_DT_FINI (map, ((void *) map->l_addr - + map->l_info[DT_FINI]->d_un.d_ptr)); -} - void _dl_close_worker (struct link_map *map, bool force) { @@ -280,17 +250,7 @@ _dl_close_worker (struct link_map *map, bool force) half-cooked objects. Temporarily disable exception handling, so that errors are fatal. */ if (imap->l_init_called) - { - /* When debugging print a message first. */ - if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, - 0)) - _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", - imap->l_name, nsid); - - if (imap->l_info[DT_FINI_ARRAY] != NULL - || imap->l_info[DT_FINI] != NULL) - _dl_catch_exception (NULL, call_destructors, imap); - } + _dl_catch_exception (NULL, _dl_call_fini, imap); #ifdef SHARED /* Auditing checkpoint: we remove an object. */ diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c index 4d5831b6f4..2e5b456c11 100644 --- a/elf/dl-find_object.c +++ b/elf/dl-find_object.c @@ -46,7 +46,7 @@ _dl_find_object_slow (void *pc, struct dl_find_object *result) struct dl_find_object_internal internal; _dl_find_object_from_map (l, &internal); _dl_find_object_to_external (&internal, result); - return 1; + return 0; } /* Object not found. */ diff --git a/elf/dl-fini.c b/elf/dl-fini.c index 030b1fcbcd..50ff94db16 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -21,11 +21,6 @@ #include #include - -/* Type of the constructor functions. */ -typedef void (*fini_t) (void); - - void _dl_fini (void) { @@ -116,38 +111,7 @@ _dl_fini (void) if (l->l_init_called) { - /* Make sure nothing happens if we are called twice. */ - l->l_init_called = 0; - - /* Is there a destructor function? */ - if (l->l_info[DT_FINI_ARRAY] != NULL - || (ELF_INITFINI && l->l_info[DT_FINI] != NULL)) - { - /* When debugging print a message first. */ - if (__builtin_expect (GLRO(dl_debug_mask) - & DL_DEBUG_IMPCALLS, 0)) - _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", - DSO_FILENAME (l->l_name), - ns); - - /* First see whether an array is given. */ - if (l->l_info[DT_FINI_ARRAY] != NULL) - { - ElfW(Addr) *array = - (ElfW(Addr) *) (l->l_addr - + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); - unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))); - while (i-- > 0) - ((fini_t) array[i]) (); - } - - /* Next try the old-style destructor. */ - if (ELF_INITFINI && l->l_info[DT_FINI] != NULL) - DL_CALL_DT_FINI - (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr); - } - + _dl_call_fini (l); #ifdef SHARED /* Auditing checkpoint: another object closed. */ _dl_audit_objclose (l); 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-init.c b/elf/dl-init.c index deefeb099a..fca8e3a05e 100644 --- a/elf/dl-init.c +++ b/elf/dl-init.c @@ -25,10 +25,14 @@ static void call_init (struct link_map *l, int argc, char **argv, char **env) { + /* Do not run constructors for proxy objects. */ + if (l != l->l_real) + return; + /* If the object has not been relocated, this is a bug. The function pointers are invalid in this case. (Executables do not - need relocation, and neither do proxy objects.) */ - assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable); + need relocation.) */ + assert (l->l_relocated || l->l_type == lt_executable); if (l->l_init_called) /* This object is all done. */ diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 4c86dc694e..67fb2e31e2 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -854,6 +854,23 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, if (__glibc_unlikely (current_value.m->l_used == 0)) current_value.m->l_used = 1; + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS)) + { + const char *reference_name = undef_map->l_name; + + _dl_debug_printf ("binding file %s [%lu] to %s [%lu]: %s symbol `%s'", + DSO_FILENAME (reference_name), + undef_map->l_ns, + DSO_FILENAME (current_value.m->l_name), + current_value.m->l_ns, + protected ? "protected" : "normal", undef_name); + if (version) + _dl_debug_printf_c (" [%s]\n", version->name); + else + _dl_debug_printf_c ("\n"); + } + + *ref = current_value.s; return LOOKUP_VALUE (current_value.m); } diff --git a/elf/dl-open.c b/elf/dl-open.c index a23e65926b..e7db5e9642 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -850,6 +850,7 @@ no more namespaces available for dlmopen()")); ++GL(dl_nns); } + GL(dl_ns)[nsid].libc_map = NULL; _dl_debug_update (nsid)->r_state = RT_CONSISTENT; } /* Never allow loading a DSO in a namespace which is empty. Such 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/dl-support.c b/elf/dl-support.c index 4af0b5b2ce..f45b630ba5 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -255,6 +255,25 @@ _dl_aux_init (ElfW(auxv_t) *av) for (int i = 0; i < array_length (auxv_values); ++i) auxv_values[i] = 0; _dl_parse_auxv (av, auxv_values); + + _dl_phdr = (void*) auxv_values[AT_PHDR]; + _dl_phnum = auxv_values[AT_PHNUM]; + + if (_dl_phdr == NULL) + { + /* Starting from binutils-2.23, the linker will define the + magic symbol __ehdr_start to point to our own ELF header + if it is visible in a segment that also includes the phdrs. + So we can set up _dl_phdr and _dl_phnum even without any + information from auxv. */ + + extern const ElfW(Ehdr) __ehdr_start attribute_hidden; + assert (__ehdr_start.e_phentsize == sizeof *GL(dl_phdr)); + _dl_phdr = (const void *) &__ehdr_start + __ehdr_start.e_phoff; + _dl_phnum = __ehdr_start.e_phnum; + } + + assert (_dl_phdr != NULL); } #endif @@ -323,20 +342,19 @@ _dl_non_dynamic_init (void) if (_dl_platform != NULL) _dl_platformlen = strlen (_dl_platform); - if (_dl_phdr != NULL) - for (const ElfW(Phdr) *ph = _dl_phdr; ph < &_dl_phdr[_dl_phnum]; ++ph) - switch (ph->p_type) - { - /* Check if the stack is nonexecutable. */ - case PT_GNU_STACK: - _dl_stack_flags = ph->p_flags; - break; - - case PT_GNU_RELRO: - _dl_main_map.l_relro_addr = ph->p_vaddr; - _dl_main_map.l_relro_size = ph->p_memsz; - break; - } + for (const ElfW(Phdr) *ph = _dl_phdr; ph < &_dl_phdr[_dl_phnum]; ++ph) + switch (ph->p_type) + { + /* Check if the stack is nonexecutable. */ + case PT_GNU_STACK: + _dl_stack_flags = ph->p_flags; + break; + + case PT_GNU_RELRO: + _dl_main_map.l_relro_addr = ph->p_vaddr; + _dl_main_map.l_relro_size = ph->p_memsz; + break; + } call_function_static_weak (_dl_find_object_init); diff --git a/elf/dl-tls.c b/elf/dl-tls.c index 093cdddb7e..bf0ff0d9e8 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -160,6 +160,7 @@ _dl_assign_tls_modid (struct link_map *l) { /* Mark the entry as used, so any dependency see it. */ atomic_store_relaxed (&runp->slotinfo[result - disp].map, l); + atomic_store_relaxed (&runp->slotinfo[result - disp].gen, 0); break; } diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c index 8e7ee9df10..76cf8b9da3 100644 --- a/elf/dl-tunables.c +++ b/elf/dl-tunables.c @@ -187,11 +187,7 @@ parse_tunables (char *tunestr, char *valstring) /* If we reach the end of the string before getting a valid name-value pair, bail out. */ if (p[len] == '\0') - { - if (__libc_enable_secure) - tunestr[off] = '\0'; - return; - } + break; /* We did not find a valid name-value pair before encountering the colon. */ @@ -251,9 +247,16 @@ parse_tunables (char *tunestr, char *valstring) } } - if (p[len] != '\0') - p += len + 1; + /* We reached the end while processing the tunable string. */ + if (p[len] == '\0') + break; + + p += len + 1; } + + /* Terminate tunestr before we leave. */ + if (__libc_enable_secure) + tunestr[off] = '\0'; } #endif diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list index e6a56b3070..9fa3b484cf 100644 --- a/elf/dl-tunables.list +++ b/elf/dl-tunables.list @@ -169,4 +169,17 @@ glibc { default: 2 } } + + gmon { + minarcs { + type: INT_32 + minval: 50 + default: 50 + } + maxarcs { + type: INT_32 + minval: 50 + default: 1048576 + } + } } 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>{}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[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[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[];%c(a1());}a1>a>];+b[b1>b>];-b[];%c(a1());}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-auditmod28.c b/elf/tst-auditmod28.c index db7ba95abe..9e0a122c38 100644 --- a/elf/tst-auditmod28.c +++ b/elf/tst-auditmod28.c @@ -71,6 +71,17 @@ la_version (unsigned int current) TEST_VERIFY (dladdr1 (&_exit, &info, &extra_info, RTLD_DL_LINKMAP) != 0); TEST_VERIFY (extra_info == handle); + /* Check _dl_find_object. */ + struct dl_find_object dlfo; + TEST_COMPARE (_dl_find_object (__builtin_return_address (0), &dlfo), 0); + /* "ld.so" is seen with --enable-hardcoded-path-in-tests. */ + if (strcmp (basename (dlfo.dlfo_link_map->l_name), "ld.so") != 0) + TEST_COMPARE_STRING (basename (dlfo.dlfo_link_map->l_name), LD_SO); + TEST_COMPARE (_dl_find_object (dlsym (handle, "environ"), &dlfo), 0); + TEST_COMPARE_STRING (basename (dlfo.dlfo_link_map->l_name), LIBC_SO); + TEST_COMPARE (_dl_find_object ((void *) 1, &dlfo), -1); + TEST_COMPARE (_dl_find_object ((void *) -1, &dlfo), -1); + /* Verify that dlmopen creates a new namespace. */ void *dlmopen_handle = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW); TEST_VERIFY (dlmopen_handle != handle); 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 + . */ + +#include + +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 + . */ + +#include +#include + +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..70c71fe19c --- /dev/null +++ b/elf/tst-dlmopen-twice.c @@ -0,0 +1,54 @@ +/* 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 + . */ + +#include +#include +#include + +/* Run the test multiple times, to check finding a new namespace while + another namespace is already in use. This used to trigger bug 29600. */ +static void +recurse (int depth) +{ + if (depth == 0) + return; + + printf ("info: running at depth %d\n", depth); + 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); + recurse (depth - 1); + xdlclose (handle); +} + +static int +do_test (void) +{ + /* First run the test without nesting. */ + recurse (1); + + /* Then with nesting. The constant needs to be less than the + internal DL_NNS namespace constant. */ + recurse (10); + return 0; +} + +#include diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c index 88182b7b25..5e9e4c5756 100644 --- a/elf/tst-env-setuid-tunables.c +++ b/elf/tst-env-setuid-tunables.c @@ -52,6 +52,8 @@ const char *teststrings[] = "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096", "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096", "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096", + "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096", + "glibc.malloc.check=2", "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2", "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096", ":glibc.malloc.garbage=2:glibc.malloc.check=1", @@ -70,6 +72,8 @@ const char *resultstrings[] = "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096", "glibc.malloc.mmap_threshold=4096", "glibc.malloc.mmap_threshold=4096", + "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096", + "", "", "", "", @@ -84,11 +88,18 @@ test_child (int off) const char *val = getenv ("GLIBC_TUNABLES"); #if HAVE_TUNABLES + printf (" [%d] GLIBC_TUNABLES is %s\n", off, val); + fflush (stdout); if (val != NULL && strcmp (val, resultstrings[off]) == 0) return 0; if (val != NULL) - printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val); + printf (" [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n", + off, val, resultstrings[off]); + else + printf (" [%d] GLIBC_TUNABLES environment variable absent\n", off); + + fflush (stdout); return 1; #else @@ -117,21 +128,26 @@ do_test (int argc, char **argv) if (ret != 0) exit (1); - exit (EXIT_SUCCESS); + /* Special return code to make sure that the child executed all the way + through. */ + exit (42); } else { - int ret = 0; - /* Spawn tests. */ for (int i = 0; i < array_length (teststrings); i++) { char buf[INT_BUFSIZE_BOUND (int)]; - printf ("Spawned test for %s (%d)\n", teststrings[i], i); + printf ("[%d] Spawned test for %s\n", i, teststrings[i]); snprintf (buf, sizeof (buf), "%d\n", i); + fflush (stdout); if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0) - exit (1); + { + printf (" [%d] Failed to set GLIBC_TUNABLES: %m", i); + support_record_failure (); + continue; + } int status = support_capture_subprogram_self_sgid (buf); @@ -139,9 +155,14 @@ do_test (int argc, char **argv) if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) return EXIT_UNSUPPORTED; - ret |= status; + if (WEXITSTATUS (status) != 42) + { + printf (" [%d] child failed with status %d\n", i, + WEXITSTATUS (status)); + support_record_failure (); + } } - return ret; + return 0; } } diff --git a/elf/tst-ldconfig-p.sh b/elf/tst-ldconfig-p.sh new file mode 100644 index 0000000000..ec937bf4ec --- /dev/null +++ b/elf/tst-ldconfig-p.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# Test that ldconfig -p prints something useful. +# Copyright (C) 2023 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 +# . + +# Check that the newly built ldconfig -p can dump the system +# /etc/ld.so.cache file. This should always work even if the ABIs are +# not compatible, except in a cross-endian build (that presumably +# involves emulation when running ldconfig). + +common_objpfx=$1 +test_wrapper_env=$2 +run_program_env=$3 + +if ! test -r /etc/ld.so.cache; then + echo "warning: /etc/ld.so.cache does not exist, test skipped" + exit 77 +fi + +testout="${common_objpfx}elf/tst-ldconfig-p.out" +# Truncate file. +: > "$testout" + +${test_wrapper_env} \ +${run_program_env} \ +${common_objpfx}elf/ldconfig -p \ + $testroot/lib >>"$testout" 2>>"$testout" +status=$? +echo "info: ldconfig exit status: $status" >>"$testout" + +errors=0 +case $status in + (0) + if head -n 1 "$testout" | \ + grep -q "libs found in cache \`/etc/ld.so.cache'\$" ; then + echo "info: initial string found" >>"$testout" + else + echo "error: initial string not found" >>"$testout" + errors=1 + fi + if grep -q "^ libc\.so\..* => " "$testout"; then + echo "info: libc.so.* string found" >>"$testout" + else + echo "error: libc.so.* string not found" >>"$testout" + errors=1 + fi + ;; + (1) + if head -n 1 "$testout" | \ + grep -q ": Cache file has wrong endianness\.$" ; then + echo "info: cache file has wrong endianess" >> "$testout" + else + echo "error: unexpected ldconfig error message" >> "$testout" + errors=1 + fi + ;; + (*) + echo "error: unexpected exit status" >> "$testout" + errors=1 + ;; +esac + +exit $errors diff --git a/gmon/Makefile b/gmon/Makefile index 552b7d7751..fbe2b0ba5c 100644 --- a/gmon/Makefile +++ b/gmon/Makefile @@ -1,4 +1,5 @@ -# Copyright (C) 1995-2022 Free Software Foundation, Inc. +# Copyright (C) 1995-2023 Free Software Foundation, Inc. +# Copyright The GNU Toolchain Authors. # This file is part of the GNU C Library. # The GNU C Library is free software; you can redistribute it and/or @@ -25,7 +26,7 @@ include ../Makeconfig headers := sys/gmon.h sys/gmon_out.h sys/profil.h routines := gmon mcount profil sprofil prof-freq -tests = tst-sprofil tst-gmon +tests = tst-sprofil tst-gmon tst-mcleanup ifeq ($(build-profile),yes) tests += tst-profile-static tests-static += tst-profile-static @@ -56,6 +57,14 @@ ifeq ($(run-built-tests),yes) tests-special += $(objpfx)tst-gmon-gprof.out endif +CFLAGS-tst-mcleanup.c := -fno-omit-frame-pointer -pg +tst-mcleanup-no-pie = yes +CRT-tst-mcleanup := $(csu-objpfx)g$(start-installed-name) +tst-mcleanup-ENV := GMON_OUT_PREFIX=$(objpfx)tst-mcleanup.data +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)tst-mcleanup.out +endif + CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg CRT-tst-gmon-static := $(csu-objpfx)g$(static-start-installed-name) tst-gmon-static-no-pie = yes @@ -103,6 +112,18 @@ $(objpfx)tst-gmon.out: clean-tst-gmon-data clean-tst-gmon-data: rm -f $(objpfx)tst-gmon.data.* +$(objpfx)tst-mcount-overflow.o: clean-tst-mcount-overflow-data +clean-tst-mcount-overflow-data: + rm -f $(objpfx)tst-mcount-overflow.data.* + +$(objpfx)tst-mcount-overflow-check.out: tst-mcount-overflow-check.sh $(objpfx)tst-mcount-overflow.out + $(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \ + $(evaluate-test) + +$(objpfx)tst-mcleanup.out: clean-tst-mcleanup-data +clean-tst-mcleanup-data: + rm -f $(objpfx)tst-mcleanup.data.* + $(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out $(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \ $(evaluate-test) diff --git a/gmon/gmon.c b/gmon/gmon.c index dee64803ad..97be1f72ca 100644 --- a/gmon/gmon.c +++ b/gmon/gmon.c @@ -97,11 +97,8 @@ __moncontrol (int mode) { struct gmonparam *p = &_gmonparam; - /* Don't change the state if we ran into an error. */ - if (p->state == GMON_PROF_ERROR) - return; - - if (mode) + /* Treat start request as stop if error or gmon not initialized. */ + if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL) { /* start */ __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale); @@ -111,7 +108,9 @@ __moncontrol (int mode) { /* stop */ __profil(NULL, 0, 0, 0); - p->state = GMON_PROF_OFF; + /* Don't change the state if we ran into an error. */ + if (p->state != GMON_PROF_ERROR) + p->state = GMON_PROF_OFF; } } libc_hidden_def (__moncontrol) @@ -124,6 +123,19 @@ __monstartup (u_long lowpc, u_long highpc) int o; char *cp; struct gmonparam *p = &_gmonparam; + long int minarcs, maxarcs; + + /* No tunables, we use hardcoded defaults */ + minarcs = MINARCS; + maxarcs = MAXARCS; + + /* + * If we are incorrectly called twice in a row (without an + * intervening call to _mcleanup), ignore the second call to + * prevent leaking memory. + */ + if (p->tos != NULL) + return; /* * round lowpc and highpc to multiples of the density we're using @@ -132,6 +144,8 @@ __monstartup (u_long lowpc, u_long highpc) p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); p->textsize = p->highpc - p->lowpc; + /* This looks like a typo, but it's here to align the p->froms + section. */ p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms)); p->hashfraction = HASHFRACTION; p->log_hashfraction = -1; @@ -142,12 +156,12 @@ __monstartup (u_long lowpc, u_long highpc) instead of integer division. Precompute shift amount. */ p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1; } - p->fromssize = p->textsize / HASHFRACTION; + p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms)); p->tolimit = p->textsize * ARCDENSITY / 100; - if (p->tolimit < MINARCS) - p->tolimit = MINARCS; - else if (p->tolimit > MAXARCS) - p->tolimit = MAXARCS; + if (p->tolimit < minarcs) + p->tolimit = minarcs; + else if (p->tolimit > maxarcs) + p->tolimit = maxarcs; p->tossize = p->tolimit * sizeof(struct tostruct); cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1); @@ -440,9 +454,14 @@ _mcleanup (void) { __moncontrol (0); - if (_gmonparam.state != GMON_PROF_ERROR) + if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL) write_gmon (); /* free the memory. */ free (_gmonparam.tos); + + /* reset buffer to initial state for safety */ + memset(&_gmonparam, 0, sizeof _gmonparam); + /* somewhat confusingly, ON=0, OFF=3 */ + _gmonparam.state = GMON_PROF_OFF; } diff --git a/gmon/mcount.c b/gmon/mcount.c index 9d4a1a50fa..f7180fdb83 100644 --- a/gmon/mcount.c +++ b/gmon/mcount.c @@ -41,6 +41,10 @@ static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93"; #include +#include +#include +#define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1) + /* * mcount is called on entry to each function compiled with the profiling * switch set. _mcount(), which is declared in a machine-dependent way @@ -170,6 +174,7 @@ done: return; overflow: p->state = GMON_PROF_ERROR; + ERR("mcount: call graph buffer size limit exceeded, gmon.out will not be generated\n"); return; } diff --git a/gmon/sys/gmon.h b/gmon/sys/gmon.h index b4cc3b043a..af0582a371 100644 --- a/gmon/sys/gmon.h +++ b/gmon/sys/gmon.h @@ -111,6 +111,8 @@ extern struct __bb *__bb_head; * Always allocate at least this many tostructs. This * hides the inadequacy of the ARCDENSITY heuristic, at least * for small programs. + * + * Value can be overridden at runtime by glibc.gmon.minarcs tunable. */ #define MINARCS 50 @@ -124,8 +126,8 @@ extern struct __bb *__bb_head; * Used to be max representable value of ARCINDEX minus 2, but now * that ARCINDEX is a long, that's too large; we don't really want * to allow a 48 gigabyte table. - * The old value of 1<<16 wasn't high enough in practice for large C++ - * programs; will 1<<20 be adequate for long? FIXME + * + * Value can be overridden at runtime by glibc.gmon.maxarcs tunable. */ #define MAXARCS (1 << 20) diff --git a/gmon/tst-mcleanup.c b/gmon/tst-mcleanup.c new file mode 100644 index 0000000000..b259653ec8 --- /dev/null +++ b/gmon/tst-mcleanup.c @@ -0,0 +1,31 @@ +/* Test program for repeated invocation of _mcleanup + Copyright The GNU Toolchain Authors. + 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 + . */ + +/* Intentionally calls _mcleanup() twice: once manually, it will be + called again as an atexit handler. This is incorrect use of the API, + but the point of the test is to make sure we don't crash when the + API is misused in this way. */ + +#include + +int +main (void) +{ + _mcleanup(); + return 0; +} diff --git a/gmon/tst-mcount-overflow-check.sh b/gmon/tst-mcount-overflow-check.sh new file mode 100644 index 0000000000..27eb5538fd --- /dev/null +++ b/gmon/tst-mcount-overflow-check.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# Test expected messages generated when mcount overflows +# Copyright (C) 2017-2023 Free Software Foundation, Inc. +# Copyright The GNU Toolchain Authors. +# 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 +# . + +LC_ALL=C +export LC_ALL +set -e +exec 2>&1 + +program="$1" + +check_msg() { + if ! grep -q "$1" "$program.out"; then + echo "FAIL: expected message not in output: $1" + exit 1 + fi +} + +check_msg 'monstartup: maxarcs < minarcs, setting maxarcs = minarcs' +check_msg 'mcount: call graph buffer size limit exceeded, gmon.out will not be generated' + +for data_file in $1.data.*; do + if [ -f "$data_file" ]; then + echo "FAIL: expected no data files, but found $data_file" + exit 1 + fi +done + +echo PASS diff --git a/gmon/tst-mcount-overflow.c b/gmon/tst-mcount-overflow.c new file mode 100644 index 0000000000..06cc93ef87 --- /dev/null +++ b/gmon/tst-mcount-overflow.c @@ -0,0 +1,72 @@ +/* Test program to trigger mcount overflow in profiling collection. + Copyright (C) 2017-2023 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 + . */ + +/* Program with sufficiently complex, yet pointless, call graph + that it will trigger an mcount overflow, when you set the + minarcs/maxarcs tunables to very low values. */ + +#define PREVENT_TAIL_CALL asm volatile ("") + +/* Calls REP(n) macro 16 times, for n=0..15. + * You need to define REP(n) before using this. + */ +#define REPS \ + REP(0) REP(1) REP(2) REP(3) REP(4) REP(5) REP(6) REP(7) \ + REP(8) REP(9) REP(10) REP(11) REP(12) REP(13) REP(14) REP(15) + +/* Defines 16 leaf functions named f1_0 to f1_15 */ +#define REP(n) \ + __attribute__ ((noinline, noclone, weak)) void f1_##n (void) {}; +REPS +#undef REP + +/* Calls all 16 leaf functions f1_* in succession */ +__attribute__ ((noinline, noclone, weak)) void +f2 (void) +{ +# define REP(n) f1_##n(); + REPS +# undef REP + PREVENT_TAIL_CALL; +} + +/* Defines 16 functions named f2_0 to f2_15, which all just call f2 */ +#define REP(n) \ + __attribute__ ((noinline, noclone, weak)) void \ + f2_##n (void) { f2(); PREVENT_TAIL_CALL; }; +REPS +#undef REP + +__attribute__ ((noinline, noclone, weak)) void +f3 (int count) +{ + for (int i = 0; i < count; ++i) + { + /* Calls f1_0(), f2_0(), f1_1(), f2_1(), f3_0(), etc */ +# define REP(n) f1_##n(); f2_##n(); + REPS +# undef REP + } +} + +int +main (void) +{ + f3 (1000); + return 0; +} diff --git a/gshadow/Makefile b/gshadow/Makefile index eff303f538..5b3fa7e387 100644 --- a/gshadow/Makefile +++ b/gshadow/Makefile @@ -26,7 +26,7 @@ headers = gshadow.h routines = getsgent getsgnam sgetsgent fgetsgent putsgent \ getsgent_r getsgnam_r sgetsgent_r fgetsgent_r -tests = tst-gshadow tst-putsgent tst-fgetsgent_r +tests = tst-gshadow tst-putsgent tst-fgetsgent_r tst-sgetsgent CFLAGS-getsgent_r.c += -fexceptions CFLAGS-getsgent.c += -fexceptions diff --git a/gshadow/sgetsgent_r.c b/gshadow/sgetsgent_r.c index 28c826c9b5..a767a643d4 100644 --- a/gshadow/sgetsgent_r.c +++ b/gshadow/sgetsgent_r.c @@ -61,7 +61,10 @@ __sgetsgent_r (const char *string, struct sgrp *resbuf, char *buffer, buffer[buflen - 1] = '\0'; sp = strncpy (buffer, string, buflen); if (buffer[buflen - 1] != '\0') - return ERANGE; + { + __set_errno (ERANGE); + return ERANGE; + } } else sp = (char *) string; diff --git a/gshadow/tst-sgetsgent.c b/gshadow/tst-sgetsgent.c new file mode 100644 index 0000000000..0370c10fd0 --- /dev/null +++ b/gshadow/tst-sgetsgent.c @@ -0,0 +1,69 @@ +/* Test large input for sgetsgent (bug 30151). + Copyright (C) 2023 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 + . */ + +#include +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + /* Create a shadow group with 1000 members. */ + struct xmemstream mem; + xopen_memstream (&mem); + const char *passwd = "k+zD0nucwfxAo3sw1NXUj6K5vt5M16+X0TVGdE1uFvq5R8V7efJ"; + fprintf (mem.out, "group-name:%s::m0", passwd); + for (int i = 1; i < 1000; ++i) + fprintf (mem.out, ",m%d", i); + xfclose_memstream (&mem); + + /* Call sgetsgent. */ + char *input = mem.buffer; + struct sgrp *e = sgetsgent (input); + TEST_VERIFY_EXIT (e != NULL); + TEST_COMPARE_STRING (e->sg_namp, "group-name"); + TEST_COMPARE_STRING (e->sg_passwd, passwd); + /* No administrators. */ + TEST_COMPARE_STRING (e->sg_adm[0], NULL); + /* Check the members list. */ + for (int i = 0; i < 1000; ++i) + { + char *member = xasprintf ("m%d", i); + TEST_COMPARE_STRING (e->sg_mem[i], member); + free (member); + } + TEST_COMPARE_STRING (e->sg_mem[1000], NULL); + + /* Check that putsgent brings back the input string. */ + xopen_memstream (&mem); + TEST_COMPARE (putsgent (e, mem.out), 0); + xfclose_memstream (&mem); + /* Compare without the trailing '\n' that putsgent added. */ + TEST_COMPARE (mem.buffer[mem.length - 1], '\n'); + mem.buffer[mem.length - 1] = '\0'; + TEST_COMPARE_STRING (mem.buffer, input); + + free (mem.buffer); + free (input); + return 0; +} + +#include 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 diff --git a/include/link.h b/include/link.h index 0ac82d7c77..87966e8397 100644 --- a/include/link.h +++ b/include/link.h @@ -278,6 +278,10 @@ struct link_map /* List of object in order of the init and fini calls. */ struct link_map **l_initfini; + /* Linked list of objects in reverse ELF constructor execution + order. Head of list is stored in _dl_init_called_list. */ + struct link_map *l_init_called_next; + /* List of the dependencies introduced through symbol binding. */ struct link_map_reldeps { 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/io/Makefile b/io/Makefile index b1710407d0..b896484320 100644 --- a/io/Makefile +++ b/io/Makefile @@ -59,6 +59,7 @@ routines := \ ftw64-time64 \ closefrom close_range + others := pwd test-srcs := ftwtest ftwtest-time64 tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ @@ -80,7 +81,9 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ tst-utimensat \ tst-closefrom \ tst-close_range \ - tst-ftw-bz28126 + tst-ftw-bz28126 \ + tst-fcntl-lock \ + tst-fcntl-lock-lfs tests-time64 := \ tst-fcntl-time64 \ diff --git a/io/tst-fcntl-lock-lfs.c b/io/tst-fcntl-lock-lfs.c new file mode 100644 index 0000000000..f2a909fb02 --- /dev/null +++ b/io/tst-fcntl-lock-lfs.c @@ -0,0 +1,2 @@ +#define _FILE_OFFSET_BITS 64 +#include diff --git a/io/tst-fcntl-lock.c b/io/tst-fcntl-lock.c new file mode 100644 index 0000000000..357c4b7b56 --- /dev/null +++ b/io/tst-fcntl-lock.c @@ -0,0 +1,97 @@ +/* Test for advisory record locking. + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include + +/* This is essentially the POSIX lockf. */ + +static int +fcntl_lockf (int fd, int cmd, off_t len) +{ + struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_CUR, + .l_len = len + }; + + switch (cmd) + { + case F_TEST: + fl.l_type = F_RDLCK; + if (fcntl (fd, F_GETLK, &fl) < 0) + return -1; + if (fl.l_type == F_UNLCK || fl.l_pid == getpid ()) + return 0; + errno = EACCES; + return -1; + + case F_ULOCK: + fl.l_type = F_UNLCK; + return fcntl (fd, F_SETLK, &fl); + + case F_LOCK: + return fcntl (fd, F_SETLKW, &fl); + + case F_TLOCK: + return fcntl (fd, F_SETLK, &fl); + } + + errno = EINVAL; + return -1; +} + +static int +fcntl64_lockf (int fd, int cmd, off64_t len64) + { + struct flock64 fl64 = { + .l_type = F_WRLCK, + .l_whence = SEEK_CUR, + .l_len = len64 + }; + + switch (cmd) + { + case F_TEST: + fl64.l_type = F_RDLCK; + if (fcntl64 (fd, F_GETLK64, &fl64) < 0) + return -1; + if (fl64.l_type == F_UNLCK || fl64.l_pid == getpid ()) + return 0; + errno = EACCES; + return -1; + + case F_ULOCK: + fl64.l_type = F_UNLCK; + return fcntl64 (fd, F_SETLK64, &fl64); + + case F_LOCK: + return fcntl64 (fd, F_SETLKW64, &fl64); + + case F_TLOCK: + return fcntl64 (fd, F_SETLK64, &fl64); + } + + errno = EINVAL; + return -1; +} + +#define TST_LOCKFD "tst-fcntl-lock." +#define LOCKF fcntl_lockf +#define LOCKF64 fcntl64_lockf +#include "tst-lockf.c" diff --git a/io/tst-lockf.c b/io/tst-lockf.c index be92f33fd1..5e41dc19df 100644 --- a/io/tst-lockf.c +++ b/io/tst-lockf.c @@ -24,13 +24,23 @@ #include #include +#ifndef TST_LOCKFD +# define TST_LOCKFD "tst-lockfd." +#endif +#ifndef LOCKF +# define LOCKF lockf +#endif +#ifndef LOCKF64 +# define LOCKF64 lockf64 +#endif + static char *temp_filename; static int temp_fd; static void do_prepare (int argc, char **argv) { - temp_fd = create_temp_file ("tst-lockfd.", &temp_filename); + temp_fd = create_temp_file (TST_LOCKFD, &temp_filename); TEST_VERIFY_EXIT (temp_fd != -1); } #define PREPARE do_prepare @@ -40,22 +50,22 @@ do_test_child_lockf (void *closure) { /* Check if parent has [0, 1024) locked. */ TEST_COMPARE (lseek (temp_fd, 0, SEEK_SET), 0); - TEST_COMPARE (lockf (temp_fd, F_TLOCK, 1024), -1); + TEST_COMPARE (LOCKF (temp_fd, F_TLOCK, 1024), -1); TEST_COMPARE (errno, EAGAIN); - TEST_COMPARE (lockf (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (LOCKF (temp_fd, F_TEST, 1024), -1); TEST_COMPARE (errno, EACCES); /* Also Check if parent has last 1024 bytes locked. */ TEST_COMPARE (lseek (temp_fd, INT32_MAX-1024, SEEK_SET), INT32_MAX-1024); - TEST_COMPARE (lockf (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (LOCKF (temp_fd, F_TEST, 1024), -1); /* And try to lock [1024, 2048). */ TEST_COMPARE (lseek (temp_fd, 1024, SEEK_SET), 1024); - TEST_COMPARE (lockf (temp_fd, F_LOCK, 1024), 0); + TEST_COMPARE (LOCKF (temp_fd, F_LOCK, 1024), 0); /* Check if non-LFS interface cap access to 32-bif off_t. */ TEST_COMPARE (lseek64 (temp_fd, (off64_t)INT32_MAX, SEEK_SET), (off64_t)INT32_MAX); - TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), 0); } static void @@ -63,32 +73,32 @@ do_test_child_lockf64 (void *closure) { /* Check if parent has [0, 1024) locked. */ TEST_COMPARE (lseek64 (temp_fd, 0, SEEK_SET), 0); - TEST_COMPARE (lockf64 (temp_fd, F_TLOCK, 1024), -1); + TEST_COMPARE (LOCKF64 (temp_fd, F_TLOCK, 1024), -1); TEST_COMPARE (errno, EAGAIN); - TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), -1); TEST_COMPARE (errno, EACCES); /* Also Check if parent has last 1024 bytes locked. */ TEST_COMPARE (lseek64 (temp_fd, INT32_MAX-1024, SEEK_SET), INT32_MAX-1024); - TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), -1); /* And try to lock [1024, 2048). */ TEST_COMPARE (lseek64 (temp_fd, 1024, SEEK_SET), 1024); - TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); /* And also [INT32_MAX, INT32_MAX+1024). */ { off64_t off = (off64_t)INT32_MAX; TEST_COMPARE (lseek64 (temp_fd, off, SEEK_SET), off); - TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); } /* Check if [INT32_MAX+1024, INT64_MAX) is locked. */ { off64_t off = (off64_t)INT32_MAX+1024; TEST_COMPARE (lseek64 (temp_fd, off, SEEK_SET), off); - TEST_COMPARE (lockf64 (temp_fd, F_TLOCK, 1024), -1); + TEST_COMPARE (LOCKF64 (temp_fd, F_TLOCK, 1024), -1); TEST_COMPARE (errno, EAGAIN); - TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), -1); TEST_COMPARE (errno, EACCES); } } @@ -97,38 +107,38 @@ static int do_test (void) { /* Basic tests to check if a lock can be obtained and checked. */ - TEST_COMPARE (lockf (temp_fd, F_LOCK, 1024), 0); - TEST_COMPARE (lockf (temp_fd, F_LOCK, INT32_MAX), 0); - TEST_COMPARE (lockf (temp_fd, F_TLOCK, 1024), 0); - TEST_COMPARE (lockf (temp_fd, F_TEST, 1024), 0); + TEST_COMPARE (LOCKF (temp_fd, F_LOCK, 1024), 0); + TEST_COMPARE (LOCKF (temp_fd, F_LOCK, INT32_MAX), 0); + TEST_COMPARE (LOCKF (temp_fd, F_TLOCK, 1024), 0); + TEST_COMPARE (LOCKF (temp_fd, F_TEST, 1024), 0); TEST_COMPARE (lseek (temp_fd, 1024, SEEK_SET), 1024); - TEST_COMPARE (lockf (temp_fd, F_ULOCK, 1024), 0); + TEST_COMPARE (LOCKF (temp_fd, F_ULOCK, 1024), 0); /* Parent process should have ([0, 1024), [2048, INT32_MAX)) ranges locked. */ { struct support_capture_subprocess result; result = support_capture_subprocess (do_test_child_lockf, NULL); - support_capture_subprocess_check (&result, "lockf", 0, sc_allow_none); + support_capture_subprocess_check (&result, "LOCKF", 0, sc_allow_none); } if (sizeof (off_t) != sizeof (off64_t)) { /* Check if previously locked regions with LFS symbol. */ TEST_COMPARE (lseek (temp_fd, 0, SEEK_SET), 0); - TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); - TEST_COMPARE (lockf64 (temp_fd, F_TLOCK, 1024), 0); - TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_TLOCK, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), 0); /* Lock region [INT32_MAX+1024, INT64_MAX). */ off64_t off = (off64_t)INT32_MAX + 1024; TEST_COMPARE (lseek64 (temp_fd, off, SEEK_SET), off); - TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); + TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); /* Parent process should have ([0, 1024), [2048, INT32_MAX), [INT32_MAX+1024, INT64_MAX)) ranges locked. */ { struct support_capture_subprocess result; result = support_capture_subprocess (do_test_child_lockf64, NULL); - support_capture_subprocess_check (&result, "lockf", 0, sc_allow_none); + support_capture_subprocess_check (&result, "LOCKF", 0, sc_allow_none); } } diff --git a/locale/weight.h b/locale/weight.h index 8be2d220f8..4a4d5aa6b2 100644 --- a/locale/weight.h +++ b/locale/weight.h @@ -27,7 +27,14 @@ findidx (const int32_t *table, const unsigned char *extra, const unsigned char **cpp, size_t len) { + /* With GCC 8 when compiling with -Os the compiler warns that + seq1.back_us and seq2.back_us might be used uninitialized. + This uninitialized use is impossible for the same reason + as described in comments in locale/weightwc.h. */ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_Os_NEEDS_COMMENT (8, "-Wmaybe-uninitialized"); int32_t i = table[*(*cpp)++]; + DIAG_POP_NEEDS_COMMENT; const unsigned char *cp; const unsigned char *usrc; diff --git a/misc/Makefile b/misc/Makefile index ba8232a0e9..66e9ded8f9 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -115,7 +115,10 @@ tests-special += $(objpfx)tst-error1-mem.out \ $(objpfx)tst-allocate_once-mem.out endif -tests-container := tst-syslog +tests-container := \ + tst-syslog \ + tst-syslog-long-progname \ + # tests-container CFLAGS-select.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-tsearch.c += $(uses-callbacks) @@ -175,6 +178,9 @@ $(objpfx)tst-allocate_once-mem.out: $(objpfx)tst-allocate_once.out $(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \ $(evaluate-test) +tst-syslog-long-progname-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \ + LD_PRELOAD=libc_malloc_debug.so.0 + $(objpfx)tst-select: $(librt) $(objpfx)tst-select-time64: $(librt) $(objpfx)tst-pselect: $(librt) diff --git a/misc/bits/syslog.h b/misc/bits/syslog.h index fd30dd3114..916d2b6f12 100644 --- a/misc/bits/syslog.h +++ b/misc/bits/syslog.h @@ -24,6 +24,20 @@ extern void __syslog_chk (int __pri, int __flag, const char *__fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); +#ifdef __USE_MISC +extern void __vsyslog_chk (int __pri, int __flag, const char *__fmt, + __gnuc_va_list __ap) + __attribute__ ((__format__ (__printf__, 3, 0))); +#endif + +#include +#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 +# include +#endif + +/* The following functions must be used only after applying all asm + redirections, e.g. long double asm redirections. */ + #ifdef __va_arg_pack __fortify_function void syslog (int __pri, const char *__fmt, ...) @@ -37,10 +51,6 @@ syslog (int __pri, const char *__fmt, ...) #ifdef __USE_MISC -extern void __vsyslog_chk (int __pri, int __flag, const char *__fmt, - __gnuc_va_list __ap) - __attribute__ ((__format__ (__printf__, 3, 0))); - __fortify_function void vsyslog (int __pri, const char *__fmt, __gnuc_va_list __ap) { diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h index f525f67547..294e633335 100644 --- a/misc/sys/cdefs.h +++ b/misc/sys/cdefs.h @@ -152,6 +152,7 @@ # define __glibc_objsize(__o) __bos (__o) #endif +#if __USE_FORTIFY_LEVEL > 0 /* Compile time conditions to choose between the regular, _chk and _chk_warn variants. These conditions should get evaluated to constant and optimized away. */ @@ -187,7 +188,7 @@ ? __ ## f ## _alias (__VA_ARGS__) \ : (__glibc_unsafe_len (__l, __s, __osz) \ ? __ ## f ## _chk_warn (__VA_ARGS__, __osz) \ - : __ ## f ## _chk (__VA_ARGS__, __osz))) \ + : __ ## f ## _chk (__VA_ARGS__, __osz))) /* Fortify function f, where object size argument passed to f is the number of elements and not total size. */ @@ -197,7 +198,8 @@ ? __ ## f ## _alias (__VA_ARGS__) \ : (__glibc_unsafe_len (__l, __s, __osz) \ ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s)) \ - : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s)))) \ + : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s)))) +#endif #if __GNUC_PREREQ (4,3) # define __warnattr(msg) __attribute__((__warning__ (msg))) diff --git a/misc/sys/syslog.h b/misc/sys/syslog.h index d933fea104..3888153ed2 100644 --- a/misc/sys/syslog.h +++ b/misc/sys/syslog.h @@ -205,11 +205,11 @@ extern void vsyslog (int __pri, const char *__fmt, __gnuc_va_list __ap) /* Define some macros helping to catch buffer overflows. */ #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function # include -#endif - -#include -#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 -# include +#else +# include +# if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 +# include +# endif #endif __END_DECLS diff --git a/misc/syslog.c b/misc/syslog.c index 554089bfc4..9336036666 100644 --- a/misc/syslog.c +++ b/misc/syslog.c @@ -41,6 +41,7 @@ static char sccsid[] = "@(#)syslog.c 8.4 (Berkeley) 3/18/94"; #include #include #include +#include static int LogType = SOCK_DGRAM; /* type of socket connection */ static int LogFile = -1; /* fd for log */ @@ -122,8 +123,9 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap, { /* Try to use a static buffer as an optimization. */ char bufs[1024]; - char *buf = NULL; - size_t bufsize = 0; + char *buf = bufs; + size_t bufsize; + int msgoff; int saved_errno = errno; @@ -167,7 +169,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) @@ -175,53 +177,95 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap, #define SYSLOG_HEADER_WITHOUT_TS(__pri, __msgoff) \ "<%d>: %n", __pri, __msgoff - int l; + int l, vl; if (has_ts) l = __snprintf (bufs, sizeof bufs, SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); else l = __snprintf (bufs, sizeof bufs, SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); - if (0 <= l && l < sizeof bufs) + if (l < 0) + goto out; + + char *pos; + size_t len; + + if (l < sizeof bufs) { - va_list apc; - va_copy (apc, ap); + /* At this point, there is still a chance that we can print the + remaining part of the log into bufs and use that. */ + pos = bufs + l; + len = sizeof (bufs) - l; + } + else + { + buf = NULL; + /* We already know that bufs is too small to use for this log message. + The next vsnprintf into bufs is used only to calculate the total + required buffer length. We will discard bufs contents and allocate + an appropriately sized buffer later instead. */ + pos = bufs; + len = sizeof (bufs); + } - /* Restore errno for %m format. */ - __set_errno (saved_errno); + { + va_list apc; + va_copy (apc, 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; - } + /* Restore errno for %m format. */ + __set_errno (saved_errno); - va_end (apc); - } + vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags); + va_end (apc); + + if (vl < 0 || vl >= INT_MAX - l) + goto out; + + if (vl >= len) + buf = NULL; + + bufsize = l + vl; + } 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; + int cl; if (has_ts) - __snprintf (bufs, sizeof bufs, - SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); + cl = __snprintf (buf, l + 1, + SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); else - __snprintf (bufs, sizeof bufs, - SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); + cl = __snprintf (buf, l + 1, + SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); + if (cl != l) + goto out; + + va_list apc; + va_copy (apc, ap); + cl = __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc, + mode_flags); + va_end (apc); + + if (cl != vl) + goto out; } else { + int bl; /* Nothing much to do but emit an error message. */ - bufsize = __snprintf (bufs, sizeof bufs, - "out of memory[%d]", __getpid ()); + bl = __snprintf (bufs, sizeof bufs, + "out of memory[%d]", __getpid ()); + if (bl < 0 || bl >= sizeof bufs) + goto out; + + bufsize = bl; buf = bufs; + msgoff = 0; } } diff --git a/misc/tst-syslog-long-progname.c b/misc/tst-syslog-long-progname.c new file mode 100644 index 0000000000..88f37a8a00 --- /dev/null +++ b/misc/tst-syslog-long-progname.c @@ -0,0 +1,39 @@ +/* Test heap buffer overflow in syslog with long __progname (CVE-2023-6246) + Copyright (C) 2023 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 + . */ + +#include +#include + +extern char * __progname; + +static int +do_test (void) +{ + char long_progname[2048]; + + memset (long_progname, 'X', sizeof (long_progname) - 1); + long_progname[sizeof (long_progname) - 1] = '\0'; + + __progname = long_progname; + + syslog (LOG_INFO, "Hello, World!"); + + return 0; +} + +#include diff --git a/misc/tst-syslog-long-progname.root/postclean.req b/misc/tst-syslog-long-progname.root/postclean.req new file mode 100644 index 0000000000..e69de29bb2 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/nis/nis_call.c b/nis/nis_call.c index 90187e30b1..5b9dd50151 100644 --- a/nis/nis_call.c +++ b/nis/nis_call.c @@ -574,7 +574,7 @@ static struct nis_server_cache unsigned int size; unsigned int server_used; unsigned int current_ep; - __time64_t expires; + time_t expires; char name[]; } *nis_server_cache[16]; static time_t nis_cold_start_mtime; @@ -583,7 +583,7 @@ __libc_lock_define_initialized (static, nis_server_cache_lock) static directory_obj * nis_server_cache_search (const_nis_name name, int search_parent, unsigned int *server_used, unsigned int *current_ep, - struct __timespec64 *now) + struct timespec *now) { directory_obj *ret = NULL; int i; @@ -641,7 +641,7 @@ nis_server_cache_search (const_nis_name name, int search_parent, static void nis_server_cache_add (const_nis_name name, int search_parent, directory_obj *dir, unsigned int server_used, - unsigned int current_ep, struct __timespec64 *now) + unsigned int current_ep, struct timespec *now) { struct nis_server_cache **loc; struct nis_server_cache *new; @@ -707,7 +707,7 @@ __nisfind_server (const_nis_name name, int search_parent, nis_error result = NIS_SUCCESS; nis_error status; directory_obj *obj; - struct __timespec64 ts; + struct timespec ts; unsigned int server_used = ~0; unsigned int current_ep = ~0; @@ -717,7 +717,7 @@ __nisfind_server (const_nis_name name, int search_parent, if (*dir != NULL) return NIS_SUCCESS; - __clock_gettime64 (CLOCK_REALTIME, &ts); + clock_gettime (CLOCK_REALTIME, &ts); if ((flags & NO_CACHE) == 0) *dir = nis_server_cache_search (name, search_parent, &server_used, diff --git a/nscd/aicache.c b/nscd/aicache.c index 51e793199f..e0baed170b 100644 --- a/nscd/aicache.c +++ b/nscd/aicache.c @@ -110,11 +110,10 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req, "gethostbyname4_r"); if (fct4 != NULL) { - struct gaih_addrtuple atmem; struct gaih_addrtuple *at; while (1) { - at = &atmem; + at = NULL; rc6 = 0; herrno = 0; status[1] = DL_CALL_FCT (fct4, (key, &at, @@ -137,7 +136,7 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req, goto next_nip; /* We found the data. Count the addresses and the size. */ - for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL; + for (const struct gaih_addrtuple *at2 = at; at2 != NULL; at2 = at2->next) { ++naddrs; 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/nscd/nscd.h b/nscd/nscd.h index 368091aef8..f15321585b 100644 --- a/nscd/nscd.h +++ b/nscd/nscd.h @@ -65,7 +65,7 @@ typedef enum struct traced_file { /* Tracks the last modified time of the traced file. */ - time_t mtime; + __time64_t mtime; /* Support multiple registered files per database. */ struct traced_file *next; int call_res_init; diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c index 9becb62033..31c64275f0 100644 --- a/nscd/nscd_gethst_r.c +++ b/nscd/nscd_gethst_r.c @@ -112,7 +112,7 @@ __nscd_get_nl_timestamp (void) if (map == NULL || (map != NO_MAPPING && map->head->nscd_certainly_running == 0 - && map->head->timestamp + MAPPING_TIMEOUT < time_now ())) + && map->head->timestamp + MAPPING_TIMEOUT < time64_now ())) map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped); if (map == NO_MAPPING) diff --git a/nss/Makefile b/nss/Makefile index a978e3927a..7a52c68791 100644 --- a/nss/Makefile +++ b/nss/Makefile @@ -81,6 +81,7 @@ tests-container := \ tst-nss-test3 \ tst-reload1 \ tst-reload2 \ + tst-nss-gai-hv2-canonname \ # tests-container # Tests which need libdl @@ -144,7 +145,17 @@ libnss_compat-inhibit-o = $(filter-out .os,$(object-suffixes)) ifeq ($(build-static-nss),yes) tests-static += tst-nss-static endif -extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os +extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os \ + nss_test_gai_hv2_canonname.os + +ifeq ($(run-built-tests),yes) +ifneq (no,$(PERL)) +tests-special += $(objpfx)mtrace-tst-nss-gai-hv2-canonname.out +endif +endif + +generated += mtrace-tst-nss-gai-hv2-canonname.out \ + tst-nss-gai-hv2-canonname.mtrace include ../Rules @@ -179,12 +190,16 @@ rtld-tests-LDFLAGS += -Wl,--dynamic-list=nss_test.ver libof-nss_test1 = extramodules libof-nss_test2 = extramodules libof-nss_test_errno = extramodules +libof-nss_test_gai_hv2_canonname = extramodules $(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(link-libc-deps) $(build-module) $(objpfx)/libnss_test2.so: $(objpfx)nss_test2.os $(link-libc-deps) $(build-module) $(objpfx)/libnss_test_errno.so: $(objpfx)nss_test_errno.os $(link-libc-deps) $(build-module) +$(objpfx)/libnss_test_gai_hv2_canonname.so: \ + $(objpfx)nss_test_gai_hv2_canonname.os $(link-libc-deps) + $(build-module) $(objpfx)nss_test2.os : nss_test1.c # Use the nss_files suffix for these objects as well. $(objpfx)/libnss_test1.so$(libnss_files.so-version): $(objpfx)/libnss_test1.so @@ -194,10 +209,14 @@ $(objpfx)/libnss_test2.so$(libnss_files.so-version): $(objpfx)/libnss_test2.so $(objpfx)/libnss_test_errno.so$(libnss_files.so-version): \ $(objpfx)/libnss_test_errno.so $(make-link) +$(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version): \ + $(objpfx)/libnss_test_gai_hv2_canonname.so + $(make-link) $(patsubst %,$(objpfx)%.out,$(tests) $(tests-container)) : \ $(objpfx)/libnss_test1.so$(libnss_files.so-version) \ $(objpfx)/libnss_test2.so$(libnss_files.so-version) \ - $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) + $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) \ + $(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version) ifeq (yes,$(have-thread-library)) $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library) @@ -206,6 +225,17 @@ endif $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)/libnss_files.so $(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)/libnss_files.so +tst-nss-gai-hv2-canonname-ENV = \ + MALLOC_TRACE=$(objpfx)tst-nss-gai-hv2-canonname.mtrace \ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so +$(objpfx)mtrace-tst-nss-gai-hv2-canonname.out: \ + $(objpfx)tst-nss-gai-hv2-canonname.out + { test -r $(objpfx)tst-nss-gai-hv2-canonname.mtrace \ + || ( echo "tst-nss-gai-hv2-canonname.mtrace does not exist"; exit 77; ) \ + && $(common-objpfx)malloc/mtrace \ + $(objpfx)tst-nss-gai-hv2-canonname.mtrace; } > $@; \ + $(evaluate-test) + # Disable DT_RUNPATH on NSS tests so that the glibc internal NSS # functions can load testing NSS modules via DT_RPATH. LDFLAGS-tst-nss-test1 = -Wl,--disable-new-dtags @@ -214,3 +244,4 @@ LDFLAGS-tst-nss-test3 = -Wl,--disable-new-dtags LDFLAGS-tst-nss-test4 = -Wl,--disable-new-dtags LDFLAGS-tst-nss-test5 = -Wl,--disable-new-dtags LDFLAGS-tst-nss-test_errno = -Wl,--disable-new-dtags +LDFLAGS-tst-nss-test_gai_hv2_canonname = -Wl,--disable-new-dtags diff --git a/nss/getent.c b/nss/getent.c index 8178b4b470..d2d2524b0c 100644 --- a/nss/getent.c +++ b/nss/getent.c @@ -58,6 +58,8 @@ static const struct argp_option args_options[] = { { "service", 's', N_("CONFIG"), 0, N_("Service configuration to be used") }, { "no-idn", 'i', NULL, 0, N_("disable IDN encoding") }, + { "no-addrconfig", 'A', NULL, 0, + N_("do not filter out unsupported IPv4/IPv6 addresses (with ahosts*)") }, { NULL, 0, NULL, 0, NULL }, }; @@ -79,6 +81,9 @@ static struct argp argp = /* Additional getaddrinfo flags for IDN encoding. */ static int idn_flags = AI_IDN | AI_CANONIDN; +/* Set to 0 by --no-addrconfig. */ +static int addrconfig_flags = AI_ADDRCONFIG; + /* Print the version information. */ static void print_version (FILE *stream, struct argp_state *state) @@ -346,7 +351,7 @@ ahosts_keys_int (int af, int xflags, int number, char *key[]) struct addrinfo hint; memset (&hint, '\0', sizeof (hint)); - hint.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME + hint.ai_flags = (AI_V4MAPPED | addrconfig_flags | AI_CANONNAME | idn_flags | xflags); hint.ai_family = af; @@ -905,6 +910,10 @@ parse_option (int key, char *arg, struct argp_state *state) idn_flags = 0; break; + case 'A': + addrconfig_flags = 0; + break; + default: return ARGP_ERR_UNKNOWN; } diff --git a/nss/nss_test_gai_hv2_canonname.c b/nss/nss_test_gai_hv2_canonname.c new file mode 100644 index 0000000000..4439c83c9f --- /dev/null +++ b/nss/nss_test_gai_hv2_canonname.c @@ -0,0 +1,56 @@ +/* NSS service provider that only provides gethostbyname2_r. + Copyright The GNU Toolchain Authors. + 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 + . */ + +#include +#include +#include +#include "nss/tst-nss-gai-hv2-canonname.h" + +/* Catch misnamed and functions. */ +#pragma GCC diagnostic error "-Wmissing-prototypes" +NSS_DECLARE_MODULE_FUNCTIONS (test_gai_hv2_canonname) + +extern enum nss_status _nss_files_gethostbyname2_r (const char *, int, + struct hostent *, char *, + size_t, int *, int *); + +enum nss_status +_nss_test_gai_hv2_canonname_gethostbyname2_r (const char *name, int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *herrnop) +{ + return _nss_files_gethostbyname2_r (name, af, result, buffer, buflen, errnop, + herrnop); +} + +enum nss_status +_nss_test_gai_hv2_canonname_getcanonname_r (const char *name, char *buffer, + size_t buflen, char **result, + int *errnop, int *h_errnop) +{ + /* We expect QUERYNAME, which is a small enough string that it shouldn't fail + the test. */ + if (memcmp (QUERYNAME, name, sizeof (QUERYNAME)) + || buflen < sizeof (QUERYNAME)) + abort (); + + strncpy (buffer, name, buflen); + *result = buffer; + return NSS_STATUS_SUCCESS; +} diff --git a/nss/tst-nss-files-hosts-long.c b/nss/tst-nss-files-hosts-long.c index 3942cf5fca..a7697e3143 100644 --- a/nss/tst-nss-files-hosts-long.c +++ b/nss/tst-nss-files-hosts-long.c @@ -28,14 +28,15 @@ do_test (void) { int ret; - /* Run getent to fetch the IPv4 address for host test4. - This forces /etc/hosts to be parsed. */ - ret = system("getent ahostsv4 test4"); + /* Run getent to fetch the IPv4 address for host test4. This forces + /etc/hosts to be parsed. Use --no-addrconfig to return addresses + even in an IPv6-only environment. */ + ret = system("getent --no-addrconfig ahostsv4 test4"); if (ret != 0) FAIL_EXIT1("ahostsv4 failed"); /* Likewise for IPv6. */ - ret = system("getent ahostsv6 test6"); + ret = system("getent --no-addrconfig ahostsv6 test6"); if (ret != 0) FAIL_EXIT1("ahostsv6 failed"); diff --git a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c new file mode 100644 index 0000000000..7db53cf09d --- /dev/null +++ b/nss/tst-nss-gai-hv2-canonname.c @@ -0,0 +1,66 @@ +/* Test NSS query path for plugins that only implement gethostbyname2 + (#30843). + Copyright The GNU Toolchain Authors. + 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include "nss/tst-nss-gai-hv2-canonname.h" + +#define PREPARE do_prepare + +static void do_prepare (int a, char **av) +{ + FILE *hosts = xfopen ("/etc/hosts", "w"); + for (unsigned i = 2; i < 255; i++) + { + fprintf (hosts, "ff01::ff02:ff03:%u:2\ttest.example.com\n", i); + fprintf (hosts, "192.168.0.%u\ttest.example.com\n", i); + } + xfclose (hosts); +} + +static int +do_test (void) +{ + mtrace (); + + __nss_configure_lookup ("hosts", "test_gai_hv2_canonname"); + + struct addrinfo hints = {}; + struct addrinfo *result = NULL; + + hints.ai_family = AF_INET6; + hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_CANONNAME; + + int ret = getaddrinfo (QUERYNAME, NULL, &hints, &result); + + if (ret != 0) + FAIL_EXIT1 ("getaddrinfo failed: %s\n", gai_strerror (ret)); + + TEST_COMPARE_STRING (result->ai_canonname, QUERYNAME); + + freeaddrinfo(result); + return 0; +} + +#include diff --git a/nss/tst-nss-gai-hv2-canonname.h b/nss/tst-nss-gai-hv2-canonname.h new file mode 100644 index 0000000000..14f2a9cb08 --- /dev/null +++ b/nss/tst-nss-gai-hv2-canonname.h @@ -0,0 +1 @@ +#define QUERYNAME "test.example.com" diff --git a/nss/tst-nss-gai-hv2-canonname.root/postclean.req b/nss/tst-nss-gai-hv2-canonname.root/postclean.req new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script new file mode 100644 index 0000000000..31848b4a28 --- /dev/null +++ b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script @@ -0,0 +1,2 @@ +cp $B/nss/libnss_test_gai_hv2_canonname.so $L/libnss_test_gai_hv2_canonname.so.2 +su diff --git a/nss/tst-reload1.c b/nss/tst-reload1.c index fdc5bdd65b..bc32bb132a 100644 --- a/nss/tst-reload1.c +++ b/nss/tst-reload1.c @@ -43,12 +43,12 @@ static struct passwd pwd_table_1[] = { static const char *hostaddr_5[] = { - "ABCD", "abcd", "1234", NULL + "ABCd", "ABCD", "ABC4", NULL }; static const char *hostaddr_15[] = { - "4321", "ghij", NULL + "4321", "4322", NULL }; static const char *hostaddr_25[] = @@ -86,12 +86,12 @@ static const char *hostaddr_6[] = static const char *hostaddr_16[] = { - "7890", "a1b2", NULL + "7890", "7891", NULL }; static const char *hostaddr_26[] = { - "qwer", "tyui", NULL + "qwer", "qweR", NULL }; static struct hostent host_table_2[] = { diff --git a/resolv/Makefile b/resolv/Makefile index 5b15321f9b..28cedf49ee 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,11 +93,15 @@ 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-noaaaa-vc \ tst-resolv-nondecimal \ tst-resolv-res_init-multi \ tst-resolv-search \ @@ -104,6 +112,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 +278,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,7 +289,10 @@ $(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-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library) diff --git a/resolv/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 -#include - -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 -#include - -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 + . */ + +#include +#include +#include + +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 + . */ + +#include +#include +#include +#include + +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 + . */ + +#include +#include +#include +#include + +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 + . */ + +#include +#include + +/* 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..227734da5c 100644 --- a/resolv/nss_dns/dns-host.c +++ b/resolv/nss_dns/dns-host.c @@ -69,6 +69,7 @@ * --Copyright-- */ +#include #include #include #include @@ -86,10 +87,6 @@ #include #include -/* Get implementations of some internal functions. */ -#include -#include - #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 + +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), + &alt_dns_packet_buffer, 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 + . */ + +#include +#include +#include +#include +#include +#include + +/* 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 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 + . */ + +#include +#include +#include +#include + +/* 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 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 + . */ + +#include +#include +#include +#include +#include + +/* 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 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 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 + . */ + +/* 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/resolv/tst-resolv-noaaaa-vc.c b/resolv/tst-resolv-noaaaa-vc.c new file mode 100644 index 0000000000..9f5aebd99f --- /dev/null +++ b/resolv/tst-resolv-noaaaa-vc.c @@ -0,0 +1,129 @@ +/* Test the RES_NOAAAA resolver option with a large response. + Copyright (C) 2022-2023 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Used to keep track of the number of queries. */ +static volatile unsigned int queries; + +/* If true, add a large TXT record at the start of the answer section. */ +static volatile bool stuff_txt; + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + /* If not using TCP, just force its use. */ + if (!ctx->tcp) + { + struct resolv_response_flags flags = {.tc = true}; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + return; + } + + /* The test needs to send four queries, the first three are used to + grow the NSS buffer via the ERANGE handshake. */ + ++queries; + TEST_VERIFY (queries <= 4); + + /* AAAA queries are supposed to be disabled. */ + TEST_COMPARE (qtype, T_A); + TEST_COMPARE (qclass, C_IN); + TEST_COMPARE_STRING (qname, "example.com"); + + 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); + + if (stuff_txt) + { + resolv_response_open_record (b, qname, qclass, T_TXT, 60); + int zero = 0; + for (int i = 0; i <= 15000; ++i) + resolv_response_add_data (b, &zero, sizeof (zero)); + resolv_response_close_record (b); + } + + for (int i = 0; i < 200; ++i) + { + resolv_response_open_record (b, qname, qclass, qtype, 60); + char ipv4[4] = {192, 0, 2, i + 1}; + resolv_response_add_data (b, &ipv4, sizeof (ipv4)); + resolv_response_close_record (b); + } +} + +static int +do_test (void) +{ + struct resolv_test *obj = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response + }); + + _res.options |= RES_NOAAAA; + + for (int do_stuff_txt = 0; do_stuff_txt < 2; ++do_stuff_txt) + { + queries = 0; + stuff_txt = do_stuff_txt; + + struct addrinfo *ai = NULL; + int ret; + ret = getaddrinfo ("example.com", "80", + &(struct addrinfo) + { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + }, &ai); + + char *expected_result; + { + struct xmemstream mem; + xopen_memstream (&mem); + for (int i = 0; i < 200; ++i) + fprintf (mem.out, "address: STREAM/TCP 192.0.2.%d 80\n", i + 1); + xfclose_memstream (&mem); + expected_result = mem.buffer; + } + + check_addrinfo ("example.com", ai, ret, expected_result); + + free (expected_result); + freeaddrinfo (ai); + } + + resolv_test_end (obj); + return 0; +} + +#include 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 # . +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 + . */ + +/* 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 + +#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 + . */ + +#include +#include +#include +#include + +#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 diff --git a/stdlib/Makefile b/stdlib/Makefile index f7b25c1981..3d49c4941a 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -171,6 +171,7 @@ tests := \ test-a64l \ test-at_quick_exit-race \ test-atexit-race \ + test-atexit-recursive \ test-bz22786 \ test-canon \ test-canon2 \ 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/stdlib/exit.c b/stdlib/exit.c index bc46109f3e..dc12e212bc 100644 --- a/stdlib/exit.c +++ b/stdlib/exit.c @@ -53,7 +53,10 @@ __run_exit_handlers (int status, struct exit_function_list **listp, exit (). */ while (true) { - struct exit_function_list *cur = *listp; + struct exit_function_list *cur; + + restart: + cur = *listp; if (cur == NULL) { @@ -118,7 +121,7 @@ __run_exit_handlers (int status, struct exit_function_list **listp, if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called)) /* The last exit function, or another thread, has registered more exit functions. Start the loop over. */ - continue; + goto restart; } *listp = cur->next; diff --git a/stdlib/longlong.h b/stdlib/longlong.h index 9b89469ac2..d8f76a43b5 100644 --- a/stdlib/longlong.h +++ b/stdlib/longlong.h @@ -593,6 +593,18 @@ extern UDItype __umulsidi3 (USItype, USItype); #define UMUL_TIME 14 #endif +#ifdef __loongarch__ +# if W_TYPE_SIZE == 32 +# define count_leading_zeros(count, x) ((count) = __builtin_clz (x)) +# define count_trailing_zeros(count, x) ((count) = __builtin_ctz (x)) +# define COUNT_LEADING_ZEROS_0 32 +# elif W_TYPE_SIZE == 64 +# define count_leading_zeros(count, x) ((count) = __builtin_clzll (x)) +# define count_trailing_zeros(count, x) ((count) = __builtin_ctzll (x)) +# define COUNT_LEADING_ZEROS_0 64 +# endif +#endif + #if defined (__M32R__) && W_TYPE_SIZE == 32 #define add_ssaaaa(sh, sl, ah, al, bh, bl) \ /* The cmp clears the condition bit. */ \ diff --git a/stdlib/test-atexit-recursive.c b/stdlib/test-atexit-recursive.c new file mode 100644 index 0000000000..0596b9763b --- /dev/null +++ b/stdlib/test-atexit-recursive.c @@ -0,0 +1,75 @@ +/* Support file for atexit/exit, etc. race tests (BZ #27749). + Copyright (C) 2023 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 + . */ + +/* Check that atexit handler registed from another handler still called. */ + +#include +#include +#include +#include +#include +#include + +static void +atexit_cb (void) +{ +} + +static void +atexit_last (void) +{ + _exit (1); +} + +static void +atexit_recursive (void) +{ + atexit (&atexit_cb); + atexit (&atexit_last); +} + +_Noreturn static void +test_and_exit (int count) +{ + for (int i = 0; i < count; ++i) + atexit (&atexit_cb); + atexit (&atexit_recursive); + exit (0); +} + +static int +do_test (void) +{ + for (int i = 0; i < 100; ++i) + if (xfork () == 0) + test_and_exit (i); + + for (int i = 0; i < 100; ++i) + { + int status; + xwaitpid (0, &status, 0); + if (!WIFEXITED (status)) + FAIL_EXIT1 ("Failed iterations %d", i); + TEST_COMPARE (WEXITSTATUS (status), 1); + } + + return 0; +} + +#define TEST_FUNCTION do_test +#include diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c index f7fa74b2a6..5e0c79475f 100644 --- a/stdlib/tst-system.c +++ b/stdlib/tst-system.c @@ -25,6 +25,7 @@ #include #include #include +#include #include static char *tmpdir; @@ -71,6 +72,20 @@ call_system (void *closure) } } +static void * +sleep_and_check_sigchld (void *closure) +{ + double *seconds = (double *) closure; + char cmd[namemax]; + sprintf (cmd, "sleep %lf" , *seconds); + TEST_COMPARE (system (cmd), 0); + + sigset_t blocked = {0}; + TEST_COMPARE (sigprocmask (SIG_BLOCK, NULL, &blocked), 0); + TEST_COMPARE (sigismember (&blocked, SIGCHLD), 0); + return NULL; +} + static int do_test (void) { @@ -154,6 +169,17 @@ do_test (void) xchmod (_PATH_BSHELL, st.st_mode); } + { + pthread_t long_sleep_thread = xpthread_create (NULL, + sleep_and_check_sigchld, + &(double) { 0.2 }); + pthread_t short_sleep_thread = xpthread_create (NULL, + sleep_and_check_sigchld, + &(double) { 0.1 }); + xpthread_join (short_sleep_thread); + xpthread_join (long_sleep_thread); + } + TEST_COMPARE (system (""), 0); return 0; diff --git a/string/test-strnlen.c b/string/test-strnlen.c index 4a9375112a..5cbaf4b734 100644 --- a/string/test-strnlen.c +++ b/string/test-strnlen.c @@ -73,7 +73,7 @@ do_test (size_t align, size_t len, size_t maxlen, int max_char) { size_t i; - align &= 63; + align &= (getpagesize () / sizeof (CHAR) - 1); if ((align + len) * sizeof (CHAR) >= page_size) return; @@ -90,38 +90,50 @@ do_test (size_t align, size_t len, size_t maxlen, int max_char) static void do_overflow_tests (void) { - size_t i, j, len; + size_t i, j, al_idx, repeats, len; const size_t one = 1; uintptr_t buf_addr = (uintptr_t) buf1; + const size_t alignments[] = { 0, 1, 7, 9, 31, 33, 63, 65, 95, 97, 127, 129 }; - for (i = 0; i < 750; ++i) + for (al_idx = 0; al_idx < sizeof (alignments) / sizeof (alignments[0]); + al_idx++) { - do_test (1, i, SIZE_MAX, BIG_CHAR); - - do_test (0, i, SIZE_MAX - i, BIG_CHAR); - do_test (0, i, i - buf_addr, BIG_CHAR); - do_test (0, i, -buf_addr - i, BIG_CHAR); - do_test (0, i, SIZE_MAX - buf_addr - i, BIG_CHAR); - do_test (0, i, SIZE_MAX - buf_addr + i, BIG_CHAR); - - len = 0; - for (j = 8 * sizeof(size_t) - 1; j ; --j) - { - len |= one << j; - do_test (0, i, len - i, BIG_CHAR); - do_test (0, i, len + i, BIG_CHAR); - do_test (0, i, len - buf_addr - i, BIG_CHAR); - do_test (0, i, len - buf_addr + i, BIG_CHAR); - - do_test (0, i, ~len - i, BIG_CHAR); - do_test (0, i, ~len + i, BIG_CHAR); - do_test (0, i, ~len - buf_addr - i, BIG_CHAR); - do_test (0, i, ~len - buf_addr + i, BIG_CHAR); - - do_test (0, i, -buf_addr, BIG_CHAR); - do_test (0, i, j - buf_addr, BIG_CHAR); - do_test (0, i, -buf_addr - j, BIG_CHAR); - } + for (repeats = 0; repeats < 2; ++repeats) + { + size_t align = repeats ? (getpagesize () - alignments[al_idx]) + : alignments[al_idx]; + align /= sizeof (CHAR); + for (i = 0; i < 750; ++i) + { + do_test (align, i, SIZE_MAX, BIG_CHAR); + + do_test (align, i, SIZE_MAX - i, BIG_CHAR); + do_test (align, i, i - buf_addr, BIG_CHAR); + do_test (align, i, -buf_addr - i, BIG_CHAR); + do_test (align, i, SIZE_MAX - buf_addr - i, BIG_CHAR); + do_test (align, i, SIZE_MAX - buf_addr + i, BIG_CHAR); + + len = 0; + for (j = 8 * sizeof (size_t) - 1; j; --j) + { + len |= one << j; + do_test (align, i, len, BIG_CHAR); + do_test (align, i, len - i, BIG_CHAR); + do_test (align, i, len + i, BIG_CHAR); + do_test (align, i, len - buf_addr - i, BIG_CHAR); + do_test (align, i, len - buf_addr + i, BIG_CHAR); + + do_test (align, i, ~len - i, BIG_CHAR); + do_test (align, i, ~len + i, BIG_CHAR); + do_test (align, i, ~len - buf_addr - i, BIG_CHAR); + do_test (align, i, ~len - buf_addr + i, BIG_CHAR); + + do_test (align, i, -buf_addr, BIG_CHAR); + do_test (align, i, j - buf_addr, BIG_CHAR); + do_test (align, i, -buf_addr - j, BIG_CHAR); + } + } + } } } diff --git a/sunrpc/netname.c b/sunrpc/netname.c index bf7f0b81c4..c1d1c43e50 100644 --- a/sunrpc/netname.c +++ b/sunrpc/netname.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "nsswitch.h" @@ -48,7 +49,12 @@ user2netname (char netname[MAXNETNAMELEN + 1], const uid_t uid, if ((strlen (dfltdom) + OPSYS_LEN + 3 + MAXIPRINT) > (size_t) MAXNETNAMELEN) return 0; + /* GCC with -Os warns that sprint might overflow while handling dfltdom, + however the above test does check if an overflow would happen. */ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_Os_NEEDS_COMMENT (8, "-Wformat-overflow"); sprintf (netname, "%s.%d@%s", OPSYS, uid, dfltdom); + DIAG_POP_NEEDS_COMMENT; i = strlen (netname); if (netname[i - 1] == '.') netname[i - 1] = '\0'; diff --git a/support/Makefile b/support/Makefile index 9b50eac117..2b661a7eb8 100644 --- a/support/Makefile +++ b/support/Makefile @@ -32,6 +32,8 @@ libsupport-routines = \ check_hostent \ check_netent \ delayed_exit \ + dtotimespec \ + dtotimespec-time64 \ ignore_stderr \ next_to_fault \ oom_error \ diff --git a/support/dtotimespec-time64.c b/support/dtotimespec-time64.c new file mode 100644 index 0000000000..b3d5e351e3 --- /dev/null +++ b/support/dtotimespec-time64.c @@ -0,0 +1,27 @@ +/* Convert double to timespec. 64-bit time support. + Copyright (C) 2011-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + 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 + . */ + +#include + +#if __TIMESIZE != 64 +# define timespec __timespec64 +# define time_t __time64_t +# define dtotimespec dtotimespec_time64 +# include "dtotimespec.c" +#endif diff --git a/support/dtotimespec.c b/support/dtotimespec.c new file mode 100644 index 0000000000..cde5b4d74c --- /dev/null +++ b/support/dtotimespec.c @@ -0,0 +1,50 @@ +/* Convert double to timespec. + Copyright (C) 2011-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + 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 + . */ + +/* Convert the double value SEC to a struct timespec. Round toward + positive infinity. On overflow, return an extremal value. */ + +#include +#include + +struct timespec +dtotimespec (double sec) +{ + if (sec <= TYPE_MINIMUM (time_t)) + return make_timespec (TYPE_MINIMUM (time_t), 0); + else if (sec >= 1.0 + TYPE_MAXIMUM (time_t)) + return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_HZ - 1); + else + { + time_t s = sec; + double frac = TIMESPEC_HZ * (sec - s); + long ns = frac; + ns += ns < frac; + s += ns / TIMESPEC_HZ; + ns %= TIMESPEC_HZ; + + if (ns < 0) + { + s--; + ns += TIMESPEC_HZ; + } + + return make_timespec (s, ns); + } +} diff --git a/support/shell-container.c b/support/shell-container.c index 1c73666f0a..6698061b9b 100644 --- a/support/shell-container.c +++ b/support/shell-container.c @@ -39,6 +39,7 @@ #include #include +#include /* Design considerations @@ -171,6 +172,32 @@ kill_func (char **argv) return 0; } +/* Emulate the "/bin/sleep" command. No suffix support. Options are + ignored. */ +static int +sleep_func (char **argv) +{ + if (argv[0] == NULL) + { + fprintf (stderr, "sleep: missing operand\n"); + return 1; + } + char *endptr = NULL; + double sec = strtod (argv[0], &endptr); + if (endptr == argv[0] || errno == ERANGE || sec < 0) + { + fprintf (stderr, "sleep: invalid time interval '%s'\n", argv[0]); + return 1; + } + struct timespec ts = dtotimespec (sec); + if (nanosleep (&ts, NULL) < 0) + { + fprintf (stderr, "sleep: failed to nanosleep: %s\n", strerror (errno)); + return 1; + } + return 0; +} + /* This is a list of all the built-in commands we understand. */ static struct { const char *name; @@ -181,6 +208,7 @@ static struct { { "cp", copy_func }, { "exit", exit_func }, { "kill", kill_func }, + { "sleep", sleep_func }, { NULL, NULL } }; diff --git a/support/timespec.h b/support/timespec.h index 4d2ac2737d..1bba3a6837 100644 --- a/support/timespec.h +++ b/support/timespec.h @@ -57,6 +57,8 @@ int support_timespec_check_in_range (struct timespec expected, struct timespec observed, double lower_bound, double upper_bound); +struct timespec dtotimespec (double sec) __attribute__((const)); + #else struct timespec __REDIRECT (timespec_add, (struct timespec, struct timespec), timespec_add_time64); @@ -82,6 +84,8 @@ int __REDIRECT (support_timespec_check_in_range, (struct timespec expected, double lower_bound, double upper_bound), support_timespec_check_in_range_time64); + +struct timespec __REDIRECT (dtotimespec, (double sec), dtotimespec_time64); #endif /* Check that the timespec on the left represents a time before the diff --git a/sysdeps/aarch64/dl-trampoline.S b/sysdeps/aarch64/dl-trampoline.S index 909b208578..d66f0b9c45 100644 --- a/sysdeps/aarch64/dl-trampoline.S +++ b/sysdeps/aarch64/dl-trampoline.S @@ -298,12 +298,11 @@ _dl_runtime_profile: stp x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1] stp x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2] stp x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3] - str x8, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4] stp q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0] stp q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1] stp q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2] stp q6, q7, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*3] - str xzr, [X29, #OFFSET_RV + DL_OFFSET_RG_VPCS] + str xzr, [X29, #OFFSET_RV + DL_OFFSET_RV_VPCS] /* Setup call to pltexit */ ldp x0, x1, [x29, #OFFSET_SAVED_CALL_X0] @@ -315,7 +314,6 @@ _dl_runtime_profile: ldp x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1] ldp x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2] ldp x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3] - ldr x8, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*4] ldp q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0] ldp q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1] ldp q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2] diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 050a3032de..c2627fced7 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -105,6 +105,9 @@ typedef struct link_map *lookup_t; DT_PREINIT_ARRAY. */ typedef void (*dl_init_t) (int, char **, char **); +/* Type of a constructor function, in DT_FINI, DT_FINI_ARRAY. */ +typedef void (*fini_t) (void); + /* On some architectures a pointer to a function is not just a pointer to the actual code of the function but rather an architecture specific descriptor. */ @@ -1048,9 +1051,16 @@ 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. */ +/* Invoke the DT_FINI_ARRAY and DT_FINI destructors for MAP, which + must be a struct link_map *. Can be used as an argument to + _dl_catch_exception. */ +void _dl_call_fini (void *map) attribute_hidden; + +/* 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 . */ + +#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/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h index c865713be1..1d51948566 100644 --- a/sysdeps/hppa/dl-machine.h +++ b/sysdeps/hppa/dl-machine.h @@ -347,6 +347,16 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[], its return value is the user program's entry point. */ #define RTLD_START \ +/* Set up dp for any non-PIC lib constructors that may be called. */ \ +static struct link_map * __attribute__((used)) \ +set_dp (struct link_map *map) \ +{ \ + register Elf32_Addr dp asm ("%r27"); \ + dp = D_PTR (map, l_info[DT_PLTGOT]); \ + asm volatile ("" : : "r" (dp)); \ + return map; \ +} \ + \ asm ( \ " .text\n" \ " .globl _start\n" \ @@ -426,6 +436,13 @@ asm ( \ direct loader invocation. Thus, argc and argv must be \ reloaded from from _dl_argc and _dl_argv. */ \ \ + /* Load main_map from _rtld_local and setup dp. */ \ +" addil LT'_rtld_local,%r19\n" \ +" ldw RT'_rtld_local(%r1),%r26\n" \ +" bl set_dp, %r2\n" \ +" ldw 0(%r26),%r26\n" \ +" copy %ret0,%r26\n" \ + \ /* Load argc from _dl_argc. */ \ " addil LT'_dl_argc,%r19\n" \ " ldw RT'_dl_argc(%r1),%r20\n" \ @@ -438,13 +455,10 @@ asm ( \ " ldw 0(%r20),%r24\n" \ " stw %r24,-44(%sp)\n" \ \ - /* Call _dl_init(main_map, argc, argv, envp). */ \ -" addil LT'_rtld_local,%r19\n" \ -" ldw RT'_rtld_local(%r1),%r26\n" \ -" ldw 0(%r26),%r26\n" \ - \ /* envp = argv + argc + 1 */ \ " sh2add %r25,%r24,%r23\n" \ + \ + /* Call _dl_init(main_map, argc, argv, envp). */ \ " bl _dl_init,%r2\n" \ " ldo 4(%r23),%r23\n" /* delay slot */ \ \ diff --git a/sysdeps/ieee754/ldbl-128/e_j1l.c b/sysdeps/ieee754/ldbl-128/e_j1l.c index 54c457681a..9a9c5c6f00 100644 --- a/sysdeps/ieee754/ldbl-128/e_j1l.c +++ b/sysdeps/ieee754/ldbl-128/e_j1l.c @@ -869,10 +869,13 @@ __ieee754_y1l (_Float128 x) { /* 0 <= x <= 2 */ SET_RESTORE_ROUNDL (FE_TONEAREST); + xx = math_opt_barrier (xx); + x = math_opt_barrier (x); z = xx * xx; p = xx * neval (z, Y0_2N, NY0_2N) / deval (z, Y0_2D, NY0_2D); p = -TWOOPI / xx + p; p = TWOOPI * __ieee754_logl (x) * __ieee754_j1l (x) + p; + math_force_eval (p); return p; } diff --git a/sysdeps/ieee754/ldbl-128ibm/e_j1l.c b/sysdeps/ieee754/ldbl-128ibm/e_j1l.c index f85ba94466..0a5fe68342 100644 --- a/sysdeps/ieee754/ldbl-128ibm/e_j1l.c +++ b/sysdeps/ieee754/ldbl-128ibm/e_j1l.c @@ -792,10 +792,13 @@ __ieee754_y1l (long double x) { /* 0 <= x <= 2 */ SET_RESTORE_ROUNDL (FE_TONEAREST); + xx = math_opt_barrier (xx); + x = math_opt_barrier (x); z = xx * xx; p = xx * neval (z, Y0_2N, NY0_2N) / deval (z, Y0_2D, NY0_2D); p = -TWOOPI / xx + p; p = TWOOPI * __ieee754_logl (x) * __ieee754_j1l (x) + p; + math_force_eval (p); return p; } diff --git a/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c b/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c index d85154e73a..d8c0de1faf 100644 --- a/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c +++ b/sysdeps/ieee754/ldbl-128ibm/s_llroundl.c @@ -66,38 +66,35 @@ __llroundl (long double x) /* Peg at max/min values, assuming that the above conversions do so. Strictly speaking, we can return anything for values that overflow, but this is more useful. */ - res = hi + lo; - - /* This is just sign(hi) == sign(lo) && sign(res) != sign(hi). */ - if (__glibc_unlikely (((~(hi ^ lo) & (res ^ hi)) < 0))) + if (__glibc_unlikely (__builtin_add_overflow (hi, lo, &res))) goto overflow; xh -= lo; ldbl_canonicalize (&xh, &xl); - hi = res; if (xh > 0.5) { - res += 1; + if (__glibc_unlikely (__builtin_add_overflow (res, 1, &res))) + goto overflow; } else if (xh == 0.5) { if (xl > 0.0 || (xl == 0.0 && res >= 0)) - res += 1; + if (__glibc_unlikely (__builtin_add_overflow (res, 1, &res))) + goto overflow; } else if (-xh > 0.5) { - res -= 1; + if (__glibc_unlikely (__builtin_add_overflow (res, -1, &res))) + goto overflow; } else if (-xh == 0.5) { if (xl < 0.0 || (xl == 0.0 && res <= 0)) - res -= 1; + if (__glibc_unlikely (__builtin_add_overflow (res, -1, &res))) + goto overflow; } - if (__glibc_unlikely (((~(hi ^ (res - hi)) & (res ^ hi)) < 0))) - goto overflow; - return res; } else 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 #define __need_NULL #include +#include /* 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 #include +#include /* 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..f975dcd2bc 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -120,6 +120,7 @@ struct gaih_result { struct gaih_addrtuple *at; char *canon; + char *h_name; bool free_at; bool got_ipv6; }; @@ -165,6 +166,7 @@ gaih_result_reset (struct gaih_result *res) if (res->free_at) free (res->at); free (res->canon); + free (res->h_name); memset (res, 0, sizeof (*res)); } @@ -203,9 +205,8 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, return 0; } -/* Convert struct hostent to a list of struct gaih_addrtuple objects. h_name - is not copied, and the struct hostent object must not be deallocated - prematurely. The new addresses are appended to the tuple array in RES. */ +/* Convert struct hostent to a list of struct gaih_addrtuple objects. The new + addresses are appended to the tuple array in RES. */ static bool convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, struct hostent *h, struct gaih_result *res) @@ -238,6 +239,15 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, res->at = array; res->free_at = true; + /* Duplicate h_name because it may get reclaimed when the underlying storage + is freed. */ + if (res->h_name == NULL) + { + res->h_name = __strdup (h->h_name); + if (res->h_name == NULL) + return false; + } + /* Update the next pointers on reallocation. */ for (size_t i = 0; i < old; i++) array[i].next = array + i + 1; @@ -262,7 +272,6 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, } array[i].next = array + i + 1; } - array[0].name = h->h_name; array[count - 1].next = NULL; return true; @@ -324,15 +333,15 @@ gethosts (nss_gethostbyname3_r fct, int family, const char *name, memory allocation failure. The returned string is allocated on the heap; the caller has to free it. */ static char * -getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name) +getcanonname (nss_action_list nip, const char *hname, const char *name) { nss_getcanonname_r *cfct = __nss_lookup_function (nip, "getcanonname_r"); char *s = (char *) name; if (cfct != NULL) { char buf[256]; - if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf), - &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS) + if (DL_CALL_FCT (cfct, (hname ?: name, buf, sizeof (buf), &s, &errno, + &h_errno)) != NSS_STATUS_SUCCESS) /* If the canonical name cannot be determined, use the passed string. */ s = (char *) name; @@ -540,11 +549,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; @@ -771,7 +780,7 @@ get_nss_addresses (const char *name, const struct addrinfo *req, if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL) { - char *canonbuf = getcanonname (nip, res->at, name); + char *canonbuf = getcanonname (nip, res->h_name, name); if (canonbuf == NULL) { __resolv_context_put (res_ctx); @@ -1187,9 +1196,7 @@ free_and_return: if (malloc_name) free ((char *) name); free (addrmem); - if (res.free_at) - free (res.at); - free (res.canon); + gaih_result_reset (&res); return result; } diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c index 8014f63355..20c9420dd4 100644 --- a/sysdeps/posix/system.c +++ b/sysdeps/posix/system.c @@ -179,16 +179,16 @@ do_system (const char *line) as if the shell had terminated using _exit(127). */ status = W_EXITCODE (127, 0); + /* sigaction can not fail with SIGINT/SIGQUIT used with old + disposition. Same applies for sigprocmask. */ DO_LOCK (); if (SUB_REF () == 0) { - /* sigaction can not fail with SIGINT/SIGQUIT used with old - disposition. Same applies for sigprocmask. */ __sigaction (SIGINT, &intr, NULL); __sigaction (SIGQUIT, &quit, NULL); - __sigprocmask (SIG_SETMASK, &omask, NULL); } DO_UNLOCK (); + __sigprocmask (SIG_SETMASK, &omask, NULL); if (ret != 0) __set_errno (ret); diff --git a/sysdeps/powerpc/mod-tlsopt-powerpc.c b/sysdeps/powerpc/mod-tlsopt-powerpc.c index 2a82e53baf..d941024963 100644 --- a/sysdeps/powerpc/mod-tlsopt-powerpc.c +++ b/sysdeps/powerpc/mod-tlsopt-powerpc.c @@ -22,7 +22,11 @@ tls_get_addr_opt_test (void) tls_index *tls_arg; #ifdef __powerpc64__ register unsigned long thread_pointer __asm__ ("r13"); - asm ("addi %0,2,foo@got@tlsgd" : "=r" (tls_arg)); +# ifdef __PCREL__ + asm ("paddi %0,0,foo@got@tlsgd@pcrel,1" : "=b" (tls_arg)); +# else + asm ("addi %0,2,foo@got@tlsgd" : "=b" (tls_arg)); +# endif #else register unsigned long thread_pointer __asm__ ("r2"); asm ("bcl 20,31,1f\n1:\t" diff --git a/sysdeps/sparc/sparc32/memset.S b/sysdeps/sparc/sparc32/memset.S index b1b67cb2d1..5154263317 100644 --- a/sysdeps/sparc/sparc32/memset.S +++ b/sysdeps/sparc/sparc32/memset.S @@ -55,7 +55,7 @@ ENTRY(memset) andcc %o0, 3, %o2 bne 3f -4: andcc %o0, 4, %g0 +5: andcc %o0, 4, %g0 be 2f mov %g3, %g2 @@ -139,7 +139,7 @@ ENTRY(memset) stb %g3, [%o0 + 0x02] 2: sub %o2, 4, %o2 add %o1, %o2, %o1 - b 4b + b 5b sub %o0, %o2, %o0 END(memset) libc_hidden_builtin_def (memset) diff --git a/sysdeps/sparc/sparc64/memmove.S b/sysdeps/sparc/sparc64/memmove.S index 8d46f2cd4e..7746684160 100644 --- a/sysdeps/sparc/sparc64/memmove.S +++ b/sysdeps/sparc/sparc64/memmove.S @@ -38,7 +38,7 @@ ENTRY(memmove) /* * normal, copy forwards */ -2: ble %XCC, .Ldbytecp +2: bleu %XCC, .Ldbytecp andcc %o1, 3, %o5 /* is src word aligned */ bz,pn %icc, .Laldst cmp %o5, 2 /* is src half-word aligned */ diff --git a/sysdeps/sparc/sysdep.h b/sysdeps/sparc/sysdep.h index 95068071cc..baab6817a6 100644 --- a/sysdeps/sparc/sysdep.h +++ b/sysdeps/sparc/sysdep.h @@ -76,6 +76,15 @@ C_LABEL(name) \ cfi_endproc; \ .size name, . - name +#define ENTRY_NOCFI(name) \ + .align 4; \ + .global C_SYMBOL_NAME(name); \ + .type name, @function; \ +C_LABEL(name) + +#define END_NOCFI(name) \ + .size name, . - name + #undef LOC #define LOC(name) .L##name diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index a139a16532..d5d9af4de2 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 @@ -354,6 +362,8 @@ sysdep_headers += netinet/if_fddi.h netinet/if_tr.h \ netrom/netrom.h netpacket/packet.h netrose/rose.h \ neteconet/ec.h netiucv/iucv.h sysdep_routines += netlink_assert_response + +CFLAGS-check_pf.c += -fexceptions endif # Don't compile the ctype glue code, since there is no old non-GNU C library. 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/arm/bits/struct_stat.h b/sysdeps/unix/sysv/linux/arm/bits/struct_stat.h new file mode 100644 index 0000000000..30ee6279d2 --- /dev/null +++ b/sysdeps/unix/sysv/linux/arm/bits/struct_stat.h @@ -0,0 +1,139 @@ +/* Definition for struct stat. Linux/arm version. + Copyright (C) 2020-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 + . */ + +#if !defined _SYS_STAT_H && !defined _FCNTL_H +# error "Never include directly; use instead." +#endif + +#ifndef _BITS_STRUCT_STAT_H +#define _BITS_STRUCT_STAT_H 1 + +#include +#include + +struct stat + { +#ifdef __USE_TIME_BITS64 +# include +#else + __dev_t st_dev; /* Device. */ + unsigned short int __pad1; +# ifndef __USE_FILE_OFFSET64 + __ino_t st_ino; /* File serial number. */ +# else + __ino_t __st_ino; /* 32bit file serial number. */ +# endif + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + unsigned short int __pad2; +# ifndef __USE_FILE_OFFSET64 + __off_t st_size; /* Size of file, in bytes. */ +# else + __off64_t st_size; /* Size of file, in bytes. */ +# endif + __blksize_t st_blksize; /* Optimal block size for I/O. */ + +# ifndef __USE_FILE_OFFSET64 + __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ +# else + __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +# endif +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# define st_atime st_atim.tv_sec /* Backward compatibility. */ +# define st_mtime st_mtim.tv_sec +# define st_ctime st_ctim.tv_sec +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif +# ifndef __USE_FILE_OFFSET64 + unsigned long int __glibc_reserved4; + unsigned long int __glibc_reserved5; +# else + __ino64_t st_ino; /* File serial number. */ +# endif +#endif /* __USE_TIME_BITS64 */ + }; + +#ifdef __USE_LARGEFILE64 +struct stat64 + { +# ifdef __USE_TIME_BITS64 +# include +# else + __dev_t st_dev; /* Device. */ + unsigned int __pad1; + + __ino_t __st_ino; /* 32bit file serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + unsigned int __pad2; + __off64_t st_size; /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + + __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif + __ino64_t st_ino; /* File serial number. */ +# endif /* __USE_TIME_BITS64 */ + }; +#endif + +/* Tell code we have these members. */ +#define _STATBUF_ST_BLKSIZE +#define _STATBUF_ST_RDEV +/* Nanosecond resolution time values are supported. */ +#define _STATBUF_ST_NSEC + + +#endif /* _BITS_STRUCT_STAT_H */ diff --git a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h index 33ff88ce59..bfc674235d 100644 --- a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h +++ b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h @@ -101,7 +101,7 @@ #endif #ifndef F_GETLK -# ifndef __USE_FILE_OFFSET64 +# if !defined __USE_FILE_OFFSET64 && __TIMESIZE != 64 # define F_GETLK 5 /* Get record locking info. */ # define F_SETLK 6 /* Set record locking info (non-blocking). */ # define F_SETLKW 7 /* Set record locking info (blocking). */ 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/bits/struct_stat.h b/sysdeps/unix/sysv/linux/bits/struct_stat.h index 25bd6cb638..fb11a3fba4 100644 --- a/sysdeps/unix/sysv/linux/bits/struct_stat.h +++ b/sysdeps/unix/sysv/linux/bits/struct_stat.h @@ -26,37 +26,36 @@ #include #include -struct stat - { -#ifdef __USE_TIME_BITS64 -# include -#else - __dev_t st_dev; /* Device. */ - unsigned short int __pad1; -# ifndef __USE_FILE_OFFSET64 - __ino_t st_ino; /* File serial number. */ -# else - __ino_t __st_ino; /* 32bit file serial number. */ +#if defined __USE_FILE_OFFSET64 +# define __field64(type, type64, name) type64 name +#elif __WORDSIZE == 64 || defined __INO_T_MATCHES_INO64_T +# if defined __INO_T_MATCHES_INO64_T && !defined __OFF_T_MATCHES_OFF64_T +# error "ino_t and off_t must both be the same type" # endif - __mode_t st_mode; /* File mode. */ - __nlink_t st_nlink; /* Link count. */ - __uid_t st_uid; /* User ID of the file's owner. */ - __gid_t st_gid; /* Group ID of the file's group.*/ - __dev_t st_rdev; /* Device number, if device. */ - unsigned short int __pad2; -# ifndef __USE_FILE_OFFSET64 - __off_t st_size; /* Size of file, in bytes. */ -# else - __off64_t st_size; /* Size of file, in bytes. */ -# endif - __blksize_t st_blksize; /* Optimal block size for I/O. */ +# define __field64(type, type64, name) type name +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define __field64(type, type64, name) \ + type name __attribute__((__aligned__ (__alignof__ (type64)))); int __##name##_pad +#else +# define __field64(type, type64, name) \ + int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name +#endif -# ifndef __USE_FILE_OFFSET64 - __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ -# else - __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ -# endif -# ifdef __USE_XOPEN2K8 +struct stat + { + __dev_t st_dev; /* Device. */ + __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + __dev_t __pad1; + __field64(__off_t, __off64_t, st_size); /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + int __pad2; + __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ +#ifdef __USE_XOPEN2K8 /* Nanosecond resolution timestamps are stored in a format equivalent to 'struct timespec'. This is the type used whenever possible but the Unix namespace rules do not allow the @@ -66,47 +65,38 @@ struct stat struct timespec st_atim; /* Time of last access. */ struct timespec st_mtim; /* Time of last modification. */ struct timespec st_ctim; /* Time of last status change. */ -# define st_atime st_atim.tv_sec /* Backward compatibility. */ -# define st_mtime st_mtim.tv_sec -# define st_ctime st_ctim.tv_sec -# else +# define st_atime st_atim.tv_sec /* Backward compatibility. */ +# define st_mtime st_mtim.tv_sec +# define st_ctime st_ctim.tv_sec +#else __time_t st_atime; /* Time of last access. */ unsigned long int st_atimensec; /* Nscecs of last access. */ __time_t st_mtime; /* Time of last modification. */ unsigned long int st_mtimensec; /* Nsecs of last modification. */ __time_t st_ctime; /* Time of last status change. */ unsigned long int st_ctimensec; /* Nsecs of last status change. */ -# endif -# ifndef __USE_FILE_OFFSET64 - unsigned long int __glibc_reserved4; - unsigned long int __glibc_reserved5; -# else - __ino64_t st_ino; /* File serial number. */ -# endif -#endif /* __USE_TIME_BITS64 */ +#endif + int __glibc_reserved[2]; }; +#undef __field64 + #ifdef __USE_LARGEFILE64 struct stat64 { -# ifdef __USE_TIME_BITS64 -# include -# else - __dev_t st_dev; /* Device. */ - unsigned int __pad1; - - __ino_t __st_ino; /* 32bit file serial number. */ - __mode_t st_mode; /* File mode. */ - __nlink_t st_nlink; /* Link count. */ - __uid_t st_uid; /* User ID of the file's owner. */ - __gid_t st_gid; /* Group ID of the file's group.*/ - __dev_t st_rdev; /* Device number, if device. */ - unsigned int __pad2; - __off64_t st_size; /* Size of file, in bytes. */ - __blksize_t st_blksize; /* Optimal block size for I/O. */ - - __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ -# ifdef __USE_XOPEN2K8 + __dev_t st_dev; /* Device. */ + __ino64_t st_ino; /* File serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + __dev_t __pad1; + __off64_t st_size; /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + int __pad2; + __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ +#ifdef __USE_XOPEN2K8 /* Nanosecond resolution timestamps are stored in a format equivalent to 'struct timespec'. This is the type used whenever possible but the Unix namespace rules do not allow the @@ -116,16 +106,15 @@ struct stat64 struct timespec st_atim; /* Time of last access. */ struct timespec st_mtim; /* Time of last modification. */ struct timespec st_ctim; /* Time of last status change. */ -# else +#else __time_t st_atime; /* Time of last access. */ unsigned long int st_atimensec; /* Nscecs of last access. */ __time_t st_mtime; /* Time of last modification. */ unsigned long int st_mtimensec; /* Nsecs of last modification. */ __time_t st_ctime; /* Time of last status change. */ unsigned long int st_ctimensec; /* Nsecs of last status change. */ -# endif - __ino64_t st_ino; /* File serial number. */ -# endif /* __USE_TIME_BITS64 */ +#endif + int __glibc_reserved[2]; }; #endif @@ -135,5 +124,4 @@ struct stat64 /* Nanosecond resolution time values are supported. */ #define _STATBUF_ST_NSEC - #endif /* _BITS_STRUCT_STAT_H */ diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c index fe73fe3ba8..ca20043408 100644 --- a/sysdeps/unix/sysv/linux/check_pf.c +++ b/sysdeps/unix/sysv/linux/check_pf.c @@ -292,6 +292,14 @@ make_request (int fd, pid_t pid) return NULL; } +#ifdef __EXCEPTIONS +static void +cancel_handler (void *arg __attribute__((unused))) +{ + /* Release the lock. */ + __libc_lock_unlock (lock); +} +#endif void attribute_hidden @@ -304,6 +312,10 @@ __check_pf (bool *seen_ipv4, bool *seen_ipv6, struct cached_data *olddata = NULL; struct cached_data *data = NULL; +#ifdef __EXCEPTIONS + /* Make sure that lock is released when the thread is cancelled. */ + __libc_cleanup_push (cancel_handler, NULL); +#endif __libc_lock_lock (lock); if (cache_valid_p ()) @@ -338,6 +350,9 @@ __check_pf (bool *seen_ipv4, bool *seen_ipv6, } } +#ifdef __EXCEPTIONS + __libc_cleanup_pop (0); +#endif __libc_lock_unlock (lock); if (data != NULL) 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/generic/bits/struct_stat.h b/sysdeps/unix/sysv/linux/csky/bits/struct_stat.h similarity index 92% rename from sysdeps/unix/sysv/linux/generic/bits/struct_stat.h rename to sysdeps/unix/sysv/linux/csky/bits/struct_stat.h index fb11a3fba4..f0ee455748 100644 --- a/sysdeps/unix/sysv/linux/generic/bits/struct_stat.h +++ b/sysdeps/unix/sysv/linux/csky/bits/struct_stat.h @@ -1,4 +1,4 @@ -/* Definition for struct stat. +/* Definition for struct stat. Linux/csky version. Copyright (C) 2020-2022 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -43,6 +43,9 @@ struct stat { +#ifdef __USE_TIME_BITS64 +# include +#else __dev_t st_dev; /* Device. */ __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ __mode_t st_mode; /* File mode. */ @@ -55,7 +58,7 @@ struct stat __blksize_t st_blksize; /* Optimal block size for I/O. */ int __pad2; __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ -#ifdef __USE_XOPEN2K8 +# ifdef __USE_XOPEN2K8 /* Nanosecond resolution timestamps are stored in a format equivalent to 'struct timespec'. This is the type used whenever possible but the Unix namespace rules do not allow the @@ -65,18 +68,19 @@ struct stat struct timespec st_atim; /* Time of last access. */ struct timespec st_mtim; /* Time of last modification. */ struct timespec st_ctim; /* Time of last status change. */ -# define st_atime st_atim.tv_sec /* Backward compatibility. */ -# define st_mtime st_mtim.tv_sec -# define st_ctime st_ctim.tv_sec -#else +# define st_atime st_atim.tv_sec /* Backward compatibility. */ +# define st_mtime st_mtim.tv_sec +# define st_ctime st_ctim.tv_sec +# else __time_t st_atime; /* Time of last access. */ unsigned long int st_atimensec; /* Nscecs of last access. */ __time_t st_mtime; /* Time of last modification. */ unsigned long int st_mtimensec; /* Nsecs of last modification. */ __time_t st_ctime; /* Time of last status change. */ unsigned long int st_ctimensec; /* Nsecs of last status change. */ -#endif +# endif int __glibc_reserved[2]; +#endif }; #undef __field64 @@ -84,6 +88,9 @@ struct stat #ifdef __USE_LARGEFILE64 struct stat64 { +# ifdef __USE_TIME_BITS64 +# include +# else __dev_t st_dev; /* Device. */ __ino64_t st_ino; /* File serial number. */ __mode_t st_mode; /* File mode. */ @@ -96,7 +103,7 @@ struct stat64 __blksize_t st_blksize; /* Optimal block size for I/O. */ int __pad2; __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ -#ifdef __USE_XOPEN2K8 +# ifdef __USE_XOPEN2K8 /* Nanosecond resolution timestamps are stored in a format equivalent to 'struct timespec'. This is the type used whenever possible but the Unix namespace rules do not allow the @@ -106,15 +113,16 @@ struct stat64 struct timespec st_atim; /* Time of last access. */ struct timespec st_mtim; /* Time of last modification. */ struct timespec st_ctim; /* Time of last status change. */ -#else +# else __time_t st_atime; /* Time of last access. */ unsigned long int st_atimensec; /* Nscecs of last access. */ __time_t st_mtime; /* Time of last modification. */ unsigned long int st_mtimensec; /* Nsecs of last modification. */ __time_t st_ctime; /* Time of last status change. */ unsigned long int st_ctimensec; /* Nsecs of last status change. */ -#endif +# endif int __glibc_reserved[2]; +# endif }; #endif diff --git a/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h b/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h new file mode 100644 index 0000000000..38b6e13e68 --- /dev/null +++ b/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h @@ -0,0 +1,139 @@ +/* Definition for struct stat. Linux/hppa version. + Copyright (C) 2020-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 + . */ + +#if !defined _SYS_STAT_H && !defined _FCNTL_H +# error "Never include directly; use instead." +#endif + +#ifndef _BITS_STRUCT_STAT_H +#define _BITS_STRUCT_STAT_H 1 + +#include +#include + +struct stat + { +#ifdef __USE_TIME_BITS64 +# include +#else + __dev_t st_dev; /* Device. */ + unsigned short int __pad1; +# ifndef __USE_FILE_OFFSET64 + __ino_t st_ino; /* File serial number. */ +# else + __ino_t __st_ino; /* 32bit file serial number. */ +# endif + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + unsigned short int __pad2; +# ifndef __USE_FILE_OFFSET64 + __off_t st_size; /* Size of file, in bytes. */ +# else + __off64_t st_size; /* Size of file, in bytes. */ +# endif + __blksize_t st_blksize; /* Optimal block size for I/O. */ + +# ifndef __USE_FILE_OFFSET64 + __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ +# else + __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +# endif +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# define st_atime st_atim.tv_sec /* Backward compatibility. */ +# define st_mtime st_mtim.tv_sec +# define st_ctime st_ctim.tv_sec +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif +# ifndef __USE_FILE_OFFSET64 + unsigned long int __glibc_reserved4; + unsigned long int __glibc_reserved5; +# else + __ino64_t st_ino; /* File serial number. */ +# endif +#endif /* __USE_TIME_BITS64 */ + }; + +#ifdef __USE_LARGEFILE64 +struct stat64 + { +# ifdef __USE_TIME_BITS64 +# include +# else + __dev_t st_dev; /* Device. */ + unsigned int __pad1; + + __ino_t __st_ino; /* 32bit file serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + unsigned int __pad2; + __off64_t st_size; /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + + __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif + __ino64_t st_ino; /* File serial number. */ +# endif /* __USE_TIME_BITS64 */ + }; +#endif + +/* Tell code we have these members. */ +#define _STATBUF_ST_BLKSIZE +#define _STATBUF_ST_RDEV +/* Nanosecond resolution time values are supported. */ +#define _STATBUF_ST_NSEC + + +#endif /* _BITS_STRUCT_STAT_H */ diff --git a/sysdeps/unix/sysv/linux/hppa/kernel-features.h b/sysdeps/unix/sysv/linux/hppa/kernel-features.h index 0cd21ef0fa..079612e4aa 100644 --- a/sysdeps/unix/sysv/linux/hppa/kernel-features.h +++ b/sysdeps/unix/sysv/linux/hppa/kernel-features.h @@ -30,3 +30,6 @@ #undef __ASSUME_CLONE_DEFAULT #define __ASSUME_CLONE_BACKWARDS 1 + +/* QEMU does not support set_robust_list. */ +#undef __ASSUME_SET_ROBUST_LIST diff --git a/sysdeps/unix/sysv/linux/ipc_priv.h b/sysdeps/unix/sysv/linux/ipc_priv.h index 87893a6757..2f50c31a8e 100644 --- a/sysdeps/unix/sysv/linux/ipc_priv.h +++ b/sysdeps/unix/sysv/linux/ipc_priv.h @@ -63,4 +63,10 @@ struct __old_ipc_perm # define __IPC_TIME64 0 #endif +#if __IPC_TIME64 || defined __ASSUME_SYSVIPC_BROKEN_MODE_T +# define IPC_CTL_NEED_TRANSLATION 1 +#else +# define IPC_CTL_NEED_TRANSLATION 0 +#endif + #include 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 . */ + +#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/mips/mips64/n64/fstatat.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fstatat.c new file mode 100644 index 0000000000..fe6c3a0dda --- /dev/null +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/fstatat.c @@ -0,0 +1,51 @@ +/* Get file status. Linux/MIPSn64 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; if not, see + . */ + +#include +#include + +/* Different than other ABIs, mips64 has different layouts for non-LFS + and LFS struct stat. */ +int +__fstatat (int fd, const char *file, struct stat *buf, int flag) +{ + struct __stat64_t64 st64; + int r = __fstatat64_time64 (fd, file, &st64, flag); + if (r == 0) + { + /* Clear internal pad and reserved fields. */ + memset (buf, 0, sizeof (*buf)); + + buf->st_dev = st64.st_dev; + buf->st_ino = st64.st_ino; + buf->st_mode = st64.st_mode; + buf->st_nlink = st64.st_nlink; + buf->st_uid = st64.st_uid; + buf->st_gid = st64.st_gid; + buf->st_rdev = st64.st_rdev; + buf->st_size = st64.st_size; + buf->st_blksize = st64.st_blksize; + buf->st_blocks = st64.st_blocks; + buf->st_atim = st64.st_atim; + buf->st_mtim = st64.st_mtim; + buf->st_ctim = st64.st_ctim; + } + return r; +} + +weak_alias (__fstatat, fstatat) diff --git a/sysdeps/unix/sysv/linux/msgctl.c b/sysdeps/unix/sysv/linux/msgctl.c index e824ebb095..2072205252 100644 --- a/sysdeps/unix/sysv/linux/msgctl.c +++ b/sysdeps/unix/sysv/linux/msgctl.c @@ -85,11 +85,19 @@ msgctl_syscall (int msqid, int cmd, msgctl_arg_t *buf) int __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf) { -#if __IPC_TIME64 +#if IPC_CTL_NEED_TRANSLATION +# if __IPC_TIME64 struct kernel_msqid64_ds ksemid, *arg = NULL; -#else +# else msgctl_arg_t *arg; -#endif +# endif + + /* Some applications pass the __IPC_64 flag in cmd, to invoke + previously unsupported commands back when there was no EINVAL + error checking in glibc. Mask the flag for the switch statements + below. msgctl_syscall adds back the __IPC_64 flag for the actual + system call. */ + cmd &= ~__IPC_64; switch (cmd) { @@ -101,19 +109,19 @@ __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf) case IPC_STAT: case MSG_STAT: case MSG_STAT_ANY: -#if __IPC_TIME64 +# if __IPC_TIME64 if (buf != NULL) { msqid64_to_kmsqid64 (buf, &ksemid); arg = &ksemid; } -# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T +# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T if (cmd == IPC_SET) arg->msg_perm.mode *= 0x10000U; -# endif -#else +# endif +# else arg = buf; -#endif +# endif break; case IPC_INFO: @@ -137,21 +145,25 @@ __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf) case IPC_STAT: case MSG_STAT: case MSG_STAT_ANY: -#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T +# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T arg->msg_perm.mode >>= 16; -#else +# else /* Old Linux kernel versions might not clear the mode padding. */ if (sizeof ((struct msqid_ds){0}.msg_perm.mode) != sizeof (__kernel_mode_t)) arg->msg_perm.mode &= 0xFFFF; -#endif +# endif -#if __IPC_TIME64 +# if __IPC_TIME64 kmsqid64_to_msqid64 (arg, buf); -#endif +# endif } return ret; + +#else /* !IPC_CTL_NEED_TRANSLATION */ + return msgctl_syscall (msqid, cmd, buf); +#endif } #if __TIMESIZE != 64 libc_hidden_def (__msgctl64) diff --git a/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h b/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h new file mode 100644 index 0000000000..e00e71173e --- /dev/null +++ b/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h @@ -0,0 +1,135 @@ +/* Definition for struct stat. Linux/nios2 version. + Copyright (C) 2020-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 + . */ + +#if !defined _SYS_STAT_H && !defined _FCNTL_H +# error "Never include directly; use instead." +#endif + +#ifndef _BITS_STRUCT_STAT_H +#define _BITS_STRUCT_STAT_H 1 + +#include +#include + +#if defined __USE_FILE_OFFSET64 +# define __field64(type, type64, name) type64 name +#elif __WORDSIZE == 64 || defined __INO_T_MATCHES_INO64_T +# if defined __INO_T_MATCHES_INO64_T && !defined __OFF_T_MATCHES_OFF64_T +# error "ino_t and off_t must both be the same type" +# endif +# define __field64(type, type64, name) type name +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define __field64(type, type64, name) \ + type name __attribute__((__aligned__ (__alignof__ (type64)))); int __##name##_pad +#else +# define __field64(type, type64, name) \ + int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name +#endif + +struct stat + { +#ifdef __USE_TIME_BITS64 +# include +#else + __dev_t st_dev; /* Device. */ + __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + __dev_t __pad1; + __field64(__off_t, __off64_t, st_size); /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + int __pad2; + __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# define st_atime st_atim.tv_sec /* Backward compatibility. */ +# define st_mtime st_mtim.tv_sec +# define st_ctime st_ctim.tv_sec +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif + int __glibc_reserved[2]; +#endif + }; + +#undef __field64 + +#ifdef __USE_LARGEFILE64 +struct stat64 + { +# ifdef __USE_TIME_BITS64 +# include +# else + __dev_t st_dev; /* Device. */ + __ino64_t st_ino; /* File serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + __dev_t __pad1; + __off64_t st_size; /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + int __pad2; + __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif + int __glibc_reserved[2]; +# endif + }; +#endif + +/* Tell code we have these members. */ +#define _STATBUF_ST_BLKSIZE +#define _STATBUF_ST_RDEV +/* Nanosecond resolution time values are supported. */ +#define _STATBUF_ST_NSEC + +#endif /* _BITS_STRUCT_STAT_H */ 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/powerpc/bits/fcntl.h b/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h index d7cf158b33..0ca6e69ee9 100644 --- a/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h +++ b/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h @@ -33,6 +33,12 @@ # define __O_LARGEFILE 0200000 #endif +#if __WORDSIZE == 64 && !defined __USE_FILE_OFFSET64 +# define F_GETLK 5 +# define F_SETLK 6 +# define F_SETLKW 7 +#endif + struct flock { short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */ 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/semctl.c b/sysdeps/unix/sysv/linux/semctl.c index 77a8130c18..3458b018bc 100644 --- a/sysdeps/unix/sysv/linux/semctl.c +++ b/sysdeps/unix/sysv/linux/semctl.c @@ -140,6 +140,13 @@ __semctl64 (int semid, int semnum, int cmd, ...) union semun64 arg64 = { 0 }; va_list ap; + /* Some applications pass the __IPC_64 flag in cmd, to invoke + previously unsupported commands back when there was no EINVAL + error checking in glibc. Mask the flag for the switch statements + below. semctl_syscall adds back the __IPC_64 flag for the actual + system call. */ + cmd &= ~__IPC_64; + /* Get the argument only if required. */ switch (cmd) { diff --git a/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h b/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h new file mode 100644 index 0000000000..0f7c9cdc89 --- /dev/null +++ b/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h @@ -0,0 +1,139 @@ +/* Definition for struct stat. Linux/sh version. + Copyright (C) 2020-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 + . */ + +#if !defined _SYS_STAT_H && !defined _FCNTL_H +# error "Never include directly; use instead." +#endif + +#ifndef _BITS_STRUCT_STAT_H +#define _BITS_STRUCT_STAT_H 1 + +#include +#include + +struct stat + { +#ifdef __USE_TIME_BITS64 +# include +#else + __dev_t st_dev; /* Device. */ + unsigned short int __pad1; +# ifndef __USE_FILE_OFFSET64 + __ino_t st_ino; /* File serial number. */ +# else + __ino_t __st_ino; /* 32bit file serial number. */ +# endif + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + unsigned short int __pad2; +# ifndef __USE_FILE_OFFSET64 + __off_t st_size; /* Size of file, in bytes. */ +# else + __off64_t st_size; /* Size of file, in bytes. */ +# endif + __blksize_t st_blksize; /* Optimal block size for I/O. */ + +# ifndef __USE_FILE_OFFSET64 + __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ +# else + __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +# endif +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# define st_atime st_atim.tv_sec /* Backward compatibility. */ +# define st_mtime st_mtim.tv_sec +# define st_ctime st_ctim.tv_sec +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif +# ifndef __USE_FILE_OFFSET64 + unsigned long int __glibc_reserved4; + unsigned long int __glibc_reserved5; +# else + __ino64_t st_ino; /* File serial number. */ +# endif +#endif /* __USE_TIME_BITS64 */ + }; + +#ifdef __USE_LARGEFILE64 +struct stat64 + { +# ifdef __USE_TIME_BITS64 +# include +# else + __dev_t st_dev; /* Device. */ + unsigned int __pad1; + + __ino_t __st_ino; /* 32bit file serial number. */ + __mode_t st_mode; /* File mode. */ + __nlink_t st_nlink; /* Link count. */ + __uid_t st_uid; /* User ID of the file's owner. */ + __gid_t st_gid; /* Group ID of the file's group.*/ + __dev_t st_rdev; /* Device number, if device. */ + unsigned int __pad2; + __off64_t st_size; /* Size of file, in bytes. */ + __blksize_t st_blksize; /* Optimal block size for I/O. */ + + __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the + identifier 'timespec' to appear in the header. + Therefore we have to handle the use of this header in strictly + standard-compliant sources special. */ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +# endif + __ino64_t st_ino; /* File serial number. */ +# endif /* __USE_TIME_BITS64 */ + }; +#endif + +/* Tell code we have these members. */ +#define _STATBUF_ST_BLKSIZE +#define _STATBUF_ST_RDEV +/* Nanosecond resolution time values are supported. */ +#define _STATBUF_ST_NSEC + + +#endif /* _BITS_STRUCT_STAT_H */ diff --git a/sysdeps/unix/sysv/linux/shmctl.c b/sysdeps/unix/sysv/linux/shmctl.c index ea38935497..f00817a6f6 100644 --- a/sysdeps/unix/sysv/linux/shmctl.c +++ b/sysdeps/unix/sysv/linux/shmctl.c @@ -85,11 +85,19 @@ shmctl_syscall (int shmid, int cmd, shmctl_arg_t *buf) int __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf) { -#if __IPC_TIME64 +#if IPC_CTL_NEED_TRANSLATION +# if __IPC_TIME64 struct kernel_shmid64_ds kshmid, *arg = NULL; -#else +# else shmctl_arg_t *arg; -#endif +# endif + + /* Some applications pass the __IPC_64 flag in cmd, to invoke + previously unsupported commands back when there was no EINVAL + error checking in glibc. Mask the flag for the switch statements + below. shmctl_syscall adds back the __IPC_64 flag for the actual + system call. */ + cmd &= ~__IPC_64; switch (cmd) { @@ -103,19 +111,19 @@ __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf) case IPC_STAT: case SHM_STAT: case SHM_STAT_ANY: -#if __IPC_TIME64 +# if __IPC_TIME64 if (buf != NULL) { shmid64_to_kshmid64 (buf, &kshmid); arg = &kshmid; } -# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T +# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T if (cmd == IPC_SET) arg->shm_perm.mode *= 0x10000U; -# endif -#else +# endif +# else arg = buf; -#endif +# endif break; case IPC_INFO: @@ -140,21 +148,25 @@ __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf) case IPC_STAT: case SHM_STAT: case SHM_STAT_ANY: -#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T +# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T arg->shm_perm.mode >>= 16; -#else +# else /* Old Linux kernel versions might not clear the mode padding. */ if (sizeof ((struct shmid_ds){0}.shm_perm.mode) != sizeof (__kernel_mode_t)) arg->shm_perm.mode &= 0xFFFF; -#endif +# endif -#if __IPC_TIME64 +# if __IPC_TIME64 kshmid64_to_shmid64 (arg, buf); -#endif +# endif } return ret; + +#else /* !IPC_CTL_NEED_TRANSLATION */ + return shmctl_syscall (shmid, cmd, buf); +#endif } #if __TIMESIZE != 64 libc_hidden_def (__shmctl64) diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/sigreturn_stub.S b/sysdeps/unix/sysv/linux/sparc/sparc32/sigreturn_stub.S index 2829e881eb..a1492ea59e 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/sigreturn_stub.S +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/sigreturn_stub.S @@ -23,12 +23,15 @@ [1] https://lkml.org/lkml/2016/5/27/465 */ -ENTRY (__rt_sigreturn_stub) + nop + nop + +ENTRY_NOCFI (__rt_sigreturn_stub) mov __NR_rt_sigreturn, %g1 ta 0x10 -END (__rt_sigreturn_stub) +END_NOCFI (__rt_sigreturn_stub) -ENTRY (__sigreturn_stub) +ENTRY_NOCFI (__sigreturn_stub) mov __NR_sigreturn, %g1 ta 0x10 -END (__sigreturn_stub) +END_NOCFI (__sigreturn_stub) diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/sigreturn_stub.S b/sysdeps/unix/sysv/linux/sparc/sparc64/sigreturn_stub.S index ac6af95e36..23b8b93f56 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/sigreturn_stub.S +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/sigreturn_stub.S @@ -23,7 +23,10 @@ [1] https://lkml.org/lkml/2016/5/27/465 */ -ENTRY (__rt_sigreturn_stub) + nop + nop + +ENTRY_NOCFI (__rt_sigreturn_stub) mov __NR_rt_sigreturn, %g1 ta 0x6d -END (__rt_sigreturn_stub) +END_NOCFI (__rt_sigreturn_stub) 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 #include -#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<. + +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 ', + 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 \n" + "#include "), + check("sys/mount.h + linux/fs.h", + "#include \n" + "#include "), + check("linux/mount.h + sys/mount.h", + "#include \n" + "#include "), + check("linux/fs.h + sys/mount.h", + "#include \n" + "#include ")) + 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 ', + 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 ', + 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 \n', '#include \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/sysdeps/x86/dl-cacheinfo.h b/sysdeps/x86/dl-cacheinfo.h index e9f3382108..d95c1efa2c 100644 --- a/sysdeps/x86/dl-cacheinfo.h +++ b/sysdeps/x86/dl-cacheinfo.h @@ -478,7 +478,7 @@ handle_zhaoxin (int name) } static void -get_common_cache_info (long int *shared_ptr, unsigned int *threads_ptr, +get_common_cache_info (long int *shared_ptr, long int * shared_per_thread_ptr, unsigned int *threads_ptr, long int core) { unsigned int eax; @@ -497,6 +497,7 @@ get_common_cache_info (long int *shared_ptr, unsigned int *threads_ptr, unsigned int family = cpu_features->basic.family; unsigned int model = cpu_features->basic.model; long int shared = *shared_ptr; + long int shared_per_thread = *shared_per_thread_ptr; unsigned int threads = *threads_ptr; bool inclusive_cache = true; bool support_count_mask = true; @@ -512,6 +513,7 @@ get_common_cache_info (long int *shared_ptr, unsigned int *threads_ptr, /* Try L2 otherwise. */ level = 2; shared = core; + shared_per_thread = core; threads_l2 = 0; threads_l3 = -1; } @@ -668,29 +670,27 @@ get_common_cache_info (long int *shared_ptr, unsigned int *threads_ptr, } else { -intel_bug_no_cache_info: - /* Assume that all logical threads share the highest cache - level. */ - threads - = ((cpu_features->features[CPUID_INDEX_1].cpuid.ebx >> 16) - & 0xff); - } - - /* Cap usage of highest cache level to the number of supported - threads. */ - if (shared > 0 && threads > 0) - shared /= threads; + intel_bug_no_cache_info: + /* Assume that all logical threads share the highest cache + level. */ + threads = ((cpu_features->features[CPUID_INDEX_1].cpuid.ebx >> 16) + & 0xff); + } + /* Get per-thread size of highest level cache. */ + if (shared_per_thread > 0 && threads > 0) + shared_per_thread /= threads; } /* Account for non-inclusive L2 and L3 caches. */ if (!inclusive_cache) { - if (threads_l2 > 0) - core /= threads_l2; + long int core_per_thread = threads_l2 > 0 ? (core / threads_l2) : core; + shared_per_thread += core_per_thread; shared += core; } *shared_ptr = shared; + *shared_per_thread_ptr = shared_per_thread; *threads_ptr = threads; } @@ -704,6 +704,7 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) int max_cpuid_ex; long int data = -1; long int shared = -1; + long int shared_per_thread = -1; long int core = -1; unsigned int threads = 0; unsigned long int level1_icache_size = -1; @@ -724,6 +725,7 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) data = handle_intel (_SC_LEVEL1_DCACHE_SIZE, cpu_features); core = handle_intel (_SC_LEVEL2_CACHE_SIZE, cpu_features); shared = handle_intel (_SC_LEVEL3_CACHE_SIZE, cpu_features); + shared_per_thread = shared; level1_icache_size = handle_intel (_SC_LEVEL1_ICACHE_SIZE, cpu_features); @@ -747,13 +749,14 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) level4_cache_size = handle_intel (_SC_LEVEL4_CACHE_SIZE, cpu_features); - get_common_cache_info (&shared, &threads, core); + get_common_cache_info (&shared, &shared_per_thread, &threads, core); } else if (cpu_features->basic.kind == arch_kind_zhaoxin) { data = handle_zhaoxin (_SC_LEVEL1_DCACHE_SIZE); core = handle_zhaoxin (_SC_LEVEL2_CACHE_SIZE); shared = handle_zhaoxin (_SC_LEVEL3_CACHE_SIZE); + shared_per_thread = shared; level1_icache_size = handle_zhaoxin (_SC_LEVEL1_ICACHE_SIZE); level1_icache_linesize = handle_zhaoxin (_SC_LEVEL1_ICACHE_LINESIZE); @@ -767,13 +770,14 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) level3_cache_assoc = handle_zhaoxin (_SC_LEVEL3_CACHE_ASSOC); level3_cache_linesize = handle_zhaoxin (_SC_LEVEL3_CACHE_LINESIZE); - get_common_cache_info (&shared, &threads, core); + get_common_cache_info (&shared, &shared_per_thread, &threads, core); } else if (cpu_features->basic.kind == arch_kind_amd) { data = handle_amd (_SC_LEVEL1_DCACHE_SIZE); core = handle_amd (_SC_LEVEL2_CACHE_SIZE); shared = handle_amd (_SC_LEVEL3_CACHE_SIZE); + shared_per_thread = shared; level1_icache_size = handle_amd (_SC_LEVEL1_ICACHE_SIZE); level1_icache_linesize = handle_amd (_SC_LEVEL1_ICACHE_LINESIZE); @@ -791,8 +795,11 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) __cpuid (0x80000000, max_cpuid_ex, ebx, ecx, edx); if (shared <= 0) - /* No shared L3 cache. All we have is the L2 cache. */ - shared = core; + { + /* No shared L3 cache. All we have is the L2 cache. */ + shared = core; + shared_per_thread = core; + } else { /* Figure out the number of logical threads that share L3. */ @@ -816,7 +823,7 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) /* Cap usage of highest cache level to the number of supported threads. */ if (threads > 0) - shared /= threads; + shared_per_thread /= threads; /* Get shared cache per ccx for Zen architectures. */ if (cpu_features->basic.family >= 0x17) @@ -827,12 +834,13 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) __cpuid_count (0x8000001D, 0x3, eax, ebx, ecx, edx); unsigned int threads_per_ccx = ((eax >> 14) & 0xfff) + 1; - shared *= threads_per_ccx; + shared_per_thread *= threads_per_ccx; } else { /* Account for exclusive L2 and L3 caches. */ shared += core; + shared_per_thread += core; } } } @@ -850,17 +858,46 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) cpu_features->level3_cache_linesize = level3_cache_linesize; cpu_features->level4_cache_size = level4_cache_size; - /* The default setting for the non_temporal threshold is 3/4 of one - thread's share of the chip's cache. For most Intel and AMD processors - with an initial release date between 2017 and 2020, a thread's typical - share of the cache is from 500 KBytes to 2 MBytes. Using the 3/4 - threshold leaves 125 KBytes to 500 KBytes of the thread's data - in cache after a maximum temporal copy, which will maintain - in cache a reasonable portion of the thread's stack and other - active data. If the threshold is set higher than one thread's - share of the cache, it has a substantial risk of negatively - impacting the performance of other threads running on the chip. */ - unsigned long int non_temporal_threshold = shared * 3 / 4; + /* The default setting for the non_temporal threshold is 1/4 of size + of the chip's cache. For most Intel and AMD processors with an + initial release date between 2017 and 2023, a thread's typical + share of the cache is from 18-64MB. Using the 1/4 L3 is meant to + estimate the point where non-temporal stores begin out-competing + REP MOVSB. As well the point where the fact that non-temporal + stores are forced back to main memory would already occurred to the + majority of the lines in the copy. Note, concerns about the + entire L3 cache being evicted by the copy are mostly alleviated + by the fact that modern HW detects streaming patterns and + provides proper LRU hints so that the maximum thrashing + capped at 1/associativity. */ + unsigned long int non_temporal_threshold = shared / 4; + + /* If the computed non_temporal_threshold <= 3/4 * per-thread L3, we most + likely have incorrect/incomplete cache info in which case, default to + 3/4 * per-thread L3 to avoid regressions. */ + unsigned long int non_temporal_threshold_lowbound + = shared_per_thread * 3 / 4; + if (non_temporal_threshold < non_temporal_threshold_lowbound) + non_temporal_threshold = non_temporal_threshold_lowbound; + + /* If no ERMS, we use the per-thread L3 chunking. Normal cacheable stores run + a higher risk of actually thrashing the cache as they don't have a HW LRU + hint. As well, their performance in highly parallel situations is + noticeably worse. */ + if (!CPU_FEATURE_USABLE_P (cpu_features, ERMS)) + non_temporal_threshold = non_temporal_threshold_lowbound; + /* SIZE_MAX >> 4 because memmove-vec-unaligned-erms right-shifts the value of + 'x86_non_temporal_threshold' by `LOG_4X_MEMCPY_THRESH` (4) and it is best + if that operation cannot overflow. Minimum of 0x4040 (16448) because the + L(large_memset_4x) loops need 64-byte to cache align and enough space for + at least 1 iteration of 4x PAGE_SIZE unrolled loop. Both values are + reflected in the manual. */ + unsigned long int maximum_non_temporal_threshold = SIZE_MAX >> 4; + unsigned long int minimum_non_temporal_threshold = 0x4040; + if (non_temporal_threshold < minimum_non_temporal_threshold) + non_temporal_threshold = minimum_non_temporal_threshold; + else if (non_temporal_threshold > maximum_non_temporal_threshold) + non_temporal_threshold = maximum_non_temporal_threshold; #if HAVE_TUNABLES /* NB: The REP MOVSB threshold must be greater than VEC_SIZE * 8. */ @@ -915,8 +952,8 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) shared = tunable_size; tunable_size = TUNABLE_GET (x86_non_temporal_threshold, long int, NULL); - /* NB: Ignore the default value 0. */ - if (tunable_size != 0) + if (tunable_size > minimum_non_temporal_threshold + && tunable_size <= maximum_non_temporal_threshold) non_temporal_threshold = tunable_size; tunable_size = TUNABLE_GET (x86_rep_movsb_threshold, long int, NULL); @@ -931,14 +968,9 @@ dl_init_cacheinfo (struct cpu_features *cpu_features) TUNABLE_SET_WITH_BOUNDS (x86_data_cache_size, data, 0, SIZE_MAX); TUNABLE_SET_WITH_BOUNDS (x86_shared_cache_size, shared, 0, SIZE_MAX); - /* SIZE_MAX >> 4 because memmove-vec-unaligned-erms right-shifts the value of - 'x86_non_temporal_threshold' by `LOG_4X_MEMCPY_THRESH` (4) and it is best - if that operation cannot overflow. Minimum of 0x4040 (16448) because the - L(large_memset_4x) loops need 64-byte to cache align and enough space for - at least 1 iteration of 4x PAGE_SIZE unrolled loop. Both values are - reflected in the manual. */ TUNABLE_SET_WITH_BOUNDS (x86_non_temporal_threshold, non_temporal_threshold, - 0x4040, SIZE_MAX >> 4); + minimum_non_temporal_threshold, + maximum_non_temporal_threshold); TUNABLE_SET_WITH_BOUNDS (x86_rep_movsb_threshold, rep_movsb_threshold, minimum_rep_movsb_threshold, SIZE_MAX); TUNABLE_SET_WITH_BOUNDS (x86_rep_stosb_threshold, rep_stosb_threshold, 1, diff --git a/sysdeps/x86/get-isa-level.h b/sysdeps/x86/get-isa-level.h index 1ade78ab73..5b4dd5f062 100644 --- a/sysdeps/x86/get-isa-level.h +++ b/sysdeps/x86/get-isa-level.h @@ -47,6 +47,8 @@ get_isa_level (const struct cpu_features *cpu_features) isa_level |= GNU_PROPERTY_X86_ISA_1_V2; if (CPU_FEATURE_USABLE_P (cpu_features, AVX) && CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && CPU_FEATURE_USABLE_P (cpu_features, BMI1) + && CPU_FEATURE_USABLE_P (cpu_features, BMI2) && CPU_FEATURE_USABLE_P (cpu_features, F16C) && CPU_FEATURE_USABLE_P (cpu_features, FMA) && CPU_FEATURE_USABLE_P (cpu_features, LZCNT) diff --git a/sysdeps/x86/isa-level.h b/sysdeps/x86/isa-level.h index 3c4480aba7..06f6c9663e 100644 --- a/sysdeps/x86/isa-level.h +++ b/sysdeps/x86/isa-level.h @@ -79,7 +79,9 @@ /* ISA level >= 3 guaranteed includes. */ #define AVX_X86_ISA_LEVEL 3 #define AVX2_X86_ISA_LEVEL 3 +#define BMI1_X86_ISA_LEVEL 3 #define BMI2_X86_ISA_LEVEL 3 +#define LZCNT_X86_ISA_LEVEL 3 #define MOVBE_X86_ISA_LEVEL 3 /* ISA level >= 2 guaranteed includes. */ diff --git a/sysdeps/x86_64/dl-tlsdesc.S b/sysdeps/x86_64/dl-tlsdesc.S index 0db2cb4152..7619e743e1 100644 --- a/sysdeps/x86_64/dl-tlsdesc.S +++ b/sysdeps/x86_64/dl-tlsdesc.S @@ -61,7 +61,7 @@ _dl_tlsdesc_return: _dl_tlsdesc_undefweak: _CET_ENDBR movq 8(%rax), %rax - subq %fs:0, %rax + sub %fs:0, %RAX_LP ret cfi_endproc .size _dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak @@ -102,7 +102,7 @@ _dl_tlsdesc_dynamic: /* Preserve call-clobbered registers that we modify. We need two scratch regs anyway. */ movq %rsi, -16(%rsp) - movq %fs:DTV_OFFSET, %rsi + mov %fs:DTV_OFFSET, %RSI_LP movq %rdi, -8(%rsp) movq TLSDESC_ARG(%rax), %rdi movq (%rsi), %rax @@ -116,7 +116,7 @@ _dl_tlsdesc_dynamic: addq TLSDESC_MODOFF(%rdi), %rax .Lret: movq -16(%rsp), %rsi - subq %fs:0, %rax + sub %fs:0, %RAX_LP movq -8(%rsp), %rdi ret .Lslow: diff --git a/sysdeps/x86_64/ffsll.c b/sysdeps/x86_64/ffsll.c index 842ebaeb4c..d352866d9f 100644 --- a/sysdeps/x86_64/ffsll.c +++ b/sysdeps/x86_64/ffsll.c @@ -26,13 +26,13 @@ int ffsll (long long int x) { long long int cnt; - long long int tmp; - asm ("bsfq %2,%0\n" /* Count low bits in X and store in %1. */ - "cmoveq %1,%0\n" /* If number was zero, use -1 as result. */ - : "=&r" (cnt), "=r" (tmp) : "rm" (x), "1" (-1)); + asm ("mov $-1,%k0\n" /* Initialize cnt to -1. */ + "bsf %1,%0\n" /* Count low bits in x and store in cnt. */ + "inc %k0\n" /* Increment cnt by 1. */ + : "=&r" (cnt) : "r" (x)); - return cnt + 1; + return cnt; } #ifndef __ILP32__ diff --git a/sysdeps/x86_64/fpu/fraiseexcpt.c b/sysdeps/x86_64/fpu/fraiseexcpt.c index 864f4777a2..23446ff4ac 100644 --- a/sysdeps/x86_64/fpu/fraiseexcpt.c +++ b/sysdeps/x86_64/fpu/fraiseexcpt.c @@ -33,7 +33,7 @@ __feraiseexcept (int excepts) /* One example of an invalid operation is 0.0 / 0.0. */ float f = 0.0; - __asm__ __volatile__ ("divss %0, %0 " : : "x" (f)); + __asm__ __volatile__ ("divss %0, %0 " : "+x" (f)); (void) &f; } @@ -43,7 +43,7 @@ __feraiseexcept (int excepts) float f = 1.0; float g = 0.0; - __asm__ __volatile__ ("divss %1, %0" : : "x" (f), "x" (g)); + __asm__ __volatile__ ("divss %1, %0" : "+x" (f) : "x" (g)); (void) &f; } diff --git a/sysdeps/x86_64/multiarch/ifunc-avx2.h b/sysdeps/x86_64/multiarch/ifunc-avx2.h index a57a9952f3..f2f5e8a211 100644 --- a/sysdeps/x86_64/multiarch/ifunc-avx2.h +++ b/sysdeps/x86_64/multiarch/ifunc-avx2.h @@ -36,7 +36,9 @@ IFUNC_SELECTOR (void) const struct cpu_features *cpu_features = __get_cpu_features (); if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI1) && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI2) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, LZCNT) && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load, )) { diff --git a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c index a71444eccb..00a91123d3 100644 --- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c +++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c @@ -69,10 +69,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, && CPU_FEATURE_USABLE (BMI2)), __memchr_evex_rtm) X86_IFUNC_IMPL_ADD_V3 (array, i, memchr, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __memchr_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, memchr, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __memchr_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -207,13 +209,19 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, memrchr, X86_IFUNC_IMPL_ADD_V4 (array, i, memrchr, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (LZCNT)), __memrchr_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, memrchr, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (LZCNT)), __memrchr_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, memrchr, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (LZCNT) && CPU_FEATURE_USABLE (RTM)), __memrchr_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -335,10 +343,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, && CPU_FEATURE_USABLE (BMI2)), __rawmemchr_evex_rtm) X86_IFUNC_IMPL_ADD_V3 (array, i, rawmemchr, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __rawmemchr_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, rawmemchr, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __rawmemchr_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -448,13 +458,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, strcasecmp, X86_IFUNC_IMPL_ADD_V4 (array, i, strcasecmp, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), __strcasecmp_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strcasecmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __strcasecmp_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strcasecmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strcasecmp_avx2_rtm) X86_IFUNC_IMPL_ADD_V2 (array, i, strcasecmp, @@ -470,13 +483,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, strcasecmp_l, X86_IFUNC_IMPL_ADD_V4 (array, i, strcasecmp, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), __strcasecmp_l_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strcasecmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __strcasecmp_l_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strcasecmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strcasecmp_l_avx2_rtm) X86_IFUNC_IMPL_ADD_V2 (array, i, strcasecmp_l, @@ -562,13 +578,19 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, strrchr, X86_IFUNC_IMPL_ADD_V4 (array, i, strrchr, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2)), __strrchr_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strrchr, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2)), __strrchr_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strrchr, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strrchr_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -585,10 +607,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, && CPU_FEATURE_USABLE (BMI2)), __strcmp_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strcmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __strcmp_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strcmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strcmp_avx2_rtm) X86_IFUNC_IMPL_ADD_V2 (array, i, strcmp, @@ -638,13 +662,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, strncasecmp, X86_IFUNC_IMPL_ADD_V4 (array, i, strncasecmp, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), __strncasecmp_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strncasecmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __strncasecmp_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strncasecmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strncasecmp_avx2_rtm) X86_IFUNC_IMPL_ADD_V2 (array, i, strncasecmp, @@ -660,13 +687,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, strncasecmp_l, X86_IFUNC_IMPL_ADD_V4 (array, i, strncasecmp, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + & CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), __strncasecmp_l_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strncasecmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __strncasecmp_l_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strncasecmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strncasecmp_l_avx2_rtm) X86_IFUNC_IMPL_ADD_V2 (array, i, strncasecmp_l, @@ -773,13 +803,18 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, X86_IFUNC_IMPL_ADD_V4 (array, i, wcsrchr, (CPU_FEATURE_USABLE (AVX512VL) && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI1) && CPU_FEATURE_USABLE (BMI2)), __wcsrchr_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, wcsrchr, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2)), __wcsrchr_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, wcsrchr, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __wcsrchr_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -796,10 +831,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, && CPU_FEATURE_USABLE (BMI2)), __wcscmp_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, wcscmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __wcscmp_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, wcscmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __wcscmp_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -816,10 +853,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, && CPU_FEATURE_USABLE (BMI2)), __wcsncmp_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, wcsncmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __wcsncmp_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, wcsncmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __wcsncmp_avx2_rtm) /* ISA V2 wrapper for GENERIC implementation because the @@ -909,10 +948,12 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, && CPU_FEATURE_USABLE (BMI2)), __wmemchr_evex_rtm) X86_IFUNC_IMPL_ADD_V3 (array, i, wmemchr, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __wmemchr_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, wmemchr, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __wmemchr_avx2_rtm) /* ISA V2 wrapper for SSE2 implementation because the SSE2 @@ -1162,13 +1203,16 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, IFUNC_IMPL (i, name, strncmp, X86_IFUNC_IMPL_ADD_V4 (array, i, strncmp, (CPU_FEATURE_USABLE (AVX512VL) - && CPU_FEATURE_USABLE (AVX512BW)), + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), __strncmp_evex) X86_IFUNC_IMPL_ADD_V3 (array, i, strncmp, - CPU_FEATURE_USABLE (AVX2), + (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2)), __strncmp_avx2) X86_IFUNC_IMPL_ADD_V3 (array, i, strncmp, (CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI2) && CPU_FEATURE_USABLE (RTM)), __strncmp_avx2_rtm) X86_IFUNC_IMPL_ADD_V2 (array, i, strncmp, diff --git a/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h b/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h index 68646ef199..7622af259c 100644 --- a/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h +++ b/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h @@ -34,6 +34,7 @@ IFUNC_SELECTOR (void) const struct cpu_features *cpu_features = __get_cpu_features (); if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI2) && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load, )) { diff --git a/sysdeps/x86_64/multiarch/memcmp-sse2.S b/sysdeps/x86_64/multiarch/memcmp-sse2.S index afd450d020..51bc9344f0 100644 --- a/sysdeps/x86_64/multiarch/memcmp-sse2.S +++ b/sysdeps/x86_64/multiarch/memcmp-sse2.S @@ -308,7 +308,17 @@ L(ret_nonzero_vec_end_0): setg %dl leal -1(%rdx, %rdx), %eax # else - addl %edx, %eax + /* Use `addq` instead of `addl` here so that even if `rax` + `rdx` + is negative value of the sum will be usable as a 64-bit offset + (negative 32-bit numbers zero-extend to a large and often + out-of-bounds 64-bit offsets). Note that `rax` + `rdx` >= 0 is + an invariant when `memcmp` is used correctly, but if the input + strings `rsi`/`rdi` are concurrently modified as the function + runs (there is a Data-Race) it is possible for `rax` + `rdx` to + be negative. Given that there is virtually no extra to cost + using `addq` instead of `addl` we may as well protect the + data-race case. */ + addq %rdx, %rax movzbl (VEC_SIZE * -1 + SIZE_OFFSET)(%rsi, %rax), %ecx movzbl (VEC_SIZE * -1 + SIZE_OFFSET)(%rdi, %rax), %eax subl %ecx, %eax diff --git a/sysdeps/x86_64/multiarch/rtld-strcpy.S b/sysdeps/x86_64/multiarch/rtld-strcpy.S new file mode 100644 index 0000000000..19439c553d --- /dev/null +++ b/sysdeps/x86_64/multiarch/rtld-strcpy.S @@ -0,0 +1,18 @@ +/* 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 + . */ + +#include "../strcpy.S" diff --git a/sysdeps/x86_64/multiarch/strcmp.c b/sysdeps/x86_64/multiarch/strcmp.c index fdd5afe3af..9d6c9f66ba 100644 --- a/sysdeps/x86_64/multiarch/strcmp.c +++ b/sysdeps/x86_64/multiarch/strcmp.c @@ -45,12 +45,12 @@ IFUNC_SELECTOR (void) const struct cpu_features *cpu_features = __get_cpu_features (); if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI2) && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load, )) { if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) - && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512BW) - && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI2)) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)) return OPTIMIZE (evex); if (CPU_FEATURE_USABLE_P (cpu_features, RTM)) diff --git a/sysdeps/x86_64/multiarch/strlen-avx2.S b/sysdeps/x86_64/multiarch/strlen-avx2.S index 0593fb303b..b9b58ef599 100644 --- a/sysdeps/x86_64/multiarch/strlen-avx2.S +++ b/sysdeps/x86_64/multiarch/strlen-avx2.S @@ -544,14 +544,11 @@ L(return_vzeroupper): L(cross_page_less_vec): tzcntl %eax, %eax # ifdef USE_AS_WCSLEN - /* NB: Multiply length by 4 to get byte count. */ - sall $2, %esi + /* NB: Divide by 4 to convert from byte-count to length. */ + shrl $2, %eax # endif cmpq %rax, %rsi cmovb %esi, %eax -# ifdef USE_AS_WCSLEN - shrl $2, %eax -# endif VZEROUPPER_RETURN # endif diff --git a/sysdeps/x86_64/multiarch/strncmp.c b/sysdeps/x86_64/multiarch/strncmp.c index 4ebe4bde30..c4f8b6bbb5 100644 --- a/sysdeps/x86_64/multiarch/strncmp.c +++ b/sysdeps/x86_64/multiarch/strncmp.c @@ -41,12 +41,12 @@ IFUNC_SELECTOR (void) const struct cpu_features *cpu_features = __get_cpu_features (); if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI2) && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load, )) { if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) - && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512BW) - && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, BMI2)) + && X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)) return OPTIMIZE (evex); if (CPU_FEATURE_USABLE_P (cpu_features, RTM)) diff --git a/time/Makefile b/time/Makefile index 470275b90c..2f4aa2d528 100644 --- a/time/Makefile +++ b/time/Makefile @@ -50,7 +50,7 @@ tests := test_time clocktest tst-posixtz tst-strptime tst_wcsftime \ tst-clock tst-clock2 tst-clock_nanosleep tst-cpuclock1 \ tst-adjtime tst-ctime tst-difftime tst-mktime4 tst-clock_settime \ tst-settimeofday tst-itimer tst-gmtime tst-timegm \ - tst-timespec_get tst-timespec_getres + tst-timespec_get tst-timespec_getres tst-strftime4 tests-time64 := \ tst-adjtime-time64 \ @@ -65,6 +65,7 @@ tests-time64 := \ tst-itimer-time64 \ tst-mktime4-time64 \ tst-settimeofday-time64 \ + tst-strftime4-time64 \ tst-timegm-time64 \ tst-timespec_get-time64 \ tst-timespec_getres-time64 \ diff --git a/time/mktime.c b/time/mktime.c index 494c89bf54..e9a6006710 100644 --- a/time/mktime.c +++ b/time/mktime.c @@ -429,8 +429,13 @@ __mktime_internal (struct tm *tp, time with the right value, and use its UTC offset. Heuristic: probe the adjacent timestamps in both directions, - looking for the desired isdst. This should work for all real - time zone histories in the tz database. */ + looking for the desired isdst. If none is found within a + reasonable duration bound, assume a one-hour DST difference. + This should work for all real time zone histories in the tz + database. */ + + /* +1 if we wanted standard time but got DST, -1 if the reverse. */ + int dst_difference = (isdst == 0) - (tm.tm_isdst == 0); /* Distance between probes when looking for a DST boundary. In tzdata2003a, the shortest period of DST is 601200 seconds @@ -441,12 +446,14 @@ __mktime_internal (struct tm *tp, periods when probing. */ int stride = 601200; - /* The longest period of DST in tzdata2003a is 536454000 seconds - (e.g., America/Jujuy starting 1946-10-01 01:00). The longest - period of non-DST is much longer, but it makes no real sense - to search for more than a year of non-DST, so use the DST - max. */ - int duration_max = 536454000; + /* In TZDB 2021e, the longest period of DST (or of non-DST), in + which the DST (or adjacent DST) difference is not one hour, + is 457243209 seconds: e.g., America/Cambridge_Bay with leap + seconds, starting 1965-10-31 00:00 in a switch from + double-daylight time (-05) to standard time (-07), and + continuing to 1980-04-27 02:00 in a switch from standard time + (-07) to daylight time (-06). */ + int duration_max = 457243209; /* Search in both directions, so the maximum distance is half the duration; add the stride to avoid off-by-1 problems. */ @@ -483,6 +490,11 @@ __mktime_internal (struct tm *tp, } } + /* No unusual DST offset was found nearby. Assume one-hour DST. */ + t += 60 * 60 * dst_difference; + if (mktime_min <= t && t <= mktime_max && convert_time (convert, t, &tm)) + goto offset_found; + __set_errno (EOVERFLOW); return -1; } diff --git a/time/strftime_l.c b/time/strftime_l.c index 75554fee7c..4d7c4ea828 100644 --- a/time/strftime_l.c +++ b/time/strftime_l.c @@ -159,6 +159,10 @@ extern char *tzname[]; #ifdef _LIBC # define tzname __tzname # define tzset __tzset + +# define time_t __time64_t +# define __gmtime_r(t, tp) __gmtime64_r (t, tp) +# define mktime(tp) __mktime64 (tp) #endif #if !HAVE_TM_GMTOFF diff --git a/time/strptime_l.c b/time/strptime_l.c index a3c5681fc2..f927448204 100644 --- a/time/strptime_l.c +++ b/time/strptime_l.c @@ -30,8 +30,10 @@ #ifdef _LIBC # define HAVE_LOCALTIME_R 0 # include "../locale/localeinfo.h" -#endif +# define time_t __time64_t +# define __localtime_r(t, tp) __localtime64_r (t, tp) +#endif #if ! HAVE_LOCALTIME_R && ! defined localtime_r # ifdef _LIBC diff --git a/time/tst-strftime4-time64.c b/time/tst-strftime4-time64.c new file mode 100644 index 0000000000..4d47ee7d79 --- /dev/null +++ b/time/tst-strftime4-time64.c @@ -0,0 +1 @@ +#include "tst-strftime4.c" diff --git a/time/tst-strftime4.c b/time/tst-strftime4.c new file mode 100644 index 0000000000..659716d0fa --- /dev/null +++ b/time/tst-strftime4.c @@ -0,0 +1,52 @@ +/* Test strftime and strptime after 2038-01-19 03:14:07 UTC (bug 30053). + Copyright (C) 2023 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 + . */ + +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + TEST_VERIFY_EXIT (setenv ("TZ", "UTC0", 1) == 0); + tzset (); + if (sizeof (time_t) > 4) + { + time_t wrap = (time_t) 2147483648LL; + char buf[80]; + struct tm *tm = gmtime (&wrap); + TEST_VERIFY_EXIT (tm != NULL); + TEST_VERIFY_EXIT (strftime (buf, sizeof buf, "%s", tm) > 0); + puts (buf); + TEST_VERIFY (strcmp (buf, "2147483648") == 0); + + struct tm tm2; + char *p = strptime (buf, "%s", &tm2); + TEST_VERIFY_EXIT (p != NULL && *p == '\0'); + time_t t = mktime (&tm2); + printf ("%lld\n", (long long) t); + TEST_VERIFY (t == wrap); + } + else + FAIL_UNSUPPORTED ("32-bit time_t"); + return 0; +} + +#include diff --git a/time/tzfile.c b/time/tzfile.c index dd75848ba9..8bba4e5b8d 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -32,7 +32,7 @@ int __use_tzfile; static dev_t tzfile_dev; static ino64_t tzfile_ino; -static time_t tzfile_mtime; +static __time64_t tzfile_mtime; struct ttinfo { @@ -61,6 +61,10 @@ static size_t num_leaps; static struct leap *leaps; static char *tzspec; +/* Used to restore the daylight variable during time conversion, as if + tzset had been called. */ +static int daylight_saved; + #include #include @@ -438,36 +442,35 @@ __tzfile_read (const char *file, size_t extra, char **extrap) if (__tzname[1] == NULL) __tzname[1] = __tzname[0]; + daylight_saved = 0; if (num_transitions == 0) /* Use the first rule (which should also be the only one). */ rule_stdoff = rule_dstoff = types[0].offset; else { - int stdoff_set = 0, dstoff_set = 0; - rule_stdoff = rule_dstoff = 0; + rule_stdoff = 0; + + /* Search for the last rule with a standard time offset. This + will be used for the global timezone variable. */ i = num_transitions - 1; do - { - if (!stdoff_set && !types[type_idxs[i]].isdst) - { - stdoff_set = 1; - rule_stdoff = types[type_idxs[i]].offset; - } - else if (!dstoff_set && types[type_idxs[i]].isdst) - { - dstoff_set = 1; - rule_dstoff = types[type_idxs[i]].offset; - } - if (stdoff_set && dstoff_set) + if (!types[type_idxs[i]].isdst) + { + rule_stdoff = types[type_idxs[i]].offset; break; - } + } + else + daylight_saved = 1; while (i-- > 0); - if (!dstoff_set) - rule_dstoff = rule_stdoff; + /* Keep searching to see if there is a DST rule. This + information will be used to set the global daylight + variable. */ + while (i-- > 0 && !daylight_saved) + daylight_saved = types[type_idxs[i]].isdst; } - __daylight = rule_stdoff != rule_dstoff; + __daylight = daylight_saved; __timezone = -rule_stdoff; done: @@ -731,7 +734,7 @@ __tzfile_compute (__time64_t timer, int use_localtime, } struct ttinfo *info = &types[i]; - __daylight = rule_stdoff != rule_dstoff; + __daylight = daylight_saved; __timezone = -rule_stdoff; if (__tzname[0] == NULL) diff --git a/timezone/Makefile b/timezone/Makefile index a789c22d26..5002de39ad 100644 --- a/timezone/Makefile +++ b/timezone/Makefile @@ -23,7 +23,7 @@ subdir := timezone include ../Makeconfig others := zdump zic -tests := test-tz tst-timezone tst-tzset tst-bz28707 +tests := test-tz tst-timezone tst-tzset tst-bz28707 tst-bz29951 generated-dirs += testdata @@ -86,11 +86,13 @@ $(objpfx)tst-timezone.out: $(addprefix $(testdata)/, \ Europe/London) $(objpfx)tst-tzset.out: $(addprefix $(testdata)/XT, 1 2 3 4) $(objpfx)tst-bz28707.out: $(testdata)/XT5 +$(objpfx)tst-bz29951.out: $(testdata)/XT6 test-tz-ENV = TZDIR=$(testdata) tst-timezone-ENV = TZDIR=$(testdata) tst-tzset-ENV = TZDIR=$(testdata) tst-bz28707-ENV = TZDIR=$(testdata) +tst-bz29951-ENV = TZDIR=$(testdata) # Note this must come second in the deps list for $(built-program-cmd) to work. zic-deps = $(objpfx)zic $(leapseconds) yearistype diff --git a/timezone/testdata/XT6 b/timezone/testdata/XT6 new file mode 100644 index 0000000000..07b393bb7d Binary files /dev/null and b/timezone/testdata/XT6 differ diff --git a/timezone/tst-bz29951.c b/timezone/tst-bz29951.c new file mode 100644 index 0000000000..abd334683b --- /dev/null +++ b/timezone/tst-bz29951.c @@ -0,0 +1,68 @@ +/* Check that daylight is set if the last DST transition did not change offset. + Copyright (C) 2023 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 + . */ + +#include +#include +#include +#include + +/* Set the specified time zone with error checking. */ +static void +set_timezone (const char *name) +{ + TEST_VERIFY (setenv ("TZ", name, 1) == 0); + errno = 0; + tzset (); + TEST_COMPARE (errno, 0); +} + +static int +do_test (void) +{ + /* Test zone based on tz-2022g version of Africa/Tripoli. The last + DST transition coincided with a change in the standard time + offset, effectively making it a no-op. + + Africa/Tripoli Thu Oct 24 23:59:59 2013 UT + = Fri Oct 25 01:59:59 2013 CEST isdst=1 gmtoff=7200 + Africa/Tripoli Fri Oct 25 00:00:00 2013 UT + = Fri Oct 25 02:00:00 2013 EET isdst=0 gmtoff=7200 + */ + set_timezone ("XT6"); + TEST_VERIFY (daylight != 0); + TEST_COMPARE (timezone, -7200); + + /* Check that localtime re-initializes the two variables. */ + daylight = timezone = 17; + time_t t = 844034401; + struct tm *tm = localtime (&t); + TEST_VERIFY (daylight != 0); + TEST_COMPARE (timezone, -7200); + TEST_COMPARE (tm->tm_year, 96); + TEST_COMPARE (tm->tm_mon, 8); + TEST_COMPARE (tm->tm_mday, 29); + TEST_COMPARE (tm->tm_hour, 23); + TEST_COMPARE (tm->tm_min, 0); + TEST_COMPARE (tm->tm_sec, 1); + TEST_COMPARE (tm->tm_gmtoff, 3600); + TEST_COMPARE (tm->tm_isdst, 0); + + return 0; +} + +#include 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 + . */ + +#ifndef _BITS_WCHAR2_DECL_H +#define _BITS_WCHAR2_DECL_H 1 + +#ifndef _WCHAR_H +# error "Never include directly; use 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 +/* Declare all functions from bits/wchar2-decl.h first. */ +# include #endif -#include +/* 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 #endif +#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function +/* Now include the function definitions and redirects too. */ +# include +#endif + __END_DECLS #endif /* wchar.h */