23int exec_arguments_child(
int argc,
char* argv[]) {
24 const char* arguments[] = {
"",
"--exec-arguments-child",
"",
"before",
"",
"after",
""};
25 const char* environment[] = {
"",
26 "PEDIGREE_EXEC_BEFORE=one",
28 "PEDIGREE_EXEC_AFTER=two",
29 "PEDIGREE_EXEC_DUP=first",
30 "PEDIGREE_EXEC_DUP=second",
32 if (argc != (
int)(
sizeof(arguments) /
sizeof(arguments[0]))) {
35 for (
size_t i = 0; i <
sizeof(arguments) /
sizeof(arguments[0]); ++i) {
36 if (strcmp(argv[i], arguments[i])) {
40 for (
size_t i = 0; i <
sizeof(environment) /
sizeof(environment[0]); ++i) {
41 if (!environ[i] || strcmp(environ[i], environment[i])) {
42 printf(
"exec environment entry %lu changed\n", (
unsigned long)i);
46 if (environ[
sizeof(environment) /
sizeof(environment[0])]) {
49 const char* duplicate = getenv(
"PEDIGREE_EXEC_DUP");
50 if (!duplicate || strcmp(duplicate,
"first")) {
54 const char commandLine[] =
"\0--exec-arguments-child\0\0before\0\0after\0\0";
56 const size_t chunks[] = {1,
sizeof(commandLine)};
57 for (
size_t pass = 0; pass <
sizeof(chunks) /
sizeof(chunks[0]); ++pass) {
58 const size_t chunk = chunks[pass];
59 int fd = open(
"/proc/self/cmdline", O_RDONLY);
63 char contents[
sizeof(commandLine)];
66 ssize_t result = read(fd, contents + length,
67 chunk <
sizeof(contents) - length ? chunk : sizeof(contents) - length);
68 if (result < 0 && errno == EINTR) {
78 length += (size_t)result;
79 if (length ==
sizeof(contents)) {
84 if (close(fd) || length !=
sizeof(commandLine) - 1 || memcmp(contents, commandLine, length)) {
91void test_exec_arguments(
const char* program) {
92 puts(
"Testing empty exec arguments, environment, and proc cmdline...");
99 char*
const arguments[] = {(
char*)
"", (
char*)
"--exec-arguments-child",
100 (
char*)
"", (
char*)
"before",
101 (
char*)
"", (
char*)
"after",
103 char*
const environment[] = {(
char*)
"",
104 (
char*)
"PEDIGREE_EXEC_BEFORE=one",
106 (
char*)
"PEDIGREE_EXEC_AFTER=two",
107 (
char*)
"PEDIGREE_EXEC_DUP=first",
108 (
char*)
"PEDIGREE_EXEC_DUP=second",
111 execve(program, arguments, environment);
117 waited = waitpid(child, &statusCode, 0);
118 }
while (waited < 0 && errno == EINTR);
119 if (waited != child || !WIFEXITED(statusCode) || WEXITSTATUS(statusCode)) {
120 printf(
"empty exec child status=%d\n", statusCode);
127static int write_fixture(
const char* path,
const char* contents,
size_t length, mode_t mode) {
128 int fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, mode);
133 while (written < length) {
134 ssize_t result = write(fd, contents + written, length - written);
135 if (result < 0 && errno == EINTR)
142 written += (size_t)result;
145 if (close(fd) || chmod(path, mode)) {
152static int write_script(
const char* path,
const char* shebang, mode_t mode) {
153 return write_fixture(path, shebang, strlen(shebang), mode);
156int exec_shebang_child(
int argc,
char* argv[]) {
157 if (!strcmp(argv[1],
"--exec-shebang-unexpected-child"))
160 if (!strcmp(argv[1],
"--exec-shebang-child beta\tgamma")) {
161 return !(argc == 10 && !strcmp(argv[0], argv[5]) && !strcmp(argv[2], argv[6]) &&
162 !strcmp(argv[3],
"alpha one") && !strcmp(argv[4], argv[7]) &&
163 !strcmp(argv[8],
"tail-one") && !strcmp(argv[9],
"tail two"));
166 if (!strcmp(argv[1],
"--exec-shebang-depth-child")) {
167 return !(argc == 12 && !strcmp(argv[0], argv[6]) && !strcmp(argv[2], argv[7]) &&
168 !strcmp(argv[3], argv[8]) && !strcmp(argv[4], argv[9]) && !strcmp(argv[5], argv[10]) &&
169 !strcmp(argv[11],
"depth-tail"));
175static int exec_returns_errno(
const char* path,
int expected) {
176 pid_t child = fork();
180 char*
const arguments[] = {(
char*)
"wrong-shebang-argv-zero", (
char*)
"tail", 0};
182 execv(path, arguments);
183 _exit(errno == expected ? 0 : 122);
187 return waitpid(child, &statusCode, 0) == child && WIFEXITED(statusCode) &&
188 !WEXITSTATUS(statusCode);
191static int exec_succeeds(
const char* path,
char*
const arguments[]) {
192 pid_t child = fork();
196 execv(path, arguments);
201 return waitpid(child, &statusCode, 0) == child && WIFEXITED(statusCode) &&
202 !WEXITSTATUS(statusCode);
205void test_exec_shebang(
const char* program) {
206 puts(
"Testing bounded shebang execution...");
209 char permissionScript[PATH_MAX];
210 char permissionOuter[PATH_MAX];
211 char permissionInterpreter[PATH_MAX];
212 char nestedOuter[PATH_MAX];
213 char nestedInterpreter[PATH_MAX];
214 char depthA[PATH_MAX];
215 char depthB[PATH_MAX];
216 char depthC[PATH_MAX];
217 char depthD[PATH_MAX];
218 char depthE[PATH_MAX];
219 char truncated[PATH_MAX];
220 char unterminated[PATH_MAX];
221 char malformed[PATH_MAX];
222 const int processId = getpid();
223 snprintf(permissionScript,
sizeof(permissionScript),
"/tmp/exec-shebang-permission-%d",
225 snprintf(permissionOuter,
sizeof(permissionOuter),
"/tmp/exec-shebang-permission-outer-%d",
227 snprintf(permissionInterpreter,
sizeof(permissionInterpreter),
228 "/tmp/exec-shebang-permission-interpreter-%d", processId);
229 snprintf(nestedOuter,
sizeof(nestedOuter),
"/tmp/exec-shebang-nested-outer-%d", processId);
230 snprintf(nestedInterpreter,
sizeof(nestedInterpreter),
"/tmp/exec-shebang-nested-interpreter-%d",
232 snprintf(depthA,
sizeof(depthA),
"/tmp/exec-shebang-depth-a-%d", processId);
233 snprintf(depthB,
sizeof(depthB),
"/tmp/exec-shebang-depth-b-%d", processId);
234 snprintf(depthC,
sizeof(depthC),
"/tmp/exec-shebang-depth-c-%d", processId);
235 snprintf(depthD,
sizeof(depthD),
"/tmp/exec-shebang-depth-d-%d", processId);
236 snprintf(depthE,
sizeof(depthE),
"/tmp/exec-shebang-depth-e-%d", processId);
237 snprintf(truncated,
sizeof(truncated),
"/tmp/exec-shebang-truncated-%d", processId);
238 snprintf(unterminated,
sizeof(unterminated),
"/tmp/exec-shebang-unterminated-%d", processId);
239 snprintf(malformed,
sizeof(malformed),
"/tmp/exec-shebang-malformed-%d", processId);
241 const char* fixtures[] = {permissionScript,
243 permissionInterpreter,
254 const size_t fixtureCount =
sizeof(fixtures) /
sizeof(fixtures[0]);
255 for (
size_t i = 0; i < fixtureCount; ++i)
259 char line[PATH_MAX + 128];
260 snprintf(line,
sizeof(line),
"#!%s\t--exec-shebang-unexpected-child\n", program);
262 write_script(permissionScript, line, 0600) || !exec_returns_errno(permissionScript, EACCES);
264 failed |= write_script(permissionInterpreter, line, 0600);
265 snprintf(line,
sizeof(line),
"#!%s\n", permissionInterpreter);
267 write_script(permissionOuter, line, 0700) || !exec_returns_errno(permissionOuter, EACCES);
269 snprintf(line,
sizeof(line),
"#!\t%s\t--exec-shebang-child beta\tgamma \t\n", program);
270 failed |= write_script(nestedInterpreter, line, 0700);
271 snprintf(line,
sizeof(line),
"#!\t%s\talpha one \t \n", nestedInterpreter);
272 failed |= write_script(nestedOuter, line, 0700);
273 char* nestedArguments[] = {(
char*)
"wrong-shebang-argv-zero",
281 failed |= !exec_succeeds(nestedOuter, nestedArguments);
283 snprintf(line,
sizeof(line),
"#!%s\t--exec-shebang-depth-child \t\n", program);
284 failed |= write_script(depthD, line, 0700);
285 snprintf(line,
sizeof(line),
"#!%s\n", depthD);
286 failed |= write_script(depthC, line, 0700);
287 snprintf(line,
sizeof(line),
"#!%s\n", depthC);
288 failed |= write_script(depthB, line, 0700);
289 snprintf(line,
sizeof(line),
"#!%s\n", depthB);
290 failed |= write_script(depthA, line, 0700);
291 char* depthArguments[] = {(
char*)
"wrong-shebang-depth-zero",
300 failed |= !exec_succeeds(depthA, depthArguments);
302 snprintf(line,
sizeof(line),
"#!%s\t--exec-shebang-unexpected-child\n", program);
303 failed |= write_script(depthE, line, 0700);
304 snprintf(line,
sizeof(line),
"#!%s\n", depthE);
305 failed |= write_script(depthD, line, 0700);
307 failed |= !exec_returns_errno(depthA, ELOOP);
309 char truncatedContents[320];
310 truncatedContents[0] =
'#';
311 truncatedContents[1] =
'!';
312 memset(truncatedContents + 2,
'a',
sizeof(truncatedContents) - 3);
313 truncatedContents[
sizeof(truncatedContents) - 1] =
'\n';
314 failed |= write_fixture(truncated, truncatedContents,
sizeof(truncatedContents), 0700) ||
315 !exec_returns_errno(truncated, ENOEXEC);
316 char unterminatedContents[256];
317 unterminatedContents[0] =
'#';
318 unterminatedContents[1] =
'!';
319 memset(unterminatedContents + 2,
'a',
sizeof(unterminatedContents) - 2);
320 failed |= write_fixture(unterminated, unterminatedContents,
sizeof(unterminatedContents), 0700) ||
321 !exec_returns_errno(unterminated, ENOEXEC);
322 failed |= write_script(malformed,
"#!\t \n", 0700) || !exec_returns_errno(malformed, ENOEXEC);
324 for (
size_t i = 0; i < fixtureCount; ++i) {
325 if (unlink(fixtures[i]) && errno != ENOENT)