1 /*
2 *
3 * honggfuzz - routines dealing with subprocesses
4 * -----------------------------------------
5 *
6 * Author: Robert Swiecki <swiecki@google.com>
7 * Felix Gröbert <groebert@google.com>
8 *
9 * Copyright 2010-2018 by Google Inc. All Rights Reserved.
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may
12 * not use this file except in compliance with the License. You may obtain
13 * a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
20 * implied. See the License for the specific language governing
21 * permissions and limitations under the License.
22 *
23 */
24
25 #include "subproc.h"
26
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <inttypes.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/resource.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40
41 #include "arch.h"
42 #include "fuzz.h"
43 #include "libhfcommon/common.h"
44 #include "libhfcommon/files.h"
45 #include "libhfcommon/log.h"
46 #include "libhfcommon/util.h"
47
48 extern char** environ;
49
subproc_StatusToStr(int status,char * str,size_t len)50 const char* subproc_StatusToStr(int status, char* str, size_t len) {
51 if (WIFEXITED(status)) {
52 snprintf(str, len, "EXITED, exit code: %d", WEXITSTATUS(status));
53 return str;
54 }
55
56 if (WIFSIGNALED(status)) {
57 snprintf(
58 str, len, "SIGNALED, signal: %d (%s)", WTERMSIG(status), strsignal(WTERMSIG(status)));
59 return str;
60 }
61 if (WIFCONTINUED(status)) {
62 snprintf(str, len, "CONTINUED");
63 return str;
64 }
65
66 if (!WIFSTOPPED(status)) {
67 snprintf(str, len, "UNKNOWN STATUS: %d", status);
68 return str;
69 }
70
71 /* Must be in a stopped state */
72 if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
73 snprintf(str, len, "STOPPED (linux syscall): %d (%s)", WSTOPSIG(status),
74 strsignal(WSTOPSIG(status)));
75 return str;
76 }
77 #if defined(PTRACE_EVENT_STOP)
78 #define __LINUX_WPTRACEEVENT(x) ((x & 0xff0000) >> 16)
79 if (WSTOPSIG(status) == SIGTRAP && __LINUX_WPTRACEEVENT(status) != 0) {
80 switch (__LINUX_WPTRACEEVENT(status)) {
81 case PTRACE_EVENT_FORK:
82 snprintf(str, len, "EVENT (Linux) - fork - with signal: %d (%s)", WSTOPSIG(status),
83 strsignal(WSTOPSIG(status)));
84 return str;
85 case PTRACE_EVENT_VFORK:
86 snprintf(str, len, "EVENT (Linux) - vfork - with signal: %d (%s)", WSTOPSIG(status),
87 strsignal(WSTOPSIG(status)));
88 return str;
89 case PTRACE_EVENT_CLONE:
90 snprintf(str, len, "EVENT (Linux) - clone - with signal: %d (%s)", WSTOPSIG(status),
91 strsignal(WSTOPSIG(status)));
92 return str;
93 case PTRACE_EVENT_EXEC:
94 snprintf(str, len, "EVENT (Linux) - exec - with signal: %d (%s)", WSTOPSIG(status),
95 strsignal(WSTOPSIG(status)));
96 return str;
97 case PTRACE_EVENT_VFORK_DONE:
98 snprintf(str, len, "EVENT (Linux) - vfork_done - with signal: %d (%s)",
99 WSTOPSIG(status), strsignal(WSTOPSIG(status)));
100 return str;
101 case PTRACE_EVENT_EXIT:
102 snprintf(str, len, "EVENT (Linux) - exit - with signal: %d (%s)", WSTOPSIG(status),
103 strsignal(WSTOPSIG(status)));
104 return str;
105 case PTRACE_EVENT_SECCOMP:
106 snprintf(str, len, "EVENT (Linux) - seccomp - with signal: %d (%s)",
107 WSTOPSIG(status), strsignal(WSTOPSIG(status)));
108 return str;
109 case PTRACE_EVENT_STOP:
110 snprintf(str, len, "EVENT (Linux) - stop - with signal: %d (%s)", WSTOPSIG(status),
111 strsignal(WSTOPSIG(status)));
112 return str;
113 default:
114 snprintf(str, len, "EVENT (Linux) UNKNOWN (%d): with signal: %d (%s)",
115 __LINUX_WPTRACEEVENT(status), WSTOPSIG(status), strsignal(WSTOPSIG(status)));
116 return str;
117 }
118 }
119 #endif /* defined(PTRACE_EVENT_STOP) */
120
121 snprintf(
122 str, len, "STOPPED with signal: %d (%s)", WSTOPSIG(status), strsignal(WSTOPSIG(status)));
123 return str;
124 }
125
subproc_persistentSendFileIndicator(run_t * run)126 static bool subproc_persistentSendFileIndicator(run_t* run) {
127 uint64_t len = (uint64_t)run->dynamicFileSz;
128 if (!files_sendToSocketNB(run->persistentSock, (uint8_t*)&len, sizeof(len))) {
129 PLOG_W("files_sendToSocketNB(len=%zu)", sizeof(len));
130 return false;
131 }
132 return true;
133 }
134
subproc_persistentGetReady(run_t * run)135 static bool subproc_persistentGetReady(run_t* run) {
136 uint8_t rcv;
137 if (recv(run->persistentSock, &rcv, sizeof(rcv), MSG_DONTWAIT) != sizeof(rcv)) {
138 return false;
139 }
140 if (rcv != HFReadyTag) {
141 LOG_E("Received invalid message from the persistent process: '%c' (0x%" PRIx8
142 ") , expected '%c' (0x%" PRIx8 ")",
143 rcv, rcv, HFReadyTag, HFReadyTag);
144 return false;
145 }
146 return true;
147 }
148
subproc_persistentModeStateMachine(run_t * run)149 bool subproc_persistentModeStateMachine(run_t* run) {
150 if (!run->global->exe.persistent) {
151 return false;
152 }
153
154 for (;;) {
155 switch (run->runState) {
156 case _HF_RS_WAITING_FOR_INITIAL_READY: {
157 if (!subproc_persistentGetReady(run)) {
158 return false;
159 }
160 run->runState = _HF_RS_SEND_DATA;
161 }; break;
162 case _HF_RS_SEND_DATA: {
163 if (!subproc_persistentSendFileIndicator(run)) {
164 LOG_E("Could not send the file size indicator to the persistent process. "
165 "Killing the process pid=%d",
166 (int)run->pid);
167 kill(run->pid, SIGKILL);
168 return false;
169 }
170 run->runState = _HF_RS_WAITING_FOR_READY;
171 }; break;
172 case _HF_RS_WAITING_FOR_READY: {
173 if (!subproc_persistentGetReady(run)) {
174 return false;
175 }
176 run->runState = _HF_RS_SEND_DATA;
177 /* The current persistent round is done */
178 return true;
179 }; break;
180 default:
181 LOG_F("Unknown runState: %d", run->runState);
182 }
183 }
184 }
185
subproc_PrepareExecv(run_t * run)186 static bool subproc_PrepareExecv(run_t* run) {
187 /*
188 * The address space limit. If big enough - roughly the size of RAM used
189 */
190 #ifdef RLIMIT_AS
191 if (run->global->exe.asLimit) {
192 const struct rlimit rl = {
193 .rlim_cur = run->global->exe.asLimit * 1024ULL * 1024ULL,
194 .rlim_max = run->global->exe.asLimit * 1024ULL * 1024ULL,
195 };
196 if (setrlimit(RLIMIT_AS, &rl) == -1) {
197 PLOG_W("Couldn't enforce the RLIMIT_AS resource limit, ignoring");
198 }
199 }
200 #endif /* ifdef RLIMIT_AS */
201 #ifdef RLIMIT_RSS
202 if (run->global->exe.rssLimit) {
203 const struct rlimit rl = {
204 .rlim_cur = run->global->exe.rssLimit * 1024ULL * 1024ULL,
205 .rlim_max = run->global->exe.rssLimit * 1024ULL * 1024ULL,
206 };
207 if (setrlimit(RLIMIT_RSS, &rl) == -1) {
208 PLOG_W("Couldn't enforce the RLIMIT_RSS resource limit, ignoring");
209 }
210 }
211 #endif /* ifdef RLIMIT_RSS */
212 #ifdef RLIMIT_DATA
213 if (run->global->exe.dataLimit) {
214 const struct rlimit rl = {
215 .rlim_cur = run->global->exe.dataLimit * 1024ULL * 1024ULL,
216 .rlim_max = run->global->exe.dataLimit * 1024ULL * 1024ULL,
217 };
218 if (setrlimit(RLIMIT_DATA, &rl) == -1) {
219 PLOG_W("Couldn't enforce the RLIMIT_DATA resource limit, ignoring");
220 }
221 }
222 #endif /* ifdef RLIMIT_DATA */
223 #ifdef RLIMIT_CORE
224 const struct rlimit rl = {
225 .rlim_cur = run->global->exe.coreLimit * 1024ULL * 1024ULL,
226 .rlim_max = run->global->exe.coreLimit * 1024ULL * 1024ULL,
227 };
228 if (setrlimit(RLIMIT_CORE, &rl) == -1) {
229 PLOG_W("Couldn't enforce the RLIMIT_CORE resource limit, ignoring");
230 }
231 #endif /* ifdef RLIMIT_CORE */
232
233 if (run->global->exe.clearEnv) {
234 environ = NULL;
235 }
236 for (size_t i = 0; i < ARRAYSIZE(run->global->exe.envs) && run->global->exe.envs[i]; i++) {
237 putenv(run->global->exe.envs[i]);
238 }
239 char fuzzNo[128];
240 snprintf(fuzzNo, sizeof(fuzzNo), "%" PRId32, run->fuzzNo);
241 setenv(_HF_THREAD_NO_ENV, fuzzNo, 1);
242 if (run->global->exe.netDriver) {
243 setenv(_HF_THREAD_NETDRIVER_ENV, "1", 1);
244 }
245
246 /* Make sure it's a new process group / session, so waitpid can wait for -(run->pid) */
247 setsid();
248
249 util_closeStdio(/* close_stdin= */ run->global->exe.nullifyStdio,
250 /* close_stdout= */ run->global->exe.nullifyStdio,
251 /* close_stderr= */ run->global->exe.nullifyStdio);
252
253 /* The bitmap structure */
254 if (run->global->feedback.bbFd != -1 &&
255 TEMP_FAILURE_RETRY(dup2(run->global->feedback.bbFd, _HF_BITMAP_FD)) == -1) {
256 PLOG_E("dup2(%d, _HF_BITMAP_FD=%d)", run->global->feedback.bbFd, _HF_BITMAP_FD);
257 return false;
258 }
259
260 /* The input file to _HF_INPUT_FD */
261 if (run->global->exe.persistent &&
262 TEMP_FAILURE_RETRY(dup2(run->dynamicFileFd, _HF_INPUT_FD)) == -1) {
263 PLOG_E("dup2('%d', _HF_INPUT_FD='%d')", run->dynamicFileFd, _HF_INPUT_FD);
264 return false;
265 }
266
267 /* The log FD */
268 if ((run->global->exe.netDriver || run->global->exe.persistent)) {
269 if (TEMP_FAILURE_RETRY(dup2(logFd(), _HF_LOG_FD)) == -1) {
270 PLOG_E("dup2(%d, _HF_LOG_FD=%d)", logFd(), _HF_LOG_FD);
271 return false;
272 }
273 char llstr[32];
274 snprintf(llstr, sizeof(llstr), "%d", logGetLevel());
275 setenv(_HF_LOG_LEVEL_ENV, llstr, 1);
276 }
277
278 sigset_t sset;
279 sigemptyset(&sset);
280 if (sigprocmask(SIG_SETMASK, &sset, NULL) == -1) {
281 PLOG_W("sigprocmask(empty_set)");
282 }
283
284 if (!run->global->exe.persistent) {
285 if ((run->dynamicFileCopyFd = files_writeBufToTmpFile(
286 run->global->io.workDir, run->dynamicFile, run->dynamicFileSz, 0)) == -1) {
287 LOG_E("Couldn't save data to a temporary file");
288 return false;
289 }
290 if (run->global->exe.fuzzStdin &&
291 TEMP_FAILURE_RETRY(dup2(run->dynamicFileCopyFd, STDIN_FILENO)) == -1) {
292 PLOG_E("dup2(_HF_INPUT_FD=%d, STDIN_FILENO=%d)", run->dynamicFileCopyFd, STDIN_FILENO);
293 return false;
294 }
295 }
296
297 return true;
298 }
299
subproc_New(run_t * run)300 static bool subproc_New(run_t* run) {
301 if (run->pid) {
302 return true;
303 }
304
305 int sv[2];
306 if (run->global->exe.persistent) {
307 if (run->persistentSock != -1) {
308 close(run->persistentSock);
309 }
310
311 int sock_type = SOCK_STREAM;
312 #if defined(SOCK_CLOEXEC)
313 sock_type |= SOCK_CLOEXEC;
314 #endif
315 if (socketpair(AF_UNIX, sock_type, 0, sv) == -1) {
316 PLOG_W("socketpair(AF_UNIX, SOCK_STREAM, 0, sv)");
317 return false;
318 }
319 run->persistentSock = sv[0];
320 }
321
322 LOG_D("Forking new process for thread: %" PRId32, run->fuzzNo);
323
324 run->pid = arch_fork(run);
325 if (run->pid == -1) {
326 PLOG_E("Couldn't fork");
327 run->pid = 0;
328 return false;
329 }
330 /* The child process */
331 if (!run->pid) {
332 logMutexReset();
333 /*
334 * Reset sighandlers, and set alarm(1). It's a guarantee against dead-locks
335 * in the child, where we ensure here that the child process will either
336 * execve or get signaled by SIGALRM within 1 second.
337 *
338 * Those deadlocks typically stem from the fact, that malloc() can behave weirdly
339 * when fork()-ing a single thread of a process: e.g. with glibc < 2.24
340 * (or, Ubuntu's 2.23-0ubuntu6). For more see
341 * http://changelogs.ubuntu.com/changelogs/pool/main/g/glibc/glibc_2.23-0ubuntu7/changelog
342 */
343 alarm(1);
344 signal(SIGALRM, SIG_DFL);
345
346 if (run->global->exe.persistent) {
347 if (TEMP_FAILURE_RETRY(dup2(sv[1], _HF_PERSISTENT_FD)) == -1) {
348 PLOG_F("dup2('%d', '%d')", sv[1], _HF_PERSISTENT_FD);
349 }
350 close(sv[0]);
351 close(sv[1]);
352 }
353
354 if (!subproc_PrepareExecv(run)) {
355 LOG_E("subproc_PrepareExecv() failed");
356 exit(EXIT_FAILURE);
357 }
358 if (!arch_launchChild(run)) {
359 LOG_E("Error launching child process");
360 kill(run->global->threads.mainPid, SIGTERM);
361 _exit(1);
362 }
363 abort();
364 }
365
366 /* Parent */
367 LOG_D("Launched new process, pid=%d, thread: %" PRId32 " (concurrency: %zd)", (int)run->pid,
368 run->fuzzNo, run->global->threads.threadsMax);
369
370 arch_prepareParentAfterFork(run);
371
372 if (run->global->exe.persistent) {
373 close(sv[1]);
374 run->runState = _HF_RS_WAITING_FOR_INITIAL_READY;
375 LOG_I("Persistent mode: Launched new persistent pid=%d", (int)run->pid);
376 }
377
378 return true;
379 }
380
subproc_Run(run_t * run)381 bool subproc_Run(run_t* run) {
382 run->timeStartedMillis = util_timeNowMillis();
383
384 if (!subproc_New(run)) {
385 LOG_E("subproc_New()");
386 return false;
387 }
388
389 arch_prepareParent(run);
390 arch_reapChild(run);
391
392 return true;
393 }
394
subproc_System(run_t * run,const char * const argv[])395 uint8_t subproc_System(run_t* run, const char* const argv[]) {
396 pid_t pid = arch_fork(run);
397 if (pid == -1) {
398 PLOG_E("Couldn't fork");
399 return 255;
400 }
401 if (!pid) {
402 logMutexReset();
403
404 setsid();
405 util_closeStdio(
406 /* close_stdin= */ true, /* close_stdout= */ false, /* close_stderr= */ false);
407
408 sigset_t sset;
409 sigemptyset(&sset);
410 if (sigprocmask(SIG_SETMASK, &sset, NULL) == -1) {
411 PLOG_W("sigprocmask(empty_set)");
412 }
413
414 execv(argv[0], (char* const*)&argv[0]);
415 PLOG_F("Couldn't execute '%s'", argv[0]);
416 return 255;
417 }
418
419 int flags = 0;
420 #if defined(__WNOTHREAD)
421 flags |= __WNOTHREAD;
422 #endif /* defined(__WNOTHREAD) */
423 #if defined(__WALL)
424 flags |= __WALL;
425 #endif /* defined(__WALL) */
426
427 for (;;) {
428 int status;
429 int ret = TEMP_FAILURE_RETRY(wait4(pid, &status, flags, NULL));
430 if (ret == -1) {
431 PLOG_E("wait4() for process pid=%d", (int)pid);
432 return 255;
433 }
434 if (ret != pid) {
435 LOG_E("wait4() returned %d, but waited for %d", ret, (int)pid);
436 return 255;
437 }
438 if (WIFSIGNALED(status)) {
439 LOG_E("Command '%s' terminated with signal: %d", argv[0], WTERMSIG(status));
440 return (100 + WTERMSIG(status));
441 }
442 if (WIFEXITED(status)) {
443 if (WEXITSTATUS(status) == 0) {
444 return 0U;
445 }
446 LOG_E("Command '%s' returned with exit code %d", argv[0], WEXITSTATUS(status));
447 return 1U;
448 }
449
450 LOG_D("wait4() returned with status: %d", status);
451 }
452 }
453
subproc_checkTimeLimit(run_t * run)454 void subproc_checkTimeLimit(run_t* run) {
455 if (!run->global->timing.tmOut) {
456 return;
457 }
458
459 int64_t curMillis = util_timeNowMillis();
460 int64_t diffMillis = curMillis - run->timeStartedMillis;
461
462 if (run->tmOutSignaled && (diffMillis > ((run->global->timing.tmOut + 1) * 1000))) {
463 /* Has this instance been already signaled due to timeout? Just, SIGKILL it */
464 LOG_W("pid=%d has already been signaled due to timeout. Killing it with SIGKILL", run->pid);
465 kill(run->pid, SIGKILL);
466 return;
467 }
468
469 if ((diffMillis > (run->global->timing.tmOut * 1000)) && !run->tmOutSignaled) {
470 run->tmOutSignaled = true;
471 LOG_W("pid=%d took too much time (limit %ld s). Killing it with %s", (int)run->pid,
472 (long)run->global->timing.tmOut,
473 run->global->timing.tmoutVTALRM ? "SIGVTALRM" : "SIGKILL");
474 if (run->global->timing.tmoutVTALRM) {
475 kill(run->pid, SIGVTALRM);
476 } else {
477 kill(run->pid, SIGKILL);
478 }
479 ATOMIC_POST_INC(run->global->cnts.timeoutedCnt);
480 }
481 }
482
subproc_checkTermination(run_t * run)483 void subproc_checkTermination(run_t* run) {
484 if (fuzz_isTerminating()) {
485 LOG_D("Killing pid=%d", (int)run->pid);
486 kill(run->pid, SIGKILL);
487 }
488 }
489
subproc_runThread(honggfuzz_t * hfuzz,pthread_t * thread,void * (* thread_func)(void *),bool joinable)490 bool subproc_runThread(
491 honggfuzz_t* hfuzz, pthread_t* thread, void* (*thread_func)(void*), bool joinable) {
492 pthread_attr_t attr;
493
494 pthread_attr_init(&attr);
495 pthread_attr_setdetachstate(
496 &attr, joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
497 pthread_attr_setstacksize(&attr, _HF_PTHREAD_STACKSIZE);
498 pthread_attr_setguardsize(&attr, (size_t)sysconf(_SC_PAGESIZE));
499
500 if (pthread_create(thread, &attr, thread_func, (void*)hfuzz) < 0) {
501 PLOG_W("Couldn't create a new thread");
502 return false;
503 }
504
505 pthread_attr_destroy(&attr);
506
507 return true;
508 }
509