1/* Test for (lack of) command execution in wordexp.
2 Copyright (C) 1997-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* This test optionally counts PIDs in a PID namespace to detect
20 forks. Without kernel support for that, it will merely look at the
21 error codes from wordexp to check that no command execution
22 happens. */
23
24#include <sched.h>
25#include <stdbool.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <support/check.h>
29#include <support/namespace.h>
30#include <support/xunistd.h>
31#include <wordexp.h>
32
33/* Set to true if the test runs in a PID namespace and can therefore
34 use next_pid below. */
35static bool pid_tests_supported;
36
37/* The next PID, as returned from next_pid below. Only meaningful if
38 pid_tests_supported. */
39static pid_t expected_pid;
40
41/* Allocate the next PID and return it. The process is terminated.
42 Note that the test itself advances the next PID. */
43static pid_t
44next_pid (void)
45{
46 pid_t pid = xfork ();
47 if (pid == 0)
48 _exit (0);
49 xwaitpid (pid, NULL, flags: 0);
50 return pid;
51}
52
53/* Check that evaluating PATTERN with WRDE_NOCMD results in
54 EXPECTED_ERROR. */
55static void
56expect_failure (const char *pattern, int expected_error)
57{
58 printf (format: "info: testing pattern: %s\n", pattern);
59 wordexp_t w;
60 TEST_COMPARE (wordexp (pattern, &w, WRDE_NOCMD), expected_error);
61 if (pid_tests_supported)
62 TEST_COMPARE (expected_pid++, next_pid ());
63}
64
65/* Run all the tests. Invoked with different IFS values. */
66static void
67run_tests (void)
68{
69 /* Integer overflow in division. */
70 {
71 static const char *const numbers[] = {
72 "0",
73 "1",
74 "65536",
75 "2147483648",
76 "4294967296"
77 "9223372036854775808",
78 "18446744073709551616",
79 "170141183460469231731687303715884105728",
80 "340282366920938463463374607431768211456",
81 NULL
82 };
83
84 for (const char *const *num = numbers; *num != NULL; ++num)
85 {
86 wordexp_t w;
87 char pattern[256];
88 snprintf (s: pattern, maxlen: sizeof (pattern), format: "$[(-%s)/(-1)]", *num);
89 int ret = wordexp (words: pattern, pwordexp: &w, flags: WRDE_NOCMD);
90 if (ret == 0)
91 {
92 /* If the call is successful, the result must match the
93 original number. */
94 TEST_COMPARE (w.we_wordc, 1);
95 TEST_COMPARE_STRING (w.we_wordv[0], *num);
96 TEST_COMPARE_STRING (w.we_wordv[1], NULL);
97 wordfree (&w);
98 }
99 else
100 /* Otherwise, the test must fail with a syntax error. */
101 TEST_COMPARE (ret, WRDE_SYNTAX);
102
103 /* In both cases, command execution is not permitted. */
104 if (pid_tests_supported)
105 TEST_COMPARE (expected_pid++, next_pid ());
106 }
107 }
108
109 /* (Lack of) command execution tests. */
110
111 expect_failure (pattern: "$(ls)", expected_error: WRDE_CMDSUB);
112
113 /* Test for CVE-2014-7817. We test 3 combinations of command
114 substitution inside an arithmetic expression to make sure that
115 no commands are executed and error is returned. */
116 expect_failure (pattern: "$((`echo 1`))", expected_error: WRDE_CMDSUB);
117 expect_failure (pattern: "$((1+`echo 1`))", expected_error: WRDE_CMDSUB);
118 expect_failure (pattern: "$((1+$((`echo 1`))))", expected_error: WRDE_CMDSUB);
119
120 expect_failure (pattern: "$[1/0]", expected_error: WRDE_SYNTAX); /* BZ 18100. */
121}
122
123static void
124subprocess (void *closure)
125{
126 expected_pid = 2;
127 if (pid_tests_supported)
128 TEST_COMPARE (expected_pid++, next_pid ());
129
130 /* Check that triggering command execution via wordexp results in a
131 PID increase. */
132 if (pid_tests_supported)
133 {
134 wordexp_t w;
135 TEST_COMPARE (wordexp ("$(echo Test)", &w, 0), 0);
136 TEST_COMPARE (w.we_wordc, 1);
137 TEST_COMPARE_STRING (w.we_wordv[0], "Test");
138 TEST_COMPARE_STRING (w.we_wordv[1], NULL);
139 wordfree (&w);
140
141 pid_t n = next_pid ();
142 printf (format: "info: self-test resulted in PID %d (processes created: %d)\n",
143 (int) n, (int) (n - expected_pid));
144 TEST_VERIFY (n > expected_pid);
145 expected_pid = n + 1;
146 }
147
148 puts (s: "info: testing without IFS");
149 unsetenv (name: "IFS");
150 run_tests ();
151
152 puts (s: "info: testing with IFS");
153 TEST_COMPARE (setenv ("IFS", " \t\n", 1), 0);
154 run_tests ();
155}
156
157static int
158do_test (void)
159{
160 support_become_root ();
161
162#ifdef CLONE_NEWPID
163 if (unshare (CLONE_NEWPID) != 0)
164 printf (format: "warning: unshare (CLONE_NEWPID) failed: %m\n"
165 "warning: This leads to reduced test coverage.\n");
166 else
167 pid_tests_supported = true;
168#else
169 printf ("warning: CLONE_NEWPID not available.\n"
170 "warning: This leads to reduced test coverage.\n");
171#endif
172
173 /* CLONE_NEWPID only has an effect after fork. */
174 support_isolate_in_subprocess (callback: subprocess, NULL);
175
176 return 0;
177}
178
179#include <support/test-driver.c>
180

source code of glibc/posix/tst-wordexp-nocmd.c