Home | History | Annotate | Download | only in honggfuzz
      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