wordexp: Split out command execution tests from posix/wordexp-test
diff mbox series

Message ID 8736g3cxbq.fsf@oldenburg2.str.redhat.com
State New
Headers show
Series
  • wordexp: Split out command execution tests from posix/wordexp-test
Related show

Commit Message

Florian Weimer Oct. 8, 2019, 11:04 a.m. UTC
Once wordexp switches to posix_spawn, testing for command execution
based on fork handlers will not work anymore.  Therefore, move these
subtests into  a new test, posix/tst-wordexp-nocmd, which uses a
different form of command execution detection, based on PID
namespaces.

2019-10-08  Florian Weimer  <fweimer@redhat.com>

	* posix/Makefile (tests): Add tst-wordexp-nocmd.
	* posix/wordexp-test.c (__app_register_atfork): Remove function.
	(registered_forks): Remove variable.
	(register_fork): Remove function.
	(test_case): Remove WRDE_NOCMD tests.
	(main): Do not set up fork tracking.  Remove integer overflow in
	division tests.
	(testit): Do not check for unexpected forks.
	* posix/tst-wordexp-nocmd.c: New file containing the tests removed
	from posix/wordexp-test.c.

Comments

Adhemerval Zanella Oct. 8, 2019, 1:40 p.m. UTC | #1
On 08/10/2019 08:04, Florian Weimer wrote:
> Once wordexp switches to posix_spawn, testing for command execution
> based on fork handlers will not work anymore.  Therefore, move these
> subtests into  a new test, posix/tst-wordexp-nocmd, which uses a
> different form of command execution detection, based on PID
> namespaces.

I was exploring using ptrace to trap on PTRACE_O_TRACECLONE, it has the
slight advantage of work on older kernels than 3.8 (CLONE_NEWPID) and
not require CAP_SYS_ADMIN (although we would need to handle YAMA). I
don't have a strong opinion, assuming pid allocation will done always 
sequentially (as explicit done by core-api/idr in Linux), this method 
should be ok.

> 
> 2019-10-08  Florian Weimer  <fweimer@redhat.com>
> 
> 	* posix/Makefile (tests): Add tst-wordexp-nocmd.
> 	* posix/wordexp-test.c (__app_register_atfork): Remove function.
> 	(registered_forks): Remove variable.
> 	(register_fork): Remove function.
> 	(test_case): Remove WRDE_NOCMD tests.
> 	(main): Do not set up fork tracking.  Remove integer overflow in
> 	division tests.
> 	(testit): Do not check for unexpected forks.
> 	* posix/tst-wordexp-nocmd.c: New file containing the tests removed
> 	from posix/wordexp-test.c.

Ok.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> 
> diff --git a/posix/Makefile b/posix/Makefile
> index 0579596d9e..fe031eb450 100644
> --- a/posix/Makefile
> +++ b/posix/Makefile
> @@ -100,7 +100,8 @@ tests		:= test-errno tstgetopt testfnm runtests runptests \
>  		   tst-posix_fadvise tst-posix_fadvise64 \
>  		   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
> +		   bug-regex38 tst-regcomp-truncated tst-spawn-chdir \
> +		   tst-wordexp-nocmd
>  tests-internal	:= bug-regex5 bug-regex20 bug-regex33 \
>  		   tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \
>  		   tst-glob_lstat_compat tst-spawn4-compat

OK.

> diff --git a/posix/tst-wordexp-nocmd.c b/posix/tst-wordexp-nocmd.c
> new file mode 100644
> index 0000000000..b2f64c819f
> --- /dev/null
> +++ b/posix/tst-wordexp-nocmd.c
> @@ -0,0 +1,179 @@
> +/* Test for (lack of) command execution in wordexp.
> +   Copyright (C) 1997-2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* This test optionally counts PIDs in a PID namespace to detect
> +   forks.  Without kernel support for that, it will merely look at the
> +   error codes from wordexp to check that no command execution
> +   happens.  */
> +
> +#include <sched.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/namespace.h>
> +#include <support/xunistd.h>
> +#include <wordexp.h>
> +
> +/* Set to true if the test runs in a PID namespace and can therefore
> +   use next_pid below.  */
> +static bool pid_tests_supported;
> +
> +/* The next PID, as returned from next_pid below.  Only meaningful if
> +   pid_tests_supported.  */
> +static pid_t expected_pid;
> +
> +/* Allocate the next PID and return it.  The process is terminated.
> +   Note that the test itself advances the next PID.  */
> +static pid_t
> +next_pid (void)
> +{
> +  pid_t pid = xfork ();
> +  if (pid == 0)
> +    _exit (0);
> +  xwaitpid (pid, NULL, 0);
> +  return pid;
> +}
> +
> +/* Check that evaluating PATTERN with WRDE_NOCMD results in
> +   EXPECTED_ERROR.  */
> +static void
> +expect_failure (const char *pattern, int expected_error)
> +{
> +  printf ("info: testing pattern: %s\n", pattern);
> +  wordexp_t w;
> +  TEST_COMPARE (wordexp (pattern, &w, WRDE_NOCMD), expected_error);
> +  if (pid_tests_supported)
> +    TEST_COMPARE (expected_pid++, next_pid ());
> +}
> +

Ok.

> +/* Run all the tests.  Invoked with different IFS values.  */
> +static void
> +run_tests (void)
> +{
> +  /* Integer overflow in division.  */
> +  {
> +    static const char *const numbers[] = {
> +      "0",
> +      "1",
> +      "65536",
> +      "2147483648",
> +      "4294967296"
> +      "9223372036854775808",
> +      "18446744073709551616",
> +      "170141183460469231731687303715884105728",
> +      "340282366920938463463374607431768211456",
> +      NULL
> +    };
> +
> +    for (const char *const *num = numbers; *num != NULL; ++num)
> +      {
> +        wordexp_t w;
> +        char pattern[256];
> +        snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num);
> +        int ret = wordexp (pattern, &w, WRDE_NOCMD);
> +        if (ret == 0)
> +          {
> +            /* If the call is successful, the result must match the
> +               original number.  */
> +            TEST_COMPARE (w.we_wordc, 1);
> +            TEST_COMPARE_STRING (w.we_wordv[0], *num);
> +            TEST_COMPARE_STRING (w.we_wordv[1], NULL);
> +            wordfree (&w);
> +          }
> +        else
> +          /* Otherwise, the test must fail with a syntax error.  */
> +          TEST_COMPARE (ret, WRDE_SYNTAX);
> +
> +        /* In both cases, command execution is not permitted.  */
> +        if (pid_tests_supported)
> +          TEST_COMPARE (expected_pid++, next_pid ());
> +      }
> +  }

Ok.

> +
> +  /* (Lack of) command execution tests.  */
> +
> +  expect_failure ("$(ls)", WRDE_CMDSUB);
> +
> +  /* Test for CVE-2014-7817. We test 3 combinations of command
> +     substitution inside an arithmetic expression to make sure that
> +     no commands are executed and error is returned.  */
> +  expect_failure ("$((`echo 1`))", WRDE_CMDSUB);
> +  expect_failure ("$((1+`echo 1`))", WRDE_CMDSUB);
> +  expect_failure ("$((1+$((`echo 1`))))", WRDE_CMDSUB);
> +
> +  expect_failure ("$[1/0]", WRDE_SYNTAX); /* BZ 18100.  */
> +}
> +
> +static void
> +subprocess (void *closure)
> +{
> +  expected_pid = 2;
> +  if (pid_tests_supported)
> +    TEST_COMPARE (expected_pid++, next_pid ());
> +
> +  /* Check that triggering command execution via wordexp results in a
> +     PID increase.  */
> +  if (pid_tests_supported)
> +    {
> +      wordexp_t w;
> +      TEST_COMPARE (wordexp ("$(echo Test)", &w, 0), 0);
> +      TEST_COMPARE (w.we_wordc, 1);
> +      TEST_COMPARE_STRING (w.we_wordv[0], "Test");
> +      TEST_COMPARE_STRING (w.we_wordv[1], NULL);
> +      wordfree (&w);
> +
> +      pid_t n = next_pid ();
> +      printf ("info: self-test resulted in PID %d (processes created: %d)\n",
> +              (int) n, (int) (n - expected_pid));
> +      TEST_VERIFY (n > expected_pid);
> +      expected_pid = n + 1;
> +  }
> +
> +  puts ("info: testing without IFS");
> +  unsetenv ("IFS");
> +  run_tests ();
> +
> +  puts ("info: testing with IFS");
> +  TEST_COMPARE (setenv ("IFS", " \t\n", 1), 0);
> +  run_tests ();
> +}

Ok.

> +
> +static int
> +do_test (void)
> +{
> +  support_become_root ();
> +
> +#ifdef CLONE_NEWPID
> +  if (unshare (CLONE_NEWPID) != 0)
> +    printf ("warning: unshare (CLONE_NEW_PID) failed: %m\n"
> +            "warning: This leads to reduced test coverage.\n");
> +  else
> +    pid_tests_supported = true;
> +#else
> +  printf ("warning: CLONE_NEW_PID not available.\n"
> +          "warning: This leads to reduced test coverage.\n");
> +#endif
> +
> +  /* CLONE_NEWPID only has an effect after fork.  */
> +  support_isolate_in_subprocess (subprocess, NULL);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
> index 957184cf47..a4d8bcf1da 100644
> --- a/posix/wordexp-test.c
> +++ b/posix/wordexp-test.c
> @@ -31,23 +31,6 @@
>  
>  #define IFS " \n\t"
>  
> -extern int __register_atfork (void (*) (void), void (*) (void), void (*) (void), void *);
> -
> -static int __app_register_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void))
> -{
> -  return __register_atfork (prepare, parent, child, __dso_handle);
> -}
> -
> -/* Number of forks seen.  */
> -static int registered_forks;
> -
> -/* For each fork increment the fork count.  */
> -static void
> -register_fork (void)
> -{
> -  registered_forks++;
> -}
> -
>  struct test_case_struct
>  {
>    int retval;

Ok.

> @@ -217,7 +200,6 @@ struct test_case_struct
>      { WRDE_BADCHAR, NULL, "close-paren)", 0, 0, { NULL, }, IFS },
>      { WRDE_BADCHAR, NULL, "{open-brace", 0, 0, { NULL, }, IFS },
>      { WRDE_BADCHAR, NULL, "close-brace}", 0, 0, { NULL, }, IFS },
> -    { WRDE_CMDSUB, NULL, "$(ls)", WRDE_NOCMD, 0, { NULL, }, IFS },
>      { WRDE_BADVAL, NULL, "$var", WRDE_UNDEF, 0, { NULL, }, IFS },
>      { WRDE_BADVAL, NULL, "$9", WRDE_UNDEF, 0, { NULL, }, IFS },
>      { WRDE_SYNTAX, NULL, "$[50+20))", 0, 0, { NULL, }, IFS },

Ok.

> @@ -227,17 +209,10 @@ struct test_case_struct
>      { WRDE_SYNTAX, NULL, "$((2+))", 0, 0, { NULL, }, IFS },
>      { WRDE_SYNTAX, NULL, "`", 0, 0, { NULL, }, IFS },
>      { WRDE_SYNTAX, NULL, "$((010+4+))", 0, 0, { NULL }, IFS },
> -    /* Test for CVE-2014-7817. We test 3 combinations of command
> -       substitution inside an arithmetic expression to make sure that
> -       no commands are executed and error is returned.  */
> -    { WRDE_CMDSUB, NULL, "$((`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS },
> -    { WRDE_CMDSUB, NULL, "$((1+`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS },
> -    { WRDE_CMDSUB, NULL, "$((1+$((`echo 1`))))", WRDE_NOCMD, 0, { NULL, }, IFS },
>  
>      { WRDE_SYNTAX, NULL, "`\\", 0, 0, { NULL, }, IFS },     /* BZ 18042  */
>      { WRDE_SYNTAX, NULL, "${", 0, 0, { NULL, }, IFS },      /* BZ 18043  */
>      { WRDE_SYNTAX, NULL, "L${a:", 0, 0, { NULL, }, IFS },   /* BZ 18043#c4  */
> -    { WRDE_SYNTAX, NULL, "$[1/0]", WRDE_NOCMD, 0, {NULL, }, IFS }, /* BZ 18100 */
>  
>      { -1, NULL, NULL, 0, 0, { NULL, }, IFS },
>    };

Ok.

> @@ -290,15 +265,6 @@ main (int argc, char *argv[])
>  	  return -1;
>      }
>  
> -  /* If we are not allowed to do command substitution, we install
> -     fork handlers to verify that no forks happened.  No forks should
> -     happen at all if command substitution is disabled.  */
> -  if (__app_register_atfork (register_fork, NULL, NULL) != 0)
> -    {
> -      printf ("Failed to register fork handler.\n");
> -      return -1;
> -    }
> -
>    for (test = 0; test_case[test].retval != -1; test++)
>      if (testit (&test_case[test]))
>        ++fail;

Ok.

> @@ -363,45 +329,6 @@ main (int argc, char *argv[])
>  	++fail;
>      }
>  
> -  /* Integer overflow in division.  */
> -  {
> -    static const char *const numbers[] = {
> -      "0",
> -      "1",
> -      "65536",
> -      "2147483648",
> -      "4294967296"
> -      "9223372036854775808",
> -      "18446744073709551616",
> -      "170141183460469231731687303715884105728",
> -      "340282366920938463463374607431768211456",
> -      NULL
> -    };
> -
> -    for (const char *const *num = numbers; *num; ++num)
> -      {
> -	wordexp_t p;
> -	char pattern[256];
> -	snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num);
> -	int ret = wordexp (pattern, &p, WRDE_NOCMD);
> -	if (ret == 0)
> -	  {
> -	    if (p.we_wordc != 1 || strcmp (p.we_wordv[0], *num) != 0)
> -	      {
> -		printf ("Integer overflow for \"%s\" failed", pattern);
> -		++fail;
> -	      }
> -	    wordfree (&p);
> -	  }
> -	else if (ret != WRDE_SYNTAX)
> -	  {
> -	    printf ("Integer overflow for \"%s\" failed with %d",
> -		    pattern, ret);
> -	    ++fail;
> -	  }
> -      }
> -  }
> -
>    puts ("tests completed, now cleaning up");
>  
>    /* Clean up */

Ok.

> @@ -472,9 +399,6 @@ testit (struct test_case_struct *tc)
>    fflush (NULL);
>    const char *words = at_page_end (tc->words);
>  
> -  if (tc->flags & WRDE_NOCMD)
> -    registered_forks = 0;
> -
>    if (tc->flags & WRDE_APPEND)
>      {
>        /* initial wordexp() call, to be appended to */
> @@ -486,13 +410,6 @@ testit (struct test_case_struct *tc)
>      }
>    retval = wordexp (words, &we, tc->flags);
>  
> -  if ((tc->flags & WRDE_NOCMD)
> -      && (registered_forks > 0))
> -    {
> -	  printf ("FAILED fork called for WRDE_NOCMD\n");
> -	  return 1;
> -    }
> -
>    if (tc->flags & WRDE_DOOFFS)
>        start_offs = sav_we.we_offs;
>  
> 

Ok.
Joseph Myers Oct. 8, 2019, 3:58 p.m. UTC | #2
On Tue, 8 Oct 2019, Florian Weimer wrote:

> +#ifdef CLONE_NEWPID
> +  if (unshare (CLONE_NEWPID) != 0)
> +    printf ("warning: unshare (CLONE_NEW_PID) failed: %m\n"
> +            "warning: This leads to reduced test coverage.\n");
> +  else
> +    pid_tests_supported = true;
> +#else
> +  printf ("warning: CLONE_NEW_PID not available.\n"

The messages are saying CLONE_NEW_PID when the actual code is using 
CLONE_NEWPID.
Florian Weimer Oct. 8, 2019, 5 p.m. UTC | #3
* Joseph Myers:

> On Tue, 8 Oct 2019, Florian Weimer wrote:
>
>> +#ifdef CLONE_NEWPID
>> +  if (unshare (CLONE_NEWPID) != 0)
>> +    printf ("warning: unshare (CLONE_NEW_PID) failed: %m\n"
>> +            "warning: This leads to reduced test coverage.\n");
>> +  else
>> +    pid_tests_supported = true;
>> +#else
>> +  printf ("warning: CLONE_NEW_PID not available.\n"
>
> The messages are saying CLONE_NEW_PID when the actual code is using 
> CLONE_NEWPID.

Sorry about that.  Fixed by the commit below.

Thanks,
Florian

8<------------------------------------------------------------------8<
Subject: posix/tst-wordexp-nocmd: Fix diagnostics output in test

2019-10-08  Florian Weimer  <fweimer@redhat.com>

	* posix/tst-wordexp-nocmd.c (do_test): Fix diagnostics output.

diff --git a/posix/tst-wordexp-nocmd.c b/posix/tst-wordexp-nocmd.c
index b2f64c819f..cb6033e15f 100644
--- a/posix/tst-wordexp-nocmd.c
+++ b/posix/tst-wordexp-nocmd.c
@@ -161,12 +161,12 @@ do_test (void)
 
 #ifdef CLONE_NEWPID
   if (unshare (CLONE_NEWPID) != 0)
-    printf ("warning: unshare (CLONE_NEW_PID) failed: %m\n"
+    printf ("warning: unshare (CLONE_NEWPID) failed: %m\n"
             "warning: This leads to reduced test coverage.\n");
   else
     pid_tests_supported = true;
 #else
-  printf ("warning: CLONE_NEW_PID not available.\n"
+  printf ("warning: CLONE_NEWPID not available.\n"
           "warning: This leads to reduced test coverage.\n");
 #endif

Patch
diff mbox series

diff --git a/posix/Makefile b/posix/Makefile
index 0579596d9e..fe031eb450 100644
--- a/posix/Makefile
+++ b/posix/Makefile
@@ -100,7 +100,8 @@  tests		:= test-errno tstgetopt testfnm runtests runptests \
 		   tst-posix_fadvise tst-posix_fadvise64 \
 		   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
+		   bug-regex38 tst-regcomp-truncated tst-spawn-chdir \
+		   tst-wordexp-nocmd
 tests-internal	:= bug-regex5 bug-regex20 bug-regex33 \
 		   tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \
 		   tst-glob_lstat_compat tst-spawn4-compat
diff --git a/posix/tst-wordexp-nocmd.c b/posix/tst-wordexp-nocmd.c
new file mode 100644
index 0000000000..b2f64c819f
--- /dev/null
+++ b/posix/tst-wordexp-nocmd.c
@@ -0,0 +1,179 @@ 
+/* Test for (lack of) command execution in wordexp.
+   Copyright (C) 1997-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* This test optionally counts PIDs in a PID namespace to detect
+   forks.  Without kernel support for that, it will merely look at the
+   error codes from wordexp to check that no command execution
+   happens.  */
+
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xunistd.h>
+#include <wordexp.h>
+
+/* Set to true if the test runs in a PID namespace and can therefore
+   use next_pid below.  */
+static bool pid_tests_supported;
+
+/* The next PID, as returned from next_pid below.  Only meaningful if
+   pid_tests_supported.  */
+static pid_t expected_pid;
+
+/* Allocate the next PID and return it.  The process is terminated.
+   Note that the test itself advances the next PID.  */
+static pid_t
+next_pid (void)
+{
+  pid_t pid = xfork ();
+  if (pid == 0)
+    _exit (0);
+  xwaitpid (pid, NULL, 0);
+  return pid;
+}
+
+/* Check that evaluating PATTERN with WRDE_NOCMD results in
+   EXPECTED_ERROR.  */
+static void
+expect_failure (const char *pattern, int expected_error)
+{
+  printf ("info: testing pattern: %s\n", pattern);
+  wordexp_t w;
+  TEST_COMPARE (wordexp (pattern, &w, WRDE_NOCMD), expected_error);
+  if (pid_tests_supported)
+    TEST_COMPARE (expected_pid++, next_pid ());
+}
+
+/* Run all the tests.  Invoked with different IFS values.  */
+static void
+run_tests (void)
+{
+  /* Integer overflow in division.  */
+  {
+    static const char *const numbers[] = {
+      "0",
+      "1",
+      "65536",
+      "2147483648",
+      "4294967296"
+      "9223372036854775808",
+      "18446744073709551616",
+      "170141183460469231731687303715884105728",
+      "340282366920938463463374607431768211456",
+      NULL
+    };
+
+    for (const char *const *num = numbers; *num != NULL; ++num)
+      {
+        wordexp_t w;
+        char pattern[256];
+        snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num);
+        int ret = wordexp (pattern, &w, WRDE_NOCMD);
+        if (ret == 0)
+          {
+            /* If the call is successful, the result must match the
+               original number.  */
+            TEST_COMPARE (w.we_wordc, 1);
+            TEST_COMPARE_STRING (w.we_wordv[0], *num);
+            TEST_COMPARE_STRING (w.we_wordv[1], NULL);
+            wordfree (&w);
+          }
+        else
+          /* Otherwise, the test must fail with a syntax error.  */
+          TEST_COMPARE (ret, WRDE_SYNTAX);
+
+        /* In both cases, command execution is not permitted.  */
+        if (pid_tests_supported)
+          TEST_COMPARE (expected_pid++, next_pid ());
+      }
+  }
+
+  /* (Lack of) command execution tests.  */
+
+  expect_failure ("$(ls)", WRDE_CMDSUB);
+
+  /* Test for CVE-2014-7817. We test 3 combinations of command
+     substitution inside an arithmetic expression to make sure that
+     no commands are executed and error is returned.  */
+  expect_failure ("$((`echo 1`))", WRDE_CMDSUB);
+  expect_failure ("$((1+`echo 1`))", WRDE_CMDSUB);
+  expect_failure ("$((1+$((`echo 1`))))", WRDE_CMDSUB);
+
+  expect_failure ("$[1/0]", WRDE_SYNTAX); /* BZ 18100.  */
+}
+
+static void
+subprocess (void *closure)
+{
+  expected_pid = 2;
+  if (pid_tests_supported)
+    TEST_COMPARE (expected_pid++, next_pid ());
+
+  /* Check that triggering command execution via wordexp results in a
+     PID increase.  */
+  if (pid_tests_supported)
+    {
+      wordexp_t w;
+      TEST_COMPARE (wordexp ("$(echo Test)", &w, 0), 0);
+      TEST_COMPARE (w.we_wordc, 1);
+      TEST_COMPARE_STRING (w.we_wordv[0], "Test");
+      TEST_COMPARE_STRING (w.we_wordv[1], NULL);
+      wordfree (&w);
+
+      pid_t n = next_pid ();
+      printf ("info: self-test resulted in PID %d (processes created: %d)\n",
+              (int) n, (int) (n - expected_pid));
+      TEST_VERIFY (n > expected_pid);
+      expected_pid = n + 1;
+  }
+
+  puts ("info: testing without IFS");
+  unsetenv ("IFS");
+  run_tests ();
+
+  puts ("info: testing with IFS");
+  TEST_COMPARE (setenv ("IFS", " \t\n", 1), 0);
+  run_tests ();
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+
+#ifdef CLONE_NEWPID
+  if (unshare (CLONE_NEWPID) != 0)
+    printf ("warning: unshare (CLONE_NEW_PID) failed: %m\n"
+            "warning: This leads to reduced test coverage.\n");
+  else
+    pid_tests_supported = true;
+#else
+  printf ("warning: CLONE_NEW_PID not available.\n"
+          "warning: This leads to reduced test coverage.\n");
+#endif
+
+  /* CLONE_NEWPID only has an effect after fork.  */
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
index 957184cf47..a4d8bcf1da 100644
--- a/posix/wordexp-test.c
+++ b/posix/wordexp-test.c
@@ -31,23 +31,6 @@ 
 
 #define IFS " \n\t"
 
-extern int __register_atfork (void (*) (void), void (*) (void), void (*) (void), void *);
-
-static int __app_register_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void))
-{
-  return __register_atfork (prepare, parent, child, __dso_handle);
-}
-
-/* Number of forks seen.  */
-static int registered_forks;
-
-/* For each fork increment the fork count.  */
-static void
-register_fork (void)
-{
-  registered_forks++;
-}
-
 struct test_case_struct
 {
   int retval;
@@ -217,7 +200,6 @@  struct test_case_struct
     { WRDE_BADCHAR, NULL, "close-paren)", 0, 0, { NULL, }, IFS },
     { WRDE_BADCHAR, NULL, "{open-brace", 0, 0, { NULL, }, IFS },
     { WRDE_BADCHAR, NULL, "close-brace}", 0, 0, { NULL, }, IFS },
-    { WRDE_CMDSUB, NULL, "$(ls)", WRDE_NOCMD, 0, { NULL, }, IFS },
     { WRDE_BADVAL, NULL, "$var", WRDE_UNDEF, 0, { NULL, }, IFS },
     { WRDE_BADVAL, NULL, "$9", WRDE_UNDEF, 0, { NULL, }, IFS },
     { WRDE_SYNTAX, NULL, "$[50+20))", 0, 0, { NULL, }, IFS },
@@ -227,17 +209,10 @@  struct test_case_struct
     { WRDE_SYNTAX, NULL, "$((2+))", 0, 0, { NULL, }, IFS },
     { WRDE_SYNTAX, NULL, "`", 0, 0, { NULL, }, IFS },
     { WRDE_SYNTAX, NULL, "$((010+4+))", 0, 0, { NULL }, IFS },
-    /* Test for CVE-2014-7817. We test 3 combinations of command
-       substitution inside an arithmetic expression to make sure that
-       no commands are executed and error is returned.  */
-    { WRDE_CMDSUB, NULL, "$((`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS },
-    { WRDE_CMDSUB, NULL, "$((1+`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS },
-    { WRDE_CMDSUB, NULL, "$((1+$((`echo 1`))))", WRDE_NOCMD, 0, { NULL, }, IFS },
 
     { WRDE_SYNTAX, NULL, "`\\", 0, 0, { NULL, }, IFS },     /* BZ 18042  */
     { WRDE_SYNTAX, NULL, "${", 0, 0, { NULL, }, IFS },      /* BZ 18043  */
     { WRDE_SYNTAX, NULL, "L${a:", 0, 0, { NULL, }, IFS },   /* BZ 18043#c4  */
-    { WRDE_SYNTAX, NULL, "$[1/0]", WRDE_NOCMD, 0, {NULL, }, IFS }, /* BZ 18100 */
 
     { -1, NULL, NULL, 0, 0, { NULL, }, IFS },
   };
@@ -290,15 +265,6 @@  main (int argc, char *argv[])
 	  return -1;
     }
 
-  /* If we are not allowed to do command substitution, we install
-     fork handlers to verify that no forks happened.  No forks should
-     happen at all if command substitution is disabled.  */
-  if (__app_register_atfork (register_fork, NULL, NULL) != 0)
-    {
-      printf ("Failed to register fork handler.\n");
-      return -1;
-    }
-
   for (test = 0; test_case[test].retval != -1; test++)
     if (testit (&test_case[test]))
       ++fail;
@@ -363,45 +329,6 @@  main (int argc, char *argv[])
 	++fail;
     }
 
-  /* Integer overflow in division.  */
-  {
-    static const char *const numbers[] = {
-      "0",
-      "1",
-      "65536",
-      "2147483648",
-      "4294967296"
-      "9223372036854775808",
-      "18446744073709551616",
-      "170141183460469231731687303715884105728",
-      "340282366920938463463374607431768211456",
-      NULL
-    };
-
-    for (const char *const *num = numbers; *num; ++num)
-      {
-	wordexp_t p;
-	char pattern[256];
-	snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num);
-	int ret = wordexp (pattern, &p, WRDE_NOCMD);
-	if (ret == 0)
-	  {
-	    if (p.we_wordc != 1 || strcmp (p.we_wordv[0], *num) != 0)
-	      {
-		printf ("Integer overflow for \"%s\" failed", pattern);
-		++fail;
-	      }
-	    wordfree (&p);
-	  }
-	else if (ret != WRDE_SYNTAX)
-	  {
-	    printf ("Integer overflow for \"%s\" failed with %d",
-		    pattern, ret);
-	    ++fail;
-	  }
-      }
-  }
-
   puts ("tests completed, now cleaning up");
 
   /* Clean up */
@@ -472,9 +399,6 @@  testit (struct test_case_struct *tc)
   fflush (NULL);
   const char *words = at_page_end (tc->words);
 
-  if (tc->flags & WRDE_NOCMD)
-    registered_forks = 0;
-
   if (tc->flags & WRDE_APPEND)
     {
       /* initial wordexp() call, to be appended to */
@@ -486,13 +410,6 @@  testit (struct test_case_struct *tc)
     }
   retval = wordexp (words, &we, tc->flags);
 
-  if ((tc->flags & WRDE_NOCMD)
-      && (registered_forks > 0))
-    {
-	  printf ("FAILED fork called for WRDE_NOCMD\n");
-	  return 1;
-    }
-
   if (tc->flags & WRDE_DOOFFS)
       start_offs = sav_we.we_offs;