* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
#include <memory.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#ifdef __arm__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Watomic-alignment"
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
#endif
#ifndef __MINGW64__
#define ATTRIBUTE_WEAK __attribute__((weak))
#else
#define ATTRIBUTE_WEAK
#endif
#if defined(__has_feature)
#if __has_feature(coverage_sanitizer)
#define __SANITIZE_ADDRESS__
#endif
#endif
#if defined(__SANITIZE_ADDRESS__)
#define MICROFUZZ_API __attribute__((no_sanitize("coverage")))
#define SANCOV_CALLBACK __attribute__((no_sanitize("coverage"))) ATTRIBUTE_WEAK
#else
#define MICROFUZZ_API
#define SANCOV_CALLBACK ATTRIBUTE_WEAK
#endif
typedef uint8_t UInt8;
typedef uint16_t UInt16;
typedef uint32_t UInt32;
typedef uint64_t UInt64;
typedef float Float32;
typedef double Float64;
#define DECLARE_ENTRY_TYPE(TYPE) \
typedef struct { \
TYPE left; \
TYPE right; \
} TYPE##Entry
DECLARE_ENTRY_TYPE(UInt8);
DECLARE_ENTRY_TYPE(UInt16);
DECLARE_ENTRY_TYPE(UInt32);
DECLARE_ENTRY_TYPE(UInt64);
DECLARE_ENTRY_TYPE(Float32);
DECLARE_ENTRY_TYPE(Float64);
static atomic_bool g_initialized = false;
static atomic_bool g_enableTrace = false;
static atomic_bool g_hasNewCoverage = false;
#define CMP_PC_MAP_SIZE (1 << 18)
static UInt8 g_cmpPcCounterMap[CMP_PC_MAP_SIZE] = {0};
static UInt8 g_cmpPcCounterMapInterrun[CMP_PC_MAP_SIZE] = {0};
static UInt8 g_cmpPcHammingDistMap[CMP_PC_MAP_SIZE] = {255};
static UInt8 g_cmpPcAbsDistmap[CMP_PC_MAP_SIZE] = {255};
static size_t g_coverageMapSize = 0;
#define MAX_COVERAGE_MAP_SIZE (1 << 20)
static UInt8 g_coverageMap[MAX_COVERAGE_MAP_SIZE];
static UInt8 g_globalCoverageMap[MAX_COVERAGE_MAP_SIZE];
#define RECENT_VALUE_MAP_SIZE (1 << 13)
static UInt8 g_cmpRecentValuesUInt8[RECENT_VALUE_MAP_SIZE / sizeof(UInt8)] = {0};
static UInt16 g_cmpRecentValuesUInt16[RECENT_VALUE_MAP_SIZE / sizeof(UInt16)] = {0};
static UInt32 g_cmpRecentValuesUInt32[RECENT_VALUE_MAP_SIZE / sizeof(UInt32)] = {0};
static UInt64 g_cmpRecentValuesUInt64[RECENT_VALUE_MAP_SIZE / sizeof(UInt64)] = {0};
static Float32 g_cmpRecentValuesFloat32[RECENT_VALUE_MAP_SIZE / sizeof(Float32)] = {0};
static Float64 g_cmpRecentValuesFloat64[RECENT_VALUE_MAP_SIZE / sizeof(Float64)] = {0};
static void (*g_debugCallback)(const char*, const char*, UInt64, UInt64) = 0;
#define STORE(atomic, value) atomic_store_explicit(&atomic, value, memory_order_relaxed)
#define LOAD(atomic) atomic_load_explicit(&atomic, memory_order_relaxed)
#define CAS(atomic, old, new) atomic_compare_exchange_strong(&atomic, &old, new)
#define PC_TO_INDEX(pc) (size_t)(((pc) >> 32) ^ (pc))
#define CLAMP_UINT8(x) ((x) > 255 ? ((UInt8)255) : ((UInt8)(x)))
#define DIST_UINT8(x, y) CLAMP_UINT8((x) > (y) ? ((x) - (y)) : ((y) - (x)))
#define POOR_RANDOM(field) (field = UINT64_C(6364136223846793005) * field + UINT64_C(1442695040888963407))
#define DEBUG(message, type, v1, v2) \
if (g_debugCallback) { \
bool traceEnabled = true; \
if (CAS(g_enableTrace, traceEnabled, false)) { \
g_debugCallback(message, #type, (UInt64)(v1), (UInt64)(v2)); \
STORE(g_enableTrace, true); \
} \
}
MICROFUZZ_API void CJ_MICROFUZZ_ENABLE_TRACE(void)
{
STORE(g_enableTrace, true);
}
MICROFUZZ_API void CJ_MICROFUZZ_DISABLE_TRACE(void)
{
STORE(g_enableTrace, false);
}
MICROFUZZ_API bool CJ_MICROFUZZ_TRACE_ENABLED(void)
{
return LOAD(g_enableTrace);
}
MICROFUZZ_API bool CJ_MICROFUZZ_IS_INITIALIZED(void)
{
return LOAD(g_initialized);
}
MICROFUZZ_API bool CJ_MICROFUZZ_HAS_NEW_COVERAGE(void)
{
return LOAD(g_hasNewCoverage);
}
MICROFUZZ_API void CJ_MICROFUZZ_RESET_HAS_NEW_COVERAGE(void)
{
STORE(g_hasNewCoverage, false);
}
MICROFUZZ_API void CJ_MICROFUZZ_SET_DBG_CALLBACK(void (*callback)(const char*, const char*, UInt64, UInt64))
{
g_debugCallback = callback;
}
#define SANITIZER_COV_TRACE_CMP_TRACE(TYPE, ARG1, ARG2) \
{ \
uintptr_t pc = (uintptr_t)__builtin_return_address(0); \
size_t index = PC_TO_INDEX(pc) % CMP_PC_MAP_SIZE; \
UInt8 newCounter = ++g_cmpPcCounterMap[index]; \
UInt8 newHamming = (UInt8)__builtin_popcountll(ARG1 ^ ARG2); \
UInt8 newAbs = DIST_UINT8(ARG1, ARG2); \
\
UInt8 oldCounter = g_cmpPcCounterMapInterrun[index]; \
if (oldCounter < newCounter) { \
g_cmpPcCounterMapInterrun[index] = newCounter; \
g_cmpPcHammingDistMap[index] = newHamming; \
g_cmpPcAbsDistmap[index] = newAbs; \
DEBUG("new coverage (pc) on ", TYPE, ARG1, ARG2); \
STORE(g_hasNewCoverage, true); \
} else if (oldCounter == newCounter) { \
UInt8 hamming = g_cmpPcHammingDistMap[index]; \
UInt8 abs = g_cmpPcAbsDistmap[index]; \
if (newHamming < hamming) { \
g_cmpPcHammingDistMap[index] = newHamming; \
DEBUG("new coverage (Hamming) on ", TYPE, ARG1, ARG2); \
STORE(g_hasNewCoverage, true); \
} \
if (newAbs < abs) { \
g_cmpPcAbsDistmap[index] = newAbs; \
DEBUG("new coverage (abs) on ", TYPE, ARG1, ARG2); \
STORE(g_hasNewCoverage, true); \
} \
} \
}
#define STORE_RECENT_CMP(TYPE, ARG1, ARG2) \
{ \
if (ARG1 != ARG2) { \
static UInt64 cmpRecentWritePrng = 800 * (UInt64)sizeof(TYPE); \
size_t index = (POOR_RANDOM(cmpRecentWritePrng) % (RECENT_VALUE_MAP_SIZE / sizeof(TYPE)) / 2) * 2; \
g_cmpRecentValues##TYPE[index] = ARG1; \
g_cmpRecentValues##TYPE[index + 1] = ARG2; \
} \
}
#define RANDOM_RECENT_CMP(TYPE, RESULT) \
{ \
static UInt64 cmpRecentReadPrng = 0xdead * (UInt64)sizeof(TYPE); \
const size_t totalSize = RECENT_VALUE_MAP_SIZE / sizeof(TYPE); \
size_t index = POOR_RANDOM(cmpRecentReadPrng) % totalSize; \
while (index < 2 * totalSize) { \
TYPE candidate = g_cmpRecentValues##TYPE[index % totalSize]; \
if (candidate != 0) { \
size_t oddIndex = (index / 2) * 2 % totalSize; \
(RESULT)->left = g_cmpRecentValues##TYPE[oddIndex]; \
(RESULT)->right = g_cmpRecentValues##TYPE[oddIndex + 1]; \
return; \
} \
index++; \
} \
return; \
}
#define RANDOM_RECENT_CMP_FOR_KEY(TYPE, KEY, RESULT) \
{ \
static UInt64 cmpRecentReadPrng = 0xbeef * (UInt64)sizeof(TYPE); \
const size_t totalSize = RECENT_VALUE_MAP_SIZE / sizeof(TYPE); \
size_t index = POOR_RANDOM(cmpRecentReadPrng) % totalSize; \
while (index < 2 * totalSize) { \
TYPE candidate = g_cmpRecentValues##TYPE[index % totalSize]; \
if (candidate == KEY) { \
size_t oddIndex = (index / 2) * 2 % totalSize; \
(RESULT)->left = g_cmpRecentValues##TYPE[oddIndex]; \
(RESULT)->right = g_cmpRecentValues##TYPE[oddIndex + 1]; \
return; \
} \
index++; \
} \
return; \
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT8(UInt8Entry* result)
{
RANDOM_RECENT_CMP(UInt8, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT16(UInt16Entry* result)
{
RANDOM_RECENT_CMP(UInt16, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT32(UInt32Entry* result)
{
RANDOM_RECENT_CMP(UInt32, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT64(UInt64Entry* result)
{
RANDOM_RECENT_CMP(UInt64, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FLOAT(Float32Entry* result)
{
RANDOM_RECENT_CMP(Float32, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_DOUBLE(Float64Entry* result)
{
RANDOM_RECENT_CMP(Float64, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT8(UInt8 key, UInt8Entry* result)
{
RANDOM_RECENT_CMP_FOR_KEY(UInt8, key, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT16(UInt16 key, UInt16Entry* result)
{
RANDOM_RECENT_CMP_FOR_KEY(UInt16, key, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT32(UInt32 key, UInt32Entry* result)
{
RANDOM_RECENT_CMP_FOR_KEY(UInt32, key, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT64(UInt64 key, UInt64Entry* result)
{
RANDOM_RECENT_CMP_FOR_KEY(UInt64, key, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_FLOAT(Float32 key, Float32Entry* result)
{
RANDOM_RECENT_CMP_FOR_KEY(Float32, key, result);
}
MICROFUZZ_API void CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_DOUBLE(Float64 key, Float64Entry* result)
{
RANDOM_RECENT_CMP_FOR_KEY(Float64, key, result);
}
#define MEMSET_M(ARR, VALUE) \
for (size_t i = 0; i < sizeof(ARR) / sizeof(ARR[0]); ++i) { ARR[i] = VALUE; }
MICROFUZZ_API void CJ_MICROFUZZ_RESET_LOCAL_COVERAGE_INFO(void)
{
STORE(g_hasNewCoverage, false);
for (size_t i = 0; i < g_coverageMapSize; ++i) {
g_coverageMap[i] = 0;
}
MEMSET_M(g_cmpPcCounterMap, 0);
}
MICROFUZZ_API void CJ_MICROFUZZ_RESET_ALL_COVERAGE_INFO(void)
{
CJ_MICROFUZZ_RESET_LOCAL_COVERAGE_INFO();
for (size_t i = 0; i < g_coverageMapSize; ++i) {
g_globalCoverageMap[i] = 0;
}
MEMSET_M(g_cmpPcCounterMapInterrun, 0);
MEMSET_M(g_cmpPcHammingDistMap, 255);
MEMSET_M(g_cmpPcAbsDistmap, 255);
MEMSET_M(g_cmpRecentValuesUInt8, 0);
MEMSET_M(g_cmpRecentValuesUInt16, 0);
MEMSET_M(g_cmpRecentValuesUInt32, 0);
MEMSET_M(g_cmpRecentValuesUInt64, 0);
MEMSET_M(g_cmpRecentValuesFloat32, 0.0f);
MEMSET_M(g_cmpRecentValuesFloat64, 0.0);
}
MICROFUZZ_API Float64 CJ_MICROFUZZ_GLOBAL_EDGE_COVERAGE_EST(void)
{
Float64 result = 0.0;
for (size_t i = 0; i < g_coverageMapSize; ++i) {
result += ((g_globalCoverageMap[i] != 0) ? 1 : 0);
}
result /= g_coverageMapSize;
return result;
}
MICROFUZZ_API Float32 CJ_MICROFUZZ_FLIP_BIT_FLOAT(Float32 value, UInt8 bit)
{
UInt32 floatBits = 0;
__builtin_memcpy(&floatBits, &value, sizeof(value));
floatBits = floatBits ^ (((UInt32)1) << (bit % (sizeof(Float32) * CHAR_BIT)));
__builtin_memcpy(&value, &floatBits, sizeof(value));
return value;
}
MICROFUZZ_API Float64 CJ_MICROFUZZ_FLIP_BIT_DOUBLE(Float64 value, UInt8 bit)
{
UInt64 floatBits = 0;
__builtin_memcpy(&floatBits, &value, sizeof(value));
floatBits = floatBits ^ (((UInt64)1) << (bit % (sizeof(Float64) * CHAR_BIT)));
__builtin_memcpy(&value, &floatBits, sizeof(value));
return value;
}
* functions below are callbacks that will be invoked by coverage sanitizer
*/
SANCOV_CALLBACK void __sanitizer_cov_pcs_init(
__attribute__((unused)) const uintptr_t* pcs_beg, __attribute__((unused)) const uintptr_t* pcs_end)
{
STORE(g_initialized, true);
return;
}
SANCOV_CALLBACK void __sanitizer_cov_trace_const_cmp1(UInt8 arg1, UInt8 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt8, arg1, arg2);
STORE_RECENT_CMP(UInt8, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_const_cmp2(UInt16 arg1, UInt16 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt16, arg1, arg2);
STORE_RECENT_CMP(UInt16, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_const_cmp4(UInt32 arg1, UInt32 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt32, arg1, arg2);
STORE_RECENT_CMP(UInt32, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_const_cmp8(UInt64 arg1, UInt64 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt64, arg1, arg2);
STORE_RECENT_CMP(UInt64, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_cmp1(UInt8 arg1, UInt8 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt8, arg1, arg2);
STORE_RECENT_CMP(UInt8, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_cmp2(UInt16 arg1, UInt16 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt16, arg1, arg2);
STORE_RECENT_CMP(UInt16, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_cmp4(UInt32 arg1, UInt32 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt32, arg1, arg2);
STORE_RECENT_CMP(UInt32, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_cmp8(UInt64 arg1, UInt64 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
SANITIZER_COV_TRACE_CMP_TRACE(UInt64, arg1, arg2);
STORE_RECENT_CMP(UInt64, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_switch(
__attribute__((unused)) UInt64 val, __attribute__((unused)) UInt64* cases)
{
}
SANCOV_CALLBACK void __sanitizer_weak_hook_strcasecmp(__attribute__((unused)) void* p,
__attribute__((unused)) const char* s1, __attribute__((unused)) const char* s2, __attribute__((unused)) int result)
{
}
SANCOV_CALLBACK void __sanitizer_weak_hook_memcmp(__attribute__((unused)) void* p,
__attribute__((unused)) const void* s1, __attribute__((unused)) const void* s2, __attribute__((unused)) size_t n,
__attribute__((unused)) int result)
{
}
SANCOV_CALLBACK void __sanitizer_weak_hook_strncmp(__attribute__((unused)) void* p,
__attribute__((unused)) const char* s1, __attribute__((unused)) const char* s2, __attribute__((unused)) size_t n,
__attribute__((unused)) int result)
{
}
SANCOV_CALLBACK void __sanitizer_weak_hook_strcmp(__attribute__((unused)) void* p,
__attribute__((unused)) const char* s1, __attribute__((unused)) const char* s2, __attribute__((unused)) int result)
{
}
SANCOV_CALLBACK void __sanitizer_weak_hook_strncasecmp(__attribute__((unused)) void* caller_pc,
__attribute__((unused)) const char* s1, __attribute__((unused)) const char* s2, __attribute__((unused)) size_t n,
__attribute__((unused)) int result)
{
}
SANCOV_CALLBACK void __sanitizer_cov_trace_cmpf(Float32 arg1, Float32 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
STORE_RECENT_CMP(Float32, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_cmpd(Float64 arg1, Float64 arg2)
{
if (!LOAD(g_enableTrace)) {
return;
}
STORE_RECENT_CMP(Float64, arg1, arg2);
}
SANCOV_CALLBACK void __sanitizer_cov_trace_pc_guard_init(UInt32* start, UInt32* stop)
{
if (start == stop || *start != 0) {
return;
}
STORE(g_initialized, true);
UInt64 size = 0;
for (UInt32* x = start; x < stop; x++) {
*x = (++size) % MAX_COVERAGE_MAP_SIZE;
}
if (size > MAX_COVERAGE_MAP_SIZE) {
size = MAX_COVERAGE_MAP_SIZE;
}
for (size_t i = 0; i < size; ++i) {
g_coverageMap[i] = 0;
}
for (size_t i = 0; i < size; ++i) {
g_globalCoverageMap[i] = 0;
}
g_coverageMapSize = size;
}
* PC guard individual initialization callback, this is called for each individual counter
*/
SANCOV_CALLBACK void __sanitizer_cov_trace_pc_guard(UInt32* guard)
{
if (*guard == 0) {
return;
}
if (!LOAD(g_enableTrace)) {
return;
}
UInt8 newCoverage = ++g_coverageMap[*guard - 1];
if (newCoverage > g_globalCoverageMap[*guard - 1]) {
DEBUG("new coverage on ", guard, *guard, 0);
g_globalCoverageMap[*guard - 1] = newCoverage;
STORE(g_hasNewCoverage, true);
}
}
* PC guard initialization callback, this is called once and constructs the edge counters
*/
SANCOV_CALLBACK UInt32* __cj_sancov_pc_guard_ctor(UInt64 edgeCount)
{
UInt32* p = (UInt32*)calloc(edgeCount, sizeof(UInt32));
if (p != NULL) {
__sanitizer_cov_trace_pc_guard_init(p, p + edgeCount);
}
return p;
}
* This callback is needed to achieve compatibility with fuzz library, doesn't do anything
*/
SANCOV_CALLBACK int LLVMFuzzerRunDriver(__attribute__((unused)) int* argc, __attribute__((unused)) char*** argv,
__attribute__((unused)) int (*userCb)(const UInt8* data, size_t size))
{
return 0;
}
#ifdef __arm__
#pragma GCC diagnostic pop
#endif