diff options
author | Florian Weimer <fweimer@redhat.com> | 2019-10-08 13:04:15 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2019-10-08 16:11:13 +0200 |
commit | 32036901707cc86e16b8d9f65bb4686a9bbb0a5c (patch) | |
tree | 6561e14fbcc02fe5d7d943d721662e2d1eeaaae1 /posix | |
parent | ca602c1536ce2777f95c07525f3c42d78812e665 (diff) | |
download | glibc-32036901707cc86e16b8d9f65bb4686a9bbb0a5c.tar.gz |
wordexp: Split out command execution tests from posix/wordexp-test
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.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'posix')
-rw-r--r-- | posix/Makefile | 3 | ||||
-rw-r--r-- | posix/tst-wordexp-nocmd.c | 179 | ||||
-rw-r--r-- | posix/wordexp-test.c | 83 |
3 files changed, 181 insertions, 84 deletions
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; |