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