diff --git a/NEWS b/NEWS
-index 485b8ddffa..f278041512 100644
+index 485b8ddffa..2afe250ccf 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,25 @@ See the end for copying conditions.
Version 2.32
Major new features:
-@@ -185,6 +204,10 @@ Security related changes:
+@@ -185,6 +204,14 @@ Security related changes:
Dytrych of the Cisco Security Assessment and Penetration Team (See
TALOS-2020-1019).
+ CVE-2020-27618: An infinite loop has been fixed in the iconv program when
+ invoked with input containing redundant shift sequences in the IBM1364,
+ IBM1371, IBM1388, IBM1390, or IBM1399 character sets.
++
++ CVE-2021-33574: The mq_notify function has a potential use-after-free
++ issue when using a notification type of SIGEV_THREAD and a thread
++ attribute with a non-default affinity mask.
+
The following bugs are resolved with this release:
-
-#include <string.h>
diff --git a/elf/Makefile b/elf/Makefile
-index 0b78721848..355e70037b 100644
+index 0b78721848..3ba7f4ecfc 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1381,6 +1381,8 @@ CFLAGS-ifuncmain7pie.c += $(pie-ccflag)
$(objpfx)ifuncmain1pie: $(objpfx)ifuncmod1.so
$(objpfx)ifuncmain1staticpie: $(objpfx)ifuncdep1pic.o
$(objpfx)ifuncmain1vispie: $(objpfx)ifuncmod1.so
+@@ -1630,8 +1632,6 @@ $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
+
+ tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
+ LD_HWCAP_MASK=0x1
+-tst-env-setuid-tunables-ENV = \
+- GLIBC_TUNABLES=glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096
+
+ $(objpfx)tst-debug1: $(libdl)
+ $(objpfx)tst-debug1.out: $(objpfx)tst-debug1mod1.so
diff --git a/elf/dl-load.c b/elf/dl-load.c
index e39980fb19..71867e7c1a 100644
--- a/elf/dl-load.c
/* We are done mapping in the file. We no longer need the descriptor. */
if (__glibc_unlikely (__close_nocancel (fd) != 0))
{
+diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
+index 26e6e26612..15b29bcb90 100644
+--- a/elf/dl-tunables.c
++++ b/elf/dl-tunables.c
+@@ -177,6 +177,7 @@ parse_tunables (char *tunestr, char *valstring)
+ return;
+
+ char *p = tunestr;
++ size_t off = 0;
+
+ while (true)
+ {
+@@ -190,7 +191,11 @@ parse_tunables (char *tunestr, char *valstring)
+ /* If we reach the end of the string before getting a valid name-value
+ pair, bail out. */
+ if (p[len] == '\0')
+- return;
++ {
++ if (__libc_enable_secure)
++ tunestr[off] = '\0';
++ return;
++ }
+
+ /* We did not find a valid name-value pair before encountering the
+ colon. */
+@@ -216,35 +221,28 @@ parse_tunables (char *tunestr, char *valstring)
+
+ if (tunable_is_name (cur->name, name))
+ {
+- /* If we are in a secure context (AT_SECURE) then ignore the tunable
+- unless it is explicitly marked as secure. Tunable values take
+- precedence over their envvar aliases. */
++ /* If we are in a secure context (AT_SECURE) then ignore the
++ tunable unless it is explicitly marked as secure. Tunable
++ values take precedence over their envvar aliases. We write
++ the tunables that are not SXID_ERASE back to TUNESTR, thus
++ dropping all SXID_ERASE tunables and any invalid or
++ unrecognized tunables. */
+ if (__libc_enable_secure)
+ {
+- if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
++ if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
+ {
+- if (p[len] == '\0')
+- {
+- /* Last tunable in the valstring. Null-terminate and
+- return. */
+- *name = '\0';
+- return;
+- }
+- else
+- {
+- /* Remove the current tunable from the string. We do
+- this by overwriting the string starting from NAME
+- (which is where the current tunable begins) with
+- the remainder of the string. We then have P point
+- to NAME so that we continue in the correct
+- position in the valstring. */
+- char *q = &p[len + 1];
+- p = name;
+- while (*q != '\0')
+- *name++ = *q++;
+- name[0] = '\0';
+- len = 0;
+- }
++ if (off > 0)
++ tunestr[off++] = ':';
++
++ const char *n = cur->name;
++
++ while (*n != '\0')
++ tunestr[off++] = *n++;
++
++ tunestr[off++] = '=';
++
++ for (size_t j = 0; j < len; j++)
++ tunestr[off++] = value[j];
+ }
+
+ if (cur->security_level != TUNABLE_SECLEVEL_NONE)
+@@ -257,9 +255,7 @@ parse_tunables (char *tunestr, char *valstring)
+ }
+ }
+
+- if (p[len] == '\0')
+- return;
+- else
++ if (p[len] != '\0')
+ p += len + 1;
+ }
+ }
diff --git a/elf/ifuncmain6pie.c b/elf/ifuncmain6pie.c
index 04faeb86ef..4a01906836 100644
--- a/elf/ifuncmain6pie.c
break;
}
+diff --git a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c
+index 971d5892b1..ca0c8c245c 100644
+--- a/elf/tst-env-setuid-tunables.c
++++ b/elf/tst-env-setuid-tunables.c
+@@ -25,35 +25,76 @@
+ #include "config.h"
+ #undef _LIBC
+
+-#define test_parent test_parent_tunables
+-#define test_child test_child_tunables
+-
+-static int test_child_tunables (void);
+-static int test_parent_tunables (void);
+-
+-#include "tst-env-setuid.c"
+-
+-#define CHILD_VALSTRING_VALUE "glibc.malloc.mmap_threshold=4096"
+-#define PARENT_VALSTRING_VALUE \
+- "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096"
++#include <errno.h>
++#include <fcntl.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/stat.h>
++#include <sys/wait.h>
++#include <unistd.h>
++#include <intprops.h>
++#include <array_length.h>
++
++#include <support/check.h>
++#include <support/support.h>
++#include <support/test-driver.h>
++#include <support/capture_subprocess.h>
++
++const char *teststrings[] =
++{
++ "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
++ "glibc.malloc.perturb=0x800",
++ "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
++ "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
++ "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
++ "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
++ ":glibc.malloc.garbage=2:glibc.malloc.check=1",
++ "glibc.malloc.check=1:glibc.malloc.check=2",
++ "not_valid.malloc.check=2",
++ "glibc.not_valid.check=2",
++};
++
++const char *resultstrings[] =
++{
++ "glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.perturb=0x800",
++ "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.mmap_threshold=4096",
++ "glibc.malloc.mmap_threshold=4096",
++ "",
++ "",
++ "",
++ "",
++ "",
++ "",
++};
+
+ static int
+-test_child_tunables (void)
++test_child (int off)
+ {
+ const char *val = getenv ("GLIBC_TUNABLES");
+
+ #if HAVE_TUNABLES
+- if (val != NULL && strcmp (val, CHILD_VALSTRING_VALUE) == 0)
++ if (val != NULL && strcmp (val, resultstrings[off]) == 0)
+ return 0;
+
+ if (val != NULL)
+- printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val);
++ printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
+
+ return 1;
+ #else
+ if (val != NULL)
+ {
+- printf ("GLIBC_TUNABLES not cleared\n");
++ printf ("[%d] GLIBC_TUNABLES not cleared\n", off);
+ return 1;
+ }
+ return 0;
+@@ -61,15 +102,48 @@ test_child_tunables (void)
+ }
+
+ static int
+-test_parent_tunables (void)
++do_test (int argc, char **argv)
+ {
+- const char *val = getenv ("GLIBC_TUNABLES");
++ /* Setgid child process. */
++ if (argc == 2)
++ {
++ if (getgid () == getegid ())
++ /* This can happen if the file system is mounted nosuid. */
++ FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
++ (intmax_t) getgid ());
+
+- if (val != NULL && strcmp (val, PARENT_VALSTRING_VALUE) == 0)
+- return 0;
++ int ret = test_child (atoi (argv[1]));
+
+- if (val != NULL)
+- printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val);
++ if (ret != 0)
++ exit (1);
+
+- return 1;
++ exit (EXIT_SUCCESS);
++ }
++ else
++ {
++ int ret = 0;
++
++ /* Spawn tests. */
++ for (int i = 0; i < array_length (teststrings); i++)
++ {
++ char buf[INT_BUFSIZE_BOUND (int)];
++
++ printf ("Spawned test for %s (%d)\n", teststrings[i], i);
++ snprintf (buf, sizeof (buf), "%d\n", i);
++ if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0)
++ exit (1);
++
++ int status = support_capture_subprogram_self_sgid (buf);
++
++ /* Bail out early if unsupported. */
++ if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
++ return EXIT_UNSUPPORTED;
++
++ ret |= status;
++ }
++ return ret;
++ }
+ }
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
+index 41dc79e83a..2dbccdb69e 100644
+--- a/elf/tst-env-setuid.c
++++ b/elf/tst-env-setuid.c
+@@ -29,173 +29,12 @@
+ #include <sys/wait.h>
+ #include <unistd.h>
+
++#include <support/check.h>
+ #include <support/support.h>
+ #include <support/test-driver.h>
++#include <support/capture_subprocess.h>
+
+ static char SETGID_CHILD[] = "setgid-child";
+-#define CHILD_STATUS 42
+-
+-/* Return a GID which is not our current GID, but is present in the
+- supplementary group list. */
+-static gid_t
+-choose_gid (void)
+-{
+- const int count = 64;
+- gid_t groups[count];
+- int ret = getgroups (count, groups);
+- if (ret < 0)
+- {
+- printf ("getgroups: %m\n");
+- exit (1);
+- }
+- gid_t current = getgid ();
+- for (int i = 0; i < ret; ++i)
+- {
+- if (groups[i] != current)
+- return groups[i];
+- }
+- return 0;
+-}
+-
+-/* Spawn and execute a program and verify that it returns the CHILD_STATUS. */
+-static pid_t
+-do_execve (char **args)
+-{
+- pid_t kid = vfork ();
+-
+- if (kid < 0)
+- {
+- printf ("vfork: %m\n");
+- return -1;
+- }
+-
+- if (kid == 0)
+- {
+- /* Child process. */
+- execve (args[0], args, environ);
+- _exit (-errno);
+- }
+-
+- if (kid < 0)
+- return 1;
+-
+- int status;
+-
+- if (waitpid (kid, &status, 0) < 0)
+- {
+- printf ("waitpid: %m\n");
+- return 1;
+- }
+-
+- if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
+- return EXIT_UNSUPPORTED;
+-
+- if (!WIFEXITED (status) || WEXITSTATUS (status) != CHILD_STATUS)
+- {
+- printf ("Unexpected exit status %d from child process\n",
+- WEXITSTATUS (status));
+- return 1;
+- }
+- return 0;
+-}
+-
+-/* Copies the executable into a restricted directory, so that we can
+- safely make it SGID with the TARGET group ID. Then runs the
+- executable. */
+-static int
+-run_executable_sgid (gid_t target)
+-{
+- char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd",
+- test_dir, (intmax_t) getpid ());
+- char *execname = xasprintf ("%s/bin", dirname);
+- int infd = -1;
+- int outfd = -1;
+- int ret = 0;
+- if (mkdir (dirname, 0700) < 0)
+- {
+- printf ("mkdir: %m\n");
+- goto err;
+- }
+- infd = open ("/proc/self/exe", O_RDONLY);
+- if (infd < 0)
+- {
+- printf ("open (/proc/self/exe): %m\n");
+- goto err;
+- }
+- outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
+- if (outfd < 0)
+- {
+- printf ("open (%s): %m\n", execname);
+- goto err;
+- }
+- char buf[4096];
+- for (;;)
+- {
+- ssize_t rdcount = read (infd, buf, sizeof (buf));
+- if (rdcount < 0)
+- {
+- printf ("read: %m\n");
+- goto err;
+- }
+- if (rdcount == 0)
+- break;
+- char *p = buf;
+- char *end = buf + rdcount;
+- while (p != end)
+- {
+- ssize_t wrcount = write (outfd, buf, end - p);
+- if (wrcount == 0)
+- errno = ENOSPC;
+- if (wrcount <= 0)
+- {
+- printf ("write: %m\n");
+- goto err;
+- }
+- p += wrcount;
+- }
+- }
+- if (fchown (outfd, getuid (), target) < 0)
+- {
+- printf ("fchown (%s): %m\n", execname);
+- goto err;
+- }
+- if (fchmod (outfd, 02750) < 0)
+- {
+- printf ("fchmod (%s): %m\n", execname);
+- goto err;
+- }
+- if (close (outfd) < 0)
+- {
+- printf ("close (outfd): %m\n");
+- goto err;
+- }
+- if (close (infd) < 0)
+- {
+- printf ("close (infd): %m\n");
+- goto err;
+- }
+-
+- char *args[] = {execname, SETGID_CHILD, NULL};
+-
+- ret = do_execve (args);
+-
+-err:
+- if (outfd >= 0)
+- close (outfd);
+- if (infd >= 0)
+- close (infd);
+- if (execname)
+- {
+- unlink (execname);
+- free (execname);
+- }
+- if (dirname)
+- {
+- rmdir (dirname);
+- free (dirname);
+- }
+- return ret;
+-}
+
+ #ifndef test_child
+ static int
+@@ -256,40 +95,32 @@ do_test (int argc, char **argv)
+ if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
+ {
+ if (getgid () == getegid ())
+- {
+- /* This can happen if the file system is mounted nosuid. */
+- fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n",
+- (intmax_t) getgid ());
+- exit (EXIT_UNSUPPORTED);
+- }
++ /* This can happen if the file system is mounted nosuid. */
++ FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
++ (intmax_t) getgid ());
+
+ int ret = test_child ();
+
+ if (ret != 0)
+ exit (1);
+
+- exit (CHILD_STATUS);
++ exit (EXIT_SUCCESS);
+ }
+ else
+ {
+ if (test_parent () != 0)
+ exit (1);
+
+- /* Try running a setgid program. */
+- gid_t target = choose_gid ();
+- if (target == 0)
+- {
+- fprintf (stderr,
+- "Could not find a suitable GID for user %jd, skipping test\n",
+- (intmax_t) getuid ());
+- exit (0);
+- }
++ int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
+
+- return run_executable_sgid (target);
+- }
++ if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
++ return EXIT_UNSUPPORTED;
++
++ if (!WIFEXITED (status))
++ FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
+
+- /* Something went wrong and our argv was corrupted. */
+- _exit (1);
++ return 0;
++ }
+ }
+
+ #define TEST_FUNCTION_ARGV do_test
diff --git a/iconv/Versions b/iconv/Versions
index 8a5f4cf780..d51af52fa3 100644
--- a/iconv/Versions
}
else if (status == NSS_STATUS_RETURN
|| status == NSS_STATUS_NOTFOUND
+diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
+index ed1b22308e..cb3f989cba 100644
+--- a/posix/wordexp-test.c
++++ b/posix/wordexp-test.c
+@@ -183,6 +183,7 @@ struct test_case_struct
+ { 0, NULL, "$var", 0, 0, { NULL, }, IFS },
+ { 0, NULL, "\"\\n\"", 0, 1, { "\\n", }, IFS },
+ { 0, NULL, "", 0, 0, { NULL, }, IFS },
++ { 0, NULL, "${1234567890123456789012}", 0, 0, { NULL, }, IFS },
+
+ /* Flags not already covered (testit() has special handling for these) */
+ { 0, NULL, "one two", WRDE_DOOFFS, 2, { "one", "two", }, IFS },
+diff --git a/posix/wordexp.c b/posix/wordexp.c
+index e082d94895..56289503a1 100644
+--- a/posix/wordexp.c
++++ b/posix/wordexp.c
+@@ -1399,7 +1399,7 @@ envsubst:
+ /* Is it a numeric parameter? */
+ else if (isdigit (env[0]))
+ {
+- int n = atoi (env);
++ unsigned long n = strtoul (env, NULL, 10);
+
+ if (n >= __libc_argc)
+ /* Substitute NULL. */
diff --git a/resolv/Makefile b/resolv/Makefile
index b61c0c3e0c..dbd8f8bf4f 100644
--- a/resolv/Makefile
((char *)charbuf.scratch.data)[fc] = 1;
}
else
+diff --git a/stdlib/tst-secure-getenv.c b/stdlib/tst-secure-getenv.c
+index 3cfe9a05c3..d4b1139c5e 100644
+--- a/stdlib/tst-secure-getenv.c
++++ b/stdlib/tst-secure-getenv.c
+@@ -30,167 +30,12 @@
+ #include <sys/wait.h>
+ #include <unistd.h>
+
++#include <support/check.h>
+ #include <support/support.h>
++#include <support/capture_subprocess.h>
+ #include <support/test-driver.h>
+
+ static char MAGIC_ARGUMENT[] = "run-actual-test";
+-#define MAGIC_STATUS 19
+-
+-/* Return a GID which is not our current GID, but is present in the
+- supplementary group list. */
+-static gid_t
+-choose_gid (void)
+-{
+- int count = getgroups (0, NULL);
+- if (count < 0)
+- {
+- printf ("getgroups: %m\n");
+- exit (1);
+- }
+- gid_t *groups;
+- groups = xcalloc (count, sizeof (*groups));
+- int ret = getgroups (count, groups);
+- if (ret < 0)
+- {
+- printf ("getgroups: %m\n");
+- exit (1);
+- }
+- gid_t current = getgid ();
+- gid_t not_current = 0;
+- for (int i = 0; i < ret; ++i)
+- {
+- if (groups[i] != current)
+- {
+- not_current = groups[i];
+- break;
+- }
+- }
+- free (groups);
+- return not_current;
+-}
+-
+-
+-/* Copies the executable into a restricted directory, so that we can
+- safely make it SGID with the TARGET group ID. Then runs the
+- executable. */
+-static int
+-run_executable_sgid (gid_t target)
+-{
+- char *dirname = xasprintf ("%s/secure-getenv.%jd",
+- test_dir, (intmax_t) getpid ());
+- char *execname = xasprintf ("%s/bin", dirname);
+- int infd = -1;
+- int outfd = -1;
+- int ret = -1;
+- if (mkdir (dirname, 0700) < 0)
+- {
+- printf ("mkdir: %m\n");
+- goto err;
+- }
+- infd = open ("/proc/self/exe", O_RDONLY);
+- if (infd < 0)
+- {
+- printf ("open (/proc/self/exe): %m\n");
+- goto err;
+- }
+- outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
+- if (outfd < 0)
+- {
+- printf ("open (%s): %m\n", execname);
+- goto err;
+- }
+- char buf[4096];
+- for (;;)
+- {
+- ssize_t rdcount = read (infd, buf, sizeof (buf));
+- if (rdcount < 0)
+- {
+- printf ("read: %m\n");
+- goto err;
+- }
+- if (rdcount == 0)
+- break;
+- char *p = buf;
+- char *end = buf + rdcount;
+- while (p != end)
+- {
+- ssize_t wrcount = write (outfd, buf, end - p);
+- if (wrcount == 0)
+- errno = ENOSPC;
+- if (wrcount <= 0)
+- {
+- printf ("write: %m\n");
+- goto err;
+- }
+- p += wrcount;
+- }
+- }
+- if (fchown (outfd, getuid (), target) < 0)
+- {
+- printf ("fchown (%s): %m\n", execname);
+- goto err;
+- }
+- if (fchmod (outfd, 02750) < 0)
+- {
+- printf ("fchmod (%s): %m\n", execname);
+- goto err;
+- }
+- if (close (outfd) < 0)
+- {
+- printf ("close (outfd): %m\n");
+- goto err;
+- }
+- if (close (infd) < 0)
+- {
+- printf ("close (infd): %m\n");
+- goto err;
+- }
+-
+- int kid = fork ();
+- if (kid < 0)
+- {
+- printf ("fork: %m\n");
+- goto err;
+- }
+- if (kid == 0)
+- {
+- /* Child process. */
+- char *args[] = { execname, MAGIC_ARGUMENT, NULL };
+- execve (execname, args, environ);
+- printf ("execve (%s): %m\n", execname);
+- _exit (1);
+- }
+- int status;
+- if (waitpid (kid, &status, 0) < 0)
+- {
+- printf ("waitpid: %m\n");
+- goto err;
+- }
+- if (!WIFEXITED (status) || WEXITSTATUS (status) != MAGIC_STATUS)
+- {
+- printf ("Unexpected exit status %d from child process\n",
+- status);
+- goto err;
+- }
+- ret = 0;
+-
+-err:
+- if (outfd >= 0)
+- close (outfd);
+- if (infd >= 0)
+- close (infd);
+- if (execname)
+- {
+- unlink (execname);
+- free (execname);
+- }
+- if (dirname)
+- {
+- rmdir (dirname);
+- free (dirname);
+- }
+- return ret;
+-}
+
+ static int
+ do_test (void)
+@@ -212,15 +57,15 @@ do_test (void)
+ exit (1);
+ }
+
+- gid_t target = choose_gid ();
+- if (target == 0)
+- {
+- fprintf (stderr,
+- "Could not find a suitable GID for user %jd, skipping test\n",
+- (intmax_t) getuid ());
+- exit (0);
+- }
+- return run_executable_sgid (target);
++ int status = support_capture_subprogram_self_sgid (MAGIC_ARGUMENT);
++
++ if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
++ return EXIT_UNSUPPORTED;
++
++ if (!WIFEXITED (status))
++ FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
++
++ return 0;
+ }
+
+ static void
+@@ -229,23 +74,15 @@ alternative_main (int argc, char **argv)
+ if (argc == 2 && strcmp (argv[1], MAGIC_ARGUMENT) == 0)
+ {
+ if (getgid () == getegid ())
+- {
+- /* This can happen if the file system is mounted nosuid. */
+- fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n",
+- (intmax_t) getgid ());
+- exit (MAGIC_STATUS);
+- }
++ /* This can happen if the file system is mounted nosuid. */
++ FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
++ (intmax_t) getgid ());
+ if (getenv ("PATH") == NULL)
+- {
+- printf ("PATH variable not present\n");
+- exit (3);
+- }
++ FAIL_EXIT (3, "PATH variable not present\n");
+ if (secure_getenv ("PATH") != NULL)
+- {
+- printf ("PATH variable not filtered out\n");
+- exit (4);
+- }
+- exit (MAGIC_STATUS);
++ FAIL_EXIT (4, "PATH variable not filtered out\n");
++
++ exit (EXIT_SUCCESS);
+ }
+ }
+
diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
index 309d0f39b2..c8d3051af8 100644
--- a/string/bits/string_fortified.h
resolv_test \
set_fortify_handler \
support-xfstat \
+diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
+index 9808750f80..421f657678 100644
+--- a/support/capture_subprocess.h
++++ b/support/capture_subprocess.h
+@@ -41,6 +41,12 @@ struct support_capture_subprocess support_capture_subprocess
+ struct support_capture_subprocess support_capture_subprogram
+ (const char *file, char *const argv[]);
+
++/* Copy the running program into a setgid binary and run it with CHILD_ID
++ argument. If execution is successful, return the exit status of the child
++ program, otherwise return a non-zero failure exit code. */
++int support_capture_subprogram_self_sgid
++ (char *child_id);
++
+ /* Deallocate the subprocess data captured by
+ support_capture_subprocess. */
+ void support_capture_subprocess_free (struct support_capture_subprocess *);
diff --git a/support/resolv_response_context_duplicate.c b/support/resolv_response_context_duplicate.c
new file mode 100644
index 0000000000..f9c5c3462a
__END_DECLS
#endif /* SUPPORT_RESOLV_TEST_H */
+diff --git a/support/subprocess.h b/support/subprocess.h
+index 8b442fd5c0..34ffd02e8e 100644
+--- a/support/subprocess.h
++++ b/support/subprocess.h
+@@ -38,6 +38,11 @@ struct support_subprocess support_subprocess
+ struct support_subprocess support_subprogram
+ (const char *file, char *const argv[]);
+
++/* Invoke program FILE with ARGV arguments by using posix_spawn and wait for it
++ to complete. Return program exit status. */
++int support_subprogram_wait
++ (const char *file, char *const argv[]);
++
+ /* Wait for the subprocess indicated by PROC::PID. Return the status
+ indicate by waitpid call. */
+ int support_process_wait (struct support_subprocess *proc);
+diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
+index eeed676e3d..28a37df67f 100644
+--- a/support/support_capture_subprocess.c
++++ b/support/support_capture_subprocess.c
+@@ -20,11 +20,14 @@
+ #include <support/capture_subprocess.h>
+
+ #include <errno.h>
++#include <fcntl.h>
+ #include <stdlib.h>
+ #include <support/check.h>
+ #include <support/xunistd.h>
+ #include <support/xsocket.h>
+ #include <support/xspawn.h>
++#include <support/support.h>
++#include <support/test-driver.h>
+
+ static void
+ transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
+@@ -36,7 +39,7 @@ transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
+ if (ret < 0)
+ {
+ support_record_failure ();
+- printf ("error: reading from subprocess %s: %m", what);
++ printf ("error: reading from subprocess %s: %m\n", what);
+ pfd->events = 0;
+ pfd->revents = 0;
+ }
+@@ -102,6 +105,129 @@ support_capture_subprogram (const char *file, char *const argv[])
+ return result;
+ }
+
++/* Copies the executable into a restricted directory, so that we can
++ safely make it SGID with the TARGET group ID. Then runs the
++ executable. */
++static int
++copy_and_spawn_sgid (char *child_id, gid_t gid)
++{
++ char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd",
++ test_dir, (intmax_t) getpid ());
++ char *execname = xasprintf ("%s/bin", dirname);
++ int infd = -1;
++ int outfd = -1;
++ int ret = 1, status = 1;
++
++ TEST_VERIFY (mkdir (dirname, 0700) == 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++
++ infd = open ("/proc/self/exe", O_RDONLY);
++ if (infd < 0)
++ FAIL_UNSUPPORTED ("unsupported: Cannot read binary from procfs\n");
++
++ outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
++ TEST_VERIFY (outfd >= 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++
++ char buf[4096];
++ for (;;)
++ {
++ ssize_t rdcount = read (infd, buf, sizeof (buf));
++ TEST_VERIFY (rdcount >= 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++ if (rdcount == 0)
++ break;
++ char *p = buf;
++ char *end = buf + rdcount;
++ while (p != end)
++ {
++ ssize_t wrcount = write (outfd, buf, end - p);
++ if (wrcount == 0)
++ errno = ENOSPC;
++ TEST_VERIFY (wrcount > 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++ p += wrcount;
++ }
++ }
++ TEST_VERIFY (fchown (outfd, getuid (), gid) == 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++ TEST_VERIFY (fchmod (outfd, 02750) == 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++ TEST_VERIFY (close (outfd) == 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++ TEST_VERIFY (close (infd) == 0);
++ if (support_record_failure_is_failed ())
++ goto err;
++
++ /* We have the binary, now spawn the subprocess. Avoid using
++ support_subprogram because we only want the program exit status, not the
++ contents. */
++ ret = 0;
++
++ char * const args[] = {execname, child_id, NULL};
++
++ status = support_subprogram_wait (args[0], args);
++
++err:
++ if (outfd >= 0)
++ close (outfd);
++ if (infd >= 0)
++ close (infd);
++ if (execname != NULL)
++ {
++ unlink (execname);
++ free (execname);
++ }
++ if (dirname != NULL)
++ {
++ rmdir (dirname);
++ free (dirname);
++ }
++
++ if (ret != 0)
++ FAIL_EXIT1("Failed to make sgid executable for test\n");
++
++ return status;
++}
++
++int
++support_capture_subprogram_self_sgid (char *child_id)
++{
++ gid_t target = 0;
++ const int count = 64;
++ gid_t groups[count];
++
++ /* Get a GID which is not our current GID, but is present in the
++ supplementary group list. */
++ int ret = getgroups (count, groups);
++ if (ret < 0)
++ FAIL_UNSUPPORTED("Could not get group list for user %jd\n",
++ (intmax_t) getuid ());
++
++ gid_t current = getgid ();
++ for (int i = 0; i < ret; ++i)
++ {
++ if (groups[i] != current)
++ {
++ target = groups[i];
++ break;
++ }
++ }
++
++ if (target == 0)
++ FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
++ (intmax_t) getuid ());
++
++ return copy_and_spawn_sgid (child_id, target);
++}
++
+ void
+ support_capture_subprocess_free (struct support_capture_subprocess *p)
+ {
+diff --git a/support/support_subprocess.c b/support/support_subprocess.c
+index 36e3a77af2..4a25828111 100644
+--- a/support/support_subprocess.c
++++ b/support/support_subprocess.c
+@@ -27,7 +27,7 @@
+ #include <support/subprocess.h>
+
+ static struct support_subprocess
+-support_suprocess_init (void)
++support_subprocess_init (void)
+ {
+ struct support_subprocess result;
+
+@@ -48,7 +48,7 @@ support_suprocess_init (void)
+ struct support_subprocess
+ support_subprocess (void (*callback) (void *), void *closure)
+ {
+- struct support_subprocess result = support_suprocess_init ();
++ struct support_subprocess result = support_subprocess_init ();
+
+ result.pid = xfork ();
+ if (result.pid == 0)
+@@ -71,7 +71,7 @@ support_subprocess (void (*callback) (void *), void *closure)
+ struct support_subprocess
+ support_subprogram (const char *file, char *const argv[])
+ {
+- struct support_subprocess result = support_suprocess_init ();
++ struct support_subprocess result = support_subprocess_init ();
+
+ posix_spawn_file_actions_t fa;
+ /* posix_spawn_file_actions_init does not fail. */
+@@ -84,7 +84,7 @@ support_subprogram (const char *file, char *const argv[])
+ xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);
+ xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);
+
+- result.pid = xposix_spawn (file, &fa, NULL, argv, NULL);
++ result.pid = xposix_spawn (file, &fa, NULL, argv, environ);
+
+ xclose (result.stdout_pipe[1]);
+ xclose (result.stderr_pipe[1]);
+@@ -92,6 +92,19 @@ support_subprogram (const char *file, char *const argv[])
+ return result;
+ }
+
++int
++support_subprogram_wait (const char *file, char *const argv[])
++{
++ posix_spawn_file_actions_t fa;
++
++ posix_spawn_file_actions_init (&fa);
++ struct support_subprocess res = support_subprocess_init ();
++
++ res.pid = xposix_spawn (file, &fa, NULL, argv, environ);
++
++ return support_process_wait (&res);
++}
++
+ int
+ support_process_wait (struct support_subprocess *proc)
+ {
diff --git a/sysdeps/aarch64/dl-bti.c b/sysdeps/aarch64/dl-bti.c
index 196e462520..cf7624aaa2 100644
--- a/sysdeps/aarch64/dl-bti.c
return true;
#endif
return false;
+diff --git a/sysdeps/s390/configure b/sysdeps/s390/configure
+index fa46e9e351..e7f576338d 100644
+--- a/sysdeps/s390/configure
++++ b/sysdeps/s390/configure
+@@ -123,7 +123,9 @@ void testinsn (char *buf)
+ __asm__ (".machine \"arch13\" \n\t"
+ ".machinemode \"zarch_nohighgprs\" \n\t"
+ "lghi %%r0,16 \n\t"
+- "mvcrl 0(%0),32(%0)" : : "a" (buf) : "memory", "r0");
++ "mvcrl 0(%0),32(%0) \n\t"
++ "vstrs %%v20,%%v20,%%v20,%%v20,0,2"
++ : : "a" (buf) : "memory", "r0");
+ }
+ EOF
+ if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS --shared conftest.c
+@@ -271,7 +273,9 @@ else
+ void testinsn (char *buf)
+ {
+ __asm__ ("lghi %%r0,16 \n\t"
+- "mvcrl 0(%0),32(%0)" : : "a" (buf) : "memory", "r0");
++ "mvcrl 0(%0),32(%0) \n\t"
++ "vstrs %%v20,%%v20,%%v20,%%v20,0,2"
++ : : "a" (buf) : "memory", "r0");
+ }
+ EOF
+ if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS --shared conftest.c
+diff --git a/sysdeps/s390/configure.ac b/sysdeps/s390/configure.ac
+index 3ed5a8ef87..5c3479e8cf 100644
+--- a/sysdeps/s390/configure.ac
++++ b/sysdeps/s390/configure.ac
+@@ -88,7 +88,9 @@ void testinsn (char *buf)
+ __asm__ (".machine \"arch13\" \n\t"
+ ".machinemode \"zarch_nohighgprs\" \n\t"
+ "lghi %%r0,16 \n\t"
+- "mvcrl 0(%0),32(%0)" : : "a" (buf) : "memory", "r0");
++ "mvcrl 0(%0),32(%0) \n\t"
++ "vstrs %%v20,%%v20,%%v20,%%v20,0,2"
++ : : "a" (buf) : "memory", "r0");
+ }
+ EOF
+ dnl test, if assembler supports S390 arch13 instructions
+@@ -195,7 +197,9 @@ cat > conftest.c <<\EOF
+ void testinsn (char *buf)
+ {
+ __asm__ ("lghi %%r0,16 \n\t"
+- "mvcrl 0(%0),32(%0)" : : "a" (buf) : "memory", "r0");
++ "mvcrl 0(%0),32(%0) \n\t"
++ "vstrs %%v20,%%v20,%%v20,%%v20,0,2"
++ : : "a" (buf) : "memory", "r0");
+ }
+ EOF
+ dnl test, if assembler supports S390 arch13 zarch instructions as default
+diff --git a/sysdeps/s390/memmove.c b/sysdeps/s390/memmove.c
+index 5fc85e129f..ee59b5de14 100644
+--- a/sysdeps/s390/memmove.c
++++ b/sysdeps/s390/memmove.c
+@@ -43,7 +43,7 @@ extern __typeof (__redirect_memmove) MEMMOVE_ARCH13 attribute_hidden;
+ s390_libc_ifunc_expr (__redirect_memmove, memmove,
+ ({
+ s390_libc_ifunc_expr_stfle_init ();
+- (HAVE_MEMMOVE_ARCH13
++ (HAVE_MEMMOVE_ARCH13 && (hwcap & HWCAP_S390_VXRS_EXT2)
+ && S390_IS_ARCH13_MIE3 (stfle_bits))
+ ? MEMMOVE_ARCH13
+ : (HAVE_MEMMOVE_Z13 && (hwcap & HWCAP_S390_VX))
+diff --git a/sysdeps/s390/multiarch/ifunc-impl-list.c b/sysdeps/s390/multiarch/ifunc-impl-list.c
+index e6195c6e26..17c0cc3952 100644
+--- a/sysdeps/s390/multiarch/ifunc-impl-list.c
++++ b/sysdeps/s390/multiarch/ifunc-impl-list.c
+@@ -171,7 +171,8 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
+ IFUNC_IMPL (i, name, memmove,
+ # if HAVE_MEMMOVE_ARCH13
+ IFUNC_IMPL_ADD (array, i, memmove,
+- S390_IS_ARCH13_MIE3 (stfle_bits),
++ ((dl_hwcap & HWCAP_S390_VXRS_EXT2)
++ && S390_IS_ARCH13_MIE3 (stfle_bits)),
+ MEMMOVE_ARCH13)
+ # endif
+ # if HAVE_MEMMOVE_Z13
diff --git a/sysdeps/sh/be/sh4/fpu/Implies b/sysdeps/sh/be/sh4/fpu/Implies
new file mode 100644
index 0000000000..71b28ee1a4
#define IS_EMAG(midr) (MIDR_IMPLEMENTOR(midr) == 'P' \
&& MIDR_PARTNUM(midr) == 0x000)
+diff --git a/sysdeps/unix/sysv/linux/mq_notify.c b/sysdeps/unix/sysv/linux/mq_notify.c
+index 61bbb03b64..b5a903c3a2 100644
+--- a/sysdeps/unix/sysv/linux/mq_notify.c
++++ b/sysdeps/unix/sysv/linux/mq_notify.c
+@@ -133,8 +133,11 @@ helper_thread (void *arg)
+ (void) __pthread_barrier_wait (¬ify_barrier);
+ }
+ else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
+- /* The only state we keep is the copy of the thread attributes. */
+- free (data.attr);
++ {
++ /* The only state we keep is the copy of the thread attributes. */
++ pthread_attr_destroy (data.attr);
++ free (data.attr);
++ }
+ }
+ return NULL;
+ }
+@@ -255,8 +258,14 @@ mq_notify (mqd_t mqdes, const struct sigevent *notification)
+ if (data.attr == NULL)
+ return -1;
+
+- memcpy (data.attr, notification->sigev_notify_attributes,
+- sizeof (pthread_attr_t));
++ int ret = __pthread_attr_copy (data.attr,
++ notification->sigev_notify_attributes);
++ if (ret != 0)
++ {
++ free (data.attr);
++ __set_errno (ret);
++ return -1;
++ }
+ }
+
+ /* Construct the new request. */
+@@ -269,8 +278,11 @@ mq_notify (mqd_t mqdes, const struct sigevent *notification)
+ int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se);
+
+ /* If it failed, free the allocated memory. */
+- if (__glibc_unlikely (retval != 0))
+- free (data.attr);
++ if (retval != 0 && data.attr != NULL)
++ {
++ pthread_attr_destroy (data.attr);
++ free (data.attr);
++ }
+
+ return retval;
+ }
diff --git a/sysdeps/unix/sysv/linux/msgctl.c b/sysdeps/unix/sysv/linux/msgctl.c
index 0776472d5e..a1f24ab242 100644
--- a/sysdeps/unix/sysv/linux/msgctl.c
+++ b/sysdeps/x86/tst-setjmp-cet.c
@@ -0,0 +1 @@
+#include <setjmp/tst-setjmp.c>
+diff --git a/sysdeps/x86_64/configure b/sysdeps/x86_64/configure
+old mode 100644
+new mode 100755
+index 84f82c2406..fc1840e23f
+--- a/sysdeps/x86_64/configure
++++ b/sysdeps/x86_64/configure
+@@ -107,39 +107,6 @@ if test x"$build_mathvec" = xnotset; then
+ build_mathvec=yes
+ fi
+
+-if test "$static_pie" = yes; then
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker static PIE support" >&5
+-$as_echo_n "checking for linker static PIE support... " >&6; }
+-if ${libc_cv_ld_static_pie+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- cat > conftest.s <<\EOF
+- .text
+- .global _start
+- .weak foo
+-_start:
+- leaq foo(%rip), %rax
+-EOF
+- libc_cv_pie_option="-Wl,-pie"
+- if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -nostartfiles -nostdlib $no_ssp $libc_cv_pie_option -o conftest conftest.s 1>&5'
+- { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+- (eval $ac_try) 2>&5
+- ac_status=$?
+- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+- test $ac_status = 0; }; }; then
+- libc_cv_ld_static_pie=yes
+- else
+- libc_cv_ld_static_pie=no
+- fi
+-rm -f conftest*
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_ld_static_pie" >&5
+-$as_echo "$libc_cv_ld_static_pie" >&6; }
+- if test "$libc_cv_ld_static_pie" != yes; then
+- as_fn_error $? "linker support for static PIE needed" "$LINENO" 5
+- fi
+-fi
+-
+ $as_echo "#define PI_STATIC_AND_HIDDEN 1" >>confdefs.h
+
+
+diff --git a/sysdeps/x86_64/configure.ac b/sysdeps/x86_64/configure.ac
+index cdaba0c075..611a7d9ba3 100644
+--- a/sysdeps/x86_64/configure.ac
++++ b/sysdeps/x86_64/configure.ac
+@@ -53,31 +53,6 @@ if test x"$build_mathvec" = xnotset; then
+ build_mathvec=yes
+ fi
+
+-dnl Check if linker supports static PIE with the fix for
+-dnl
+-dnl https://sourceware.org/bugzilla/show_bug.cgi?id=21782
+-dnl
+-if test "$static_pie" = yes; then
+- AC_CACHE_CHECK(for linker static PIE support, libc_cv_ld_static_pie, [dnl
+-cat > conftest.s <<\EOF
+- .text
+- .global _start
+- .weak foo
+-_start:
+- leaq foo(%rip), %rax
+-EOF
+- libc_cv_pie_option="-Wl,-pie"
+- if AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -nostartfiles -nostdlib $no_ssp $libc_cv_pie_option -o conftest conftest.s 1>&AS_MESSAGE_LOG_FD); then
+- libc_cv_ld_static_pie=yes
+- else
+- libc_cv_ld_static_pie=no
+- fi
+-rm -f conftest*])
+- if test "$libc_cv_ld_static_pie" != yes; then
+- AC_MSG_ERROR([linker support for static PIE needed])
+- fi
+-fi
+-
+ dnl It is always possible to access static and hidden symbols in an
+ dnl position independent way.
+ AC_DEFINE(PI_STATIC_AND_HIDDEN)
diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
index ca73d8fef9..363a749cb2 100644
--- a/sysdeps/x86_64/dl-machine.h