Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | |
Robert Swiecki | 10eeb0a | 2017-09-28 15:42:52 +0200 | [diff] [blame] | 13 | #include "libcommon/common.h" |
Robert Swiecki | ec7b845 | 2017-06-01 13:25:56 +0200 | [diff] [blame] | 14 | #include "libcommon/files.h" |
| 15 | #include "libcommon/log.h" |
| 16 | #include "libcommon/util.h" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 17 | |
| 18 | /* Stringify */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 19 | #define XSTR(x) #x |
| 20 | #define STR(x) XSTR(x) |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 21 | |
| 22 | /* |
Anestis Bechtsoudis | c2722ee | 2017-01-29 09:40:03 +0200 | [diff] [blame] | 23 | * All clang sanitizers, except ASan, can be activated for target binaries |
| 24 | * with or without the matching runtime library (libcompiler_rt). If runtime |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 25 | * libraries are included in target fuzzing environment, we can benefit from the |
| 26 | * various Die() callbacks and abort/exit logic manipulation. However, some |
Anestis Bechtsoudis | c2722ee | 2017-01-29 09:40:03 +0200 | [diff] [blame] | 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. |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 31 | * |
Anestis Bechtsoudis | c2722ee | 2017-01-29 09:40:03 +0200 | [diff] [blame] | 32 | * Normally SIGABRT is not a wanted signal to monitor for Android, since it produces |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | * |
Anestis Bechtsoudis | c2722ee | 2017-01-29 09:40:03 +0200 | [diff] [blame] | 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 |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 41 | * alternative exists. |
Anestis Bechtsoudis | 523fccb | 2016-12-28 12:11:13 +0200 | [diff] [blame] | 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. |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 50 | */ |
| 51 | |
| 52 | /* 'log_path' output directory for sanitizer reports */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 53 | #define kSANLOGDIR "log_path=" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 54 | |
| 55 | /* 'coverage_dir' output directory for coverage data files is set dynamically */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 56 | #define kSANCOVDIR "coverage_dir=" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 57 | |
| 58 | /* Raise SIGABRT on error or continue with exitcode logic */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 59 | #define kABORT_ENABLED "abort_on_error=1" |
| 60 | #define kABORT_DISABLED "abort_on_error=0" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 61 | |
| 62 | /* |
| 63 | * Common sanitizer flags |
| 64 | * |
| 65 | * symbolize: Disable symbolication since it changes logs (which are parsed) format |
| 66 | */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 67 | #define kSAN_COMMON "symbolize=0" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 68 | |
| 69 | /* --{ ASan }-- */ |
| 70 | /* |
| 71 | *Sanitizer specific flags (notice that if enabled 'abort_on_error' has priority |
| 72 | * over exitcode') |
| 73 | */ |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 74 | #define kASAN_COMMON_OPTS \ |
| 75 | "allow_user_segv_handler=1:" \ |
| 76 | "handle_segv=0:" \ |
Robert Swiecki | 0b56611 | 2017-10-17 17:39:07 +0200 | [diff] [blame] | 77 | "allocator_may_return_null=1:" kSAN_COMMON ":exitcode=" STR(HF_SAN_EXIT_CODE) |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 84 | #define kASAN_OPTS kASAN_COMMON_OPTS ":start_deactivated=1" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 85 | #else |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 86 | #define kASAN_OPTS kASAN_COMMON_OPTS |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 87 | #endif |
| 88 | |
| 89 | /* --{ UBSan }-- */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 90 | #define kUBSAN_OPTS kSAN_COMMON ":exitcode=" STR(HF_SAN_EXIT_CODE) |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 91 | |
| 92 | /* --{ MSan }-- */ |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 93 | #define kMSAN_OPTS \ |
Robert Swiecki | 0b56611 | 2017-10-17 17:39:07 +0200 | [diff] [blame] | 94 | kSAN_COMMON ":exit_code=" STR(HF_SAN_EXIT_CODE) ":" \ |
| 95 | "wrap_signals=0:print_stats=1" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 96 | |
Robert Swiecki | b4e13c0 | 2017-02-10 03:59:40 +0100 | [diff] [blame] | 97 | /* If no sanitzer support was requested, simply make it use abort() on errors */ |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 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:" \ |
Robert Swiecki | 0b56611 | 2017-10-17 17:39:07 +0200 | [diff] [blame] | 101 | "symbolize=1:detect_leaks=0:disable_coredump=0:log_path=stderr" |
Robert Swiecki | b4e13c0 | 2017-02-10 03:59:40 +0100 | [diff] [blame] | 102 | |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | */ |
Robert Swiecki | d0fa62c | 2017-09-28 18:11:05 +0200 | [diff] [blame] | 111 | #define kSAN_COV_OPTS "coverage=1:coverage_direct=1" |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 112 | |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 113 | static bool sanitizers_Regular(void) { |
Robert Swiecki | b736263 | 2017-02-10 14:40:11 +0100 | [diff] [blame] | 114 | if (setenv("ASAN_OPTIONS", kSAN_REGULAR, 1) == -1) { |
Robert Swiecki | f29ec74 | 2017-02-10 14:47:36 +0100 | [diff] [blame] | 115 | PLOG_E("setenv(ASAN_OPTIONS=%s", kSAN_REGULAR); |
| 116 | return false; |
Robert Swiecki | b736263 | 2017-02-10 14:40:11 +0100 | [diff] [blame] | 117 | } |
| 118 | if (setenv("MSAN_OPTIONS", kSAN_REGULAR, 1) == -1) { |
Robert Swiecki | f29ec74 | 2017-02-10 14:47:36 +0100 | [diff] [blame] | 119 | PLOG_E("setenv(MSAN_OPTIONS=%s", kSAN_REGULAR); |
| 120 | return false; |
Robert Swiecki | b736263 | 2017-02-10 14:40:11 +0100 | [diff] [blame] | 121 | } |
| 122 | if (setenv("UBSAN_OPTIONS", kSAN_REGULAR, 1) == -1) { |
Robert Swiecki | f29ec74 | 2017-02-10 14:47:36 +0100 | [diff] [blame] | 123 | PLOG_E("setenv(UBSAN_OPTIONS=%s", kSAN_REGULAR); |
| 124 | return false; |
Robert Swiecki | b736263 | 2017-02-10 14:40:11 +0100 | [diff] [blame] | 125 | } |
| 126 | return true; |
Robert Swiecki | b4e13c0 | 2017-02-10 03:59:40 +0100 | [diff] [blame] | 127 | } |
| 128 | |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 129 | bool sanitizers_Init(honggfuzz_t* hfuzz) { |
Robert Swiecki | b4e13c0 | 2017-02-10 03:59:40 +0100 | [diff] [blame] | 130 | if (hfuzz->linux.pid > 0) { |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | if (hfuzz->enableSanitizers == false) { |
Robert Swiecki | b736263 | 2017-02-10 14:40:11 +0100 | [diff] [blame] | 135 | return sanitizers_Regular(); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 */ |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 142 | size_t bufSz = sizeof(kASAN_OPTS) + 1 + sizeof(kABORT_ENABLED) + 1 + sizeof(kSANLOGDIR) + |
| 143 | PATH_MAX + 1 + sizeof(kSANCOVDIR) + PATH_MAX + 1; |
Robert Swiecki | 4e595fb | 2017-10-11 17:26:51 +0200 | [diff] [blame] | 144 | char* san_opts = util_Calloc(bufSz); |
Robert Swiecki | 0b56611 | 2017-10-17 17:39:07 +0200 | [diff] [blame] | 145 | defer { free(san_opts); }; |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 146 | |
Robert Swiecki | 4e595fb | 2017-10-11 17:26:51 +0200 | [diff] [blame] | 147 | char* abortFlag; |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 148 | if (hfuzz->monitorSIGABRT) { |
| 149 | abortFlag = kABORT_ENABLED; |
| 150 | } else { |
| 151 | abortFlag = kABORT_DISABLED; |
| 152 | } |
| 153 | |
Anestis Bechtsoudis | 523fccb | 2016-12-28 12:11:13 +0200 | [diff] [blame] | 154 | /* Address Sanitizer (ASan) */ |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 155 | if (hfuzz->useSanCov) { |
| 156 | snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kASAN_OPTS, abortFlag, kSAN_COV_OPTS, |
Robert Swiecki | 82c707c | 2017-11-14 16:36:23 +0100 | [diff] [blame] | 157 | kSANCOVDIR, hfuzz->io.workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->io.workDir, |
| 158 | kLOGPREFIX); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 159 | } else { |
| 160 | snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kASAN_OPTS, abortFlag, kSANLOGDIR, |
Robert Swiecki | 82c707c | 2017-11-14 16:36:23 +0100 | [diff] [blame] | 161 | hfuzz->io.workDir, kLOGPREFIX); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | |
Anestis Bechtsoudis | 523fccb | 2016-12-28 12:11:13 +0200 | [diff] [blame] | 169 | /* Undefined Behavior Sanitizer (UBSan) */ |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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, |
Robert Swiecki | 82c707c | 2017-11-14 16:36:23 +0100 | [diff] [blame] | 173 | kSANCOVDIR, hfuzz->io.workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->io.workDir, |
| 174 | kLOGPREFIX); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 175 | } else { |
| 176 | snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kUBSAN_OPTS, abortFlag, kSANLOGDIR, |
Robert Swiecki | 82c707c | 2017-11-14 16:36:23 +0100 | [diff] [blame] | 177 | hfuzz->io.workDir, kLOGPREFIX); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | |
Anestis Bechtsoudis | 523fccb | 2016-12-28 12:11:13 +0200 | [diff] [blame] | 185 | /* Memory Sanitizer (MSan) */ |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 186 | memset(san_opts, 0, bufSz); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 187 | |
| 188 | if (hfuzz->useSanCov) { |
Robert Swiecki | 618dc2f | 2017-07-04 03:07:05 +0200 | [diff] [blame] | 189 | snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kMSAN_OPTS, abortFlag, kSAN_COV_OPTS, |
Robert Swiecki | 82c707c | 2017-11-14 16:36:23 +0100 | [diff] [blame] | 190 | kSANCOVDIR, hfuzz->io.workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->io.workDir, |
| 191 | kLOGPREFIX); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 192 | } else { |
Robert Swiecki | 618dc2f | 2017-07-04 03:07:05 +0200 | [diff] [blame] | 193 | snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kMSAN_OPTS, abortFlag, kSANLOGDIR, |
Robert Swiecki | 82c707c | 2017-11-14 16:36:23 +0100 | [diff] [blame] | 194 | hfuzz->io.workDir, kLOGPREFIX); |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 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 | |
Robert Swiecki | d50ed42 | 2017-11-13 23:32:26 +0100 | [diff] [blame] | 205 | bool sanitizers_prepareExecve(run_t* run) { |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 206 | /* Address Sanitizer (ASan) */ |
Robert Swiecki | 78633d1 | 2017-11-13 23:24:55 +0100 | [diff] [blame] | 207 | if (run->global->sanOpts.asanOpts) { |
| 208 | if (setenv("ASAN_OPTIONS", run->global->sanOpts.asanOpts, 1) == -1) { |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 209 | PLOG_E("setenv(ASAN_OPTIONS) failed"); |
| 210 | return false; |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | /* Memory Sanitizer (MSan) */ |
Robert Swiecki | 78633d1 | 2017-11-13 23:24:55 +0100 | [diff] [blame] | 215 | if (run->global->sanOpts.msanOpts) { |
| 216 | if (setenv("MSAN_OPTIONS", run->global->sanOpts.msanOpts, 1) == -1) { |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 217 | PLOG_E("setenv(MSAN_OPTIONS) failed"); |
| 218 | return false; |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | /* Undefined Behavior Sanitizer (UBSan) */ |
Robert Swiecki | 78633d1 | 2017-11-13 23:24:55 +0100 | [diff] [blame] | 223 | if (run->global->sanOpts.ubsanOpts) { |
| 224 | if (setenv("UBSAN_OPTIONS", run->global->sanOpts.ubsanOpts, 1) == -1) { |
Anestis Bechtsoudis | e5f09f8 | 2016-12-27 16:06:05 +0200 | [diff] [blame] | 225 | PLOG_E("setenv(UBSAN_OPTIONS) failed"); |
| 226 | return false; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | return true; |
| 231 | } |