Home | History | Annotate | Download | only in honggfuzz
      1 #include "sanitizers.h"
      2 
      3 #include <ctype.h>
      4 #include <dirent.h>
      5 #include <inttypes.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <sys/mman.h>
     10 #include <sys/stat.h>
     11 #include <sys/types.h>
     12 
     13 #include "libcommon/common.h"
     14 #include "libcommon/files.h"
     15 #include "libcommon/log.h"
     16 #include "libcommon/util.h"
     17 
     18 /* Stringify */
     19 #define XSTR(x) #x
     20 #define STR(x) XSTR(x)
     21 
     22 /*
     23  * All clang sanitizers, except ASan, can be activated for target binaries
     24  * with or without the matching runtime library (libcompiler_rt). If runtime
     25  * libraries are included in target fuzzing environment, we can benefit from the
     26  * various Die() callbacks and abort/exit logic manipulation. However, some
     27  * setups (e.g. Android production ARM/ARM64 devices) enable sanitizers, such as
     28  * UBSan, without the runtime libraries. As such, their default ftrap is activated
     29  * which is for most cases a SIGABRT. For these cases end-user needs to enable
     30  * SIGABRT monitoring flag, otherwise these crashes will be missed.
     31  *
     32  * Normally SIGABRT is not a wanted signal to monitor for Android, since it produces
     33  * lots of useless crashes due to way Android process termination hacks work. As
     34  * a result the sanitizer's 'abort_on_error' flag cannot be utilized since it
     35  * invokes abort() internally. In order to not lose crashes a custom exitcode can
     36  * be registered and monitored. Since exitcode is a global flag, it's assumed
     37  * that target is compiled with only one sanitizer type enabled at a time.
     38  *
     39  * For cases where clang runtime library linking is not an option, SIGABRT should
     40  * be monitored even for noisy targets, such as the Android OS, since no viable
     41  * alternative exists.
     42  *
     43  * There might be cases where ASan instrumented targets crash while generating
     44  * reports for detected errors (inside __asan_report_error() proc). Under such
     45  * scenarios target fails to exit or SIGABRT (AsanDie() proc) as defined in
     46  * ASAN_OPTIONS flags, leaving garbage logs. An attempt is made to parse such
     47  * logs for cases where enough data are written to identify potentially missed
     48  * crashes. If ASan internal error results into a SIGSEGV being raised, it
     49  * will get caught from ptrace API, handling the discovered ASan internal crash.
     50  */
     51 
     52 /* 'log_path' output directory for sanitizer reports */
     53 #define kSANLOGDIR "log_path="
     54 
     55 /* 'coverage_dir' output directory for coverage data files is set dynamically */
     56 #define kSANCOVDIR "coverage_dir="
     57 
     58 /* Raise SIGABRT on error or continue with exitcode logic */
     59 #define kABORT_ENABLED "abort_on_error=1"
     60 #define kABORT_DISABLED "abort_on_error=0"
     61 
     62 /*
     63  * Common sanitizer flags
     64  *
     65  * symbolize: Disable symbolication since it changes logs (which are parsed) format
     66  */
     67 #define kSAN_COMMON "symbolize=0"
     68 
     69 /* --{ ASan }-- */
     70 /*
     71  *Sanitizer specific flags (notice that if enabled 'abort_on_error' has priority
     72  * over exitcode')
     73  */
     74 #define kASAN_COMMON_OPTS        \
     75     "allow_user_segv_handler=1:" \
     76     "handle_segv=0:"             \
     77     "allocator_may_return_null=1:" kSAN_COMMON ":exitcode=" STR(HF_SAN_EXIT_CODE)
     78 /* Platform specific flags */
     79 #if defined(__ANDROID__)
     80 /*
     81  * start_deactivated: Enable on Android to reduce memory usage (useful when not all
     82  *                    target's DSOs are compiled with sanitizer enabled
     83  */
     84 #define kASAN_OPTS kASAN_COMMON_OPTS ":start_deactivated=1"
     85 #else
     86 #define kASAN_OPTS kASAN_COMMON_OPTS
     87 #endif
     88 
     89 /* --{ UBSan }-- */
     90 #define kUBSAN_OPTS kSAN_COMMON ":exitcode=" STR(HF_SAN_EXIT_CODE)
     91 
     92 /* --{ MSan }-- */
     93 #define kMSAN_OPTS \
     94     kSAN_COMMON ":exit_code=" STR(HF_SAN_EXIT_CODE) ":"                                            \
     95                                                     "wrap_signals=0:print_stats=1"
     96 
     97 /* If no sanitzer support was requested, simply make it use abort() on errors */
     98 #define kSAN_REGULAR                                                 \
     99     "abort_on_error=1:handle_segv=0:handle_sigbus=0:handle_abort=0:" \
    100     "handle_sigill=0:handle_sigfpe=0:allocator_may_return_null=1:"   \
    101     "symbolize=1:detect_leaks=0:disable_coredump=0:log_path=stderr"
    102 
    103 /*
    104  * If the program ends with a signal that ASan does not handle (or can not
    105  * handle at all, like SIGKILL), coverage data will be lost. This is a big
    106  * problem on Android, where SIGKILL is a normal way of evicting applications
    107  * from memory. With 'coverage_direct=1' coverage data is written to a
    108  * memory-mapped file as soon as it collected. Non-Android targets can disable
    109  * coverage direct when more coverage data collection methods are implemented.
    110  */
    111 #define kSAN_COV_OPTS "coverage=1:coverage_direct=1"
    112 
    113 static bool sanitizers_Regular(void) {
    114     if (setenv("ASAN_OPTIONS", kSAN_REGULAR, 1) == -1) {
    115         PLOG_E("setenv(ASAN_OPTIONS=%s", kSAN_REGULAR);
    116         return false;
    117     }
    118     if (setenv("MSAN_OPTIONS", kSAN_REGULAR, 1) == -1) {
    119         PLOG_E("setenv(MSAN_OPTIONS=%s", kSAN_REGULAR);
    120         return false;
    121     }
    122     if (setenv("UBSAN_OPTIONS", kSAN_REGULAR, 1) == -1) {
    123         PLOG_E("setenv(UBSAN_OPTIONS=%s", kSAN_REGULAR);
    124         return false;
    125     }
    126     return true;
    127 }
    128 
    129 bool sanitizers_Init(honggfuzz_t* hfuzz) {
    130     if (hfuzz->linux.pid > 0) {
    131         return true;
    132     }
    133 
    134     if (hfuzz->enableSanitizers == false) {
    135         return sanitizers_Regular();
    136     }
    137 
    138     /* Set sanitizer flags once to avoid performance overhead per worker spawn */
    139     size_t flagsSz = 0;
    140 
    141     /* Larger constant combination + 2 dynamic paths */
    142     size_t bufSz = sizeof(kASAN_OPTS) + 1 + sizeof(kABORT_ENABLED) + 1 + sizeof(kSANLOGDIR) +
    143                    PATH_MAX + 1 + sizeof(kSANCOVDIR) + PATH_MAX + 1;
    144     char* san_opts = util_Calloc(bufSz);
    145     defer { free(san_opts); };
    146 
    147     char* abortFlag;
    148     if (hfuzz->monitorSIGABRT) {
    149         abortFlag = kABORT_ENABLED;
    150     } else {
    151         abortFlag = kABORT_DISABLED;
    152     }
    153 
    154     /* Address Sanitizer (ASan) */
    155     if (hfuzz->useSanCov) {
    156         snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kASAN_OPTS, abortFlag, kSAN_COV_OPTS,
    157             kSANCOVDIR, hfuzz->io.workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->io.workDir,
    158             kLOGPREFIX);
    159     } else {
    160         snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kASAN_OPTS, abortFlag, kSANLOGDIR,
    161             hfuzz->io.workDir, kLOGPREFIX);
    162     }
    163 
    164     flagsSz = strlen(san_opts) + 1;
    165     hfuzz->sanOpts.asanOpts = util_Calloc(flagsSz);
    166     memcpy(hfuzz->sanOpts.asanOpts, san_opts, flagsSz);
    167     LOG_D("ASAN_OPTIONS=%s", hfuzz->sanOpts.asanOpts);
    168 
    169     /* Undefined Behavior Sanitizer (UBSan) */
    170     memset(san_opts, 0, bufSz);
    171     if (hfuzz->useSanCov) {
    172         snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kUBSAN_OPTS, abortFlag, kSAN_COV_OPTS,
    173             kSANCOVDIR, hfuzz->io.workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->io.workDir,
    174             kLOGPREFIX);
    175     } else {
    176         snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kUBSAN_OPTS, abortFlag, kSANLOGDIR,
    177             hfuzz->io.workDir, kLOGPREFIX);
    178     }
    179 
    180     flagsSz = strlen(san_opts) + 1;
    181     hfuzz->sanOpts.ubsanOpts = util_Calloc(flagsSz);
    182     memcpy(hfuzz->sanOpts.ubsanOpts, san_opts, flagsSz);
    183     LOG_D("UBSAN_OPTIONS=%s", hfuzz->sanOpts.ubsanOpts);
    184 
    185     /* Memory Sanitizer (MSan) */
    186     memset(san_opts, 0, bufSz);
    187 
    188     if (hfuzz->useSanCov) {
    189         snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kMSAN_OPTS, abortFlag, kSAN_COV_OPTS,
    190             kSANCOVDIR, hfuzz->io.workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->io.workDir,
    191             kLOGPREFIX);
    192     } else {
    193         snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kMSAN_OPTS, abortFlag, kSANLOGDIR,
    194             hfuzz->io.workDir, kLOGPREFIX);
    195     }
    196 
    197     flagsSz = strlen(san_opts) + 1;
    198     hfuzz->sanOpts.msanOpts = util_Calloc(flagsSz);
    199     memcpy(hfuzz->sanOpts.msanOpts, san_opts, flagsSz);
    200     LOG_D("MSAN_OPTIONS=%s", hfuzz->sanOpts.msanOpts);
    201 
    202     return true;
    203 }
    204 
    205 bool sanitizers_prepareExecve(run_t* run) {
    206     /* Address Sanitizer (ASan) */
    207     if (run->global->sanOpts.asanOpts) {
    208         if (setenv("ASAN_OPTIONS", run->global->sanOpts.asanOpts, 1) == -1) {
    209             PLOG_E("setenv(ASAN_OPTIONS) failed");
    210             return false;
    211         }
    212     }
    213 
    214     /* Memory Sanitizer (MSan) */
    215     if (run->global->sanOpts.msanOpts) {
    216         if (setenv("MSAN_OPTIONS", run->global->sanOpts.msanOpts, 1) == -1) {
    217             PLOG_E("setenv(MSAN_OPTIONS) failed");
    218             return false;
    219         }
    220     }
    221 
    222     /* Undefined Behavior Sanitizer (UBSan) */
    223     if (run->global->sanOpts.ubsanOpts) {
    224         if (setenv("UBSAN_OPTIONS", run->global->sanOpts.ubsanOpts, 1) == -1) {
    225             PLOG_E("setenv(UBSAN_OPTIONS) failed");
    226             return false;
    227         }
    228     }
    229 
    230     return true;
    231 }
    232