commit 0b03996304f86d6dba8f0d4b7048b9bb7186f17d Author: Siddhesh Poyarekar Date: Tue Aug 3 21:10:10 2021 +0530 ldconfig: avoid leak on empty paths in config file Reviewed-by: Arjun Shankar (cherry picked from commit b0234d79e7d82475d1666f25326ec045c045b3ed) diff --git a/elf/ldconfig.c b/elf/ldconfig.c index 1037e8d0cf..b8893637f8 100644 --- a/elf/ldconfig.c +++ b/elf/ldconfig.c @@ -503,7 +503,11 @@ add_dir_1 (const char *line, const char *from_file, int from_line) entry->path[--i] = '\0'; if (i == 0) - return; + { + free (entry->path); + free (entry); + return; + } char *path = entry->path; if (opt_chroot != NULL) commit 3a48da47a91ccc6f5de260574809e7a44551b876 Author: Siddhesh Poyarekar Date: Tue Aug 3 21:10:20 2021 +0530 gconv_parseconfdir: Fix memory leak The allocated `conf` would leak if we have to skip over the file due to the underlying filesystem not supporting dt_type. Reviewed-by: Arjun Shankar (cherry picked from commit 5f9b78fe35d08739b6da1e5b356786d41116c108) diff --git a/iconv/gconv_parseconfdir.h b/iconv/gconv_parseconfdir.h index a4153e54c6..2f062689ec 100644 --- a/iconv/gconv_parseconfdir.h +++ b/iconv/gconv_parseconfdir.h @@ -153,12 +153,11 @@ gconv_parseconfdir (const char *dir, size_t dir_len) struct stat64 st; if (asprintf (&conf, "%s/%s", buf, ent->d_name) < 0) continue; - if (ent->d_type == DT_UNKNOWN - && (lstat64 (conf, &st) == -1 - || !S_ISREG (st.st_mode))) - continue; - found |= read_conf_file (conf, dir, dir_len); + if (ent->d_type != DT_UNKNOWN + || (lstat64 (conf, &st) != -1 && S_ISREG (st.st_mode))) + found |= read_conf_file (conf, dir, dir_len); + free (conf); } } commit a5bd2e10e0c25b80286dc36068e22a4cb4893af0 Author: Siddhesh Poyarekar Date: Tue Aug 3 21:11:03 2021 +0530 gaiconf_init: Avoid double-free in label and precedence lists labellist and precedencelist could get freed a second time if there are allocation failures, so set them to NULL to avoid a double-free. Reviewed-by: Arjun Shankar (cherry picked from commit 77a34079d8f3d63b61543bf3af93043f8674e4c4) diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index 838a68f022..43dfc6739e 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -2008,6 +2008,7 @@ gaiconf_init (void) l = l->next; } free_prefixlist (labellist); + labellist = NULL; /* Sort the entries so that the most specific ones are at the beginning. */ @@ -2046,6 +2047,7 @@ gaiconf_init (void) l = l->next; } free_prefixlist (precedencelist); + precedencelist = NULL; /* Sort the entries so that the most specific ones are at the beginning. */ commit 7ff4da3dc26de351a5abe7c2905038cbe55c8041 Author: Siddhesh Poyarekar Date: Tue Aug 3 21:10:53 2021 +0530 copy_and_spawn_sgid: Avoid double calls to close() If close() on infd and outfd succeeded, reset the fd numbers so that we don't attempt to close them again. Reviewed-by: Arjun Shankar (cherry picked from commit 45caed9d67a00af917d8b5b88d4b5eb1225b7aef) diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c index 27bfd19c93..0bacf6dbc2 100644 --- a/support/support_capture_subprocess.c +++ b/support/support_capture_subprocess.c @@ -170,6 +170,7 @@ copy_and_spawn_sgid (char *child_id, gid_t gid) support_subprogram because we only want the program exit status, not the contents. */ ret = 0; + infd = outfd = -1; char * const args[] = {execname, child_id, NULL}; commit 9995d0588f4f9adc68419224d2b3698e2ca4f77e Author: Siddhesh Poyarekar Date: Tue Aug 3 21:10:29 2021 +0530 iconv_charmap: Close output file when done Reviewed-by: Arjun Shankar (cherry picked from commit 1e0e6d656db9dfa12ef7eb67976385d3deb0d4ff) diff --git a/iconv/iconv_charmap.c b/iconv/iconv_charmap.c index e2d53fee3c..a8b6b56124 100644 --- a/iconv/iconv_charmap.c +++ b/iconv/iconv_charmap.c @@ -234,6 +234,8 @@ charmap_conversion (const char *from_code, struct charmap_t *from_charmap, while (++remaining < argc); /* All done. */ + if (output != stdout) + fclose (output); free_table (cvtbl); return status; } commit 31902ae639d6a50e768a85f1cd2a17e56b8463c2 Author: Florian Weimer Date: Fri Aug 6 09:51:38 2021 +0200 Linux: Fix fcntl, ioctl, prctl redirects for _TIME_BITS=64 (bug 28182) __REDIRECT and __THROW are not compatible with C++ due to the ordering of the __asm__ alias and the throw specifier. __REDIRECT_NTH has to be used instead. Fixes commit 8a40aff86ba5f64a3a84883e539cb67b ("io: Add time64 alias for fcntl"), commit 82c395d91ea4f69120d453aeec398e30 ("misc: Add time64 alias for ioctl"), commit b39ffab860cd743a82c91946619f1b8158 ("Linux: Add time64 alias for prctl"). Reviewed-by: Carlos O'Donell (cherry picked from commit c87fcacc50505d550f1bb038382bcc7ea73a5926) diff --git a/NEWS b/NEWS index 3c610744c9..89e20cf062 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,13 @@ See the end for copying conditions. Please send GNU C library bug reports via using `glibc' in the "product" field. + +Version 2.34.1 + +The following bugs are resolved with this release: + + [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl + Version 2.34 diff --git a/io/fcntl.h b/io/fcntl.h index 8917a73b42..1c96f98f4d 100644 --- a/io/fcntl.h +++ b/io/fcntl.h @@ -187,10 +187,10 @@ extern int fcntl64 (int __fd, int __cmd, ...); # endif #else /* __USE_TIME_BITS64 */ # ifdef __REDIRECT -extern int __REDIRECT (fcntl, (int __fd, int __request, ...), - __fcntl_time64) __THROW; -extern int __REDIRECT (fcntl64, (int __fd, int __request, ...), - __fcntl_time64) __THROW; +extern int __REDIRECT_NTH (fcntl, (int __fd, int __request, ...), + __fcntl_time64); +extern int __REDIRECT_NTH (fcntl64, (int __fd, int __request, ...), + __fcntl_time64); # else extern int __fcntl_time64 (int __fd, int __request, ...) __THROW; # define fcntl64 __fcntl_time64 diff --git a/misc/sys/ioctl.h b/misc/sys/ioctl.h index 6884d9925f..9945c1e918 100644 --- a/misc/sys/ioctl.h +++ b/misc/sys/ioctl.h @@ -42,8 +42,8 @@ __BEGIN_DECLS extern int ioctl (int __fd, unsigned long int __request, ...) __THROW; #else # ifdef __REDIRECT -extern int __REDIRECT (ioctl, (int __fd, unsigned long int __request, ...), - __ioctl_time64) __THROW; +extern int __REDIRECT_NTH (ioctl, (int __fd, unsigned long int __request, ...), + __ioctl_time64); # else extern int __ioctl_time64 (int __fd, unsigned long int __request, ...) __THROW; # define ioctl __ioctl_time64 diff --git a/sysdeps/unix/sysv/linux/sys/prctl.h b/sysdeps/unix/sysv/linux/sys/prctl.h index db88938b3a..f0e0d2f27f 100644 --- a/sysdeps/unix/sysv/linux/sys/prctl.h +++ b/sysdeps/unix/sysv/linux/sys/prctl.h @@ -42,7 +42,7 @@ __BEGIN_DECLS extern int prctl (int __option, ...) __THROW; #else # ifdef __REDIRECT -extern int __REDIRECT (prctl, (int __option, ...), __prctl_time64) __THROW; +extern int __REDIRECT_NTH (prctl, (int __option, ...), __prctl_time64); # else extern int __prctl_time64 (int __option,d ...) __THROW; # define ioctl __prctl_time64 commit 79474303223c5665bec75ffbdb2a86ee04a2514b Author: Nikita Popov Date: Mon Aug 9 20:17:34 2021 +0530 librt: fix NULL pointer dereference (bug 28213) Helper thread frees copied attribute on NOTIFY_REMOVED message received from the OS kernel. Unfortunately, it fails to check whether copied attribute actually exists (data.attr != NULL). This worked earlier because free() checks passed pointer before actually attempting to release corresponding memory. But __pthread_attr_destroy assumes pointer is not NULL. So passing NULL pointer to __pthread_attr_destroy will result in segmentation fault. This scenario is possible if notification->sigev_notify_attributes == NULL (which means default thread attributes should be used). Signed-off-by: Nikita Popov Reviewed-by: Siddhesh Poyarekar (cherry picked from commit b805aebd42364fe696e417808a700fdb9800c9e8) diff --git a/sysdeps/unix/sysv/linux/mq_notify.c b/sysdeps/unix/sysv/linux/mq_notify.c index 9799dcdaa4..eccae2e4c6 100644 --- a/sysdeps/unix/sysv/linux/mq_notify.c +++ b/sysdeps/unix/sysv/linux/mq_notify.c @@ -131,7 +131,7 @@ helper_thread (void *arg) to wait until it is done with it. */ (void) __pthread_barrier_wait (¬ify_barrier); } - else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED) + else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED && data.attr != NULL) { /* The only state we keep is the copy of the thread attributes. */ __pthread_attr_destroy (data.attr); commit 7c987a5ccb31df80456d53a094e47f81310f549b Author: Nikita Popov Date: Thu Aug 12 16:09:50 2021 +0530 librt: add test (bug 28213) This test implements following logic: 1) Create POSIX message queue. Register a notification with mq_notify (using NULL attributes). Then immediately unregister the notification with mq_notify. Helper thread in a vulnerable version of glibc should cause NULL pointer dereference after these steps. 2) Once again, register the same notification. Try to send a dummy message. Test is considered successfulif the dummy message is successfully received by the callback function. Signed-off-by: Nikita Popov Reviewed-by: Siddhesh Poyarekar (cherry picked from commit 4cc79c217744743077bf7a0ec5e0a4318f1e6641) diff --git a/rt/Makefile b/rt/Makefile index 113cea03a5..910e775995 100644 --- a/rt/Makefile +++ b/rt/Makefile @@ -74,6 +74,7 @@ tests := tst-shm tst-timer tst-timer2 \ tst-aio7 tst-aio8 tst-aio9 tst-aio10 \ tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4 \ tst-mqueue5 tst-mqueue6 tst-mqueue7 tst-mqueue8 tst-mqueue9 \ + tst-bz28213 \ tst-timer3 tst-timer4 tst-timer5 \ tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \ tst-shm-cancel \ diff --git a/rt/tst-bz28213.c b/rt/tst-bz28213.c new file mode 100644 index 0000000000..0c096b5a0a --- /dev/null +++ b/rt/tst-bz28213.c @@ -0,0 +1,101 @@ +/* Bug 28213: test for NULL pointer dereference in mq_notify. + Copyright (C) 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 +#include +#include + +static mqd_t m = -1; +static const char msg[] = "hello"; + +static void +check_bz28213_cb (union sigval sv) +{ + char buf[sizeof (msg)]; + + (void) sv; + + TEST_VERIFY_EXIT ((size_t) mq_receive (m, buf, sizeof (buf), NULL) + == sizeof (buf)); + TEST_VERIFY_EXIT (memcmp (buf, msg, sizeof (buf)) == 0); + + exit (0); +} + +static void +check_bz28213 (void) +{ + struct sigevent sev; + + memset (&sev, '\0', sizeof (sev)); + sev.sigev_notify = SIGEV_THREAD; + sev.sigev_notify_function = check_bz28213_cb; + + /* Step 1: Register & unregister notifier. + Helper thread should receive NOTIFY_REMOVED notification. + In a vulnerable version of glibc, NULL pointer dereference follows. */ + TEST_VERIFY_EXIT (mq_notify (m, &sev) == 0); + TEST_VERIFY_EXIT (mq_notify (m, NULL) == 0); + + /* Step 2: Once again, register notification. + Try to send one message. + Test is considered successful, if the callback does exit (0). */ + TEST_VERIFY_EXIT (mq_notify (m, &sev) == 0); + TEST_VERIFY_EXIT (mq_send (m, msg, sizeof (msg), 1) == 0); + + /* Wait... */ + pause (); +} + +static int +do_test (void) +{ + static const char m_name[] = "/bz28213_queue"; + struct mq_attr m_attr; + + memset (&m_attr, '\0', sizeof (m_attr)); + m_attr.mq_maxmsg = 1; + m_attr.mq_msgsize = sizeof (msg); + + m = mq_open (m_name, + O_RDWR | O_CREAT | O_EXCL, + 0600, + &m_attr); + + if (m < 0) + { + if (errno == ENOSYS) + FAIL_UNSUPPORTED ("POSIX message queues are not implemented\n"); + FAIL_EXIT1 ("Failed to create POSIX message queue: %m\n"); + } + + TEST_VERIFY_EXIT (mq_unlink (m_name) == 0); + + check_bz28213 (); + + return 0; +} + +#include commit 9acab0bba6a5a57323b1f94bf95b21618a9e5aa4 Author: Arjun Shankar Date: Fri Aug 20 16:24:05 2021 +0200 elf: Fix missing colon in LD_SHOW_AUXV output [BZ #28253] This commit adds a missing colon in the AT_MINSIGSTKSZ entry in the _dl_show_auxv function. (cherry picked from commit 82fbcd7118d760492e2ecc9fa291e358b9ba0361) diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c index d47bef1340..2c684c2db2 100644 --- a/elf/dl-sysdep.c +++ b/elf/dl-sysdep.c @@ -317,7 +317,7 @@ _dl_show_auxv (void) [AT_SYSINFO_EHDR - 2] = { "SYSINFO_EHDR: 0x", hex }, [AT_RANDOM - 2] = { "RANDOM: 0x", hex }, [AT_HWCAP2 - 2] = { "HWCAP2: 0x", hex }, - [AT_MINSIGSTKSZ - 2] = { "MINSIGSTKSZ ", dec }, + [AT_MINSIGSTKSZ - 2] = { "MINSIGSTKSZ: ", dec }, [AT_L1I_CACHESIZE - 2] = { "L1I_CACHESIZE: ", dec }, [AT_L1I_CACHEGEOMETRY - 2] = { "L1I_CACHEGEOMETRY: 0x", hex }, [AT_L1D_CACHESIZE - 2] = { "L1D_CACHESIZE: ", dec }, commit f2413f2710d5d5cc884b413b83fcf8198e3717fa Author: H.J. Lu Date: Sat Aug 28 06:10:38 2021 -0700 x86-64: Use testl to check __x86_string_control Use testl, instead of andl, to check __x86_string_control to avoid updating __x86_string_control. Reviewed-by: Carlos O'Donell (cherry picked from commit 3c8b9879cab6d41787bc5b14c1748f62fd6d0e5f) diff --git a/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S b/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S index 9f02624375..abde8438d4 100644 --- a/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S +++ b/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S @@ -325,7 +325,7 @@ L(movsb): /* Avoid slow backward REP MOVSB. */ jb L(more_8x_vec_backward) # if AVOID_SHORT_DISTANCE_REP_MOVSB - andl $X86_STRING_CONTROL_AVOID_SHORT_DISTANCE_REP_MOVSB, __x86_string_control(%rip) + testl $X86_STRING_CONTROL_AVOID_SHORT_DISTANCE_REP_MOVSB, __x86_string_control(%rip) jz 3f movq %rdi, %rcx subq %rsi, %rcx @@ -333,7 +333,7 @@ L(movsb): # endif 1: # if AVOID_SHORT_DISTANCE_REP_MOVSB - andl $X86_STRING_CONTROL_AVOID_SHORT_DISTANCE_REP_MOVSB, __x86_string_control(%rip) + testl $X86_STRING_CONTROL_AVOID_SHORT_DISTANCE_REP_MOVSB, __x86_string_control(%rip) jz 3f movq %rsi, %rcx subq %rdi, %rcx commit 52d0119743180164d1664b6773ac5d873f224608 Author: Jiaxun Yang Date: Tue Sep 7 13:31:42 2021 +0800 MIPS: Setup errno for {f,l,}xstat {f,l,}xstat stub for MIPS is using INTERNAL_SYSCALL to do xstat syscall for glibc ver, However it leaves errno untouched and thus giving bad errno output. Setup errno properly when syscall returns non-zero. Signed-off-by: Jiaxun Yang Reviewed-by: Adhemerval Zanella (cherry picked from commit 66016ec8aeefd40e016d7040d966484c764b0e9c) diff --git a/sysdeps/unix/sysv/linux/mips/fxstat.c b/sysdeps/unix/sysv/linux/mips/fxstat.c index 11511d30b3..4a6016ff12 100644 --- a/sysdeps/unix/sysv/linux/mips/fxstat.c +++ b/sysdeps/unix/sysv/linux/mips/fxstat.c @@ -35,7 +35,9 @@ __fxstat (int vers, int fd, struct stat *buf) { struct kernel_stat kbuf; int r = INTERNAL_SYSCALL_CALL (fstat, fd, &kbuf); - return r ?: __xstat_conv (vers, &kbuf, buf); + if (r == 0) + return __xstat_conv (vers, &kbuf, buf); + return INLINE_SYSCALL_ERROR_RETURN_VALUE (-r); } } } diff --git a/sysdeps/unix/sysv/linux/mips/lxstat.c b/sysdeps/unix/sysv/linux/mips/lxstat.c index 871fb6c6c5..54f990a250 100644 --- a/sysdeps/unix/sysv/linux/mips/lxstat.c +++ b/sysdeps/unix/sysv/linux/mips/lxstat.c @@ -35,7 +35,9 @@ __lxstat (int vers, const char *name, struct stat *buf) { struct kernel_stat kbuf; int r = INTERNAL_SYSCALL_CALL (lstat, name, &kbuf); - return r ?: __xstat_conv (vers, &kbuf, buf); + if (r == 0) + return __xstat_conv (vers, &kbuf, buf); + return INLINE_SYSCALL_ERROR_RETURN_VALUE (-r); } } } diff --git a/sysdeps/unix/sysv/linux/mips/xstat.c b/sysdeps/unix/sysv/linux/mips/xstat.c index 9d810b6f65..86f4dc31a8 100644 --- a/sysdeps/unix/sysv/linux/mips/xstat.c +++ b/sysdeps/unix/sysv/linux/mips/xstat.c @@ -35,7 +35,9 @@ __xstat (int vers, const char *name, struct stat *buf) { struct kernel_stat kbuf; int r = INTERNAL_SYSCALL_CALL (stat, name, &kbuf); - return r ?: __xstat_conv (vers, &kbuf, buf); + if (r == 0) + return __xstat_conv (vers, &kbuf, buf); + return INLINE_SYSCALL_ERROR_RETURN_VALUE (-r); } } } commit addc9d62d61eea790a35328cbfce53333a07bd3e Author: Florian Weimer Date: Mon Aug 30 13:43:56 2021 +0200 support: Add support_wait_for_thread_exit (cherry picked from commit 032d74eaf6179100048a5bf0ce942e97dc8b9a60) diff --git a/support/Makefile b/support/Makefile index a462781718..ef2b1a980a 100644 --- a/support/Makefile +++ b/support/Makefile @@ -82,9 +82,10 @@ libsupport-routines = \ support_test_compare_blob \ support_test_compare_failure \ support_test_compare_string \ - support_write_file_string \ support_test_main \ support_test_verify_impl \ + support_wait_for_thread_exit \ + support_write_file_string \ temp_file \ timespec \ timespec-time64 \ diff --git a/support/support.h b/support/support.h index 834dba9097..a5978b939a 100644 --- a/support/support.h +++ b/support/support.h @@ -174,6 +174,10 @@ timer_t support_create_timer (uint64_t sec, long int nsec, bool repeat, /* Disable the timer TIMER. */ void support_delete_timer (timer_t timer); +/* Wait until all threads except the current thread have exited (as + far as the kernel is concerned). */ +void support_wait_for_thread_exit (void); + struct support_stack { void *stack; diff --git a/support/support_wait_for_thread_exit.c b/support/support_wait_for_thread_exit.c new file mode 100644 index 0000000000..658a813810 --- /dev/null +++ b/support/support_wait_for_thread_exit.c @@ -0,0 +1,72 @@ +/* Wait until all threads except the current thread has exited. + Copyright (C) 2021 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 + +void +support_wait_for_thread_exit (void) +{ +#ifdef __linux__ + DIR *proc_self_task = opendir ("/proc/self/task"); + TEST_VERIFY_EXIT (proc_self_task != NULL); + + while (true) + { + errno = 0; + struct dirent *e = readdir (proc_self_task); + if (e == NULL && errno != 0) + FAIL_EXIT1 ("readdir: %m"); + if (e == NULL) + { + /* Only the main thread remains. Testing may continue. */ + closedir (proc_self_task); + return; + } + + if (strcmp (e->d_name, ".") == 0 || strcmp (e->d_name, "..") == 0) + continue; + + int task_tid = atoi (e->d_name); + if (task_tid <= 0) + FAIL_EXIT1 ("Invalid /proc/self/task entry: %s", e->d_name); + + if (task_tid == gettid ()) + /* The current thread. Keep scanning for other + threads. */ + continue; + + /* task_tid does not refer to this thread here, i.e., there is + another running thread. */ + + /* Small timeout to give the thread a chance to exit. */ + usleep (50 * 1000); + + /* Start scanning the directory from the start. */ + rewinddir (proc_self_task); + } +#else + /* Use a large timeout because we cannot verify that the thread has + exited. */ + usleep (5 * 1000 * 1000); +#endif +} commit 3abf3bd4edc86fb28c099cc85203cb46a811e0b8 Author: Florian Weimer Date: Mon Sep 13 11:06:08 2021 +0200 nptl: pthread_kill, pthread_cancel should not fail after exit (bug 19193) This closes one remaining race condition related to bug 12889: if the thread already exited on the kernel side, returning ESRCH is not correct because that error is reserved for the thread IDs (pthread_t values) whose lifetime has ended. In case of a kernel-side exit and a valid thread ID, no signal needs to be sent and cancellation does not have an effect, so just return 0. sysdeps/pthread/tst-kill4.c triggers undefined behavior and is removed with this commit. Reviewed-by: Adhemerval Zanella (cherry picked from commit 8af8456004edbab71f8903a60a3cae442cf6fe69) diff --git a/NEWS b/NEWS index 89e20cf062..8e9e56d2c2 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ Version 2.34.1 The following bugs are resolved with this release: + [19193] nptl: pthread_kill, pthread_cancel should not fail after exit [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index cc25ff21f3..9bac6e3b76 100644 --- a/nptl/pthread_cancel.c +++ b/nptl/pthread_cancel.c @@ -62,10 +62,11 @@ __pthread_cancel (pthread_t th) { volatile struct pthread *pd = (volatile struct pthread *) th; - /* Make sure the descriptor is valid. */ - if (INVALID_TD_P (pd)) - /* Not a valid thread handle. */ - return ESRCH; + if (pd->tid == 0) + /* The thread has already exited on the kernel side. Its outcome + (regular exit, other cancelation) has already been + determined. */ + return 0; static int init_sigcancel = 0; if (atomic_load_relaxed (&init_sigcancel) == 0) diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c index f79a2b26fc..5d4c86f920 100644 --- a/nptl/pthread_kill.c +++ b/nptl/pthread_kill.c @@ -46,7 +46,12 @@ __pthread_kill_internal (pthread_t threadid, int signo) ? INTERNAL_SYSCALL_ERRNO (val) : 0); } else - val = ESRCH; + /* The kernel reports that the thread has exited. POSIX specifies + the ESRCH error only for the case when the lifetime of a thread + ID has ended, but calling pthread_kill on such a thread ID is + undefined in glibc. Therefore, do not treat kernel thread exit + as an error. */ + val = 0; return val; } diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 42f9fc5072..dedfa0d290 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -89,7 +89,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-join8 tst-join9 tst-join10 tst-join11 tst-join12 tst-join13 \ tst-join14 tst-join15 \ tst-key1 tst-key2 tst-key3 tst-key4 \ - tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \ + tst-kill1 tst-kill2 tst-kill3 tst-kill5 tst-kill6 \ tst-locale1 tst-locale2 \ tst-memstream \ tst-mutex-errorcheck tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 \ @@ -118,6 +118,9 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-unload \ tst-unwind-thread \ tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \ + tst-pthread_cancel-exited \ + tst-pthread_kill-exited \ + # tests tests-time64 := \ tst-abstime-time64 \ diff --git a/sysdeps/pthread/tst-kill4.c b/sysdeps/pthread/tst-kill4.c deleted file mode 100644 index 9563939792..0000000000 --- a/sysdeps/pthread/tst-kill4.c +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (C) 2003-2021 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 2003. - - 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 void * -tf (void *a) -{ - return NULL; -} - - -int -do_test (void) -{ - pthread_attr_t at; - if (pthread_attr_init (&at) != 0) - { - puts ("attr_create failed"); - exit (1); - } - - /* Limit thread stack size, because if it is too large, pthread_join - will free it immediately rather than put it into stack cache. */ - if (pthread_attr_setstacksize (&at, 2 * 1024 * 1024) != 0) - { - puts ("setstacksize failed"); - exit (1); - } - - pthread_t th; - if (pthread_create (&th, &at, tf, NULL) != 0) - { - puts ("create failed"); - exit (1); - } - - pthread_attr_destroy (&at); - - if (pthread_join (th, NULL) != 0) - { - puts ("join failed"); - exit (1); - } - - /* The following only works because we assume here something about - the implementation. Namely, that the memory allocated for the - thread descriptor is not going away, that the TID field is - cleared and therefore the signal is sent to process 0, and that - we can savely assume there is no other process with this ID at - that time. */ - int e = pthread_kill (th, 0); - if (e == 0) - { - puts ("pthread_kill succeeded"); - exit (1); - } - if (e != ESRCH) - { - puts ("pthread_kill didn't return ESRCH"); - exit (1); - } - - return 0; -} - - -#define TEST_FUNCTION do_test () -#include "../test-skeleton.c" diff --git a/sysdeps/pthread/tst-pthread_cancel-exited.c b/sysdeps/pthread/tst-pthread_cancel-exited.c new file mode 100644 index 0000000000..811c9bee07 --- /dev/null +++ b/sysdeps/pthread/tst-pthread_cancel-exited.c @@ -0,0 +1,45 @@ +/* Test that pthread_kill succeeds for an exited thread. + Copyright (C) 2021 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 + . */ + +/* This test verifies that pthread_kill returns 0 (and not ESRCH) for + a thread that has exited on the kernel side. */ + +#include +#include +#include + +static void * +noop_thread (void *closure) +{ + return NULL; +} + +static int +do_test (void) +{ + pthread_t thr = xpthread_create (NULL, noop_thread, NULL); + + support_wait_for_thread_exit (); + + xpthread_cancel (thr); + xpthread_join (thr); + + return 0; +} + +#include diff --git a/sysdeps/pthread/tst-pthread_kill-exited.c b/sysdeps/pthread/tst-pthread_kill-exited.c new file mode 100644 index 0000000000..7575fb6d58 --- /dev/null +++ b/sysdeps/pthread/tst-pthread_kill-exited.c @@ -0,0 +1,46 @@ +/* Test that pthread_kill succeeds for an exited thread. + Copyright (C) 2021 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 + . */ + +/* This test verifies that pthread_kill returns 0 (and not ESRCH) for + a thread that has exited on the kernel side. */ + +#include +#include +#include +#include + +static void * +noop_thread (void *closure) +{ + return NULL; +} + +static int +do_test (void) +{ + pthread_t thr = xpthread_create (NULL, noop_thread, NULL); + + support_wait_for_thread_exit (); + + xpthread_kill (thr, SIGUSR1); + xpthread_join (thr); + + return 0; +} + +#include commit a8ac8c4725ddb1119764126a8674a04c9dd5aea8 Author: Florian Weimer Date: Mon Sep 13 11:06:08 2021 +0200 nptl: Fix race between pthread_kill and thread exit (bug 12889) A new thread exit lock and flag are introduced. They are used to detect that the thread is about to exit or has exited in __pthread_kill_internal, and the signal is not sent in this case. The test sysdeps/pthread/tst-pthread_cancel-select-loop.c is derived from a downstream test originally written by Marek Polacek. Reviewed-by: Adhemerval Zanella (cherry picked from commit 526c3cf11ee9367344b6b15d669e4c3cb461a2be) diff --git a/NEWS b/NEWS index 8e9e56d2c2..00fae31e92 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ Version 2.34.1 The following bugs are resolved with this release: + [12889] nptl: Fix race between pthread_kill and thread exit [19193] nptl: pthread_kill, pthread_cancel should not fail after exit [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index cfe37a3443..50065bc9bd 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Default alignment of stack. */ #ifndef STACK_ALIGN @@ -127,6 +128,8 @@ get_cached_stack (size_t *sizep, void **memp) /* No pending event. */ result->nextevent = NULL; + result->exiting = false; + __libc_lock_init (result->exit_lock); result->tls_state = (struct tls_internal_t) { 0 }; /* Clear the DTV. */ diff --git a/nptl/descr.h b/nptl/descr.h index c85778d449..4de84138fb 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -396,6 +396,12 @@ struct pthread PTHREAD_CANCEL_ASYNCHRONOUS). */ unsigned char canceltype; + /* Used in __pthread_kill_internal to detected a thread that has + exited or is about to exit. exit_lock must only be acquired + after blocking signals. */ + bool exiting; + int exit_lock; /* A low-level lock (for use with __libc_lock_init etc). */ + /* Used on strsignal. */ struct tls_internal_t tls_state; diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index d8ec299cb1..33b426fc68 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -485,6 +486,19 @@ start_thread (void *arg) /* This was the last thread. */ exit (0); + /* This prevents sending a signal from this thread to itself during + its final stages. This must come after the exit call above + because atexit handlers must not run with signals blocked. */ + __libc_signal_block_all (NULL); + + /* Tell __pthread_kill_internal that this thread is about to exit. + If there is a __pthread_kill_internal in progress, this delays + the thread exit until the signal has been queued by the kernel + (so that the TID used to send it remains valid). */ + __libc_lock_lock (pd->exit_lock); + pd->exiting = true; + __libc_lock_unlock (pd->exit_lock); + #ifndef __ASSUME_SET_ROBUST_LIST /* If this thread has any robust mutexes locked, handle them now. */ # if __PTHREAD_MUTEX_HAVE_PREV diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c index 5d4c86f920..fb7862eff7 100644 --- a/nptl/pthread_kill.c +++ b/nptl/pthread_kill.c @@ -16,6 +16,7 @@ License along with the GNU C Library; if not, see . */ +#include #include #include #include @@ -23,37 +24,51 @@ int __pthread_kill_internal (pthread_t threadid, int signo) { - pid_t tid; struct pthread *pd = (struct pthread *) threadid; - if (pd == THREAD_SELF) - /* It is a special case to handle raise() implementation after a vfork - call (which does not update the PD tid field). */ - tid = INLINE_SYSCALL_CALL (gettid); - else - /* Force load of pd->tid into local variable or register. Otherwise - if a thread exits between ESRCH test and tgkill, we might return - EINVAL, because pd->tid would be cleared by the kernel. */ - tid = atomic_forced_read (pd->tid); - - int val; - if (__glibc_likely (tid > 0)) { - pid_t pid = __getpid (); - - val = INTERNAL_SYSCALL_CALL (tgkill, pid, tid, signo); - val = (INTERNAL_SYSCALL_ERROR_P (val) - ? INTERNAL_SYSCALL_ERRNO (val) : 0); + /* Use the actual TID from the kernel, so that it refers to the + current thread even if called after vfork. There is no + signal blocking in this case, so that the signal is delivered + immediately, before __pthread_kill_internal returns: a signal + sent to the thread itself needs to be delivered + synchronously. (It is unclear if Linux guarantees the + delivery of all pending signals after unblocking in the code + below. POSIX only guarantees delivery of a single signal, + which may not be the right one.) */ + pid_t tid = INTERNAL_SYSCALL_CALL (gettid); + int ret = INTERNAL_SYSCALL_CALL (kill, tid, signo); + return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0; } + + /* Block all signals, as required by pd->exit_lock. */ + sigset_t old_mask; + __libc_signal_block_all (&old_mask); + __libc_lock_lock (pd->exit_lock); + + int ret; + if (pd->exiting) + /* The thread is about to exit (or has exited). Sending the + signal is either not observable (the target thread has already + blocked signals at this point), or it will fail, or it might be + delivered to a new, unrelated thread that has reused the TID. + So do not actually send the signal. Do not report an error + because the threadid argument is still valid (the thread ID + lifetime has not ended), and ESRCH (for example) would be + misleading. */ + ret = 0; else - /* The kernel reports that the thread has exited. POSIX specifies - the ESRCH error only for the case when the lifetime of a thread - ID has ended, but calling pthread_kill on such a thread ID is - undefined in glibc. Therefore, do not treat kernel thread exit - as an error. */ - val = 0; + { + /* Using tgkill is a safety measure. pd->exit_lock ensures that + the target thread cannot exit. */ + ret = INTERNAL_SYSCALL_CALL (tgkill, __getpid (), pd->tid, signo); + ret = INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0; + } + + __libc_lock_unlock (pd->exit_lock); + __libc_signal_restore_set (&old_mask); - return val; + return ret; } int diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index dedfa0d290..48dba717a1 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -119,7 +119,9 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-unwind-thread \ tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \ tst-pthread_cancel-exited \ + tst-pthread_cancel-select-loop \ tst-pthread_kill-exited \ + tst-pthread_kill-exiting \ # tests tests-time64 := \ diff --git a/sysdeps/pthread/tst-pthread_cancel-select-loop.c b/sysdeps/pthread/tst-pthread_cancel-select-loop.c new file mode 100644 index 0000000000..a62087589c --- /dev/null +++ b/sysdeps/pthread/tst-pthread_cancel-select-loop.c @@ -0,0 +1,87 @@ +/* Test that pthread_cancel succeeds during thread exit. + Copyright (C) 2021 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 + . */ + +/* This test tries to trigger an internal race condition in + pthread_cancel, where the cancellation signal is sent after the + thread has begun the cancellation process. This can result in a + spurious ESRCH error. For the original bug 12889, the window is + quite small, so the bug was not reproduced in every run. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Set to true by timeout_thread_function when the test should + terminate. */ +static bool timeout; + +static void * +timeout_thread_function (void *unused) +{ + usleep (5 * 1000 * 1000); + __atomic_store_n (&timeout, true, __ATOMIC_RELAXED); + return NULL; +} + +/* Used for blocking the select function below. */ +static int pipe_fds[2]; + +static void * +canceled_thread_function (void *unused) +{ + while (true) + { + fd_set rfs; + fd_set wfs; + fd_set efs; + FD_ZERO (&rfs); + FD_ZERO (&wfs); + FD_ZERO (&efs); + FD_SET (pipe_fds[0], &rfs); + + /* If the cancellation request is recognized early, the thread + begins exiting while the cancellation signal arrives. */ + select (FD_SETSIZE, &rfs, &wfs, &efs, NULL); + } + return NULL; +} + +static int +do_test (void) +{ + xpipe (pipe_fds); + pthread_t thr_timeout = xpthread_create (NULL, timeout_thread_function, NULL); + + while (!__atomic_load_n (&timeout, __ATOMIC_RELAXED)) + { + pthread_t thr = xpthread_create (NULL, canceled_thread_function, NULL); + xpthread_cancel (thr); + TEST_VERIFY (xpthread_join (thr) == PTHREAD_CANCELED); + } + + xpthread_join (thr_timeout); + xclose (pipe_fds[0]); + xclose (pipe_fds[1]); + return 0; +} + +#include diff --git a/sysdeps/pthread/tst-pthread_kill-exiting.c b/sysdeps/pthread/tst-pthread_kill-exiting.c new file mode 100644 index 0000000000..f803e94f11 --- /dev/null +++ b/sysdeps/pthread/tst-pthread_kill-exiting.c @@ -0,0 +1,123 @@ +/* Test that pthread_kill succeeds during thread exit. + Copyright (C) 2021 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 + . */ + +/* This test verifies that pthread_kill for a thread that is exiting + succeeds (with or without actually delivering the signal). */ + +#include +#include +#include +#include +#include +#include + +/* Set to true by timeout_thread_function when the test should + terminate. */ +static bool timeout; + +static void * +timeout_thread_function (void *unused) +{ + usleep (1000 * 1000); + __atomic_store_n (&timeout, true, __ATOMIC_RELAXED); + return NULL; +} + +/* Used to synchronize the sending threads with the target thread and + main thread. */ +static pthread_barrier_t barrier_1; +static pthread_barrier_t barrier_2; + +/* The target thread to which signals are to be sent. */ +static pthread_t target_thread; + +/* Set by the main thread to true after timeout has been set to + true. */ +static bool exiting; + +static void * +sender_thread_function (void *unused) +{ + while (true) + { + /* Wait until target_thread has been initialized. The target + thread and main thread participate in this barrier. */ + xpthread_barrier_wait (&barrier_1); + + if (exiting) + break; + + xpthread_kill (target_thread, SIGUSR1); + + /* Communicate that the signal has been sent. The main thread + participates in this barrier. */ + xpthread_barrier_wait (&barrier_2); + } + return NULL; +} + +static void * +target_thread_function (void *unused) +{ + target_thread = pthread_self (); + xpthread_barrier_wait (&barrier_1); + return NULL; +} + +static int +do_test (void) +{ + xsignal (SIGUSR1, SIG_IGN); + + pthread_t thr_timeout = xpthread_create (NULL, timeout_thread_function, NULL); + + pthread_t threads[4]; + xpthread_barrier_init (&barrier_1, NULL, array_length (threads) + 2); + xpthread_barrier_init (&barrier_2, NULL, array_length (threads) + 1); + + for (int i = 0; i < array_length (threads); ++i) + threads[i] = xpthread_create (NULL, sender_thread_function, NULL); + + while (!__atomic_load_n (&timeout, __ATOMIC_RELAXED)) + { + xpthread_create (NULL, target_thread_function, NULL); + + /* Wait for the target thread to be set up and signal sending to + start. */ + xpthread_barrier_wait (&barrier_1); + + /* Wait for signal sending to complete. */ + xpthread_barrier_wait (&barrier_2); + + xpthread_join (target_thread); + } + + exiting = true; + + /* Signal the sending threads to exit. */ + xpthread_create (NULL, target_thread_function, NULL); + xpthread_barrier_wait (&barrier_1); + + for (int i = 0; i < array_length (threads); ++i) + xpthread_join (threads[i]); + xpthread_join (thr_timeout); + + return 0; +} + +#include commit 3fc51f35b4f32e1bb99d85c1578e930e725ff929 Author: Siddhesh Poyarekar Date: Mon Sep 13 20:48:35 2021 +0530 iconvconfig: Fix behaviour with --prefix [BZ #28199] The consolidation of configuration parsing broke behaviour with --prefix, where the prefix bled into the modules cache. Accept a prefix which, when non-NULL, is prepended to the path when looking for configuration files but only the original directory is added to the modules cache. This has no effect on the codegen of gconv_conf since it passes NULL. Reported-by: Patrick McCarty Reported-by: Michael Hudson-Doyle Reviewed-by: Andreas Schwab (cherry picked from commit 43cea6d5652b6b9e61ac6ecc69419c909b504f47) diff --git a/iconv/gconv_conf.c b/iconv/gconv_conf.c index 62bee28769..cc391d8f93 100644 --- a/iconv/gconv_conf.c +++ b/iconv/gconv_conf.c @@ -478,7 +478,7 @@ __gconv_read_conf (void) __gconv_get_path (); for (cnt = 0; __gconv_path_elem[cnt].name != NULL; ++cnt) - gconv_parseconfdir (__gconv_path_elem[cnt].name, + gconv_parseconfdir (NULL, __gconv_path_elem[cnt].name, __gconv_path_elem[cnt].len); #endif diff --git a/iconv/gconv_parseconfdir.h b/iconv/gconv_parseconfdir.h index 2f062689ec..a586268abc 100644 --- a/iconv/gconv_parseconfdir.h +++ b/iconv/gconv_parseconfdir.h @@ -39,7 +39,6 @@ /* Name of the file containing the module information in the directories along the path. */ static const char gconv_conf_filename[] = "gconv-modules"; -static const char gconv_conf_dirname[] = "gconv-modules.d"; static void add_alias (char *); static void add_module (char *, const char *, size_t, int); @@ -110,19 +109,28 @@ read_conf_file (const char *filename, const char *directory, size_t dir_len) return true; } +/* Prefix DIR (with length DIR_LEN) with PREFIX if the latter is non-NULL and + parse configuration in it. */ + static __always_inline bool -gconv_parseconfdir (const char *dir, size_t dir_len) +gconv_parseconfdir (const char *prefix, const char *dir, size_t dir_len) { - /* No slash needs to be inserted between dir and gconv_conf_filename; - dir already ends in a slash. */ - char *buf = malloc (dir_len + sizeof (gconv_conf_dirname)); + /* No slash needs to be inserted between dir and gconv_conf_filename; dir + already ends in a slash. The additional 2 is to accommodate the ".d" + when looking for configuration files in gconv-modules.d. */ + size_t buflen = dir_len + sizeof (gconv_conf_filename) + 2; + char *buf = malloc (buflen + (prefix != NULL ? strlen (prefix) : 0)); + char *cp = buf; bool found = false; if (buf == NULL) return false; - char *cp = mempcpy (mempcpy (buf, dir, dir_len), gconv_conf_filename, - sizeof (gconv_conf_filename)); + if (prefix != NULL) + cp = stpcpy (cp, prefix); + + cp = mempcpy (mempcpy (cp, dir, dir_len), gconv_conf_filename, + sizeof (gconv_conf_filename)); /* Read the gconv-modules configuration file first. */ found = read_conf_file (buf, dir, dir_len); diff --git a/iconv/iconvconfig.c b/iconv/iconvconfig.c index 783b2bbdbb..273a71f673 100644 --- a/iconv/iconvconfig.c +++ b/iconv/iconvconfig.c @@ -653,13 +653,21 @@ add_module (char *rp, const char *directory, static int handle_dir (const char *dir) { + char *newp = NULL; size_t dirlen = strlen (dir); bool found = false; - char *fulldir = xasprintf ("%s%s%s", dir[0] == '/' ? prefix : "", - dir, dir[dirlen - 1] != '/' ? "/" : ""); + /* End directory path with a '/' if it doesn't already. */ + if (dir[dirlen - 1] != '/') + { + newp = xmalloc (dirlen + 2); + memcpy (newp, dir, dirlen); + newp[dirlen++] = '/'; + newp[dirlen] = '\0'; + dir = newp; + } - found = gconv_parseconfdir (fulldir, strlen (fulldir)); + found = gconv_parseconfdir (dir[0] == '/' ? prefix : NULL, dir, dirlen); if (!found) { @@ -671,7 +679,7 @@ handle_dir (const char *dir) "configuration files with names ending in .conf."); } - free (fulldir); + free (newp); return found ? 0 : 1; } commit ae925404a10bf0ea63d6e8d41e3821f68b4d776c Author: Aurelien Jarno Date: Fri Sep 3 00:28:14 2021 +0200 Fix failing nss/tst-nss-files-hosts-long with local resolver When a local resolver like unbound is listening on the IPv4 loopback address 127.0.0.1, the nss/tst-nss-files-hosts-long test fails. This is due to: - the default resolver in the absence of resolv.conf being 127.0.0.1 - the default DNS NSS database configuration in the absence of nsswitch.conf being 'hosts: dns [!UNAVAIL=return] file' This causes the requests for 'test4' and 'test6' to first be sent to the local resolver, which responds with NXDOMAIN in the likely case those records do no exist. In turn that causes the access to /etc/hosts to be skipped, which is the purpose of that test. Fix that by providing a simple nsswitch.conf file forcing access to /etc/hosts for that test. I have tested that the only changed result in the testsuite is that test. (cherry picked from commit 2738480a4b0866723fb8c633f36bdd34a8767581) diff --git a/nss/tst-nss-files-hosts-long.root/etc/nsswitch.conf b/nss/tst-nss-files-hosts-long.root/etc/nsswitch.conf new file mode 100644 index 0000000000..5b0c6a4199 --- /dev/null +++ b/nss/tst-nss-files-hosts-long.root/etc/nsswitch.conf @@ -0,0 +1 @@ +hosts: files commit 007d699d0e0d0957eead78ad252ad592656284de Author: Joseph Myers Date: Tue Sep 7 13:08:38 2021 +0000 Use Linux 5.14 in build-many-glibcs.py This patch makes build-many-glibcs.py use Linux 5.14. Tested with build-many-glibcs.py (host-libraries, compilers and glibcs builds). (cherry picked from commit 4e04a47208e1712fcf202a6d9831f0900d575225) diff --git a/scripts/build-many-glibcs.py b/scripts/build-many-glibcs.py index 5a77af90a6..86537fa800 100755 --- a/scripts/build-many-glibcs.py +++ b/scripts/build-many-glibcs.py @@ -782,7 +782,7 @@ class Context(object): 'gcc': 'vcs-11', 'glibc': 'vcs-mainline', 'gmp': '6.2.1', - 'linux': '5.13', + 'linux': '5.14', 'mpc': '1.2.1', 'mpfr': '4.1.0', 'mig': 'vcs-mainline', commit 005bafcf5b8a85d4c82831401f052747e160a7e8 Author: Joseph Myers Date: Wed Sep 8 12:42:06 2021 +0000 Update syscall lists for Linux 5.14 Linux 5.14 has two new syscalls, memfd_secret (on some architectures only) and quotactl_fd. Update syscall-names.list and regenerate the arch-syscall.h headers with build-many-glibcs.py update-syscalls. Tested with build-many-glibcs.py. (cherry picked from commit 89dc0372a9055e7ef86fe19be6201fa0b16b2f0e) diff --git a/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h b/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h index e9eb707d0a..bedab1abba 100644 --- a/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h @@ -126,6 +126,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 @@ -187,6 +188,7 @@ #define __NR_pwritev 70 #define __NR_pwritev2 287 #define __NR_quotactl 60 +#define __NR_quotactl_fd 443 #define __NR_read 63 #define __NR_readahead 213 #define __NR_readlinkat 78 diff --git a/sysdeps/unix/sysv/linux/alpha/arch-syscall.h b/sysdeps/unix/sysv/linux/alpha/arch-syscall.h index bd6b7d4003..91354ed9e2 100644 --- a/sysdeps/unix/sysv/linux/alpha/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/alpha/arch-syscall.h @@ -337,6 +337,7 @@ #define __NR_pwritev2 521 #define __NR_query_module 347 #define __NR_quotactl 148 +#define __NR_quotactl_fd 553 #define __NR_read 3 #define __NR_readahead 379 #define __NR_readlink 58 diff --git a/sysdeps/unix/sysv/linux/arc/arch-syscall.h b/sysdeps/unix/sysv/linux/arc/arch-syscall.h index 10650549c1..ff5c7eb36d 100644 --- a/sysdeps/unix/sysv/linux/arc/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/arc/arch-syscall.h @@ -190,6 +190,7 @@ #define __NR_pwritev 70 #define __NR_pwritev2 287 #define __NR_quotactl 60 +#define __NR_quotactl_fd 443 #define __NR_read 63 #define __NR_readahead 213 #define __NR_readlinkat 78 diff --git a/sysdeps/unix/sysv/linux/arm/arch-syscall.h b/sysdeps/unix/sysv/linux/arm/arch-syscall.h index 85c9b236ce..5772333cee 100644 --- a/sysdeps/unix/sysv/linux/arm/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/arm/arch-syscall.h @@ -244,6 +244,7 @@ #define __NR_pwritev 362 #define __NR_pwritev2 393 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 225 #define __NR_readlink 85 diff --git a/sysdeps/unix/sysv/linux/csky/arch-syscall.h b/sysdeps/unix/sysv/linux/csky/arch-syscall.h index 24b0d1f94e..4af6d6202f 100644 --- a/sysdeps/unix/sysv/linux/csky/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/csky/arch-syscall.h @@ -199,6 +199,7 @@ #define __NR_pwritev 70 #define __NR_pwritev2 287 #define __NR_quotactl 60 +#define __NR_quotactl_fd 443 #define __NR_read 63 #define __NR_readahead 213 #define __NR_readlinkat 78 diff --git a/sysdeps/unix/sysv/linux/hppa/arch-syscall.h b/sysdeps/unix/sysv/linux/hppa/arch-syscall.h index feb70abc3e..b07fc8549d 100644 --- a/sysdeps/unix/sysv/linux/hppa/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/hppa/arch-syscall.h @@ -231,6 +231,7 @@ #define __NR_pwritev 316 #define __NR_pwritev2 348 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 207 #define __NR_readlink 85 diff --git a/sysdeps/unix/sysv/linux/i386/arch-syscall.h b/sysdeps/unix/sysv/linux/i386/arch-syscall.h index 3b1894a79b..6e4264698b 100644 --- a/sysdeps/unix/sysv/linux/i386/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/i386/arch-syscall.h @@ -183,6 +183,7 @@ #define __NR_mbind 274 #define __NR_membarrier 375 #define __NR_memfd_create 356 +#define __NR_memfd_secret 447 #define __NR_migrate_pages 294 #define __NR_mincore 218 #define __NR_mkdir 39 @@ -266,6 +267,7 @@ #define __NR_pwritev2 379 #define __NR_query_module 167 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 225 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/ia64/arch-syscall.h b/sysdeps/unix/sysv/linux/ia64/arch-syscall.h index fb388a5fa4..1ca706d721 100644 --- a/sysdeps/unix/sysv/linux/ia64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/ia64/arch-syscall.h @@ -218,6 +218,7 @@ #define __NR_pwritev 1320 #define __NR_pwritev2 1349 #define __NR_quotactl 1137 +#define __NR_quotactl_fd 1467 #define __NR_read 1026 #define __NR_readahead 1216 #define __NR_readlink 1092 diff --git a/sysdeps/unix/sysv/linux/m68k/arch-syscall.h b/sysdeps/unix/sysv/linux/m68k/arch-syscall.h index 7bc8c4af92..2f10f71f90 100644 --- a/sysdeps/unix/sysv/linux/m68k/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/m68k/arch-syscall.h @@ -254,6 +254,7 @@ #define __NR_pwritev2 378 #define __NR_query_module 167 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 240 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h b/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h index cf560d3af4..0607a4dfa6 100644 --- a/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h @@ -266,6 +266,7 @@ #define __NR_pwritev2 394 #define __NR_query_module 167 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 225 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h b/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h index f346460f48..0055eec0b1 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h @@ -251,6 +251,7 @@ #define __NR_pwritev2 4362 #define __NR_query_module 4187 #define __NR_quotactl 4131 +#define __NR_quotactl_fd 4443 #define __NR_read 4003 #define __NR_readahead 4223 #define __NR_readdir 4089 diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h b/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h index 38ed84997a..8e8e9f91cc 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h @@ -232,6 +232,7 @@ #define __NR_pwritev2 6326 #define __NR_query_module 6171 #define __NR_quotactl 6172 +#define __NR_quotactl_fd 6443 #define __NR_read 6000 #define __NR_readahead 6179 #define __NR_readlink 6087 diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h b/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h index e6a10c8421..ebd1545f80 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h @@ -219,6 +219,7 @@ #define __NR_pwritev2 5322 #define __NR_query_module 5171 #define __NR_quotactl 5172 +#define __NR_quotactl_fd 5443 #define __NR_read 5000 #define __NR_readahead 5179 #define __NR_readlink 5087 diff --git a/sysdeps/unix/sysv/linux/nios2/arch-syscall.h b/sysdeps/unix/sysv/linux/nios2/arch-syscall.h index 5314890289..2b530b1f88 100644 --- a/sysdeps/unix/sysv/linux/nios2/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/nios2/arch-syscall.h @@ -198,6 +198,7 @@ #define __NR_pwritev 70 #define __NR_pwritev2 287 #define __NR_quotactl 60 +#define __NR_quotactl_fd 443 #define __NR_read 63 #define __NR_readahead 213 #define __NR_readlinkat 78 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h b/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h index b5b0758532..a32984a9c1 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h @@ -260,6 +260,7 @@ #define __NR_pwritev2 381 #define __NR_query_module 166 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 191 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h b/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h index c77435ca61..b01e464fb9 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h @@ -243,6 +243,7 @@ #define __NR_pwritev2 381 #define __NR_query_module 166 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 191 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h index 70854bb9e3..24d0a2c455 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h @@ -179,6 +179,7 @@ #define __NR_pwritev 70 #define __NR_pwritev2 287 #define __NR_quotactl 60 +#define __NR_quotactl_fd 443 #define __NR_read 63 #define __NR_readahead 213 #define __NR_readlinkat 78 diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h index 83b9f31aba..e526c89ae7 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h @@ -187,6 +187,7 @@ #define __NR_pwritev 70 #define __NR_pwritev2 287 #define __NR_quotactl 60 +#define __NR_quotactl_fd 443 #define __NR_read 63 #define __NR_readahead 213 #define __NR_readlinkat 78 diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h b/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h index b224c4aad4..d4c7b101b6 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h @@ -251,6 +251,7 @@ #define __NR_pwritev2 377 #define __NR_query_module 167 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 222 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h b/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h index 59864af125..bd8c78d705 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h @@ -221,6 +221,7 @@ #define __NR_pwritev2 377 #define __NR_query_module 167 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 222 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/sh/arch-syscall.h b/sysdeps/unix/sysv/linux/sh/arch-syscall.h index 23612c9092..3b6ac3d084 100644 --- a/sysdeps/unix/sysv/linux/sh/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/sh/arch-syscall.h @@ -246,6 +246,7 @@ #define __NR_pwritev 334 #define __NR_pwritev2 382 #define __NR_quotactl 131 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 225 #define __NR_readdir 89 diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h b/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h index 380cddb2d8..35221a707e 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h @@ -252,6 +252,7 @@ #define __NR_pwritev2 359 #define __NR_query_module 184 #define __NR_quotactl 165 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 205 #define __NR_readdir 204 diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h b/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h index 2175eeb6ed..5ba2b20509 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h @@ -231,6 +231,7 @@ #define __NR_pwritev2 359 #define __NR_query_module 184 #define __NR_quotactl 165 +#define __NR_quotactl_fd 443 #define __NR_read 3 #define __NR_readahead 205 #define __NR_readdir 204 diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list index 89c5895b9b..fd98893b0e 100644 --- a/sysdeps/unix/sysv/linux/syscall-names.list +++ b/sysdeps/unix/sysv/linux/syscall-names.list @@ -21,8 +21,8 @@ # This file can list all potential system calls. The names are only # used if the installed kernel headers also provide them. -# The list of system calls is current as of Linux 5.13. -kernel 5.13 +# The list of system calls is current as of Linux 5.14. +kernel 5.14 FAST_atomic_update FAST_cmpxchg @@ -247,6 +247,7 @@ madvise mbind membarrier memfd_create +memfd_secret memory_ordering migrate_pages mincore @@ -452,6 +453,7 @@ pwritev pwritev2 query_module quotactl +quotactl_fd read readahead readdir diff --git a/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h b/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h index 8e028eb62b..26d6ac68a6 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h @@ -154,6 +154,7 @@ #define __NR_mbind 237 #define __NR_membarrier 324 #define __NR_memfd_create 319 +#define __NR_memfd_secret 447 #define __NR_migrate_pages 256 #define __NR_mincore 27 #define __NR_mkdir 83 @@ -224,6 +225,7 @@ #define __NR_pwritev2 328 #define __NR_query_module 178 #define __NR_quotactl 179 +#define __NR_quotactl_fd 443 #define __NR_read 0 #define __NR_readahead 187 #define __NR_readlink 89 diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h b/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h index 004feb53f1..36847783f6 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h +++ b/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h @@ -148,6 +148,7 @@ #define __NR_mbind 1073742061 #define __NR_membarrier 1073742148 #define __NR_memfd_create 1073742143 +#define __NR_memfd_secret 1073742271 #define __NR_migrate_pages 1073742080 #define __NR_mincore 1073741851 #define __NR_mkdir 1073741907 @@ -216,6 +217,7 @@ #define __NR_pwritev 1073742359 #define __NR_pwritev2 1073742371 #define __NR_quotactl 1073742003 +#define __NR_quotactl_fd 1073742267 #define __NR_read 1073741824 #define __NR_readahead 1073742011 #define __NR_readlink 1073741913 commit 114581bf53864aaee562ee237461fc394bc61963 Author: Joseph Myers Date: Tue Sep 14 13:51:58 2021 +0000 Update kernel version to 5.14 in tst-mman-consts.py This patch updates the kernel version in the test tst-mman-consts.py to 5.14. (There are no new MAP_* constants covered by this test in 5.14 that need any other header changes.) Tested with build-many-glibcs.py. (cherry picked from commit 4b39e3498324d1aea802fea8d4b8764f5ddb4fd1) diff --git a/sysdeps/unix/sysv/linux/tst-mman-consts.py b/sysdeps/unix/sysv/linux/tst-mman-consts.py index ee5b13ee12..810433c238 100644 --- a/sysdeps/unix/sysv/linux/tst-mman-consts.py +++ b/sysdeps/unix/sysv/linux/tst-mman-consts.py @@ -33,7 +33,7 @@ def main(): help='C compiler (including options) to use') args = parser.parse_args() linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) - linux_version_glibc = (5, 13) + linux_version_glibc = (5, 14) sys.exit(glibcextract.compare_macro_consts( '#define _GNU_SOURCE 1\n' '#include \n', commit 4ed990e5b97a61f29f929bdeb36c5b2abb547a64 Author: Joseph Myers Date: Tue Sep 14 14:19:24 2021 +0000 Add MADV_POPULATE_READ and MADV_POPULATE_WRITE from Linux 5.14 to bits/mman-linux.h Linux 5.14 adds constants MADV_POPULATE_READ and MADV_POPULATE_WRITE (with the same values on all architectures). Add these to glibc's bits/mman-linux.h. Tested for x86_64. (cherry picked from commit 3561106278cddd2f007bd27fd4c3e90caaf14b43) diff --git a/sysdeps/unix/sysv/linux/bits/mman-linux.h b/sysdeps/unix/sysv/linux/bits/mman-linux.h index 3b1ae418e0..31451c28d9 100644 --- a/sysdeps/unix/sysv/linux/bits/mman-linux.h +++ b/sysdeps/unix/sysv/linux/bits/mman-linux.h @@ -89,6 +89,10 @@ # define MADV_KEEPONFORK 19 /* Undo MADV_WIPEONFORK. */ # define MADV_COLD 20 /* Deactivate these pages. */ # define MADV_PAGEOUT 21 /* Reclaim these pages. */ +# define MADV_POPULATE_READ 22 /* Populate (prefault) page tables + readable. */ +# define MADV_POPULATE_WRITE 23 /* Populate (prefault) page tables + writable. */ # define MADV_HWPOISON 100 /* Poison a page for testing. */ #endif commit 433ec4f14a5753c7689c83c20c9972915c53c204 Author: Aurelien Jarno Date: Fri Sep 10 19:39:35 2021 +0200 posix: Fix attribute access mode on getcwd [BZ #27476] There is a GNU extension that allows to call getcwd(NULL, >0). It is described in the documentation, but also directly in the unistd.h header, just above the declaration. Therefore the attribute access mode added in commit 06febd8c6705 is not correct. Drop it. diff --git a/posix/bits/unistd.h b/posix/bits/unistd.h index f0831386c7..622adeb2b2 100644 --- a/posix/bits/unistd.h +++ b/posix/bits/unistd.h @@ -199,10 +199,9 @@ __NTH (readlinkat (int __fd, const char *__restrict __path, #endif extern char *__getcwd_chk (char *__buf, size_t __size, size_t __buflen) - __THROW __wur __attr_access ((__write_only__, 1, 2)); + __THROW __wur; extern char *__REDIRECT_NTH (__getcwd_alias, - (char *__buf, size_t __size), getcwd) - __wur __attr_access ((__write_only__, 1, 2)); + (char *__buf, size_t __size), getcwd) __wur; extern char *__REDIRECT_NTH (__getcwd_chk_warn, (char *__buf, size_t __size, size_t __buflen), __getcwd_chk) diff --git a/posix/unistd.h b/posix/unistd.h index 3dca65732f..8224c5fbc9 100644 --- a/posix/unistd.h +++ b/posix/unistd.h @@ -528,8 +528,7 @@ extern int fchdir (int __fd) __THROW __wur; an array is allocated with `malloc'; the array is SIZE bytes long, unless SIZE == 0, in which case it is as big as necessary. */ -extern char *getcwd (char *__buf, size_t __size) __THROW __wur - __attr_access ((__write_only__, 1, 2)); +extern char *getcwd (char *__buf, size_t __size) __THROW __wur; #ifdef __USE_GNU /* Return a malloc'd string containing the current directory name. commit 73c7f5a87971de2797f261e1a447f68dce09284b Author: Florian Weimer Date: Mon Sep 20 14:56:08 2021 +0200 nptl: pthread_kill needs to return ESRCH for old programs (bug 19193) The fix for bug 19193 breaks some old applications which appear to use pthread_kill to probe if a thread is still running, something that is not supported by POSIX. (cherry picked from commit 95dba35bf05e4a5d69dfae5e9c9d4df3646a7f93) diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c index fb7862eff7..a44dc8f2d9 100644 --- a/nptl/pthread_kill.c +++ b/nptl/pthread_kill.c @@ -21,8 +21,11 @@ #include #include -int -__pthread_kill_internal (pthread_t threadid, int signo) +/* Sends SIGNO to THREADID. If the thread is about to exit or has + already exited on the kernel side, return NO_TID. Otherwise return + 0 or an error code. */ +static int +__pthread_kill_implementation (pthread_t threadid, int signo, int no_tid) { struct pthread *pd = (struct pthread *) threadid; if (pd == THREAD_SELF) @@ -52,11 +55,8 @@ __pthread_kill_internal (pthread_t threadid, int signo) signal is either not observable (the target thread has already blocked signals at this point), or it will fail, or it might be delivered to a new, unrelated thread that has reused the TID. - So do not actually send the signal. Do not report an error - because the threadid argument is still valid (the thread ID - lifetime has not ended), and ESRCH (for example) would be - misleading. */ - ret = 0; + So do not actually send the signal. */ + ret = no_tid; else { /* Using tgkill is a safety measure. pd->exit_lock ensures that @@ -71,6 +71,15 @@ __pthread_kill_internal (pthread_t threadid, int signo) return ret; } +int +__pthread_kill_internal (pthread_t threadid, int signo) +{ + /* Do not report an error in the no-tid case because the threadid + argument is still valid (the thread ID lifetime has not ended), + and ESRCH (for example) would be misleading. */ + return __pthread_kill_implementation (threadid, signo, 0); +} + int __pthread_kill (pthread_t threadid, int signo) { @@ -81,6 +90,7 @@ __pthread_kill (pthread_t threadid, int signo) return __pthread_kill_internal (threadid, signo); } + /* Some architectures (for instance arm) might pull raise through libgcc, so avoid the symbol version if it ends up being used on ld.so. */ #if !IS_IN(rtld) @@ -88,6 +98,17 @@ libc_hidden_def (__pthread_kill) versioned_symbol (libc, __pthread_kill, pthread_kill, GLIBC_2_34); # if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34) -compat_symbol (libc, __pthread_kill, pthread_kill, GLIBC_2_0); +/* Variant which returns ESRCH in the no-TID case, for backwards + compatibility. */ +int +attribute_compat_text_section +__pthread_kill_esrch (pthread_t threadid, int signo) +{ + if (__is_internal_signal (signo)) + return EINVAL; + + return __pthread_kill_implementation (threadid, signo, ESRCH); +} +compat_symbol (libc, __pthread_kill_esrch, pthread_kill, GLIBC_2_0); # endif #endif diff --git a/sysdeps/pthread/tst-pthread_kill-exited.c b/sysdeps/pthread/tst-pthread_kill-exited.c index 7575fb6d58..a2fddad526 100644 --- a/sysdeps/pthread/tst-pthread_kill-exited.c +++ b/sysdeps/pthread/tst-pthread_kill-exited.c @@ -16,11 +16,15 @@ License along with the GNU C Library; if not, see . */ -/* This test verifies that pthread_kill returns 0 (and not ESRCH) for - a thread that has exited on the kernel side. */ +/* This test verifies that the default pthread_kill returns 0 (and not + ESRCH) for a thread that has exited on the kernel side. */ +#include +#include +#include #include #include +#include #include #include @@ -30,6 +34,12 @@ noop_thread (void *closure) return NULL; } +#if TEST_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34) && PTHREAD_IN_LIBC +extern __typeof (pthread_kill) compat_pthread_kill; +compat_symbol_reference (libpthread, compat_pthread_kill, pthread_kill, + GLIBC_2_0); +#endif + static int do_test (void) { @@ -37,7 +47,14 @@ do_test (void) support_wait_for_thread_exit (); + /* NB: Always uses the default symbol due to separate compilation. */ xpthread_kill (thr, SIGUSR1); + +#if TEST_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34) && PTHREAD_IN_LIBC + /* Old binaries need the non-conforming ESRCH error code. */ + TEST_COMPARE (compat_pthread_kill (thr, SIGUSR1), ESRCH); +#endif + xpthread_join (thr); return 0; commit 8b8a1d0b7375c547ae905917a03743ed6759c5bc Author: Florian Weimer Date: Tue Sep 21 07:12:56 2021 +0200 nptl: Fix type of pthread_mutexattr_getrobust_np, pthread_mutexattr_setrobust_np (bug 28036) Reviewed-by: Carlos O'Donell Tested-by: Carlos O'Donell (cherry picked from commit f3e664563361dc17530113b3205998d1f19dc4d9) diff --git a/NEWS b/NEWS index 00fae31e92..a0c27ecf55 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ The following bugs are resolved with this release: [12889] nptl: Fix race between pthread_kill and thread exit [19193] nptl: pthread_kill, pthread_cancel should not fail after exit + [28036] Incorrect types for pthread_mutexattr_set/getrobust_np [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h index f1b7f2bdc6..43146e91c9 100644 --- a/sysdeps/nptl/pthread.h +++ b/sysdeps/nptl/pthread.h @@ -933,7 +933,7 @@ extern int pthread_mutexattr_getrobust (const pthread_mutexattr_t *__attr, # ifdef __USE_GNU # ifdef __REDIRECT_NTH extern int __REDIRECT_NTH (pthread_mutexattr_getrobust_np, - (pthread_mutex_t *, int *), + (pthread_mutexattr_t *, int *), pthread_mutexattr_getrobust) __nonnull ((1)) __attribute_deprecated_msg__ ("\ pthread_mutexattr_getrobust_np is deprecated, use pthread_mutexattr_getrobust"); @@ -949,7 +949,7 @@ extern int pthread_mutexattr_setrobust (pthread_mutexattr_t *__attr, # ifdef __USE_GNU # ifdef __REDIRECT_NTH extern int __REDIRECT_NTH (pthread_mutexattr_setrobust_np, - (pthread_mutex_t *, int), + (pthread_mutexattr_t *, int), pthread_mutexattr_setrobust) __nonnull ((1)) __attribute_deprecated_msg__ ("\ pthread_mutexattr_setrobust_np is deprecated, use pthread_mutexattr_setrobust"); commit 5ad589d63bc2d9b1fc3d9f32144acaebb85e0803 Author: Adhemerval Zanella Date: Tue Aug 24 16:12:24 2021 -0300 support: Add support_open_dev_null_range It returns a range of file descriptor referring to the '/dev/null' pathname. The function takes care of restarting the open range if a file descriptor is found within the specified range and also increases RLIMIT_NOFILE if required. Checked on x86_64-linux-gnu. (cherry picked from commit e814f4b04ee413a7bb3dfa43e74c8fb4abf58359) diff --git a/support/Makefile b/support/Makefile index ef2b1a980a..2a0731796f 100644 --- a/support/Makefile +++ b/support/Makefile @@ -66,6 +66,7 @@ libsupport-routines = \ support_path_support_time64 \ support_process_state \ support_ptrace \ + support-open-dev-null-range \ support_openpty \ support_paths \ support_quote_blob \ @@ -265,6 +266,7 @@ tests = \ tst-support_capture_subprocess \ tst-support_descriptors \ tst-support_format_dns_packet \ + tst-support-open-dev-null-range \ tst-support-process_state \ tst-support_quote_blob \ tst-support_quote_string \ diff --git a/support/support-open-dev-null-range.c b/support/support-open-dev-null-range.c new file mode 100644 index 0000000000..80d9dba504 --- /dev/null +++ b/support/support-open-dev-null-range.c @@ -0,0 +1,134 @@ +/* Return a range of open file descriptors. + Copyright (C) 2021 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 + +static void +increase_nofile (void) +{ + struct rlimit rl; + if (getrlimit (RLIMIT_NOFILE, &rl) == -1) + FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m"); + + rl.rlim_cur += 128; + + if (setrlimit (RLIMIT_NOFILE, &rl) == 1) + FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m"); +} + +static int +open_dev_null (int flags, mode_t mode) +{ + int fd = open64 ("/dev/null", flags, mode); + if (fd > 0) + return fd; + + if (fd < 0 && errno != EMFILE) + FAIL_EXIT1 ("open64 (\"/dev/null\", 0x%x, 0%o): %m", flags, mode); + + increase_nofile (); + + return xopen ("/dev/null", flags, mode); +} + +struct range +{ + int lowfd; + size_t len; +}; + +struct range_list +{ + size_t total; + size_t used; + struct range *ranges; +}; + +static void +range_init (struct range_list *r) +{ + r->total = 8; + r->used = 0; + r->ranges = xmalloc (r->total * sizeof (struct range)); +} + +static void +range_add (struct range_list *r, int lowfd, size_t len) +{ + if (r->used == r->total) + { + r->total *= 2; + r->ranges = xrealloc (r->ranges, r->total * sizeof (struct range)); + } + r->ranges[r->used].lowfd = lowfd; + r->ranges[r->used].len = len; + r->used++; +} + +static void +range_close (struct range_list *r) +{ + for (size_t i = 0; i < r->used; i++) + { + int minfd = r->ranges[i].lowfd; + int maxfd = r->ranges[i].lowfd + r->ranges[i].len; + for (int fd = minfd; fd < maxfd; fd++) + xclose (fd); + } + free (r->ranges); +} + +int +support_open_dev_null_range (int num, int flags, mode_t mode) +{ + /* We keep track of the ranges that hit an already opened descriptor, so + we close them after we get a working range. */ + struct range_list rl; + range_init (&rl); + + int lowfd = open_dev_null (flags, mode); + int prevfd = lowfd; + while (true) + { + int i = 1; + for (; i < num; i++) + { + int fd = open_dev_null (flags, mode); + if (fd != lowfd + i) + { + range_add (&rl, lowfd, prevfd - lowfd + 1); + + prevfd = lowfd = fd; + break; + } + prevfd = fd; + } + if (i == num) + break; + } + + range_close (&rl); + + return lowfd; +} diff --git a/support/support.h b/support/support.h index a5978b939a..c219e0d9d1 100644 --- a/support/support.h +++ b/support/support.h @@ -197,6 +197,14 @@ struct support_stack support_stack_alloc (size_t size); /* Deallocate the STACK. */ void support_stack_free (struct support_stack *stack); + +/* Create a range of NUM opened '/dev/null' file descriptors using FLAGS and + MODE. The function takes care of restarting the open range if a file + descriptor is found within the specified range and also increases + RLIMIT_NOFILE if required. + The returned value is the lowest file descriptor number. */ +int support_open_dev_null_range (int num, int flags, mode_t mode); + __END_DECLS #endif /* SUPPORT_H */ diff --git a/support/tst-support-open-dev-null-range.c b/support/tst-support-open-dev-null-range.c new file mode 100644 index 0000000000..8e29def1ce --- /dev/null +++ b/support/tst-support-open-dev-null-range.c @@ -0,0 +1,155 @@ +/* Tests for support_open_dev_null_range. + Copyright (C) 2021 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 + +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +#include + +static void +check_path (int fd) +{ + char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd); + char file_path[PATH_MAX]; + ssize_t file_path_length + = readlink (proc_fd_path, file_path, sizeof (file_path)); + free (proc_fd_path); + if (file_path_length < 0) + FAIL_EXIT1 ("readlink (%s, %p, %zu)", proc_fd_path, file_path, + sizeof (file_path)); + file_path[file_path_length] = '\0'; + TEST_COMPARE_STRING (file_path, "/dev/null"); +} + +static int +number_of_opened_files (void) +{ + DIR *fds = opendir ("/proc/self/fd"); + if (fds == NULL) + FAIL_EXIT1 ("opendir (\"/proc/self/fd\"): %m"); + + int r = 0; + while (true) + { + errno = 0; + struct dirent64 *e = readdir64 (fds); + if (e == NULL) + { + if (errno != 0) + FAIL_EXIT1 ("readdir: %m"); + break; + } + + if (e->d_name[0] == '.') + continue; + + char *endptr; + long int fd = strtol (e->d_name, &endptr, 10); + if (*endptr != '\0' || fd < 0 || fd > INT_MAX) + FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s", + e->d_name); + + /* Skip the descriptor which is used to enumerate the + descriptors. */ + if (fd == dirfd (fds)) + continue; + + r = r + 1; + } + + closedir (fds); + + return r; +} + +static int +do_test (void) +{ + const int nfds1 = 8; + int lowfd = support_open_dev_null_range (nfds1, O_RDONLY, 0600); + for (int i = 0; i < nfds1; i++) + { + TEST_VERIFY (fcntl (lowfd + i, F_GETFL) > -1); + check_path (lowfd + i); + } + + /* create some gaps. */ + xclose (lowfd + 1); + xclose (lowfd + 5); + xclose (lowfd + 6); + + const int nfds2 = 16; + int lowfd2 = support_open_dev_null_range (nfds2, O_RDONLY, 0600); + for (int i = 0; i < nfds2; i++) + { + TEST_VERIFY (fcntl (lowfd2 + i, F_GETFL) > -1); + check_path (lowfd2 + i); + } + + /* Decrease the maximum number of files. */ + { + struct rlimit rl; + if (getrlimit (RLIMIT_NOFILE, &rl) == -1) + FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m"); + + rl.rlim_cur = number_of_opened_files (); + + if (setrlimit (RLIMIT_NOFILE, &rl) == 1) + FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m"); + } + + const int nfds3 = 16; + int lowfd3 = support_open_dev_null_range (nfds3, O_RDONLY, 0600); + for (int i = 0; i < nfds3; i++) + { + TEST_VERIFY (fcntl (lowfd3 + i, F_GETFL) > -1); + check_path (lowfd3 + i); + } + + /* create a lot of gaps to trigger the range extension. */ + xclose (lowfd3 + 1); + xclose (lowfd3 + 3); + xclose (lowfd3 + 5); + xclose (lowfd3 + 7); + xclose (lowfd3 + 9); + xclose (lowfd3 + 11); + xclose (lowfd3 + 13); + + const int nfds4 = 16; + int lowfd4 = support_open_dev_null_range (nfds4, O_RDONLY, 0600); + for (int i = 0; i < nfds4; i++) + { + TEST_VERIFY (fcntl (lowfd4 + i, F_GETFL) > -1); + check_path (lowfd4 + i); + } + + return 0; +} + +#include commit 772e33411bc730f832f415f93eb3e7c67e4d5488 Author: Adhemerval Zanella Date: Tue Aug 24 16:15:50 2021 -0300 Use support_open_dev_null_range io/tst-closefrom, misc/tst-close_range, and posix/tst-spawn5 (BZ #28260) It ensures a continuous range of file descriptor and avoid hitting the RLIMIT_NOFILE. Checked on x86_64-linux-gnu. (cherry picked from commit 6b20880b22d1d0fce7e9f506baa6fe2d5c7fcfdc) diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c index d4c187073c..395ec0d894 100644 --- a/io/tst-closefrom.c +++ b/io/tst-closefrom.c @@ -24,31 +24,22 @@ #include #include #include +#include #include #define NFDS 100 -static int -open_multiple_temp_files (void) -{ - /* Check if the temporary file descriptor has no no gaps. */ - int lowfd = xopen ("/dev/null", O_RDONLY, 0600); - for (int i = 1; i <= NFDS; i++) - TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600), lowfd + i); - return lowfd; -} - static int closefrom_test (void) { struct support_descriptors *descrs = support_descriptors_list (); - int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); - const int maximum_fd = lowfd + NFDS; + const int maximum_fd = lowfd + NFDS - 1; const int half_fd = lowfd + NFDS / 2; - const int gap = maximum_fd / 4; + const int gap = lowfd + NFDS / 4; /* Close half of the descriptors and check result. */ closefrom (half_fd); @@ -58,7 +49,7 @@ closefrom_test (void) TEST_COMPARE (fcntl (i, F_GETFL), -1); TEST_COMPARE (errno, EBADF); } - for (int i = 0; i < half_fd; i++) + for (int i = lowfd; i < half_fd; i++) TEST_VERIFY (fcntl (i, F_GETFL) > -1); /* Create some gaps, close up to a threshold, and check result. */ @@ -74,7 +65,7 @@ closefrom_test (void) TEST_COMPARE (fcntl (i, F_GETFL), -1); TEST_COMPARE (errno, EBADF); } - for (int i = 0; i < gap; i++) + for (int i = lowfd; i < gap; i++) TEST_VERIFY (fcntl (i, F_GETFL) > -1); /* Close the remmaining but the last one. */ diff --git a/posix/tst-spawn5.c b/posix/tst-spawn5.c index ac66738004..a95199af6b 100644 --- a/posix/tst-spawn5.c +++ b/posix/tst-spawn5.c @@ -47,17 +47,6 @@ static int initial_argv_count; #define NFDS 100 -static int -open_multiple_temp_files (void) -{ - /* Check if the temporary file descriptor has no no gaps. */ - int lowfd = xopen ("/dev/null", O_RDONLY, 0600); - for (int i = 1; i <= NFDS; i++) - TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600), - lowfd + i); - return lowfd; -} - static int parse_fd (const char *str) { @@ -185,7 +174,7 @@ spawn_closefrom_test (posix_spawn_file_actions_t *fa, int lowfd, int highfd, static void do_test_closefrom (void) { - int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); const int half_fd = lowfd + NFDS / 2; /* Close half of the descriptors and check result. */ diff --git a/sysdeps/unix/sysv/linux/tst-close_range.c b/sysdeps/unix/sysv/linux/tst-close_range.c index dccb6189c5..f5069d1b8a 100644 --- a/sysdeps/unix/sysv/linux/tst-close_range.c +++ b/sysdeps/unix/sysv/linux/tst-close_range.c @@ -36,23 +36,12 @@ #define NFDS 100 -static int -open_multiple_temp_files (void) -{ - /* Check if the temporary file descriptor has no no gaps. */ - int lowfd = xopen ("/dev/null", O_RDONLY, 0600); - for (int i = 1; i <= NFDS; i++) - TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600), - lowfd + i); - return lowfd; -} - static void close_range_test_max_upper_limit (void) { struct support_descriptors *descrs = support_descriptors_list (); - int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); { int r = close_range (lowfd, ~0U, 0); @@ -68,7 +57,7 @@ close_range_test_max_upper_limit (void) static void close_range_test_common (int lowfd, unsigned int flags) { - const int maximum_fd = lowfd + NFDS; + const int maximum_fd = lowfd + NFDS - 1; const int half_fd = lowfd + NFDS / 2; const int gap_1 = maximum_fd - 8; @@ -121,7 +110,7 @@ close_range_test (void) struct support_descriptors *descrs = support_descriptors_list (); /* Check if the temporary file descriptor has no no gaps. */ - int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); close_range_test_common (lowfd, 0); @@ -146,7 +135,7 @@ close_range_test_subprocess (void) struct support_descriptors *descrs = support_descriptors_list (); /* Check if the temporary file descriptor has no no gaps. */ - int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); struct support_stack stack = support_stack_alloc (4096); @@ -184,7 +173,7 @@ close_range_unshare_test (void) struct support_descriptors *descrs1 = support_descriptors_list (); /* Check if the temporary file descriptor has no no gaps. */ - int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); struct support_descriptors *descrs2 = support_descriptors_list (); @@ -200,7 +189,7 @@ close_range_unshare_test (void) support_stack_free (&stack); - for (int i = 0; i < NFDS; i++) + for (int i = lowfd; i < lowfd + NFDS; i++) TEST_VERIFY (fcntl (i, F_GETFL) > -1); support_descriptors_check (descrs2); @@ -226,9 +215,9 @@ static void close_range_cloexec_test (void) { /* Check if the temporary file descriptor has no no gaps. */ - const int lowfd = open_multiple_temp_files (); + int lowfd = support_open_dev_null_range (NFDS, O_RDONLY, 0600); - const int maximum_fd = lowfd + NFDS; + const int maximum_fd = lowfd + NFDS - 1; const int half_fd = lowfd + NFDS / 2; const int gap_1 = maximum_fd - 8; @@ -251,13 +240,13 @@ close_range_cloexec_test (void) /* Create some gaps, close up to a threshold, and check result. */ static int gap_close[] = { 57, 78, 81, 82, 84, 90 }; for (int i = 0; i < array_length (gap_close); i++) - xclose (gap_close[i]); + xclose (lowfd + gap_close[i]); TEST_COMPARE (close_range (half_fd + 1, gap_1, CLOSE_RANGE_CLOEXEC), 0); for (int i = half_fd + 1; i < gap_1; i++) { int flags = fcntl (i, F_GETFD); - if (is_in_array (gap_close, array_length (gap_close), i)) + if (is_in_array (gap_close, array_length (gap_close), i - lowfd)) TEST_COMPARE (flags, -1); else { commit 33adeaa3e2b9143c38884bc5aa65ded222ed274e Author: Florian Weimer Date: Thu Sep 23 09:55:54 2021 +0200 nptl: Avoid setxid deadlock with blocked signals in thread exit [BZ #28361] As part of the fix for bug 12889, signals are blocked during thread exit, so that application code cannot run on the thread that is about to exit. This would cause problems if the application expected signals to be delivered after the signal handler revealed the thread to still exist, despite pthread_kill can no longer be used to send signals to it. However, glibc internally uses the SIGSETXID signal in a way that is incompatible with signal blocking, due to the way the setxid handshake delays thread exit until the setxid operation has completed. With a blocked SIGSETXID, the handshake can never complete, causing a deadlock. As a band-aid, restore the previous handshake protocol by not blocking SIGSETXID during thread exit. The new test sysdeps/pthread/tst-pthread-setuid-loop.c is based on a downstream test by Martin Osvald. Reviewed-by: Carlos O'Donell Tested-by: Carlos O'Donell (cherry picked from commit 2849e2f53311b66853cb5159b64cba2bddbfb854) diff --git a/NEWS b/NEWS index a0c27ecf55..cccbf11ad9 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ The following bugs are resolved with this release: [19193] nptl: pthread_kill, pthread_cancel should not fail after exit [28036] Incorrect types for pthread_mutexattr_set/getrobust_np [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl + [28361] nptl: Avoid setxid deadlock with blocked signals in thread exit Version 2.34 diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index 33b426fc68..bc213f0bc4 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -488,8 +488,16 @@ start_thread (void *arg) /* This prevents sending a signal from this thread to itself during its final stages. This must come after the exit call above - because atexit handlers must not run with signals blocked. */ - __libc_signal_block_all (NULL); + because atexit handlers must not run with signals blocked. + + Do not block SIGSETXID. The setxid handshake below expects the + signal to be delivered. (SIGSETXID cannot run application code, + nor does it use pthread_kill.) Reuse the pd->sigmask space for + computing the signal mask, to save stack space. */ + __sigfillset (&pd->sigmask); + __sigdelset (&pd->sigmask, SIGSETXID); + INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_BLOCK, &pd->sigmask, NULL, + __NSIG_BYTES); /* Tell __pthread_kill_internal that this thread is about to exit. If there is a __pthread_kill_internal in progress, this delays diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 48dba717a1..d4bd2d4e3e 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -118,6 +118,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-unload \ tst-unwind-thread \ tst-pt-vfork1 tst-pt-vfork2 tst-vfork1x tst-vfork2x \ + tst-pthread-setuid-loop \ tst-pthread_cancel-exited \ tst-pthread_cancel-select-loop \ tst-pthread_kill-exited \ diff --git a/sysdeps/pthread/tst-pthread-setuid-loop.c b/sysdeps/pthread/tst-pthread-setuid-loop.c new file mode 100644 index 0000000000..fda2a49b7f --- /dev/null +++ b/sysdeps/pthread/tst-pthread-setuid-loop.c @@ -0,0 +1,61 @@ +/* Test that setuid, pthread_create, thread exit do not deadlock (bug 28361). + Copyright (C) 2021 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 + +/* How many threads to launch during each iteration. */ +enum { threads = 4 }; + +/* How many iterations to perform. This value seems to reproduce + bug 28361 in a bout one in three runs. */ +enum { iterations = 5000 }; + +/* Cache of the real user ID used by setuid_thread. */ +static uid_t uid; + +/* Start routine for the threads. */ +static void * +setuid_thread (void *closure) +{ + TEST_COMPARE (setuid (uid), 0); + return NULL; +} + +static int +do_test (void) +{ + /* The setxid machinery is still invoked even if the UID is + unchanged. (The kernel might reset other credentials as part of + the system call.) */ + uid = getuid (); + + for (int i = 0; i < iterations; ++i) + { + pthread_t thread_ids[threads]; + for (int j = 0; j < threads; ++j) + thread_ids[j] = xpthread_create (NULL, setuid_thread, NULL); + for (int j = 0; j < threads; ++j) + xpthread_join (thread_ids[j]); + } + + return 0; +} + +#include commit 4bf72519987ebc2be4a2058c670379040fae90ea Author: Florian Weimer Date: Fri Oct 1 18:16:41 2021 +0200 support: Add check for TID zero in support_wait_for_thread_exit Some kernel versions (observed with kernel 5.14 and earlier) can list "0" entries in /proc/self/task. This happens when a thread exits while the task list is being constructed. Treat this entry as not present, like the proposed kernel patch does: [PATCH] procfs: Do not list TID 0 in /proc//task Fixes commit 032d74eaf6179100048a5bf0ce942e97dc8b9a60 ("support: Add support_wait_for_thread_exit"). Reviewed-by: Carlos O'Donell Tested-by: Carlos O'Donell (cherry picked from commit 176c88f5214d8107d330971cbbfbbba5186a111f) diff --git a/support/support_wait_for_thread_exit.c b/support/support_wait_for_thread_exit.c index 658a813810..5e3be421a7 100644 --- a/support/support_wait_for_thread_exit.c +++ b/support/support_wait_for_thread_exit.c @@ -43,7 +43,10 @@ support_wait_for_thread_exit (void) return; } - if (strcmp (e->d_name, ".") == 0 || strcmp (e->d_name, "..") == 0) + /* In some kernels, "0" entries denote a thread that has just + exited. */ + if (strcmp (e->d_name, ".") == 0 || strcmp (e->d_name, "..") == 0 + || strcmp (e->d_name, "0") == 0) continue; int task_tid = atoi (e->d_name); commit 40bade26d5bcbda3d21fb598c5063d9df62de966 Author: Florian Weimer Date: Fri Oct 1 18:16:41 2021 +0200 nptl: pthread_kill must send signals to a specific thread [BZ #28407] The choice between the kill vs tgkill system calls is not just about the TID reuse race, but also about whether the signal is sent to the whole process (and any thread in it) or to a specific thread. This was caught by the openposix test suite: LTP: openposix test suite - FAIL: SIGUSR1 is member of new thread pendingset. Fixes commit 526c3cf11ee9367344b6b15d669e4c3cb461a2be ("nptl: Fix race between pthread_kill and thread exit (bug 12889)"). Reviewed-by: Carlos O'Donell Tested-by: Carlos O'Donell (cherry picked from commit eae81d70574e923ce3c59078b8df857ae192efa6) diff --git a/NEWS b/NEWS index cccbf11ad9..b7994015d9 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ The following bugs are resolved with this release: [28036] Incorrect types for pthread_mutexattr_set/getrobust_np [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl [28361] nptl: Avoid setxid deadlock with blocked signals in thread exit + [28407] pthread_kill assumes that kill and tgkill are equivalent Version 2.34 diff --git a/nptl/pthread_kill.c b/nptl/pthread_kill.c index a44dc8f2d9..35bf1f973e 100644 --- a/nptl/pthread_kill.c +++ b/nptl/pthread_kill.c @@ -40,7 +40,7 @@ __pthread_kill_implementation (pthread_t threadid, int signo, int no_tid) below. POSIX only guarantees delivery of a single signal, which may not be the right one.) */ pid_t tid = INTERNAL_SYSCALL_CALL (gettid); - int ret = INTERNAL_SYSCALL_CALL (kill, tid, signo); + int ret = INTERNAL_SYSCALL_CALL (tgkill, __getpid (), tid, signo); return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0; } @@ -59,8 +59,6 @@ __pthread_kill_implementation (pthread_t threadid, int signo, int no_tid) ret = no_tid; else { - /* Using tgkill is a safety measure. pd->exit_lock ensures that - the target thread cannot exit. */ ret = INTERNAL_SYSCALL_CALL (tgkill, __getpid (), pd->tid, signo); ret = INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0; } diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index d4bd2d4e3e..0af9c59b42 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -121,6 +121,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-pthread-setuid-loop \ tst-pthread_cancel-exited \ tst-pthread_cancel-select-loop \ + tst-pthread-raise-blocked-self \ tst-pthread_kill-exited \ tst-pthread_kill-exiting \ # tests diff --git a/sysdeps/pthread/tst-pthread-raise-blocked-self.c b/sysdeps/pthread/tst-pthread-raise-blocked-self.c new file mode 100644 index 0000000000..128e1a6071 --- /dev/null +++ b/sysdeps/pthread/tst-pthread-raise-blocked-self.c @@ -0,0 +1,92 @@ +/* Test that raise sends signal to current thread even if blocked. + Copyright (C) 2021 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 + +/* Used to create a dummy thread ID distinct from all other thread + IDs. */ +static void * +noop (void *ignored) +{ + return NULL; +} + +static volatile pthread_t signal_thread; + +static void +signal_handler (int signo) +{ + signal_thread = pthread_self (); +} + +/* Used to ensure that waiting_thread has launched and can accept + signals. */ +static pthread_barrier_t barrier; + +static void * +waiting_thread (void *ignored) +{ + xpthread_barrier_wait (&barrier); + pause (); + return NULL; +} + +static int +do_test (void) +{ + xsignal (SIGUSR1, signal_handler); + xpthread_barrier_init (&barrier, NULL, 2); + + /* Distinct thread ID value to */ + pthread_t dummy = xpthread_create (NULL, noop, NULL); + signal_thread = dummy; + + pthread_t helper = xpthread_create (NULL, waiting_thread, NULL); + + /* Make sure that the thread is running. */ + xpthread_barrier_wait (&barrier); + + /* Block signals on this thread. */ + sigset_t set; + sigfillset (&set); + xpthread_sigmask (SIG_BLOCK, &set, NULL); + + /* Send the signal to this thread. It must not be delivered. */ + raise (SIGUSR1); + TEST_VERIFY (signal_thread == dummy); + + /* Wait a bit to give a chance for signal delivery (increases + chances of failure with bug 28407). */ + usleep (50 * 1000); + + /* Unblocking should cause synchronous delivery of the signal. */ + xpthread_sigmask (SIG_UNBLOCK, &set, NULL); + TEST_VERIFY (signal_thread == pthread_self ()); + + xpthread_cancel (helper); + xpthread_join (helper); + xpthread_join (dummy); + return 0; +} + +#include commit e870aac8974cda746157a5a3c9f452ccd70da29b Author: Adhemerval Zanella Date: Mon Sep 6 12:22:54 2021 -0300 misc: Add __get_nprocs_sched This is an internal function meant to return the number of avaliable processor where the process can scheduled, different than the __get_nprocs which returns a the system available online CPU. The Linux implementation currently only calls __get_nprocs(), which in tuns calls sched_getaffinity. Reviewed-by: Florian Weimer (cherry picked from commit 11a02b035b464ab6813676adfd19c4a59c36d907) diff --git a/include/sys/sysinfo.h b/include/sys/sysinfo.h index 7388356a19..c490561581 100644 --- a/include/sys/sysinfo.h +++ b/include/sys/sysinfo.h @@ -9,10 +9,15 @@ extern int __get_nprocs_conf (void); libc_hidden_proto (__get_nprocs_conf) -/* Return number of available processors. */ +/* Return number of available processors (not all of them will be + available to the caller process). */ extern int __get_nprocs (void); libc_hidden_proto (__get_nprocs) +/* Return the number of available processors which the process can + be scheduled. */ +extern int __get_nprocs_sched (void) attribute_hidden; + /* Return number of physical pages of memory in the system. */ extern long int __get_phys_pages (void); libc_hidden_proto (__get_phys_pages) diff --git a/malloc/arena.c b/malloc/arena.c index 667484630e..f1f0af8648 100644 --- a/malloc/arena.c +++ b/malloc/arena.c @@ -879,7 +879,7 @@ arena_get2 (size_t size, mstate avoid_arena) narenas_limit = mp_.arena_max; else if (narenas > mp_.arena_test) { - int n = __get_nprocs (); + int n = __get_nprocs_sched (); if (n >= 1) narenas_limit = NARENAS_FROM_NCORES (n); diff --git a/misc/getsysstats.c b/misc/getsysstats.c index 0eedface6d..57d93601e2 100644 --- a/misc/getsysstats.c +++ b/misc/getsysstats.c @@ -45,6 +45,12 @@ weak_alias (__get_nprocs, get_nprocs) link_warning (get_nprocs, "warning: get_nprocs will always return 1") +int +__get_nprocs_sched (void) +{ + return 1; +} + long int __get_phys_pages (void) { diff --git a/sysdeps/mach/getsysstats.c b/sysdeps/mach/getsysstats.c index 1267f39da2..cc8023f979 100644 --- a/sysdeps/mach/getsysstats.c +++ b/sysdeps/mach/getsysstats.c @@ -62,6 +62,12 @@ __get_nprocs (void) libc_hidden_def (__get_nprocs) weak_alias (__get_nprocs, get_nprocs) +int +__get_nprocs_sched (void) +{ + return __get_nprocs (); +} + /* Return the number of physical pages on the system. */ long int __get_phys_pages (void) diff --git a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c index 1391e360b8..120ce1bb75 100644 --- a/sysdeps/unix/sysv/linux/getsysstats.c +++ b/sysdeps/unix/sysv/linux/getsysstats.c @@ -88,6 +88,12 @@ __get_nprocs (void) libc_hidden_def (__get_nprocs) weak_alias (__get_nprocs, get_nprocs) +int +__get_nprocs_sched (void) +{ + return __get_nprocs (); +} + /* On some architectures it is possible to distinguish between configured and active cpus. */ commit cda99af14e82b4bb6abaecd717ebe3b57c0aa534 Author: Adhemerval Zanella Date: Mon Sep 6 12:28:24 2021 -0300 linux: Simplify get_nprocs This patch simplifies the memory allocation code and uses the sched routines instead of reimplement it. This still uses a stack allocation buffer, so it can be used on malloc initialization code. Linux currently supports at maximum of 4096 cpus for most architectures: $ find -iname Kconfig | xargs git grep -A10 -w NR_CPUS | grep -w range arch/alpha/Kconfig- range 2 32 arch/arc/Kconfig- range 2 4096 arch/arm/Kconfig- range 2 16 if DEBUG_KMAP_LOCAL arch/arm/Kconfig- range 2 32 if !DEBUG_KMAP_LOCAL arch/arm64/Kconfig- range 2 4096 arch/csky/Kconfig- range 2 32 arch/hexagon/Kconfig- range 2 6 if SMP arch/ia64/Kconfig- range 2 4096 arch/mips/Kconfig- range 2 256 arch/openrisc/Kconfig- range 2 32 arch/parisc/Kconfig- range 2 32 arch/riscv/Kconfig- range 2 32 arch/s390/Kconfig- range 2 512 arch/sh/Kconfig- range 2 32 arch/sparc/Kconfig- range 2 32 if SPARC32 arch/sparc/Kconfig- range 2 4096 if SPARC64 arch/um/Kconfig- range 1 1 arch/x86/Kconfig-# [NR_CPUS_RANGE_BEGIN ... NR_CPUS_RANGE_END] range. arch/x86/Kconfig- range NR_CPUS_RANGE_BEGIN NR_CPUS_RANGE_END arch/xtensa/Kconfig- range 2 32 With x86 supporting 8192: arch/x86/Kconfig 976 config NR_CPUS_RANGE_END 977 int 978 depends on X86_64 979 default 8192 if SMP && CPUMASK_OFFSTACK 980 default 512 if SMP && !CPUMASK_OFFSTACK 981 default 1 if !SMP So using a maximum of 32k cpu should cover all cases (and I would expect once we start to have many more CPUs that Linux would provide a more straightforward way to query for such information). A test is added to check if sched_getaffinity can successfully return with large buffers. Checked on x86_64-linux-gnu and i686-linux-gnu. Reviewed-by: Florian Weimer (cherry picked from commit 33099d72e41cf8a129b362e9709eb2be9372d844) diff --git a/posix/Makefile b/posix/Makefile index 059efb3cd2..09460a28e8 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -107,7 +107,8 @@ tests := test-errno tstgetopt testfnm runtests runptests \ tst-sysconf-empty-chroot tst-glob_symlinks tst-fexecve \ tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \ bug-regex38 tst-regcomp-truncated tst-spawn-chdir \ - tst-wordexp-nocmd tst-execveat tst-spawn5 + tst-wordexp-nocmd tst-execveat tst-spawn5 \ + tst-sched_getaffinity # Test for the glob symbol version that was replaced in glibc 2.27. ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) diff --git a/posix/tst-sched_getaffinity.c b/posix/tst-sched_getaffinity.c new file mode 100644 index 0000000000..db9d517a96 --- /dev/null +++ b/posix/tst-sched_getaffinity.c @@ -0,0 +1,48 @@ +/* Tests for sched_getaffinity with large buffers. + Copyright (C) 2021 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 + +/* NB: this test may fail on system with more than 32k cpus. */ + +static int +do_test (void) +{ + /* The values are larger than the default cpu_set_t. */ + const int bufsize[] = { 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 1<<16, 1<<17 }; + int cpucount[array_length (bufsize)]; + + for (int i = 0; i < array_length (bufsize); i++) + { + cpu_set_t *cpuset = CPU_ALLOC (bufsize[i]); + TEST_VERIFY (cpuset != NULL); + size_t size = CPU_ALLOC_SIZE (bufsize[i]); + TEST_COMPARE (sched_getaffinity (0, size, cpuset), 0); + cpucount[i] = CPU_COUNT_S (size, cpuset); + CPU_FREE (cpuset); + } + + for (int i = 0; i < array_length (cpucount) - 1; i++) + TEST_COMPARE (cpucount[i], cpucount[i + 1]); + + return 0; +} + +#include diff --git a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c index 120ce1bb75..61d20e7bab 100644 --- a/sysdeps/unix/sysv/linux/getsysstats.c +++ b/sysdeps/unix/sysv/linux/getsysstats.c @@ -29,61 +29,29 @@ #include #include -/* Compute the population count of the entire array. */ -static int -__get_nprocs_count (const unsigned long int *array, size_t length) -{ - int count = 0; - for (size_t i = 0; i < length; ++i) - if (__builtin_add_overflow (count, __builtin_popcountl (array[i]), - &count)) - return INT_MAX; - return count; -} - -/* __get_nprocs with a large buffer. */ -static int -__get_nprocs_large (void) -{ - /* This code cannot use scratch_buffer because it is used during - malloc initialization. */ - size_t pagesize = GLRO (dl_pagesize); - unsigned long int *page = __mmap (0, pagesize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (page == MAP_FAILED) - return 2; - int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, pagesize, page); - int count; - if (r > 0) - count = __get_nprocs_count (page, pagesize / sizeof (unsigned long int)); - else if (r == -EINVAL) - /* One page is still not enough to store the bits. A more-or-less - arbitrary value. This assumes t hat such large systems never - happen in practice. */ - count = GLRO (dl_pagesize) * CHAR_BIT; - else - count = 2; - __munmap (page, GLRO (dl_pagesize)); - return count; -} - int __get_nprocs (void) { - /* Fast path for most systems. The kernel expects a buffer size - that is a multiple of 8. */ - unsigned long int small_buffer[1024 / CHAR_BIT / sizeof (unsigned long int)]; - int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, - sizeof (small_buffer), small_buffer); + enum + { + max_num_cpus = 32768, + cpu_bits_size = CPU_ALLOC_SIZE (32768) + }; + + /* This cannot use malloc because it is used on malloc initialization. */ + __cpu_mask cpu_bits[cpu_bits_size / sizeof (__cpu_mask)]; + int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, cpu_bits_size, + cpu_bits); if (r > 0) - return __get_nprocs_count (small_buffer, r / sizeof (unsigned long int)); + return CPU_COUNT_S (cpu_bits_size, (cpu_set_t*) cpu_bits); else if (r == -EINVAL) - /* The kernel requests a larger buffer to store the data. */ - return __get_nprocs_large (); - else - /* Some other error. 2 is conservative (not a uniprocessor - system, so atomics are needed). */ - return 2; + /* The input buffer is still not enough to store the number of cpus. This + is an arbitrary values assuming such systems should be rare and there + is no offline cpus. */ + return max_num_cpus; + /* Some other error. 2 is conservative (not a uniprocessor system, so + atomics are needed). */ + return 2; } libc_hidden_def (__get_nprocs) weak_alias (__get_nprocs, get_nprocs) commit 822662cf2a4b170ade4c5342f035d68815a03276 Author: Adhemerval Zanella Date: Mon Sep 6 14:19:51 2021 -0300 linux: Revert the use of sched_getaffinity on get_nproc (BZ #28310) The use of sched_getaffinity on get_nproc and sysconf (_SC_NPROCESSORS_ONLN) done in 903bc7dcc2acafc40 (BZ #27645) breaks the top command in common hypervisor configurations and also other monitoring tools. The main issue using sched_getaffinity changed the symbols semantic from system-wide scope of online CPUs to per-process one (which can be changed with kernel cpusets or book parameters in VM). This patch reverts mostly of the 903bc7dcc2acafc40, with the exceptions: * No more cached values and atomic updates, since they are inherent racy. * No /proc/cpuinfo fallback, since /proc/stat is already used and it would require to revert more arch-specific code. * The alloca is replace with a static buffer of 1024 bytes. So the implementation first consult the sysfs, and fallbacks to procfs. Checked on x86_64-linux-gnu. Reviewed-by: Florian Weimer (cherry picked from commit 342298278eabc75baabcaced110a11a02c3d3580) diff --git a/NEWS b/NEWS index b7994015d9..273a85da4d 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ The following bugs are resolved with this release: [19193] nptl: pthread_kill, pthread_cancel should not fail after exit [28036] Incorrect types for pthread_mutexattr_set/getrobust_np [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl + [28310] Do not use affinity mask for sysconf (_SC_NPROCESSORS_CONF) [28361] nptl: Avoid setxid deadlock with blocked signals in thread exit [28407] pthread_kill assumes that kill and tgkill are equivalent diff --git a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c index 61d20e7bab..d70ed95869 100644 --- a/sysdeps/unix/sysv/linux/getsysstats.c +++ b/sysdeps/unix/sysv/linux/getsysstats.c @@ -18,6 +18,8 @@ . */ #include +#include +#include #include #include #include @@ -30,7 +32,7 @@ #include int -__get_nprocs (void) +__get_nprocs_sched (void) { enum { @@ -53,14 +55,141 @@ __get_nprocs (void) atomics are needed). */ return 2; } -libc_hidden_def (__get_nprocs) -weak_alias (__get_nprocs, get_nprocs) + +static char * +next_line (int fd, char *const buffer, char **cp, char **re, + char *const buffer_end) +{ + char *res = *cp; + char *nl = memchr (*cp, '\n', *re - *cp); + if (nl == NULL) + { + if (*cp != buffer) + { + if (*re == buffer_end) + { + memmove (buffer, *cp, *re - *cp); + *re = buffer + (*re - *cp); + *cp = buffer; + + ssize_t n = __read_nocancel (fd, *re, buffer_end - *re); + if (n < 0) + return NULL; + + *re += n; + + nl = memchr (*cp, '\n', *re - *cp); + while (nl == NULL && *re == buffer_end) + { + /* Truncate too long lines. */ + *re = buffer + 3 * (buffer_end - buffer) / 4; + n = __read_nocancel (fd, *re, buffer_end - *re); + if (n < 0) + return NULL; + + nl = memchr (*re, '\n', n); + **re = '\n'; + *re += n; + } + } + else + nl = memchr (*cp, '\n', *re - *cp); + + res = *cp; + } + + if (nl == NULL) + nl = *re - 1; + } + + *cp = nl + 1; + assert (*cp <= *re); + + return res == *re ? NULL : res; +} + int -__get_nprocs_sched (void) +__get_nprocs (void) { - return __get_nprocs (); + enum { buffer_size = 1024 }; + char buffer[buffer_size]; + char *buffer_end = buffer + buffer_size; + char *cp = buffer_end; + char *re = buffer_end; + + const int flags = O_RDONLY | O_CLOEXEC; + /* This file contains comma-separated ranges. */ + int fd = __open_nocancel ("/sys/devices/system/cpu/online", flags); + char *l; + int result = 0; + if (fd != -1) + { + l = next_line (fd, buffer, &cp, &re, buffer_end); + if (l != NULL) + do + { + char *endp; + unsigned long int n = strtoul (l, &endp, 10); + if (l == endp) + { + result = 0; + break; + } + + unsigned long int m = n; + if (*endp == '-') + { + l = endp + 1; + m = strtoul (l, &endp, 10); + if (l == endp) + { + result = 0; + break; + } + } + + result += m - n + 1; + + l = endp; + if (l < re && *l == ',') + ++l; + } + while (l < re && *l != '\n'); + + __close_nocancel_nostatus (fd); + + if (result > 0) + return result; + } + + cp = buffer_end; + re = buffer_end; + + /* Default to an SMP system in case we cannot obtain an accurate + number. */ + result = 2; + + fd = __open_nocancel ("/proc/stat", flags); + if (fd != -1) + { + result = 0; + + while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) + /* The current format of /proc/stat has all the cpu* entries + at the front. We assume here that stays this way. */ + if (strncmp (l, "cpu", 3) != 0) + break; + else if (isdigit (l[3])) + ++result; + + __close_nocancel_nostatus (fd); + } + + return result; } +libc_hidden_def (__get_nprocs) +weak_alias (__get_nprocs, get_nprocs) /* On some architectures it is possible to distinguish between configured commit 80a009119ba2330768120476aaad63767b81d543 Author: Jonathan Wakely Date: Wed May 19 16:48:19 2021 +0100 Suppress -Wcast-qual warnings in bsearch The first cast to (void *) is redundant but should be (const void *) anyway, because that's the type of the lvalue being assigned to. The second cast is necessary and intentionally not const-correct, so tell the compiler not to warn about it. Reviewed-by: Florian Weimer (cherry picked from commit a725ff1de965f4cc4f36a7e8ae795d40ca0350d7) diff --git a/bits/stdlib-bsearch.h b/bits/stdlib-bsearch.h index 4132dc6af0..d688ed2e15 100644 --- a/bits/stdlib-bsearch.h +++ b/bits/stdlib-bsearch.h @@ -29,14 +29,21 @@ bsearch (const void *__key, const void *__base, size_t __nmemb, size_t __size, while (__l < __u) { __idx = (__l + __u) / 2; - __p = (void *) (((const char *) __base) + (__idx * __size)); + __p = (const void *) (((const char *) __base) + (__idx * __size)); __comparison = (*__compar) (__key, __p); if (__comparison < 0) __u = __idx; else if (__comparison > 0) __l = __idx + 1; else +#if __GNUC_PREREQ(4, 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" +#endif return (void *) __p; +#if __GNUC_PREREQ(4, 6) +# pragma GCC diagnostic pop +#endif } return NULL; commit a996d13b8a2e101bedbb1bdaa7ffcfea3b959bb2 Author: Florian Weimer Date: Thu Sep 30 18:44:06 2021 +0200 Add missing braces to bsearch inline implementation [BZ #28400] GCC treats the pragma as a statement, so that the else branch only consists of the pragma, not the return statement. Fixes commit a725ff1de965f4cc4f36a7e8ae795d40ca0350d7 ("Suppress -Wcast-qual warnings in bsearch"). Reviewed-by: H.J. Lu (cherry picked from commit 32b96d0dec0294465d2221a8f049703599d9d8e4) diff --git a/bits/stdlib-bsearch.h b/bits/stdlib-bsearch.h index d688ed2e15..e2fcea6e17 100644 --- a/bits/stdlib-bsearch.h +++ b/bits/stdlib-bsearch.h @@ -36,14 +36,16 @@ bsearch (const void *__key, const void *__base, size_t __nmemb, size_t __size, else if (__comparison > 0) __l = __idx + 1; else + { #if __GNUC_PREREQ(4, 6) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcast-qual" #endif - return (void *) __p; + return (void *) __p; #if __GNUC_PREREQ(4, 6) # pragma GCC diagnostic pop #endif + } } return NULL; commit 558168c78ea1eb8efb33959c1da9d6b5a997fd7b Author: Siddhesh Poyarekar Date: Wed Oct 6 21:48:35 2021 +0530 support: Also return fd when it is 0 The fd validity check in open_dev_null checks if fd > 0, which would lead to a leaked fd if it is == 0. Signed-off-by: Siddhesh Poyarekar Reviewed-by: Adhemerval Zanella (cherry picked from commit 27b6edbb090f736b101f569620d8ad0e7217ddf8) diff --git a/support/support-open-dev-null-range.c b/support/support-open-dev-null-range.c index 80d9dba504..66a8504105 100644 --- a/support/support-open-dev-null-range.c +++ b/support/support-open-dev-null-range.c @@ -40,16 +40,16 @@ increase_nofile (void) static int open_dev_null (int flags, mode_t mode) { - int fd = open64 ("/dev/null", flags, mode); - if (fd > 0) - return fd; + int fd = open64 ("/dev/null", flags, mode); + if (fd >= 0) + return fd; - if (fd < 0 && errno != EMFILE) - FAIL_EXIT1 ("open64 (\"/dev/null\", 0x%x, 0%o): %m", flags, mode); + if (fd < 0 && errno != EMFILE) + FAIL_EXIT1 ("open64 (\"/dev/null\", 0x%x, 0%o): %m", flags, mode); - increase_nofile (); + increase_nofile (); - return xopen ("/dev/null", flags, mode); + return xopen ("/dev/null", flags, mode); } struct range commit cb44a620ef2336449af60694b6696efced161774 Author: Stefan Liebler Date: Tue Oct 5 16:14:10 2021 +0200 S390: Add PCI_MIO and SIE HWCAPs Both new HWCAPs were introduced in these kernel commits: - 7e8403ecaf884f307b627f3c371475913dd29292 "s390: add HWCAP_S390_PCI_MIO to ELF hwcaps" - 7e82523f2583e9813e4109df3656707162541297 "s390/hwcaps: make sie capability regular hwcap" Also note that the kernel commit 511ad531afd4090625def4d9aba1f5227bd44b8e "s390/hwcaps: shorten HWCAP defines" has shortened the prefix of the macros from "HWCAP_S390_" to "HWCAP_". For compatibility reasons, we do not change the prefix in public glibc header file. (cherry picked from commit f2e06656d04a9fcb0603802a4f8ce7aa3a1f055e) diff --git a/sysdeps/s390/dl-procinfo.c b/sysdeps/s390/dl-procinfo.c index c174e27b35..155f0bd99e 100644 --- a/sysdeps/s390/dl-procinfo.c +++ b/sysdeps/s390/dl-procinfo.c @@ -46,13 +46,13 @@ #if !defined PROCINFO_DECL && defined SHARED ._dl_s390_cap_flags #else -PROCINFO_CLASS const char _dl_s390_cap_flags[21][9] +PROCINFO_CLASS const char _dl_s390_cap_flags[23][9] #endif #ifndef PROCINFO_DECL = { "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", "edat", "etf3eh", "highgprs", "te", "vx", "vxd", "vxe", "gs", "vxe2", "vxp", "sort", "dflt", - "vxp2", "nnpa" + "vxp2", "nnpa", "pcimio", "sie" } #endif #if !defined SHARED || defined PROCINFO_DECL diff --git a/sysdeps/s390/dl-procinfo.h b/sysdeps/s390/dl-procinfo.h index 2d9c305808..e4e3e334a5 100644 --- a/sysdeps/s390/dl-procinfo.h +++ b/sysdeps/s390/dl-procinfo.h @@ -21,7 +21,7 @@ #define _DL_PROCINFO_H 1 #include -#define _DL_HWCAP_COUNT 21 +#define _DL_HWCAP_COUNT 23 #define _DL_PLATFORMS_COUNT 10 @@ -63,6 +63,8 @@ enum HWCAP_S390_DFLT = 1 << 18, HWCAP_S390_VXRS_PDE2 = 1 << 19, HWCAP_S390_NNPA = 1 << 20, + HWCAP_S390_PCI_MIO = 1 << 21, + HWCAP_S390_SIE = 1 << 22, }; #define HWCAP_IMPORTANT (HWCAP_S390_ZARCH | HWCAP_S390_LDISP \ diff --git a/sysdeps/unix/sysv/linux/s390/bits/hwcap.h b/sysdeps/unix/sysv/linux/s390/bits/hwcap.h index e9bd3684db..00e73a3e3b 100644 --- a/sysdeps/unix/sysv/linux/s390/bits/hwcap.h +++ b/sysdeps/unix/sysv/linux/s390/bits/hwcap.h @@ -22,6 +22,11 @@ /* * The following must match the kernels asm/elf.h. + * Note: The kernel commit 511ad531afd4090625def4d9aba1f5227bd44b8e + * "s390/hwcaps: shorten HWCAP defines" has shortened the prefix of the macros + * from "HWCAP_S390_" to "HWCAP_". For compatibility reasons, we do not + * change the prefix in public glibc header file. + * * Note that these are *not* the same as the STORE FACILITY LIST bits. */ #define HWCAP_S390_ESAN3 1 @@ -48,3 +53,5 @@ #define HWCAP_S390_DFLT 262144 #define HWCAP_S390_VXRS_PDE2 524288 #define HWCAP_S390_NNPA 1048576 +#define HWCAP_S390_PCI_MIO 2097152 +#define HWCAP_S390_SIE 4194304 commit 79528414dc1578800cbf1fba2fbdb6335f4f39bf Author: H.J. Lu Date: Thu Sep 30 10:29:17 2021 -0700 elf: Replace nsid with args.nsid [BZ #27609] commit ec935dea6332cb22f9881cd1162bad156173f4b0 Author: Florian Weimer Date: Fri Apr 24 22:31:15 2020 +0200 elf: Implement __libc_early_init has @@ -856,6 +876,11 @@ no more namespaces available for dlmopen()")); /* See if an error occurred during loading. */ if (__glibc_unlikely (exception.errstring != NULL)) { + /* Avoid keeping around a dangling reference to the libc.so link + map in case it has been cached in libc_map. */ + if (!args.libc_already_loaded) + GL(dl_ns)[nsid].libc_map = NULL; + do_dlopen calls _dl_open with nsid == __LM_ID_CALLER (-2), which calls dl_open_worker with args.nsid = nsid. dl_open_worker updates args.nsid if it is __LM_ID_CALLER. After dl_open_worker returns, it is wrong to use nsid. Replace nsid with args.nsid after dl_open_worker returns. This fixes BZ #27609. (cherry picked from commit 1e1ecea62e899acb58c3fdf3b320a0833ddd0dff) diff --git a/elf/dl-open.c b/elf/dl-open.c index ec386626f9..41c7250bf6 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -886,7 +886,7 @@ no more namespaces available for dlmopen()")); /* Avoid keeping around a dangling reference to the libc.so link map in case it has been cached in libc_map. */ if (!args.libc_already_loaded) - GL(dl_ns)[nsid].libc_map = NULL; + GL(dl_ns)[args.nsid].libc_map = NULL; /* Remove the object from memory. It may be in an inconsistent state if relocation failed, for example. */ commit 76843f3b3ecb886b8d300220e6ec378e0fd09a8b Author: Adhemerval Zanella Date: Wed Oct 6 08:10:13 2021 -0300 y2038: Use a common definition for stat for sparc32 The sparc32 misses support for support done by 4e8521333bea6. Checked on sparcv9-linux-gnu. (cherry picked from commit d2b1254db208b35ff060f00a15f22a1eed5306d2) diff --git a/sysdeps/unix/sysv/linux/sparc/bits/struct_stat.h b/sysdeps/unix/sysv/linux/sparc/bits/struct_stat.h index b481b4f9f8..45db6b6ffb 100644 --- a/sysdeps/unix/sysv/linux/sparc/bits/struct_stat.h +++ b/sysdeps/unix/sysv/linux/sparc/bits/struct_stat.h @@ -28,32 +28,35 @@ struct stat { +#ifdef __USE_TIME_BITS64 +# include +#else __dev_t st_dev; /* Device. */ -#if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64 +# if __WORDSIZE == 64 || !defined __USE_FILE_OFFSET64 unsigned short int __pad1; __ino_t st_ino; /* File serial number. */ -#else +# else __ino64_t st_ino; /* File serial number. */ -#endif +# 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 +# ifndef __USE_FILE_OFFSET64 __off_t st_size; /* Size of file, in bytes. */ -#else +# else __off64_t st_size; /* Size of file, in bytes. */ -#endif +# endif __blksize_t st_blksize; /* Optimal block size for I/O. */ -#ifndef __USE_FILE_OFFSET64 +# ifndef __USE_FILE_OFFSET64 __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ -#else +# else __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ -#endif -#ifdef __USE_XOPEN2K8 +# 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 @@ -63,28 +66,32 @@ 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 unsigned long int __glibc_reserved4; unsigned long int __glibc_reserved5; +#endif /* __USE_TIME_BITS64 */ }; #ifdef __USE_LARGEFILE64 struct stat64 { +# ifdef __USE_TIME_BITS64 +# include +# else __dev_t st_dev; /* Device. */ -# if __WORDSIZE == 64 +# if __WORDSIZE == 64 unsigned short int __pad1; -# endif +# endif __ino64_t st_ino; /* File serial number. */ __mode_t st_mode; /* File mode. */ __nlink_t st_nlink; /* Link count. */ @@ -96,7 +103,7 @@ struct stat64 __blksize_t st_blksize; /* Optimal block size for I/O. */ __blkcnt64_t st_blocks; /* Number 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,19 +113,20 @@ 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. */ -# 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 unsigned long int __glibc_reserved4; unsigned long int __glibc_reserved5; +# endif /* __USE_TIME_BITS64 */ }; #endif commit cb9b8b5d7670df1a13544857640efd86a299d0e4 Author: Stafford Horne Date: Fri Oct 15 06:17:41 2021 +0900 timex: Use 64-bit fields on 32-bit TIMESIZE=64 systems (BZ #28469) This was found when testing the OpenRISC port I am working on. These two tests fail with SIGSEGV: FAIL: misc/tst-ntp_gettime FAIL: misc/tst-ntp_gettimex This was found to be due to the kernel overwriting the stack space allocated by the timex structure. The reason for the overwrite being that the kernel timex has 64-bit fields and user space code only allocates enough stack space for timex with 32-bit fields. On 32-bit systems with TIMESIZE=64 __USE_TIME_BITS64 is not defined. This causes the timex structure to use 32-bit fields with type __syscall_slong_t. This patch adjusts the ifdef condition to allow 32-bit systems with TIMESIZE=64 to use the 64-bit long long timex definition. Reviewed-by: Adhemerval Zanella (Cherry picked from commit 1d550265a75b412cea4889a50b101395f6a8e025) diff --git a/sysdeps/unix/sysv/linux/bits/timex.h b/sysdeps/unix/sysv/linux/bits/timex.h index ee37694e8f..4a5db6deca 100644 --- a/sysdeps/unix/sysv/linux/bits/timex.h +++ b/sysdeps/unix/sysv/linux/bits/timex.h @@ -25,7 +25,7 @@ struct timex { -# ifdef __USE_TIME_BITS64 +# if defined __USE_TIME_BITS64 || (__TIMESIZE == 64 && __WORDSIZE == 32) unsigned int modes; /* mode selector */ int :32; /* pad */ long long offset; /* time offset (usec) */ commit 024a7640ab9ecea80e527f4e4d7f7a1868e952c5 Author: Szabolcs Nagy Date: Wed Sep 15 15:16:19 2021 +0100 elf: Avoid deadlock between pthread_create and ctors [BZ #28357] The fix for bug 19329 caused a regression such that pthread_create can deadlock when concurrent ctors from dlopen are waiting for it to finish. Use a new GL(dl_load_tls_lock) in pthread_create that is not taken around ctors in dlopen. The new lock is also used in __tls_get_addr instead of GL(dl_load_lock). The new lock is held in _dl_open_worker and _dl_close_worker around most of the logic before/after the init/fini routines. When init/fini routines are running then TLS is in a consistent, usable state. In _dl_open_worker the new lock requires catching and reraising dlopen failures that happen in the critical section. The new lock is reinitialized in a fork child, to keep the existing behaviour and it is kept recursive in case malloc interposition or TLS access from signal handlers can retake it. It is not obvious if this is necessary or helps, but avoids changing the preexisting behaviour. The new lock may be more appropriate for dl_iterate_phdr too than GL(dl_load_write_lock), since TLS state of an incompletely loaded module may be accessed. If the new lock can replace the old one, that can be a separate change. Fixes bug 28357. Reviewed-by: Adhemerval Zanella (cherry picked from commit 83b5323261bb72313bffcf37476c1b8f0847c736) diff --git a/NEWS b/NEWS index 273a85da4d..c0952d7558 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ The following bugs are resolved with this release: [28036] Incorrect types for pthread_mutexattr_set/getrobust_np [28182] _TIME_BITS=64 in C++ has issues with fcntl, ioctl, prctl [28310] Do not use affinity mask for sysconf (_SC_NPROCESSORS_CONF) + [28357] deadlock between pthread_create and ELF constructors [28361] nptl: Avoid setxid deadlock with blocked signals in thread exit [28407] pthread_kill assumes that kill and tgkill are equivalent diff --git a/elf/dl-close.c b/elf/dl-close.c index f39001cab9..cd7b9c9fe8 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -549,6 +549,9 @@ _dl_close_worker (struct link_map *map, bool force) size_t tls_free_end; tls_free_start = tls_free_end = NO_TLS_OFFSET; + /* Protects global and module specitic TLS state. */ + __rtld_lock_lock_recursive (GL(dl_load_tls_lock)); + /* We modify the list of loaded objects. */ __rtld_lock_lock_recursive (GL(dl_load_write_lock)); @@ -784,6 +787,9 @@ _dl_close_worker (struct link_map *map, bool force) GL(dl_tls_static_used) = tls_free_start; } + /* TLS is cleaned up for the unloaded modules. */ + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + #ifdef SHARED /* Auditing checkpoint: we have deleted all objects. */ if (__glibc_unlikely (do_audit)) diff --git a/elf/dl-open.c b/elf/dl-open.c index 41c7250bf6..bc68e2c376 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -66,6 +66,9 @@ struct dl_open_args libc_map value in the namespace in case of a dlopen failure. */ bool libc_already_loaded; + /* Set to true if the end of dl_open_worker_begin was reached. */ + bool worker_continue; + /* Original parameters to the program and the current environment. */ int argc; char **argv; @@ -482,7 +485,7 @@ call_dl_init (void *closure) } static void -dl_open_worker (void *a) +dl_open_worker_begin (void *a) { struct dl_open_args *args = a; const char *file = args->file; @@ -774,6 +777,36 @@ dl_open_worker (void *a) _dl_call_libc_early_init (libc_map, false); } + args->worker_continue = true; +} + +static void +dl_open_worker (void *a) +{ + struct dl_open_args *args = a; + + args->worker_continue = false; + + { + /* Protects global and module specific TLS state. */ + __rtld_lock_lock_recursive (GL(dl_load_tls_lock)); + + struct dl_exception ex; + int err = _dl_catch_exception (&ex, dl_open_worker_begin, args); + + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + + if (__glibc_unlikely (ex.errstring != NULL)) + /* Reraise the error. */ + _dl_signal_exception (err, &ex, NULL); + } + + if (!args->worker_continue) + return; + + int mode = args->mode; + struct link_map *new = args->map; + /* Run the initializer functions of new objects. Temporarily disable the exception handler, so that lazy binding failures are fatal. */ diff --git a/elf/dl-support.c b/elf/dl-support.c index 0155718175..d8c06ba7eb 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -229,6 +229,13 @@ __rtld_lock_define_initialized_recursive (, _dl_load_lock) list of loaded objects while an object is added to or removed from that list. */ __rtld_lock_define_initialized_recursive (, _dl_load_write_lock) + /* This lock protects global and module specific TLS related data. + E.g. it is held in dlopen and dlclose when GL(dl_tls_generation), + GL(dl_tls_max_dtv_idx) or GL(dl_tls_dtv_slotinfo_list) are + accessed and when TLS related relocations are processed for a + module. It was introduced to keep pthread_create accessing TLS + state that is being set up. */ +__rtld_lock_define_initialized_recursive (, _dl_load_tls_lock) #ifdef HAVE_AUX_VECTOR diff --git a/elf/dl-tls.c b/elf/dl-tls.c index 423e380f7c..40263cf586 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -532,7 +532,7 @@ _dl_allocate_tls_init (void *result) size_t maxgen = 0; /* Protects global dynamic TLS related state. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); + __rtld_lock_lock_recursive (GL(dl_load_tls_lock)); /* Check if the current dtv is big enough. */ if (dtv[-1].counter < GL(dl_tls_max_dtv_idx)) @@ -606,7 +606,7 @@ _dl_allocate_tls_init (void *result) listp = listp->next; assert (listp != NULL); } - __rtld_lock_unlock_recursive (GL(dl_load_lock)); + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); /* The DTV version is up-to-date now. */ dtv[0].counter = maxgen; @@ -745,7 +745,7 @@ _dl_update_slotinfo (unsigned long int req_modid) Here the dtv needs to be updated to new_gen generation count. - This code may be called during TLS access when GL(dl_load_lock) + This code may be called during TLS access when GL(dl_load_tls_lock) is not held. In that case the user code has to synchronize with dlopen and dlclose calls of relevant modules. A module m is relevant if the generation of m <= new_gen and dlclose of m is @@ -867,11 +867,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) if (__glibc_unlikely (the_map->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)) { - __rtld_lock_lock_recursive (GL(dl_load_lock)); + __rtld_lock_lock_recursive (GL(dl_load_tls_lock)); if (__glibc_likely (the_map->l_tls_offset == NO_TLS_OFFSET)) { the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET; - __rtld_lock_unlock_recursive (GL(dl_load_lock)); + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); } else if (__glibc_likely (the_map->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)) @@ -883,7 +883,7 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) #else # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" #endif - __rtld_lock_unlock_recursive (GL(dl_load_lock)); + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); dtv[GET_ADDR_MODULE].pointer.to_free = NULL; dtv[GET_ADDR_MODULE].pointer.val = p; @@ -891,7 +891,7 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) return (char *) p + GET_ADDR_OFFSET; } else - __rtld_lock_unlock_recursive (GL(dl_load_lock)); + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); } struct dtv_pointer result = allocate_and_init (the_map); dtv[GET_ADDR_MODULE].pointer = result; @@ -962,7 +962,7 @@ _dl_tls_get_addr_soft (struct link_map *l) return NULL; dtv_t *dtv = THREAD_DTV (); - /* This may be called without holding the GL(dl_load_lock). Reading + /* This may be called without holding the GL(dl_load_tls_lock). Reading arbitrary gen value is fine since this is best effort code. */ size_t gen = atomic_load_relaxed (&GL(dl_tls_generation)); if (__glibc_unlikely (dtv[0].counter != gen)) diff --git a/elf/rtld.c b/elf/rtld.c index d733359eaf..08cf50145a 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -322,6 +322,7 @@ struct rtld_global _rtld_global = #ifdef _LIBC_REENTRANT ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, ._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, + ._dl_load_tls_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, #endif ._dl_nns = 1, ._dl_ns = diff --git a/posix/fork.c b/posix/fork.c index c471f7b15f..021691b9b7 100644 --- a/posix/fork.c +++ b/posix/fork.c @@ -99,6 +99,9 @@ __libc_fork (void) /* Reset the lock the dynamic loader uses to protect its data. */ __rtld_lock_initialize (GL(dl_load_lock)); + /* Reset the lock protecting dynamic TLS related data. */ + __rtld_lock_initialize (GL(dl_load_tls_lock)); + reclaim_stacks (); /* Run the handlers registered for the child. */ diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 9c15259236..1ceb9c3212 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -372,6 +372,13 @@ struct rtld_global list of loaded objects while an object is added to or removed from that list. */ __rtld_lock_define_recursive (EXTERN, _dl_load_write_lock) + /* This lock protects global and module specific TLS related data. + E.g. it is held in dlopen and dlclose when GL(dl_tls_generation), + GL(dl_tls_max_dtv_idx) or GL(dl_tls_dtv_slotinfo_list) are + accessed and when TLS related relocations are processed for a + module. It was introduced to keep pthread_create accessing TLS + state that is being set up. */ + __rtld_lock_define_recursive (EXTERN, _dl_load_tls_lock) /* Incremented whenever something may have been added to dl_loaded. */ EXTERN unsigned long long _dl_load_adds; @@ -1261,7 +1268,7 @@ extern int _dl_scope_free (void *) attribute_hidden; /* Add module to slot information data. If DO_ADD is false, only the required memory is allocated. Must be called with GL - (dl_load_lock) acquired. If the function has already been called + (dl_load_tls_lock) acquired. If the function has already been called for the link map L with !do_add, then this function will not raise an exception, otherwise it is possible that it encounters a memory allocation failure. */ diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 0af9c59b42..df8943f486 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -152,15 +152,17 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx6 tst-cancelx8 tst-cancelx9 \ tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 ifeq ($(build-shared),yes) -tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 +tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 tst-create1 tests-nolibpthread += tst-fini1 endif modules-names += tst-atfork2mod tst-tls4moda tst-tls4modb \ - tst-_res1mod1 tst-_res1mod2 tst-fini1mod + tst-_res1mod1 tst-_res1mod2 tst-fini1mod \ + tst-create1mod test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names))) tst-atfork2mod.so-no-z-defs = yes +tst-create1mod.so-no-z-defs = yes ifeq ($(build-shared),yes) # Build all the modules even when not actually running test programs. @@ -279,4 +281,8 @@ LDFLAGS-tst-join7mod.so = -Wl,-soname,tst-join7mod.so CFLAGS-tst-unwind-thread.c += -funwind-tables +LDFLAGS-tst-create1 = -Wl,-export-dynamic +$(objpfx)tst-create1: $(shared-thread-library) +$(objpfx)tst-create1.out: $(objpfx)tst-create1mod.so + endif diff --git a/sysdeps/pthread/tst-create1.c b/sysdeps/pthread/tst-create1.c new file mode 100644 index 0000000000..932586c309 --- /dev/null +++ b/sysdeps/pthread/tst-create1.c @@ -0,0 +1,119 @@ +/* Verify that pthread_create does not deadlock when ctors take locks. + Copyright (C) 2021 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 + +/* +Check if ctor and pthread_create deadlocks in + +thread 1: dlopen -> ctor -> lock(user_lock) +thread 2: lock(user_lock) -> pthread_create + +or in + +thread 1: dlclose -> dtor -> lock(user_lock) +thread 2: lock(user_lock) -> pthread_create +*/ + +static pthread_barrier_t bar_ctor; +static pthread_barrier_t bar_dtor; +static pthread_mutex_t user_lock = PTHREAD_MUTEX_INITIALIZER; + +void +ctor (void) +{ + xpthread_barrier_wait (&bar_ctor); + dprintf (1, "thread 1: in ctor: started.\n"); + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 1: in ctor: locked user_lock.\n"); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 1: in ctor: unlocked user_lock.\n"); + dprintf (1, "thread 1: in ctor: done.\n"); +} + +void +dtor (void) +{ + xpthread_barrier_wait (&bar_dtor); + dprintf (1, "thread 1: in dtor: started.\n"); + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 1: in dtor: locked user_lock.\n"); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 1: in dtor: unlocked user_lock.\n"); + dprintf (1, "thread 1: in dtor: done.\n"); +} + +static void * +thread3 (void *a) +{ + dprintf (1, "thread 3: started.\n"); + dprintf (1, "thread 3: done.\n"); + return 0; +} + +static void * +thread2 (void *a) +{ + pthread_t t3; + dprintf (1, "thread 2: started.\n"); + + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 2: locked user_lock.\n"); + xpthread_barrier_wait (&bar_ctor); + t3 = xpthread_create (0, thread3, 0); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 2: unlocked user_lock.\n"); + xpthread_join (t3); + + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 2: locked user_lock.\n"); + xpthread_barrier_wait (&bar_dtor); + t3 = xpthread_create (0, thread3, 0); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 2: unlocked user_lock.\n"); + xpthread_join (t3); + + dprintf (1, "thread 2: done.\n"); + return 0; +} + +static void +thread1 (void) +{ + dprintf (1, "thread 1: started.\n"); + xpthread_barrier_init (&bar_ctor, NULL, 2); + xpthread_barrier_init (&bar_dtor, NULL, 2); + pthread_t t2 = xpthread_create (0, thread2, 0); + void *p = xdlopen ("tst-create1mod.so", RTLD_NOW | RTLD_GLOBAL); + dprintf (1, "thread 1: dlopen done.\n"); + xdlclose (p); + dprintf (1, "thread 1: dlclose done.\n"); + xpthread_join (t2); + dprintf (1, "thread 1: done.\n"); +} + +static int +do_test (void) +{ + thread1 (); + return 0; +} + +#include diff --git a/sysdeps/pthread/tst-create1mod.c b/sysdeps/pthread/tst-create1mod.c new file mode 100644 index 0000000000..62c9006961 --- /dev/null +++ b/sysdeps/pthread/tst-create1mod.c @@ -0,0 +1,41 @@ +/* Verify that pthread_create does not deadlock when ctors take locks. + Copyright (C) 2021 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 + +/* Require TLS setup for the module. */ +__thread int tlsvar; + +void ctor (void); +void dtor (void); + +static void __attribute__ ((constructor)) +do_init (void) +{ + dprintf (1, "constructor started: %d.\n", tlsvar++); + ctor (); + dprintf (1, "constructor done: %d.\n", tlsvar++); +} + +static void __attribute__ ((destructor)) +do_end (void) +{ + dprintf (1, "destructor started: %d.\n", tlsvar++); + dtor (); + dprintf (1, "destructor done: %d.\n", tlsvar++); +} commit 01bffc013cdad1e0c45db7aa57efb2bee61f3338 Author: Siddhesh Poyarekar Date: Fri Oct 29 14:53:55 2021 +0530 Handle NULL input to malloc_usable_size [BZ #28506] Hoist the NULL check for malloc_usable_size into its entry points in malloc-debug and malloc and assume non-NULL in all callees. This fixes BZ #28506 Signed-off-by: Siddhesh Poyarekar Reviewed-by: Florian Weimer Reviewed-by: Richard W.M. Jones (cherry picked from commit 88e316b06414ee7c944cd6f8b30b07a972b78499) diff --git a/malloc/malloc-debug.c b/malloc/malloc-debug.c index 9922ef5f25..3d7e6d44fd 100644 --- a/malloc/malloc-debug.c +++ b/malloc/malloc-debug.c @@ -1,5 +1,6 @@ /* Malloc debug DSO. Copyright (C) 2021 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 @@ -399,17 +400,17 @@ strong_alias (__debug_calloc, calloc) size_t malloc_usable_size (void *mem) { + if (mem == NULL) + return 0; + if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)) return mcheck_usable_size (mem); if (__is_malloc_debug_enabled (MALLOC_CHECK_HOOK)) return malloc_check_get_size (mem); - if (mem != NULL) - { - mchunkptr p = mem2chunk (mem); - if (DUMPED_MAIN_ARENA_CHUNK (p)) - return chunksize (p) - SIZE_SZ; - } + mchunkptr p = mem2chunk (mem); + if (DUMPED_MAIN_ARENA_CHUNK (p)) + return chunksize (p) - SIZE_SZ; return musable (mem); } diff --git a/malloc/malloc.c b/malloc/malloc.c index e065785af7..7882c70f0a 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -1,5 +1,6 @@ /* Malloc implementation for multiple threads without lock contention. Copyright (C) 1996-2021 Free Software Foundation, Inc. + Copyright The GNU Toolchain Authors. This file is part of the GNU C Library. Contributed by Wolfram Gloger and Doug Lea , 2001. @@ -5009,20 +5010,13 @@ __malloc_trim (size_t s) static size_t musable (void *mem) { - mchunkptr p; - if (mem != 0) - { - size_t result = 0; - - p = mem2chunk (mem); + mchunkptr p = mem2chunk (mem); - if (chunk_is_mmapped (p)) - result = chunksize (p) - CHUNK_HDR_SZ; - else if (inuse (p)) - result = memsize (p); + if (chunk_is_mmapped (p)) + return chunksize (p) - CHUNK_HDR_SZ; + else if (inuse (p)) + return memsize (p); - return result; - } return 0; } @@ -5030,10 +5024,9 @@ musable (void *mem) size_t __malloc_usable_size (void *m) { - size_t result; - - result = musable (m); - return result; + if (m == NULL) + return 0; + return musable (m); } #endif diff --git a/malloc/tst-malloc-usable.c b/malloc/tst-malloc-usable.c index a1074b782a..b0d702be10 100644 --- a/malloc/tst-malloc-usable.c +++ b/malloc/tst-malloc-usable.c @@ -2,6 +2,7 @@ MALLOC_CHECK_ exported to a positive value. Copyright (C) 2012-2021 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 @@ -21,29 +22,24 @@ #include #include #include +#include +#include static int do_test (void) { size_t usable_size; void *p = malloc (7); - if (!p) - { - printf ("memory allocation failed\n"); - return 1; - } + TEST_VERIFY_EXIT (p != NULL); usable_size = malloc_usable_size (p); - if (usable_size != 7) - { - printf ("malloc_usable_size: expected 7 but got %zu\n", usable_size); - return 1; - } - + TEST_COMPARE (usable_size, 7); memset (p, 0, usable_size); free (p); + + TEST_COMPARE (malloc_usable_size (NULL), 0); + return 0; } -#define TEST_FUNCTION do_test () -#include "../test-skeleton.c" +#include "support/test-driver.c"