+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