/*
 *
 * honggfuzz - core structures and macros
 * -----------------------------------------
 *
 * Author: Robert Swiecki <swiecki@google.com>
 *
 * Copyright 2010-2018 by Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

#ifndef _HF_HONGGFUZZ_H_
#define _HF_HONGGFUZZ_H_

#include <dirent.h>
#include <inttypes.h>
#include <limits.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <time.h>

#include "libhfcommon/util.h"

#define PROG_NAME "honggfuzz"
#define PROG_VERSION "1.8"

/* Name of the template which will be replaced with the proper name of the file */
#define _HF_FILE_PLACEHOLDER "___FILE___"

/* Default name of the report created with some architectures */
#define _HF_REPORT_FILE "HONGGFUZZ.REPORT.TXT"

/* Default stack-size of created threads. */
#define _HF_PTHREAD_STACKSIZE (1024ULL * 1024ULL * 2ULL) /* 2MB */

/* Name of envvar which indicates sequential number of fuzzer */
#define _HF_THREAD_NO_ENV "HFUZZ_THREAD_NO"

/* Name of envvar which indicates that the netDriver should be used */
#define _HF_THREAD_NETDRIVER_ENV "HFUZZ_USE_NETDRIVER"

/* Name of envvar which indicates honggfuzz's log level in use */
#define _HF_LOG_LEVEL_ENV "HFUZZ_LOG_LEVEL"

/* Number of crash verifier iterations before tag crash as stable */
#define _HF_VERIFIER_ITER 5

/* Size (in bytes) for report data to be stored in stack before written to file */
#define _HF_REPORT_SIZE 8192

/* Perf bitmap size */
#define _HF_PERF_BITMAP_SIZE_16M (1024U * 1024U * 16U)
#define _HF_PERF_BITMAP_BITSZ_MASK 0x7FFFFFFULL
/* Maximum number of PC guards (=trace-pc-guard) we support */
#define _HF_PC_GUARD_MAX (1024ULL * 1024ULL * 64ULL)

/* Maximum size of the input file in bytes (128 MiB) */
#define _HF_INPUT_MAX_SIZE (1024ULL * 1024ULL * 128ULL)

/* FD used to log inside the child process */
#define _HF_LOG_FD 1020
/* FD used to represent the input file */
#define _HF_INPUT_FD 1021
/* FD used to pass feedback bitmap a process */
#define _HF_BITMAP_FD 1022
/* FD used to pass data to a persistent process */
#define _HF_PERSISTENT_FD 1023

/* Message indicating that the fuzzed process is ready for new data */
static const uint8_t HFReadyTag = 'R';

/* Maximum number of active fuzzing threads */
#define _HF_THREAD_MAX 1024U

/* Persistent-binary signature - if found within file, it means it's a persistent mode binary */
#define _HF_PERSISTENT_SIG "\x01_LIBHFUZZ_PERSISTENT_BINARY_SIGNATURE_\x02\xFF"
/* HF NetDriver signature - if found within file, it means it's a NetDriver-based binary */
#define _HF_NETDRIVER_SIG "\x01_LIBHFUZZ_NETDRIVER_BINARY_SIGNATURE_\x02\xFF"

typedef enum {
    _HF_DYNFILE_NONE = 0x0,
    _HF_DYNFILE_INSTR_COUNT = 0x1,
    _HF_DYNFILE_BRANCH_COUNT = 0x2,
    _HF_DYNFILE_BTS_EDGE = 0x10,
    _HF_DYNFILE_IPT_BLOCK = 0x20,
    _HF_DYNFILE_SOFT = 0x40,
} dynFileMethod_t;

typedef struct {
    uint64_t cpuInstrCnt;
    uint64_t cpuBranchCnt;
    uint64_t bbCnt;
    uint64_t newBBCnt;
    uint64_t softCntPc;
    uint64_t softCntEdge;
    uint64_t softCntCmp;
} hwcnt_t;

typedef struct {
    uint32_t capacity;
    uint32_t* pChunks;
    uint32_t nChunks;
} bitmap_t;

/* Memory map struct */
typedef struct __attribute__((packed)) {
    uint64_t start;          // region start addr
    uint64_t end;            // region end addr
    uint64_t base;           // region base addr
    char mapName[NAME_MAX];  // bin/DSO name
    uint64_t bbCnt;
    uint64_t newBBCnt;
} memMap_t;

/* Trie node data struct */
typedef struct __attribute__((packed)) {
    bitmap_t* pBM;
} trieData_t;

/* Trie node struct */
typedef struct node {
    char key;
    trieData_t data;
    struct node* next;
    struct node* prev;
    struct node* children;
    struct node* parent;
} node_t;

/* EOF Sanitizer coverage specific data structures */

typedef enum {
    _HF_STATE_UNSET = 0,
    _HF_STATE_STATIC = 1,
    _HF_STATE_DYNAMIC_DRY_RUN = 2,
    _HF_STATE_DYNAMIC_MAIN = 3,
} fuzzState_t;

struct dynfile_t {
    uint8_t* data;
    size_t size;
    TAILQ_ENTRY(dynfile_t)
    pointers;
};

struct strings_t {
    char* s;
    size_t len;
    TAILQ_ENTRY(strings_t)
    pointers;
};

typedef struct {
    bool pcGuardMap[_HF_PC_GUARD_MAX];
    uint8_t bbMapPc[_HF_PERF_BITMAP_SIZE_16M];
    uint32_t bbMapCmp[_HF_PERF_BITMAP_SIZE_16M];
    uint64_t pidFeedbackPc[_HF_THREAD_MAX];
    uint64_t pidFeedbackEdge[_HF_THREAD_MAX];
    uint64_t pidFeedbackCmp[_HF_THREAD_MAX];
} feedback_t;

typedef struct {
    struct {
        size_t threadsMax;
        size_t threadsFinished;
        uint32_t threadsActiveCnt;
        pthread_t mainThread;
        pid_t mainPid;
        pthread_t threads[_HF_THREAD_MAX];
    } threads;
    struct {
        const char* inputDir;
        DIR* inputDirPtr;
        size_t fileCnt;
        const char* fileExtn;
        bool fileCntDone;
        const char* workDir;
        const char* crashDir;
        const char* covDirAll;
        const char* covDirNew;
        bool saveUnique;
        size_t dynfileqCnt;
        pthread_rwlock_t dynfileq_mutex;
        TAILQ_HEAD(dyns_t, dynfile_t) dynfileq;
    } io;
    struct {
        int argc;
        const char* const* cmdline;
        bool nullifyStdio;
        bool fuzzStdin;
        const char* externalCommand;
        const char* postExternalCommand;
        bool netDriver;
        bool persistent;
        uint64_t asLimit;
        uint64_t rssLimit;
        uint64_t dataLimit;
        uint64_t coreLimit;
        bool clearEnv;
        char* envs[128];
        sigset_t waitSigSet;
    } exe;
    struct {
        time_t timeStart;
        time_t runEndTime;
        time_t tmOut;
        time_t lastCovUpdate;
        bool tmoutVTALRM;
    } timing;
    struct {
        const char* dictionaryFile;
        TAILQ_HEAD(strq_t, strings_t) dictq;
        size_t dictionaryCnt;
        size_t mutationsMax;
        unsigned mutationsPerRun;
        size_t maxFileSz;
    } mutate;
    struct {
        bool useScreen;
        char cmdline_txt[65];
        int64_t lastDisplayMillis;
    } display;
    struct {
        bool useVerifier;
        bool exitUponCrash;
        const char* reportFile;
        pthread_mutex_t report_mutex;
        bool monitorSIGABRT;
        size_t dynFileIterExpire;
        bool only_printable;
    } cfg;
    struct {
        bool enable;
    } sanitizer;
    struct {
        fuzzState_t state;
        feedback_t* feedbackMap;
        int bbFd;
        pthread_mutex_t feedback_mutex;
        const char* blacklistFile;
        uint64_t* blacklist;
        size_t blacklistCnt;
        bool skipFeedbackOnTimeout;
        dynFileMethod_t dynFileMethod;
    } feedback;
    struct {
        size_t mutationsCnt;
        size_t crashesCnt;
        size_t uniqueCrashesCnt;
        size_t verifiedCrashesCnt;
        size_t blCrashesCnt;
        size_t timeoutedCnt;
    } cnts;
    struct {
        bool enabled;
        int serverSocket;
        int clientSocket;
    } socketFuzzer;
    /* For the Linux code */
    struct {
        int exeFd;
        hwcnt_t hwCnts;
        uint64_t dynamicCutOffAddr;
        bool disableRandomization;
        void* ignoreAddr;
        size_t numMajorFrames;
        const char* symsBlFile;
        char** symsBl;
        size_t symsBlCnt;
        const char* symsWlFile;
        char** symsWl;
        size_t symsWlCnt;
        uintptr_t cloneFlags;
        bool kernelOnly;
        bool useClone;
    } linux;
    /* For the NetBSD code */
    struct {
        void* ignoreAddr;
        size_t numMajorFrames;
        const char* symsBlFile;
        char** symsBl;
        size_t symsBlCnt;
        const char* symsWlFile;
        char** symsWl;
        size_t symsWlCnt;
    } netbsd;
} honggfuzz_t;

typedef enum {
    _HF_RS_UNKNOWN = 0,
    _HF_RS_WAITING_FOR_INITIAL_READY = 1,
    _HF_RS_WAITING_FOR_READY = 2,
    _HF_RS_SEND_DATA = 3,
} runState_t;

typedef struct {
    honggfuzz_t* global;
    pid_t pid;
    int64_t timeStartedMillis;
    char origFileName[PATH_MAX];
    char crashFileName[PATH_MAX];
    uint64_t pc;
    uint64_t backtrace;
    uint64_t access;
    int exception;
    char report[_HF_REPORT_SIZE];
    bool mainWorker;
    unsigned mutationsPerRun;
    struct dynfile_t* dynfileqCurrent;
    uint8_t* dynamicFile;
    size_t dynamicFileSz;
    int dynamicFileFd;
    int dynamicFileCopyFd;
    uint32_t fuzzNo;
    int persistentSock;
    bool waitingForReady;
    runState_t runState;
    bool tmOutSignaled;
#if !defined(_HF_ARCH_DARWIN)
    timer_t timerId;
#endif  // !defined(_HF_ARCH_DARWIN)

    struct {
        /* For Linux code */
        uint8_t* perfMmapBuf;
        uint8_t* perfMmapAux;
        hwcnt_t hwCnts;
        int cpuInstrFd;
        int cpuBranchFd;
        int cpuIptBtsFd;
    } linux;

    struct {
        /* For NetBSD code */
        uint8_t* perfMmapBuf;
        uint8_t* perfMmapAux;
        hwcnt_t hwCnts;
        int cpuInstrFd;
        int cpuBranchFd;
        int cpuIptBtsFd;
    } netbsd;
} run_t;

/*
 * Go-style defer scoped implementation
 *
 * When compiled with clang, use: -fblocks -lBlocksRuntime
 *
 * Example of use:
 *
 * {
 *   int fd = open(fname, O_RDONLY);
 *   if (fd == -1) {
 *     error(....);
 *     return;
 *   }
 *   defer { close(fd); };
 *   ssize_t sz = read(fd, buf, sizeof(buf));
 *   ...
 *   ...
 * }
 *
 */

#define __STRMERGE(a, b) a##b
#define _STRMERGE(a, b) __STRMERGE(a, b)
#ifdef __clang__
#if __has_extension(blocks)
static void __attribute__((unused)) __clang_cleanup_func(void (^*dfunc)(void)) {
    (*dfunc)();
}

#define defer                                        \
    void (^_STRMERGE(__defer_f_, __COUNTER__))(void) \
        __attribute__((cleanup(__clang_cleanup_func))) __attribute__((unused)) = ^

#else /* __has_extension(blocks) */
#define defer UNIMPLEMENTED - NO - SUPPORT - FOR - BLOCKS - IN - YOUR - CLANG - ENABLED
#endif /*  __has_extension(blocks) */
#else  /* !__clang__, e.g.: gcc */

#define __block
#define _DEFER(a, count)                                                                      \
    auto void _STRMERGE(__defer_f_, count)(void* _defer_arg __attribute__((unused)));         \
    int _STRMERGE(__defer_var_, count) __attribute__((cleanup(_STRMERGE(__defer_f_, count)))) \
        __attribute__((unused));                                                              \
    void _STRMERGE(__defer_f_, count)(void* _defer_arg __attribute__((unused)))
#define defer _DEFER(a, __COUNTER__)
#endif /* ifdef __clang__ */

/* Block scoped mutexes */
#define MX_SCOPED_LOCK(m) \
    MX_LOCK(m);           \
    defer {               \
        MX_UNLOCK(m);     \
    }

#define MX_SCOPED_RWLOCK_READ(m) \
    MX_RWLOCK_READ(m);           \
    defer {                      \
        MX_RWLOCK_UNLOCK(m);     \
    }
#define MX_SCOPED_RWLOCK_WRITE(m) \
    MX_RWLOCK_WRITE(m);           \
    defer {                       \
        MX_RWLOCK_UNLOCK(m);      \
    }

#endif