"Fossies" - the Fresh Open Source Software Archive

Member "honggfuzz-2.2/sanitizers.c" (23 Apr 2020, 10887 Bytes) of package /linux/privat/honggfuzz-2.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sanitizers.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1_vs_2.2.

    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 "cmdline.h"
   14 #include "libhfcommon/common.h"
   15 #include "libhfcommon/files.h"
   16 #include "libhfcommon/log.h"
   17 #include "libhfcommon/util.h"
   18 
   19 /*
   20  * Common sanitizer flags if --sanitizers is enabled
   21  */
   22 #define kSAN_COMMON                \
   23     "symbolize=1:"                 \
   24     "detect_leaks=0:"              \
   25     "disable_coredump=0:"          \
   26     "detect_odr_violation=0:"      \
   27     "allocator_may_return_null=1:" \
   28     "allow_user_segv_handler=0:"   \
   29     "handle_segv=2:"               \
   30     "handle_sigbus=2:"             \
   31     "handle_abort=2:"              \
   32     "handle_sigill=2:"             \
   33     "handle_sigfpe=2:"             \
   34     "abort_on_error=1"
   35 
   36 /* --{ ASan }-- */
   37 /*
   38  * Sanitizer specific flags (notice that if enabled 'abort_on_error' has priority
   39  * over exitcode')
   40  */
   41 #define kASAN_OPTS kSAN_COMMON
   42 
   43 /* --{ UBSan }-- */
   44 #define kUBSAN_OPTS kSAN_COMMON
   45 
   46 /* --{ MSan }-- */
   47 #define kMSAN_OPTS kSAN_COMMON ":wrap_signals=0:print_stats=1"
   48 
   49 /* --{ LSan }-- */
   50 #define kLSAN_OPTS kSAN_COMMON
   51 
   52 /* If no sanitzer support was requested, simply abort() on errors */
   53 #define kSAN_REGULAR               \
   54     "symbolize=1:"                 \
   55     "detect_leaks=0:"              \
   56     "disable_coredump=0:"          \
   57     "detect_odr_violation=0:"      \
   58     "allocator_may_return_null=1:" \
   59     "allow_user_segv_handler=1:"   \
   60     "handle_segv=0:"               \
   61     "handle_sigbus=0:"             \
   62     "handle_abort=0:"              \
   63     "handle_sigill=0:"             \
   64     "handle_sigfpe=0:"             \
   65     "abort_on_error=1"
   66 
   67 static void sanitizers_AddFlag(honggfuzz_t* hfuzz, const char* env, const char* val) {
   68     if (getenv(env)) {
   69         LOG_W("The '%s' envar is already set. Not overriding it!", env);
   70         return;
   71     }
   72 
   73     char buf[4096] = {};
   74     if (hfuzz->sanitizer.enable) {
   75         snprintf(buf, sizeof(buf), "%s=%s:log_path=%s/%s", env, val, hfuzz->io.workDir, kLOGPREFIX);
   76     } else {
   77         snprintf(buf, sizeof(buf), "%s=%s:log_path=%s/%s", env, kSAN_REGULAR, hfuzz->io.workDir,
   78             kLOGPREFIX);
   79     }
   80     /*
   81      * It will make ASAN to start background thread to check RSS mem use, which
   82      * will prevent the NetDrvier from using unshare(CLONE_NEWNET), which cannot
   83      * be used in multi-threaded contexts
   84      */
   85     if (!hfuzz->exe.netDriver && hfuzz->exe.rssLimit) {
   86         util_ssnprintf(buf, sizeof(buf), ":soft_rss_limit_mb=%" PRId64, hfuzz->exe.rssLimit);
   87     }
   88 
   89     cmdlineAddEnv(hfuzz, buf);
   90     LOG_D("%s", buf);
   91 }
   92 
   93 bool sanitizers_Init(honggfuzz_t* hfuzz) {
   94     sanitizers_AddFlag(hfuzz, "ASAN_OPTIONS", kASAN_OPTS);
   95     sanitizers_AddFlag(hfuzz, "UBSAN_OPTIONS", kUBSAN_OPTS);
   96     sanitizers_AddFlag(hfuzz, "MSAN_OPTIONS", kMSAN_OPTS);
   97     sanitizers_AddFlag(hfuzz, "LSAN_OPTIONS", kLSAN_OPTS);
   98 
   99     return true;
  100 }
  101 
  102 /* Get numeric value of the /proc/<pid>/status "Tgid: <PID>" field */
  103 static pid_t sanitizers_PidForTid(pid_t pid) {
  104     char status_path[PATH_MAX];
  105     snprintf(status_path, sizeof(status_path), "/proc/%d/status", (int)pid);
  106     FILE* f = fopen(status_path, "rb");
  107     if (!f) {
  108         return pid;
  109     }
  110     defer {
  111         fclose(f);
  112     };
  113     char* lineptr = NULL;
  114     size_t n = 0;
  115     defer {
  116         free(lineptr);
  117     };
  118 
  119     while (getline(&lineptr, &n, f) > 0) {
  120         int retpid;
  121         if (sscanf(lineptr, "Tgid:%d", &retpid) == 1) {
  122             LOG_D("Tid %d has Pid %d", (int)pid, retpid);
  123             return (pid_t)retpid;
  124         }
  125     }
  126     return pid;
  127 }
  128 
  129 size_t sanitizers_parseReport(run_t* run, pid_t pid, funcs_t* funcs, uint64_t* pc,
  130     uint64_t* crashAddr, char description[HF_STR_LEN]) {
  131     char crashReport[PATH_MAX];
  132     const char* crashReportCpy = crashReport;
  133 
  134     /* Under Linux the crash is seen in TID, but the sanitizer report is created for PID */
  135     pid = sanitizers_PidForTid(pid);
  136     snprintf(
  137         crashReport, sizeof(crashReport), "%s/%s.%d", run->global->io.workDir, kLOGPREFIX, pid);
  138 
  139     FILE* fReport = fopen(crashReport, "rb");
  140     if (fReport == NULL) {
  141         PLOG_D("fopen('%s', 'rb')", crashReport);
  142         return 0;
  143     }
  144     defer {
  145         fclose(fReport);
  146         if (run->global->sanitizer.del_report) {
  147             unlink(crashReportCpy);
  148         }
  149     };
  150 
  151     bool headerFound = false;
  152     bool frameFound = false;
  153     unsigned int frameIdx = 0;
  154 
  155     char* lineptr = NULL;
  156     size_t n = 0;
  157     defer {
  158         free(lineptr);
  159     };
  160     for (;;) {
  161         if (getline(&lineptr, &n, fReport) == -1) {
  162             break;
  163         }
  164 
  165         /* First step is to identify header */
  166         if (!headerFound) {
  167             int reportpid = 0;
  168             if (sscanf(lineptr, "==%d==ERROR: ", &reportpid) != 1) {
  169                 continue;
  170             }
  171             if (reportpid != pid) {
  172                 LOG_W(
  173                     "SAN report found in '%s', but its PID:%d is different from the needed PID:%d",
  174                     crashReport, reportpid, pid);
  175                 break;
  176             }
  177             headerFound = true;
  178             sscanf(lineptr,
  179                 "==%*d==ERROR: %*[^:]: %*[^ ] on address 0x%" PRIx64 " at pc 0x%" PRIx64, crashAddr,
  180                 pc);
  181             sscanf(lineptr,
  182                 "==%*d==ERROR: %*[^:]: %*[^ ] on %*s address 0x%" PRIx64 " (pc 0x%" PRIx64,
  183                 crashAddr, pc);
  184             sscanf(lineptr, "==%*d==ERROR: %" HF_XSTR(HF_STR_LEN_MINUS_1) "[^\n]", description);
  185         } else {
  186             char* pLineLC = lineptr;
  187             /* Trim leading spaces */
  188             while (*pLineLC != '\0' && isspace((int)*pLineLC)) {
  189                 ++pLineLC;
  190             }
  191 
  192             /* End separator for crash thread stack trace is an empty line */
  193             if ((*pLineLC == '\0') && (frameIdx != 0)) {
  194                 break;
  195             }
  196 
  197             if (sscanf(pLineLC, "#%u", &frameIdx) != 1) {
  198                 continue;
  199             }
  200             if (frameIdx >= _HF_MAX_FUNCS) {
  201                 frameIdx = _HF_MAX_FUNCS - 1;
  202                 break;
  203             }
  204 
  205             frameFound = true;
  206             snprintf(funcs[frameIdx].func, sizeof(funcs[frameIdx].func), "UNKNOWN");
  207 
  208             /*
  209              * Frames with demangled symbols and with debug info
  210              *     A::A(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>,
  211              * std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char,
  212              * std::char_traits<char>, std::allocator<char> > > >) /home/fuzz/test/fork.cc:12:51
  213              */
  214             if (sscanf(pLineLC,
  215                     "#%*u 0x%p in %" HF_XSTR(_HF_FUNC_NAME_SZ_MINUS_1) "[^)]) %" HF_XSTR(
  216                         _HF_FUNC_NAME_SZ_MINUS_1) "[^:]:%zu",
  217                     &funcs[frameIdx].pc, funcs[frameIdx].func, funcs[frameIdx].file,
  218                     &funcs[frameIdx].line) == 4) {
  219                 util_ssnprintf(funcs[frameIdx].func, sizeof(funcs[frameIdx].func), ")");
  220                 continue;
  221             }
  222 
  223             /*
  224              * Frames with demangled symbols but w/o debug info
  225              *     #0 0x59d74e in printf_common(void*, char const*, __va_list_tag*)
  226              * (/home/smbd/smbd+0x59d74e)
  227              */
  228             if (sscanf(pLineLC, "#%*u 0x%p in %" HF_XSTR(_HF_FUNC_NAME_SZ_MINUS_1) "[^)]) (%[^)])",
  229                     &funcs[frameIdx].pc, funcs[frameIdx].func, funcs[frameIdx].module) == 3) {
  230                 util_ssnprintf(funcs[frameIdx].func, sizeof(funcs[frameIdx].func), ")");
  231                 continue;
  232             }
  233             /*
  234              * Frames with symbols but w/o debug info
  235              *     #0 0x7ffff59a3668 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x9668)
  236              */
  237             if (sscanf(pLineLC,
  238                     "#%*u 0x%p in %" HF_XSTR(_HF_FUNC_NAME_SZ_MINUS_1) "s%*[^(](%" HF_XSTR(
  239                         HF_STR_LEN_MINUS_1) "[^)]",
  240                     &funcs[frameIdx].pc, funcs[frameIdx].func, funcs[frameIdx].module) == 3) {
  241                 continue;
  242             }
  243             /*
  244              * Frames with symbols and with debug info
  245              *     #0 0x1e94738 in smb2_signing_decrypt_pdu /home/test/signing.c:617:3
  246              */
  247             if (sscanf(pLineLC,
  248                     "#%*u 0x%p in %" HF_XSTR(_HF_FUNC_NAME_SZ_MINUS_1) "[^ ] %" HF_XSTR(
  249                         HF_STR_LEN_MINUS_1) "[^:\n]:%zu",
  250                     &funcs[frameIdx].pc, funcs[frameIdx].func, funcs[frameIdx].file,
  251                     &funcs[frameIdx].line) == 4) {
  252                 continue;
  253             }
  254             /*
  255              * Frames w/o symbols
  256              *     #0 0x565584f4  (/mnt/z/test+0x34f4)
  257              */
  258             if (sscanf(pLineLC, "#%*u 0x%p%*[^(](%" HF_XSTR(HF_STR_LEN_MINUS_1) "[^)\n]",
  259                     &funcs[frameIdx].pc, funcs[frameIdx].module) == 2) {
  260                 continue;
  261             }
  262             /*
  263              * Frames w/o symbols, but with debug info
  264              *     #0 0x7ffff57cf08f  /build/glibc-bBRi4l/.../erms.S:199
  265              */
  266             if (sscanf(pLineLC, "#%*u 0x%p  %" HF_XSTR(HF_STR_LEN_MINUS_1) "[^:]:%zu",
  267                     &funcs[frameIdx].pc, funcs[frameIdx].file, &funcs[frameIdx].line) == 3) {
  268                 continue;
  269             }
  270         }
  271     }
  272 
  273     return (frameFound == false) ? 0 : (frameIdx + 1);
  274 }
  275 
  276 /*
  277  * Size in characters required to store a string representation of a
  278  * register value (0xdeadbeef style))
  279  */
  280 #define REGSIZEINCHAR (2 * sizeof(uint64_t) + 3)
  281 
  282 uint64_t sanitizers_hashCallstack(run_t* run, funcs_t* funcs, size_t funcCnt, bool enableMasking) {
  283     size_t numFrames = 7;
  284     /*
  285      * If sanitizer fuzzing enabled increase number of major frames, since top 7-9 frames will be
  286      * occupied with sanitizer runtime library & libc symbols
  287      */
  288     if (run->global->sanitizer.enable) {
  289         numFrames = 14;
  290     }
  291 
  292     uint64_t hash = 0;
  293     for (size_t i = 0; i < funcCnt && i < numFrames; i++) {
  294         /*
  295          * Convert PC to char array to be compatible with hash function
  296          */
  297         char pcStr[REGSIZEINCHAR] = {0};
  298         snprintf(pcStr, REGSIZEINCHAR, "0x%016" PRIx64, (uint64_t)(long)funcs[i].pc);
  299 
  300         /*
  301          * Hash the last three nibbles
  302          */
  303         hash ^= util_hash(&pcStr[strlen(pcStr) - 3], 3);
  304     }
  305 
  306     /*
  307      * If only one frame, hash is not safe to be used for uniqueness. We mask it
  308      * here with a constant prefix, so analyzers can pick it up and create filenames
  309      * accordingly. 'enableMasking' is controlling masking for cases where it should
  310      * not be enabled (e.g. fuzzer worker is from verifier).
  311      */
  312     if (enableMasking && funcCnt == 1) {
  313         hash |= _HF_SINGLE_FRAME_MASK;
  314     }
  315 
  316     return hash;
  317 }