glibc/posix/tst-wordexp-nocmd.c

180 lines
5.2 KiB
C

/* Test for (lack of) command execution in wordexp.
Copyright (C) 1997-2023 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_NEWPID) failed: %m\n"
"warning: This leads to reduced test coverage.\n");
else
pid_tests_supported = true;
#else
printf ("warning: CLONE_NEWPID 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>