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