From 7955120232585b7e6d35a65fae6ef155cfa547b8 Mon Sep 17 00:00:00 2001 From: nj00001 <42004790+nj00001@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:54:51 +0800 Subject: [PATCH 1/7] Port qemu forkserver version to v1 In addition to upgrading the forkserver version, some ijon support code has been added. However, the afl-fuzz part also needs to be modified, mainly to expand the bitmap, but it is a bit complicated. --- accel/tcg/cpu-exec.c | 88 +++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index bfa68dd339b36..3b6531037cb44 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -281,6 +281,15 @@ static void restore_memory_snapshot(void) { } +static int use_ijon = 0; +static unsigned char *ijon_map_ptr = dummy; +static unsigned char *ijon_max_ptr = dummy; + +static void qemu_ijon_init() { + use_ijon = !!getenv("AFL_QEMU_IJON"); + if (use_ijon == 0) return; +} + /* Set up SHM region and initialize other stuff. */ static void afl_map_shm_fuzz(void) { @@ -632,6 +641,8 @@ void afl_setup(void) { (persistent_exits ? "exits ": "")); } + qemu_ijon_init(); + } /* Fork server logic, invoked once we hit _start. */ @@ -643,56 +654,81 @@ void afl_forkserver(CPUState *cpu) { if (getenv("AFL_QEMU_DEBUG_MAPS")) open_self_maps(cpu->env_ptr, 1); - //u32 map_size = 0; - unsigned char tmp[4] = {0}; pid_t child_pid; int t_fd[2]; u8 child_stopped = 0; u32 was_killed; - int status = 0; + u32 version = 0x41464c00 + FS_NEW_VERSION_MAX; + u32 tmp = version ^ 0xffffffff, status2, status = version; + u8 *msg = (u8 *)&status; + u8 *reply = (u8 *)&status2; - if (!getenv("AFL_OLD_FORKSERVER")) { - - // with the max ID value - if (MAP_SIZE <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(MAP_SIZE) | FS_OPT_MAPSIZE); - if (lkm_snapshot) status |= FS_OPT_SNAPSHOT; - if (sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ; - if (status) status |= (FS_OPT_ENABLED | FS_OPT_NEWCMPLOG); - - } - - memcpy(tmp, &status, 4); if (getenv("AFL_DEBUG")) fprintf(stderr, "Debug: Sending status 0x%08x\n", status); /* Tell the parent that we're alive. If the parent doesn't want to talk, assume that we're not running in forkserver mode. */ - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + if (write(FORKSRV_FD + 1, msg, 4) != 4) return; afl_forksrv_pid = getpid(); int first_run = 1; - if (sharedmem_fuzzing) { - if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(2); + if (!getenv("AFL_OLD_FORKSERVER")) { - if ((was_killed & (0xffffffff & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ))) == - (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) - afl_map_shm_fuzz(); - else { + if (read(FORKSRV_FD, reply, 4) != 4) { _exit(1); } + if (tmp != status2) { - fprintf(stderr, - "[AFL] ERROR: afl-fuzz is old and does not support" - " shmem input"); - exit(1); + fprintf(stderr, "wrong forkserver message from AFL++ tool"); + _exit(1); } + // send the set/requested options to forkserver + status = FS_NEW_OPT_MAPSIZE; // we always send the map size + if (lkm_snapshot) status |= FS_OPT_SNAPSHOT; + if (sharedmem_fuzzing) status |= FS_NEW_OPT_SHDMEM_FUZZ; + + + u32 __afl_map_size = MAP_SIZE; + + if (use_ijon) { + + __afl_map_size = (((__afl_map_size + 63) >> 6) << 6); + __afl_map_size += MAP_SIZE_IJON_MAP + MAP_SIZE_IJON_BYTES; + + ijon_map_ptr = afl_area_ptr + MAP_SIZE; + ijon_max_ptr = ijon_map_ptr + MAP_SIZE; + + status |= FS_OPT_IJON; + + } + + if (write(FORKSRV_FD + 1, msg, 4) != 4) { + + errno = 0; + _exit(1); + + } + + // Now send the parameters for the set options, increasing by option number + + // FS_NEW_OPT_MAPSIZE - we always send the map size + status = __afl_map_size; + if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); } + + // send welcome message as final message + status = version; + if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); } + } + // END forkserver handshake + + if (sharedmem_fuzzing) { afl_map_shm_fuzz(); } + /* All right, let's await orders... */ while (1) { From 6ac7086fb249b19508462003e2768d088c87fbdf Mon Sep 17 00:00:00 2001 From: nj00001 <42004790+nj00001@users.noreply.github.com> Date: Tue, 30 Sep 2025 11:35:09 +0800 Subject: [PATCH 2/7] Add ijon related functions, which requires updates to the afl-fuzz repository Updated forkserver code and ijon code. Next, we will add yaml parsing function to read user ijon settings from the configuration file. --- accel/tcg/cpu-exec.c | 297 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 293 insertions(+), 4 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 3b6531037cb44..b781d907663a9 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -47,6 +47,7 @@ #include "qemuafl/common.h" #include "qemuafl/imported/snapshot-inl.h" +#include "qemuafl/imported/afl-ijon-min.h" #include #include @@ -285,11 +286,289 @@ static int use_ijon = 0; static unsigned char *ijon_map_ptr = dummy; static unsigned char *ijon_max_ptr = dummy; +/* IJON state tracking globals */ +#if defined(__ANDROID__) || defined(__HAIKU__) || defined(NO_TLS) +u32 __afl_ijon_state = 0; // Current IJON state +u32 __afl_ijon_state_log = 0; // State history log +#else +__thread u32 __afl_ijon_state = 0; +__thread u32 __afl_ijon_state_log = 0; +#endif + static void qemu_ijon_init() { use_ijon = !!getenv("AFL_QEMU_IJON"); if (use_ijon == 0) return; } + +// copy from afl-compiler-rt.o.c + +/* IJON max tracking runtime functions */ + +#include +#include + +/* Supporting hash functions */ +uint64_t ijon_simple_hash(uint64_t x) { + + const uint64_t golden_ratio = 0x9E3779B97F4A7C15ULL; + return x * golden_ratio; + +} + +uint32_t ijon_hashint(uint32_t old, uint32_t val) { + + // PERFECT HASH: Bit-interleaving approach for coordinate pairs + // Guarantees no hash collisions for coordinates < 65536 + // Interleave bits of x and y to create unique 32-bit hash + + uint32_t x = old; + uint32_t y = val; + uint32_t result = 0; + + // Interleave the lower 16 bits of x and y + for (int i = 0; i < 16; i++) { + + result |= ((x & (1U << i)) << i) | ((y & (1U << i)) << (i + 1)); + + } + + // Apply mixing for better distribution in coverage map + result ^= result >> 16; + result *= 0x85ebca6b; + result ^= result >> 13; + result *= 0xc2b2ae35; + result ^= result >> 16; + + return result; + +} + +uint32_t ijon_hashstr(uint32_t old, char *val) { + + return ijon_hashmem(old, val, strlen(val)); + +} + +uint32_t ijon_hashmem(uint32_t old, char *val, size_t len) { + + old = ijon_hashint(old, len); + for (size_t i = 0; i < len; i++) { + + old = ijon_hashint(old, val[i]); + + } + + return old; + +} + +void ijon_max(uint32_t addr, u64 val) { + + u32 var_id = (u32)(ijon_simple_hash((uint64_t)addr) % MAP_SIZE_IJON_ENTRIES); + // u32 var_id = (u32)(addr % MAP_SIZE_IJON_ENTRIES); + + if (ijon_max_ptr[var_id] < val) { ijon_max_ptr[var_id] = val; } + +} + +void ijon_min(uint32_t addr, u64 val) { + + val = 0xffffffffffffffff - val; + ijon_max(addr, val); + +} + +void ijon_set(uint32_t loc_addr, uint32_t val) { + + // ORIGINAL IJON APPROACH: XOR location hash with value to create unique + // coverage point This follows the original: + // ijon_map_set(ijon_hashstr(__LINE__,__FILE__)^(x)) + u32 combined_hash = loc_addr ^ val; + u32 coverage_id = combined_hash % MAP_SIZE_IJON_MAP; + + ijon_map_ptr[coverage_id] = 1; + +} + +void ijon_inc(uint32_t loc_addr, uint32_t val) { + + // ORIGINAL IJON APPROACH: XOR location hash with value to create unique + // coverage point This follows the original: + // ijon_map_set(ijon_hashstr(__LINE__,__FILE__)^(x)) + uint32_t combined_hash = loc_addr ^ val; + + u32 coverage_id = combined_hash % MAP_SIZE_IJON_MAP; + + // Memory-safe: Use actual available shared memory size + // Use AFL's incremental coverage approach (same as __afl_trace) + ijon_map_ptr[coverage_id] += 1; + +} + +/* Variadic runtime functions */ +void ijon_max_variadic(uint32_t addr, ...) { + + va_list args; + va_start(args, addr); + + u64 combined = 1; // Start with 1 for Java-style hash + u64 value; + int arg_count = 0; + + // Process all arguments until we hit the sentinel (0) + // Using Java-style hash: hash = 31 * hash + value + while ((value = va_arg(args, u64)) != 0) { + + combined = combined * 31 + value; + arg_count++; + + // CRITICAL: Prevent infinite loops if sentinel is missing + if (arg_count > 20) { break; } + + } + + va_end(args); + + // Call the basic ijon_max function + ijon_max(addr, combined); + +} + +void ijon_min_variadic(uint32_t addr, ...) { + + va_list args; + va_start(args, addr); + + u64 combined = 1; // Start with 1 for Java-style hash + u64 value; + int arg_count = 0; + + // Process all arguments until we hit the sentinel (0) + // Using Java-style hash: hash = 31 * hash + value + while ((value = va_arg(args, u64)) != 0) { + + combined = combined * 31 + value; + arg_count++; + + // CRITICAL: Prevent infinite loops if sentinel is missing + if (arg_count > 20) { break; } + + } + + va_end(args); + + // Call the basic ijon_min function + ijon_min(addr, combined); + +} + +/* IJON state management functions */ + +void ijon_xor_state(uint32_t val) { + + __afl_ijon_state = (__afl_ijon_state ^ val) % (u32)MAP_SIZE_IJON_MAP; + +} + +void ijon_reset_state(void) { + + __afl_ijon_state = 0; + __afl_ijon_state_log = 0; + +} + +/* String and memory distance functions */ + +uint32_t ijon_strdist(char *a, char *b) { + + if (!a && !b) return 0; + if (!a) return strlen(b); + if (!b) return strlen(a); + + size_t len_a = strlen(a); + size_t len_b = strlen(b); + + return ijon_memdist(a, b, len_a > len_b ? len_a : len_b); + +} + +uint32_t ijon_memdist(char *a, char *b, size_t len) { + + if (!a && !b) return 0; + if (!a || !b) return (uint32_t)len; + if (len == 0) return 0; + + // For efficiency with large strings, use a bounded Levenshtein distance + // Limit the maximum distance calculation to avoid performance issues + size_t max_dist = len > 1024 ? 1024 : len; + + // Use Levenshtein distance algorithm (edit distance) + // For memory efficiency, use a rolling array approach for large strings + if (max_dist <= 256) { + + // Small strings: use full matrix approach + uint32_t matrix[257][257]; // max_dist + 1 + + // Initialize first row and column + for (size_t i = 0; i <= max_dist; i++) { + + matrix[i][0] = i; + matrix[0][i] = i; + + } + + // Fill the matrix + for (size_t i = 1; i <= max_dist && i <= strlen(a); i++) { + + for (size_t j = 1; j <= max_dist && j <= strlen(b); j++) { + + uint32_t cost = (a[i - 1] == b[j - 1]) ? 0 : 1; + + uint32_t deletion = matrix[i - 1][j] + 1; + uint32_t insertion = matrix[i][j - 1] + 1; + uint32_t substitution = matrix[i - 1][j - 1] + cost; + + matrix[i][j] = + deletion < insertion + ? (deletion < substitution ? deletion : substitution) + : (insertion < substitution ? insertion : substitution); + + } + + } + + size_t actual_len_a = strlen(a) > max_dist ? max_dist : strlen(a); + size_t actual_len_b = strlen(b) > max_dist ? max_dist : strlen(b); + + return matrix[actual_len_a][actual_len_b]; + + } else { + + // Large strings: use simplified byte-by-byte comparison with early + // termination + uint32_t differences = 0; + size_t min_len = strlen(a) < strlen(b) ? strlen(a) : strlen(b); + size_t max_len = strlen(a) > strlen(b) ? strlen(a) : strlen(b); + + // Count character differences up to min_len + for (size_t i = 0; i < min_len && i < max_dist; i++) { + + if (a[i] != b[i]) { differences++; } + + } + + // Add length difference + differences += (uint32_t)(max_len - min_len); + + return differences > max_dist ? max_dist : differences; + + } + +} + +//end copy + /* Set up SHM region and initialize other stuff. */ static void afl_map_shm_fuzz(void) { @@ -654,6 +933,7 @@ void afl_forkserver(CPUState *cpu) { if (getenv("AFL_QEMU_DEBUG_MAPS")) open_self_maps(cpu->env_ptr, 1); + u32 __afl_old_forkserver = 0; pid_t child_pid; int t_fd[2]; u8 child_stopped = 0; @@ -666,6 +946,17 @@ void afl_forkserver(CPUState *cpu) { if (getenv("AFL_DEBUG")) fprintf(stderr, "Debug: Sending status 0x%08x\n", status); + if (getenv("AFL_OLD_FORKSERVER")) { + + __afl_old_forkserver = 1; + status = 0; + + fprintf(stderr, "The current version of afl++ qemu mode " + "supports forkserver v1, but afl-fuzz still retains " + "support for the old forkserver (qemu) version\n"); + + } + /* Tell the parent that we're alive. If the parent doesn't want to talk, assume that we're not running in forkserver mode. */ @@ -675,8 +966,7 @@ void afl_forkserver(CPUState *cpu) { int first_run = 1; - - if (!getenv("AFL_OLD_FORKSERVER")) { + if (!__afl_old_forkserver) { if (read(FORKSRV_FD, reply, 4) != 4) { _exit(1); } if (tmp != status2) { @@ -691,7 +981,6 @@ void afl_forkserver(CPUState *cpu) { if (lkm_snapshot) status |= FS_OPT_SNAPSHOT; if (sharedmem_fuzzing) status |= FS_NEW_OPT_SHDMEM_FUZZ; - u32 __afl_map_size = MAP_SIZE; if (use_ijon) { @@ -700,7 +989,7 @@ void afl_forkserver(CPUState *cpu) { __afl_map_size += MAP_SIZE_IJON_MAP + MAP_SIZE_IJON_BYTES; ijon_map_ptr = afl_area_ptr + MAP_SIZE; - ijon_max_ptr = ijon_map_ptr + MAP_SIZE; + ijon_max_ptr = ijon_map_ptr + MAP_SIZE_IJON_MAP; status |= FS_OPT_IJON; From 313d3cc507f3b510b73c4b9e1bdabea37436a58c Mon Sep 17 00:00:00 2001 From: "chenchen.wu" Date: Thu, 9 Oct 2025 09:59:02 +0800 Subject: [PATCH 3/7] add qemu ijon support --- accel/tcg/cpu-exec.c | 230 +++++++++++++++++++++++++++++++++--- accel/tcg/tcg-runtime.c | 9 ++ accel/tcg/tcg-runtime.h | 2 + qemuafl/imported/cmplog.h | 16 ++- qemuafl/qemu-ijon-support.h | 49 ++++++++ target/i386/tcg/translate.c | 17 +++ 6 files changed, 307 insertions(+), 16 deletions(-) create mode 100644 qemuafl/qemu-ijon-support.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index b781d907663a9..787d46afd24dd 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -36,6 +36,13 @@ #include "exec/log.h" #include "qemu/main-loop.h" #include "qemu/selfmap.h" +#include "qapi/qmp/qjson.h" /* qobject_from_json() */ +#include "qapi/qmp/qdict.h" /* QDict, qdict_first, qdict_next, qdict_get_try_str 等 */ +#include "qapi/qmp/qstring.h"/* qobject_to_qstring, qstring_get_str */ +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/error.h" + #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) #include "hw/i386/apic.h" #endif @@ -47,7 +54,7 @@ #include "qemuafl/common.h" #include "qemuafl/imported/snapshot-inl.h" -#include "qemuafl/imported/afl-ijon-min.h" +#include "qemuafl/qemu-ijon-support.h" #include #include @@ -295,13 +302,209 @@ __thread u32 __afl_ijon_state = 0; __thread u32 __afl_ijon_state_log = 0; #endif -static void qemu_ijon_init() { - use_ijon = !!getenv("AFL_QEMU_IJON"); - if (use_ijon == 0) return; +uint32_t ijon_hooker_cnt = 0; +target_ulong hook_code_addr[0x1000]; +target_ulong g_var_addr[0x1000]; +uint32_t g_var_len[0x1000]; +uint32_t ijon_type[0x1000]; + +/* 读取整个文件到 malloc 的缓冲区(调用者负责 free) */ +static ssize_t read_file_all(const char *path, char **out_buf) { + FILE *f = fopen(path, "rb"); + if (!f) return -1; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return -1; } + long sz = ftell(f); + if (sz < 0) { fclose(f); return -1; } + rewind(f); + char *buf = g_malloc(sz + 1); + if (!buf) { fclose(f); return -1; } + size_t r = fread(buf, 1, sz, f); + fclose(f); + buf[r] = '\0'; + *out_buf = buf; + return (ssize_t)r; } +/* 解析入口(使用 QDict / qjson / qlist / qstring) */ +static void qemu_ijon_init(void) { + const char *path = getenv("AFL_QEMU_IJON"); + use_ijon = !!path; + if (!use_ijon) return; + + char *json = NULL; + ssize_t len = read_file_all(path, &json); + if (len < 0) { + fprintf(stderr, "ijon: cannot read '%s'\n", path ? path : "(null)"); + exit(-1); + } + + Error *err = NULL; + QObject *root_obj = qobject_from_json(json, &err); + if (!root_obj) { + error_reportf_err(err, "ijon: qobject_from_json failed: \n"); + exit(-1); + } + + /* 期望顶层是数组(QList) */ + QList *top_list = qobject_to(QList, root_obj); + if (!top_list) { + fprintf(stderr, "ijon: top-level JSON is not an array\n"); + exit(-1); + } + + /* 遍历外层数组(元素应为数组/列表,每个内层包含 4 个元素) */ + const QListEntry *le; + for (le = qlist_first(top_list); le != NULL; le = qlist_next(le)) { + + QObject *elem = le->value; + if (!elem) continue; + + /* elem 本身应为内层数组(QList) */ + QList *inner = qobject_to(QList, elem); + if (!inner) { + fprintf(stderr, "ijon: element is not an array, skipping\n"); + continue; + } + + /* 遍历内层数组并按顺序读取四个元素 */ + const QListEntry *ile = qlist_first(inner); + if (!ile) { fprintf(stderr, "ijon: empty inner array, skip\n"); continue; } + + /* 1) hook addr string */ + QObject *o_hook = ile->value; + ile = qlist_next(ile); + if (!o_hook || !ile) { fprintf(stderr, "ijon: inner array too short (hook)\n"); continue; } + + target_ulong hook_addr; + QString *qhook = qobject_to(QString, o_hook); + if (qhook) { + + const char *hook_s = qstring_get_str(qhook); + + errno = 0; + char *endptr = NULL; + hook_addr = strtoul(hook_s, &endptr, 0); + if (errno || endptr == hook_s) { fprintf(stderr, "ijon: invalid hook addr '%s'\n", hook_s); continue;} -// copy from afl-compiler-rt.o.c + } else { + + QNum *qhook = qobject_to(QNum, o_hook); + if (!qnum_get_try_uint(qhook, &hook_addr)) { fprintf(stderr, "ijon: cant get hook addr, skip\n"); continue;} + + } + + /* 2) method/type string */ + QObject *o_type = ile->value; + ile = qlist_next(ile); + if (!o_type || !ile) { fprintf(stderr, "ijon: inner array too short (type)\n"); continue; } + + QString *qtype = qobject_to(QString, o_type); + if (!qtype) { fprintf(stderr, "ijon: type element not a string, skip\n"); continue; } + + const char *type_s = qstring_get_str(qtype); + int itype = str_to_ijon(type_s); + if (itype == -1) { fprintf(stderr, "ijon: unknown method '%s' (hook 0x%lx)\n", type_s, (unsigned long)hook_addr); continue;} + + /* 3) var addr string */ + QObject *o_var = ile->value; + ile = qlist_next(ile); + if (!o_var || !ile) { fprintf(stderr, "ijon: inner array too short (var)\n"); continue; } + + target_ulong var_addr; + QString *qvar = qobject_to(QString, o_var); + if (qvar) { + + const char *var_s = qstring_get_str(qvar); + + errno = 0; + var_addr = strtoul(var_s, NULL, 0); + if (errno) { fprintf(stderr, "ijon: invalid var addr '%s' (hook 0x%lx)\n", var_s, (unsigned long)hook_addr); continue;} + + } else { + + QNum *qvar = qobject_to(QNum, o_var); + if (!qnum_get_try_uint(qvar, &var_addr)) { fprintf(stderr, "ijon: cant get var addr, skip\n"); continue;} + + } + + + /* 4) length (可以是字符串或数字,但这里我们期望字符串或 primitive,可以尝试 qobject_to_qstring 或 qobject_to_qint) */ + QObject *o_len = ile->value; + /* ile = qlist_next(ile); // 不需要进一步移动 */ + if (!o_len) { fprintf(stderr, "ijon: inner array missing length, skip\n"); continue; } + + int64_t var_len = 0; + /* 尝试当作 QString 处理 */ + QString *qlen = qobject_to(QString, o_len); + if (qlen) { + + const char *len_s = qstring_get_str(qlen); + + errno = 0; + var_len = strtoul(len_s, NULL, 0); + if (errno) { fprintf(stderr, "ijon: invalid length '%s' (hook 0x%lx)\n", len_s, (unsigned long)hook_addr); continue; } + + } else { + + QNum *qlen = qobject_to(QNum, o_len); + if (!qnum_get_try_int(qlen, &var_len)) { fprintf(stderr, "ijon: invalid var length (hook 0x%lx)\n", (unsigned long)hook_addr); continue;} + + } + + /* 插入 mapping(直接写入数组并增加计数),保持边界检查 */ + if (ijon_hooker_cnt >= 0x1000) { + fprintf(stderr, "ijon: entry limit reached, skip\n"); + break; + } + + hook_code_addr[ijon_hooker_cnt] = hook_addr; + g_var_addr[ijon_hooker_cnt] = var_addr; + ijon_type[ijon_hooker_cnt] = (uint32_t)itype; + g_var_len[ijon_hooker_cnt] = var_len; + ijon_hooker_cnt++; + } + + /* 清理并打印结果 */ + qobject_unref(root_obj); + g_free(json); + + fprintf(stderr, "ijon: loaded %u mappings\n", ijon_hooker_cnt); + for (uint32_t i = 0; i < ijon_hooker_cnt; i++) { + fprintf(stderr, "ijon[%u]: hook=0x%lx type=%s var=0x%lx len=%u\n", + i, (unsigned long)hook_code_addr[i], + ijon_to_str(ijon_type[i]), + (unsigned long)g_var_addr[i], + (unsigned)g_var_len[i]); + } + +} + +const char* ijon_to_str(IJON v) { + switch(v) { +#define X(name, func) case e_##name: return #name; + IJON_LIST +#undef X + default: return "UNKNOWN"; + } +} + +IJON str_to_ijon(const char* str) { +#define X(name, func) if (strcmp(str, #name) == 0) return e_##name; + IJON_LIST +#undef X + return -1; +} + +void ijon_dispatch(IJON v, uint32_t addr, u64 val) { + switch(v) { +#define X(name, func) case e_##name: func(addr, val); break; + IJON_LIST +#undef X + default: printf("Unknown IJON value\n"); break; + } +} + +// Reference from afl-compiler-rt.o.c /* IJON max tracking runtime functions */ @@ -365,8 +568,8 @@ uint32_t ijon_hashmem(uint32_t old, char *val, size_t len) { void ijon_max(uint32_t addr, u64 val) { - u32 var_id = (u32)(ijon_simple_hash((uint64_t)addr) % MAP_SIZE_IJON_ENTRIES); - // u32 var_id = (u32)(addr % MAP_SIZE_IJON_ENTRIES); + // u32 var_id = (u32)(ijon_simple_hash((uint64_t)addr) % MAP_SIZE_IJON_ENTRIES); + u32 var_id = (u32)(addr % MAP_SIZE_IJON_ENTRIES); if (ijon_max_ptr[var_id] < val) { ijon_max_ptr[var_id] = val; } @@ -384,7 +587,7 @@ void ijon_set(uint32_t loc_addr, uint32_t val) { // ORIGINAL IJON APPROACH: XOR location hash with value to create unique // coverage point This follows the original: // ijon_map_set(ijon_hashstr(__LINE__,__FILE__)^(x)) - u32 combined_hash = loc_addr ^ val; + u32 combined_hash = ijon_simple_hash(loc_addr) ^ val; u32 coverage_id = combined_hash % MAP_SIZE_IJON_MAP; ijon_map_ptr[coverage_id] = 1; @@ -396,8 +599,7 @@ void ijon_inc(uint32_t loc_addr, uint32_t val) { // ORIGINAL IJON APPROACH: XOR location hash with value to create unique // coverage point This follows the original: // ijon_map_set(ijon_hashstr(__LINE__,__FILE__)^(x)) - uint32_t combined_hash = loc_addr ^ val; - + uint32_t combined_hash = ijon_simple_hash(loc_addr) ^ val; u32 coverage_id = combined_hash % MAP_SIZE_IJON_MAP; // Memory-safe: Use actual available shared memory size @@ -465,13 +667,13 @@ void ijon_min_variadic(uint32_t addr, ...) { /* IJON state management functions */ -void ijon_xor_state(uint32_t val) { +void ijon_xor_state(uint32_t addr, u64 val) { - __afl_ijon_state = (__afl_ijon_state ^ val) % (u32)MAP_SIZE_IJON_MAP; + __afl_ijon_state = (__afl_ijon_state ^ addr) % (u32)MAP_SIZE_IJON_MAP; } -void ijon_reset_state(void) { +void ijon_reset_state(uint32_t addr, u64 val) { __afl_ijon_state = 0; __afl_ijon_state_log = 0; @@ -567,8 +769,6 @@ uint32_t ijon_memdist(char *a, char *b, size_t len) { } -//end copy - /* Set up SHM region and initialize other stuff. */ static void afl_map_shm_fuzz(void) { diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 49df924c25847..04bb709c918d3 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -33,9 +33,18 @@ #include "tcg/tcg.h" #include "qemuafl/common.h" +#include "qemuafl/qemu-ijon-support.h" uint32_t afl_hash_ip(uint64_t); +void HELPER(ijon_func_call)(target_ulong var_addr, target_ulong var_len, target_ulong itype, target_ulong idx) +{ + uint64_t buf = 0; + memcpy(&buf, var_addr, var_len); + ijon_dispatch(itype, idx, buf); + fprintf(stderr, "trigger ijon: addr=0x%016" PRIx64 " tag=%s value %ld\n", var_addr, ijon_to_str(itype), buf); +} + void HELPER(afl_entry_routine)(CPUArchState *env) { afl_forkserver(env_cpu(env)); diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index d8562470c3164..679bc10c95777 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -357,3 +357,5 @@ DEF_HELPER_FLAGS_2(qasan_store4, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(qasan_store8, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(qasan_shadow_stack_push, TCG_CALL_NO_RWG, void, tl) DEF_HELPER_FLAGS_1(qasan_shadow_stack_pop, TCG_CALL_NO_RWG, void, tl) + +DEF_HELPER_FLAGS_4(ijon_func_call, TCG_CALL_NO_RWG, void, tl, tl, tl, tl) \ No newline at end of file diff --git a/qemuafl/imported/cmplog.h b/qemuafl/imported/cmplog.h index e45b1092d45d5..604dbf0a0ccb9 100644 --- a/qemuafl/imported/cmplog.h +++ b/qemuafl/imported/cmplog.h @@ -41,6 +41,10 @@ #define CMP_TYPE_INS 0 #define CMP_TYPE_RTN 1 +#define ADDR_ATTR_COMBINE(v0attr, v1attr) ((v0attr & 3) + ((v1attr & 3) << 2)) +#define ADDR_ATTR_V0(x) (x & 3) +#define ADDR_ATTR_V1(x) ((x >> 2) & 3) + struct cmp_header { // 16 bit = 2 bytes unsigned hits : 6; // up to 63 entries, we have CMP_MAP_H = 32 @@ -70,7 +74,8 @@ struct cmpfn_operands { u8 v1[32]; u8 v0_len; u8 v1_len; - u8 unused[6]; // 2 bits could be used for "is constant operand" + u8 addr_attr; + u8 unused[5]; // 2 bits could be used for "is constant operand" } __attribute__((packed)); @@ -88,5 +93,14 @@ struct cmp_map { struct afl_forkserver; void cmplog_exec_child(struct afl_forkserver *fsrv, char **argv); +// Attribute of whether the Buffer points to the memory area mapped by ELF +enum { + + ADDR_ATTR_NOTFOUND = 0, + ADDR_ATTR_RO = 1, + ADDR_ATTR_RW = 2, + +}; + #endif diff --git a/qemuafl/qemu-ijon-support.h b/qemuafl/qemu-ijon-support.h new file mode 100644 index 0000000000000..0f1bc70c9d7c4 --- /dev/null +++ b/qemuafl/qemu-ijon-support.h @@ -0,0 +1,49 @@ +// +// Created by wu on 2025/9/30. +// + +#ifndef QEMUAFL_QEMU_IJON_SUPPORT_H +#define QEMUAFL_QEMU_IJON_SUPPORT_H + + +extern uint32_t ijon_hooker_cnt; +extern target_ulong hook_code_addr[0x1000]; +extern target_ulong g_var_addr[0x1000]; +extern uint32_t g_var_len[0x1000]; +extern uint32_t ijon_type[0x1000]; + + +void ijon_max(uint32_t addr, u64 val); +void ijon_min(uint32_t addr, u64 val); +void ijon_set(uint32_t addr, uint32_t val); +void ijon_inc(uint32_t addr, uint32_t val); + +/* IJON state management functions */ +void ijon_xor_state(uint32_t addr, u64 val); +void ijon_reset_state(uint32_t addr, u64 val); +uint32_t ijon_memdist(char *a, char *b, size_t len); +uint32_t ijon_hashmem(uint32_t old, char *val, size_t len); + +#define IJON_LIST \ +X(ijon_max, ijon_max) \ +X(ijon_min, ijon_min) \ +X(ijon_set, ijon_set) \ +X(ijon_inc, ijon_inc) \ +X(ijon_xor_state, ijon_xor_state) \ +X(ijon_reset_state, ijon_reset_state) + +// +// 定义枚举(带 e_ 前缀) +// +typedef enum { + #define X(name, func) e_##name, + IJON_LIST +#undef X + e_IJON_COUNT +} IJON; + +const char* ijon_to_str(IJON v); +IJON str_to_ijon(const char* str); +void ijon_dispatch(IJON v, uint32_t addr, u64 val); + +#endif //QEMUAFL_QEMU_IJON_SUPPORT_H \ No newline at end of file diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 271cbf6455767..dd8ce1e350254 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -37,6 +37,8 @@ #include "qemuafl/cpu-translate.h" #include "qemuafl/api.h" +#include "qemuafl/qemu-ijon-support.h" + #define AFL_QEMU_TARGET_I386_SNIPPET \ if (is_persistent) { \ \ @@ -8715,6 +8717,21 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } #endif + for (int i = 0; i < ijon_hooker_cnt; i++) { + if (dc->base.pc_next == hook_code_addr[i]) { + TCGv var_addr = tcg_const_tl(g_var_addr[i]); + TCGv var_len = tcg_const_tl(g_var_len[i]); + TCGv itype = tcg_const_tl(ijon_type[i]); + TCGv idx = tcg_const_tl(i); + gen_helper_ijon_func_call(var_addr, var_len, itype, idx); + fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); + tcg_temp_free(var_addr); + tcg_temp_free(var_len); + tcg_temp_free(itype); + tcg_temp_free(idx); + } + } + pc_next = disas_insn(dc, cpu); if (dc->tf || (dc->base.tb->flags & HF_INHIBIT_IRQ_MASK)) { From 82f78e641b1e48e88635b1839ff2830df0e73743 Mon Sep 17 00:00:00 2001 From: "chenchen.wu" Date: Fri, 10 Oct 2025 17:18:11 +0800 Subject: [PATCH 4/7] Simplified the ijon configuration file format and added support for register data --- accel/tcg/cpu-exec.c | 289 ++++++++++++++++++++---------------- qemuafl/qemu-ijon-support.h | 3 +- 2 files changed, 167 insertions(+), 125 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 787d46afd24dd..1e4a9f9a42bca 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -325,148 +325,137 @@ static ssize_t read_file_all(const char *path, char **out_buf) { return (ssize_t)r; } -/* 解析入口(使用 QDict / qjson / qlist / qstring) */ +static char *trim(char *s) { + if (!s) return s; + // left + while (*s && isspace((unsigned char)*s)) s++; + if (*s == 0) return s; + // right + char *end = s + strlen(s) - 1; + while (end > s && isspace((unsigned char)*end)) { *end = '\0'; end--; } + return s; +} + +// parse unsigned integer (hex if 0x prefix, or decimal) +// returns 0 on success, non-zero on failure +static int parse_uint64_str(const char *s, target_ulong *out) { + if (!s || !*s) return -1; + errno = 0; + char *endp = NULL; + unsigned long long val = strtoull(s, &endp, 0); // base 0 自动识别 0x + if (endp == s) return -1; // no conversion + if (errno == ERANGE) return -1; + *out = (target_ulong)val; + return 0; +} + +/* Parse the ijon configuration file, which stores the following data structure: + * 0x4001200,ijon_set,0x4000005064,8 + * 0x40012bb,ijon_min,RAX,4 + * code addr, ijon method, var addr/reg, var len + */ static void qemu_ijon_init(void) { const char *path = getenv("AFL_QEMU_IJON"); use_ijon = !!path; if (!use_ijon) return; - char *json = NULL; - ssize_t len = read_file_all(path, &json); - if (len < 0) { + char *buf = NULL; + ssize_t buf_len = read_file_all(path, &buf); + if (buf_len < 0) { fprintf(stderr, "ijon: cannot read '%s'\n", path ? path : "(null)"); exit(-1); } - Error *err = NULL; - QObject *root_obj = qobject_from_json(json, &err); - if (!root_obj) { - error_reportf_err(err, "ijon: qobject_from_json failed: \n"); - exit(-1); - } - - /* 期望顶层是数组(QList) */ - QList *top_list = qobject_to(QList, root_obj); - if (!top_list) { - fprintf(stderr, "ijon: top-level JSON is not an array\n"); - exit(-1); - } - - /* 遍历外层数组(元素应为数组/列表,每个内层包含 4 个元素) */ - const QListEntry *le; - for (le = qlist_first(top_list); le != NULL; le = qlist_next(le)) { - - QObject *elem = le->value; - if (!elem) continue; - - /* elem 本身应为内层数组(QList) */ - QList *inner = qobject_to(QList, elem); - if (!inner) { - fprintf(stderr, "ijon: element is not an array, skipping\n"); - continue; - } - - /* 遍历内层数组并按顺序读取四个元素 */ - const QListEntry *ile = qlist_first(inner); - if (!ile) { fprintf(stderr, "ijon: empty inner array, skip\n"); continue; } - - /* 1) hook addr string */ - QObject *o_hook = ile->value; - ile = qlist_next(ile); - if (!o_hook || !ile) { fprintf(stderr, "ijon: inner array too short (hook)\n"); continue; } - - target_ulong hook_addr; - QString *qhook = qobject_to(QString, o_hook); - if (qhook) { - - const char *hook_s = qstring_get_str(qhook); - - errno = 0; - char *endptr = NULL; - hook_addr = strtoul(hook_s, &endptr, 0); - if (errno || endptr == hook_s) { fprintf(stderr, "ijon: invalid hook addr '%s'\n", hook_s); continue;} - - } else { - - QNum *qhook = qobject_to(QNum, o_hook); - if (!qnum_get_try_uint(qhook, &hook_addr)) { fprintf(stderr, "ijon: cant get hook addr, skip\n"); continue;} - - } - - /* 2) method/type string */ - QObject *o_type = ile->value; - ile = qlist_next(ile); - if (!o_type || !ile) { fprintf(stderr, "ijon: inner array too short (type)\n"); continue; } - - QString *qtype = qobject_to(QString, o_type); - if (!qtype) { fprintf(stderr, "ijon: type element not a string, skip\n"); continue; } - - const char *type_s = qstring_get_str(qtype); - int itype = str_to_ijon(type_s); - if (itype == -1) { fprintf(stderr, "ijon: unknown method '%s' (hook 0x%lx)\n", type_s, (unsigned long)hook_addr); continue;} - - /* 3) var addr string */ - QObject *o_var = ile->value; - ile = qlist_next(ile); - if (!o_var || !ile) { fprintf(stderr, "ijon: inner array too short (var)\n"); continue; } - - target_ulong var_addr; - QString *qvar = qobject_to(QString, o_var); - if (qvar) { - - const char *var_s = qstring_get_str(qvar); - - errno = 0; - var_addr = strtoul(var_s, NULL, 0); - if (errno) { fprintf(stderr, "ijon: invalid var addr '%s' (hook 0x%lx)\n", var_s, (unsigned long)hook_addr); continue;} - - } else { - - QNum *qvar = qobject_to(QNum, o_var); - if (!qnum_get_try_uint(qvar, &var_addr)) { fprintf(stderr, "ijon: cant get var addr, skip\n"); continue;} - + char *buf_copy = malloc(buf_len + 1); + if (!buf_copy) exit(-1); + memcpy(buf_copy, buf, buf_len); + buf_copy[buf_len] = '\0'; + + char *line_ctx; + char *line = strtok_r(buf_copy, "\n", &line_ctx); + while (line && ijon_hooker_cnt < 0x1000) { + char *s = trim(line); + if (*s == '\0') { line = strtok_r(NULL, "\n", &line_ctx); continue; } // 空行 + if (*s == '#') { line = strtok_r(NULL, "\n", &line_ctx); continue; } // 注释行 + + // 分割 4 个字段(允许字段中间有空白) + // 采用手动查找逗号,避免 strtok 在字段内容含逗号时出问题(这里不考虑字段含逗号) + char *f1 = s; + char *p = strchr(s, ','); + if (!p) { line = strtok_r(NULL, "\n", &line_ctx); continue; } // 格式错误,跳过 + *p = '\0'; char *f2 = trim(p + 1); + + // next comma + p = strchr(f2, ','); + if (!p) { line = strtok_r(NULL, "\n", &line_ctx); continue; } + *p = '\0'; char *f3 = trim(p + 1); + + // last comma + p = strchr(f3, ','); + if (!p) { line = strtok_r(NULL, "\n", &line_ctx); continue; } + *p = '\0'; char *f4 = trim(p + 1); + + f1 = trim(f1); + f2 = trim(f2); + f3 = trim(f3); + f4 = trim(f4); + + // 解析每个字段,任一失败则跳过该行 + int ok = 1; + + // field1 -> hook_code_addr[ijon_hooker_cnt] + target_ulong addr0; + if (parse_uint64_str(f1, &addr0) != 0) { ok = 0; } + + // field2 -> ijon_type[ijon_hooker_cnt] via str_to_ijon + uint32_t type2 = str_to_ijon(f2); + if (type2 == -1) { ok = 0; } + + // field3 -> g_var_addr[ijon_hooker_cnt] : numeric -> addr, string -> convert via str_to_ijon_reg (enum -> store as target_ulong) + target_ulong gaddr = 0; + if (ok) { + // 判断是否以数字开头或以 0x 开头或全数字(允许带负号? 这里假设不带) + int looks_like_number = 0; + const char *t = f3; + if (*t == '0' && (t[1]=='x' || t[1]=='X')) looks_like_number = 1; + else if (isdigit((unsigned char)*t)) looks_like_number = 1; + + if (looks_like_number) { + if (parse_uint64_str(f3, &gaddr) != 0) ok = 0; + } else { + uint32_t reg_enum = ijon_reg_to_addr(f3); + if (reg_enum == -1) ok = 0; + else { + CPUArchState* env = first_cpu->env_ptr; + gaddr = &env->regs[reg_enum]; + } + } } - - /* 4) length (可以是字符串或数字,但这里我们期望字符串或 primitive,可以尝试 qobject_to_qstring 或 qobject_to_qint) */ - QObject *o_len = ile->value; - /* ile = qlist_next(ile); // 不需要进一步移动 */ - if (!o_len) { fprintf(stderr, "ijon: inner array missing length, skip\n"); continue; } - - int64_t var_len = 0; - /* 尝试当作 QString 处理 */ - QString *qlen = qobject_to(QString, o_len); - if (qlen) { - - const char *len_s = qstring_get_str(qlen); - + // field4 -> g_var_len[ijon_hooker_cnt] + uint32_t len4 = 0; + if (ok) { errno = 0; - var_len = strtoul(len_s, NULL, 0); - if (errno) { fprintf(stderr, "ijon: invalid length '%s' (hook 0x%lx)\n", len_s, (unsigned long)hook_addr); continue; } - - } else { - - QNum *qlen = qobject_to(QNum, o_len); - if (!qnum_get_try_int(qlen, &var_len)) { fprintf(stderr, "ijon: invalid var length (hook 0x%lx)\n", (unsigned long)hook_addr); continue;} - + char *endp = NULL; + unsigned long v = strtoul(f4, &endp, 0); + if (endp == f4 || errno == ERANGE) ok = 0; + else len4 = (uint32_t)v; } - /* 插入 mapping(直接写入数组并增加计数),保持边界检查 */ - if (ijon_hooker_cnt >= 0x1000) { - fprintf(stderr, "ijon: entry limit reached, skip\n"); - break; + if (ok) { + hook_code_addr[ijon_hooker_cnt] = addr0; + ijon_type[ijon_hooker_cnt] = type2; + g_var_addr[ijon_hooker_cnt] = gaddr; + g_var_len[ijon_hooker_cnt] = len4; + ijon_hooker_cnt++; + } else { + // skip } - hook_code_addr[ijon_hooker_cnt] = hook_addr; - g_var_addr[ijon_hooker_cnt] = var_addr; - ijon_type[ijon_hooker_cnt] = (uint32_t)itype; - g_var_len[ijon_hooker_cnt] = var_len; - ijon_hooker_cnt++; + line = strtok_r(NULL, "\n", &line_ctx); } - /* 清理并打印结果 */ - qobject_unref(root_obj); - g_free(json); + free(buf_copy); fprintf(stderr, "ijon: loaded %u mappings\n", ijon_hooker_cnt); for (uint32_t i = 0; i < ijon_hooker_cnt; i++) { @@ -479,6 +468,58 @@ static void qemu_ijon_init(void) { } +int ijon_reg_to_addr(const char *reg_str) { + if (reg_str == NULL) return -1; + + /* 可修改的拷贝以便 trim/upper 化 */ + size_t len = strlen(reg_str); + char *buf = (char *)malloc(len + 1); + if (!buf) return -1; + memcpy(buf, reg_str, len + 1); + + char *s = trim(buf); + if (!s || *s == '\0') { free(buf); return -1; } + + /* 允许 AT&T 风格的前导 '%' */ + if (s[0] == '%') s++; + + /* 统一转大写(就地)*/ + for (char *p = s; *p; ++p) *p = (char)toupper((unsigned char)*p); + + /* 常见寄存器别名(RAX/EAX -> R_EAX 等) */ + if (strcmp(s, "RAX") == 0 || strcmp(s, "EAX") == 0) { free(buf); return R_EAX; } + if (strcmp(s, "RCX") == 0 || strcmp(s, "ECX") == 0) { free(buf); return R_ECX; } + if (strcmp(s, "RDX") == 0 || strcmp(s, "EDX") == 0) { free(buf); return R_EDX; } + if (strcmp(s, "RBX") == 0 || strcmp(s, "EBX") == 0) { free(buf); return R_EBX; } + if (strcmp(s, "RSP") == 0 || strcmp(s, "ESP") == 0) { free(buf); return R_ESP; } + if (strcmp(s, "RBP") == 0 || strcmp(s, "EBP") == 0) { free(buf); return R_EBP; } + if (strcmp(s, "RSI") == 0 || strcmp(s, "ESI") == 0) { free(buf); return R_ESI; } + if (strcmp(s, "RDI") == 0 || strcmp(s, "EDI") == 0) { free(buf); return R_EDI; } + + /* 检查 R8..R15(允许后缀,如 R12D、R12W、R12B) */ + if (s[0] == 'R' && isdigit((unsigned char)s[1])) { + char *endptr = NULL; + long v = strtol(s + 1, &endptr, 10); + if (endptr != s + 1 && v >= 8 && v <= 15) { + free(buf); + return (int)v; /* enum R_R8..R_R15 对应数值 8..15 */ + } + } + + /* 直接数字形式 "0".."15" 也接受(有时配置中会用数字)*/ + if (isdigit((unsigned char)s[0])) { + char *endptr = NULL; + long v = strtol(s, &endptr, 10); + if (endptr != s && v >= 0 && v <= 15) { + free(buf); + return (int)v; + } + } + + free(buf); + return -1; +} + const char* ijon_to_str(IJON v) { switch(v) { #define X(name, func) case e_##name: return #name; diff --git a/qemuafl/qemu-ijon-support.h b/qemuafl/qemu-ijon-support.h index 0f1bc70c9d7c4..a3427f71b96fb 100644 --- a/qemuafl/qemu-ijon-support.h +++ b/qemuafl/qemu-ijon-support.h @@ -36,12 +36,13 @@ X(ijon_reset_state, ijon_reset_state) // 定义枚举(带 e_ 前缀) // typedef enum { - #define X(name, func) e_##name, +#define X(name, func) e_##name, IJON_LIST #undef X e_IJON_COUNT } IJON; +int ijon_reg_to_addr(const char *reg_str); const char* ijon_to_str(IJON v); IJON str_to_ijon(const char* str); void ijon_dispatch(IJON v, uint32_t addr, u64 val); From 7fef989b3ebefa4b80baf60ca2958d1394ffbb3d Mon Sep 17 00:00:00 2001 From: "chenchen.wu" Date: Sat, 11 Oct 2025 14:55:43 +0800 Subject: [PATCH 5/7] Optimized the parsing logic of the ijon configuration file. If an error occurs, the error reason will be printed and the system will exit. Fixed the problem of register address acquisition function on different architectures. --- accel/tcg/cpu-exec.c | 434 +++++++++++++++++++----------------- qemuafl/qemu-ijon-support.h | 5 +- 2 files changed, 233 insertions(+), 206 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 1e4a9f9a42bca..35a42127ea350 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -36,12 +36,6 @@ #include "exec/log.h" #include "qemu/main-loop.h" #include "qemu/selfmap.h" -#include "qapi/qmp/qjson.h" /* qobject_from_json() */ -#include "qapi/qmp/qdict.h" /* QDict, qdict_first, qdict_next, qdict_get_try_str 等 */ -#include "qapi/qmp/qstring.h"/* qobject_to_qstring, qstring_get_str */ -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/error.h" #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) #include "hw/i386/apic.h" @@ -199,14 +193,14 @@ static void collect_memory_snapshot(void) { if (afl_shm_id_str) { afl_shm_inode = atoi(afl_shm_id_str); } - + size_t memory_snapshot_allocd = 32; if (!lkm_snapshot) memory_snapshot = malloc(memory_snapshot_allocd * sizeof(struct saved_region)); while ((read = getline(&line, &len, fp)) != -1) { - + int fields, dev_maj, dev_min, inode; uint64_t min, max, offset; char flag_r, flag_w, flag_x, flag_p; @@ -218,9 +212,9 @@ static void collect_memory_snapshot(void) { if ((fields < 10) || (fields > 11) || !h2g_valid(min)) continue; - + int flags = page_get_flags(h2g(min)); - + max = h2g_valid(max - 1) ? max : (uintptr_t)AFL_G2H(GUEST_ADDR_MAX) + 1; if (page_check_range(h2g(min), max - min, flags) == -1) continue; @@ -231,9 +225,9 @@ static void collect_memory_snapshot(void) { if (afl_shm_id_str && inode == afl_shm_inode) continue; if (lkm_snapshot) { - + afl_snapshot_include_vmrange((void*)min, (void*)max); - + } else { if (!(flags & PROT_WRITE)) continue; @@ -243,22 +237,22 @@ static void collect_memory_snapshot(void) { memory_snapshot = realloc(memory_snapshot, memory_snapshot_allocd * sizeof(struct saved_region)); } - + void* saved = malloc(max - min); memcpy(saved, (void*)min, max - min); - + size_t i = memory_snapshot_len++; memory_snapshot[i].addr = (void*)min; memory_snapshot[i].size = max - min; memory_snapshot[i].saved = saved; - + } - + } - + if (lkm_snapshot) afl_snapshot_take(AFL_SNAPSHOT_BLOCK | AFL_SNAPSHOT_FDS); - + fclose(fp); } @@ -266,25 +260,25 @@ static void collect_memory_snapshot(void) { static void restore_memory_snapshot(void) { afl_set_brk(saved_brk); - + if (lkm_snapshot) { - + afl_snapshot_restore(); - + } else { - + size_t i; for (i = 0; i < memory_snapshot_len; ++i) { - + // TODO avoid munmap of snapshot pages - + memcpy(memory_snapshot[i].addr, memory_snapshot[i].saved, memory_snapshot[i].size); - + } - + } - + afl_target_unmap_trackeds(); } @@ -308,21 +302,20 @@ target_ulong g_var_addr[0x1000]; uint32_t g_var_len[0x1000]; uint32_t ijon_type[0x1000]; -/* 读取整个文件到 malloc 的缓冲区(调用者负责 free) */ static ssize_t read_file_all(const char *path, char **out_buf) { - FILE *f = fopen(path, "rb"); - if (!f) return -1; - if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return -1; } - long sz = ftell(f); - if (sz < 0) { fclose(f); return -1; } - rewind(f); - char *buf = g_malloc(sz + 1); - if (!buf) { fclose(f); return -1; } - size_t r = fread(buf, 1, sz, f); - fclose(f); - buf[r] = '\0'; - *out_buf = buf; - return (ssize_t)r; + FILE *f = fopen(path, "rb"); + if (!f) return -1; + if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return -1; } + long sz = ftell(f); + if (sz < 0) { fclose(f); return -1; } + rewind(f); + char *buf = g_malloc(sz + 1); + if (!buf) { fclose(f); return -1; } + size_t r = fread(buf, 1, sz, f); + fclose(f); + buf[r] = '\0'; + *out_buf = buf; + return (ssize_t)r; } static char *trim(char *s) { @@ -342,120 +335,152 @@ static int parse_uint64_str(const char *s, target_ulong *out) { if (!s || !*s) return -1; errno = 0; char *endp = NULL; - unsigned long long val = strtoull(s, &endp, 0); // base 0 自动识别 0x + unsigned long long val = strtoull(s, &endp, 0); if (endp == s) return -1; // no conversion if (errno == ERANGE) return -1; + // allow 0x123abc, not allow 123abc + if (*endp != '\0') return -1; *out = (target_ulong)val; return 0; } +// Helper macro for consistent error reporting +#define IJON_PARSE_ERROR(line_num, original_line, format, ...) \ + do { \ + fprintf(stderr, "ijon: Parse error on line %d: " format "\n", line_num, ##__VA_ARGS__); \ + fprintf(stderr, " -> Content: \"%s\"\n", original_line); \ + exit(-1); \ + } while (0) + +/** + * Parses a single, non-empty, non-comment line from the config file. + * On any error, it prints a message and terminates the program. + * On success, it populates the out_entry struct. + */ +static void parse_ijon_line(const char *line, int line_num) { + char *fields[4]; + int field_count = 0; + + // Create a mutable copy for parsing + char *line_copy = strdup(line); + if (!line_copy) { + fprintf(stderr, "ijon: Out of memory\n"); + exit(-1); + } + + char *p = line_copy; + while(field_count < 4) { + char *start = p; + char *end = strchr(start, ','); + + if (end) { + *end = '\0'; + p = end + 1; + } + + fields[field_count++] = trim(start); + + if (!end) break; // Reached the last part + } + + if (field_count != 4) { + free(line_copy); + IJON_PARSE_ERROR(line_num, line, "Expected 4 comma-separated fields, but found %d", field_count); + } + + // --- Field 1: Code Address --- + if (parse_uint64_str(fields[0], &hook_code_addr[ijon_hooker_cnt]) != 0) { + IJON_PARSE_ERROR(line_num, line, "Invalid code address in field 1. Value: '%s'", fields[0]); + } + + // --- Field 2: Ijon Method --- + ijon_type[ijon_hooker_cnt] = str_to_ijon(fields[1]); + if (ijon_type[ijon_hooker_cnt] == -1) { + IJON_PARSE_ERROR(line_num, line, "Unknown ijon method in field 2. Value: '%s'", fields[1]); + } + + // --- Field 3: Variable Address or Register Name --- + if (isdigit((unsigned char)*fields[2]) || (strncmp(fields[2], "0x", 2) == 0)) { + + if (parse_uint64_str(fields[2], &g_var_addr[ijon_hooker_cnt]) != 0) { + IJON_PARSE_ERROR(line_num, line, "Invalid variable address in field 3. Value: '%s'", fields[2]); + } + + } else { + + g_var_addr[ijon_hooker_cnt] = ijon_reg_to_addr(fields[2]); + if (g_var_addr[ijon_hooker_cnt] == 0) { + IJON_PARSE_ERROR(line_num, line, "Invalid register name in field 3. Value: '%s'", fields[2]); + } + + } + + // --- Field 4: Variable Length --- + errno = 0; + char *endp = NULL; + unsigned long len = strtoul(fields[3], &endp, 0); + if (endp == fields[3] || *endp != '\0' || errno == ERANGE) { + IJON_PARSE_ERROR(line_num, line, "Invalid variable length in field 4. Value: '%s'", fields[3]); + } + g_var_len[ijon_hooker_cnt] = (uint32_t)len; + + if (len > 8) { + fprintf(stderr, "ijon: Variables larger than 8 bytes are not supported: %s\n", fields[3]); + exit(-1); + } + + ijon_hooker_cnt++; + + free(line_copy); +} + /* Parse the ijon configuration file, which stores the following data structure: * 0x4001200,ijon_set,0x4000005064,8 * 0x40012bb,ijon_min,RAX,4 * code addr, ijon method, var addr/reg, var len */ static void qemu_ijon_init(void) { - const char *path = getenv("AFL_QEMU_IJON"); - use_ijon = !!path; - if (!use_ijon) return; - - char *buf = NULL; - ssize_t buf_len = read_file_all(path, &buf); - if (buf_len < 0) { - fprintf(stderr, "ijon: cannot read '%s'\n", path ? path : "(null)"); - exit(-1); - } - - char *buf_copy = malloc(buf_len + 1); - if (!buf_copy) exit(-1); - memcpy(buf_copy, buf, buf_len); - buf_copy[buf_len] = '\0'; - - char *line_ctx; - char *line = strtok_r(buf_copy, "\n", &line_ctx); - while (line && ijon_hooker_cnt < 0x1000) { - char *s = trim(line); - if (*s == '\0') { line = strtok_r(NULL, "\n", &line_ctx); continue; } // 空行 - if (*s == '#') { line = strtok_r(NULL, "\n", &line_ctx); continue; } // 注释行 - - // 分割 4 个字段(允许字段中间有空白) - // 采用手动查找逗号,避免 strtok 在字段内容含逗号时出问题(这里不考虑字段含逗号) - char *f1 = s; - char *p = strchr(s, ','); - if (!p) { line = strtok_r(NULL, "\n", &line_ctx); continue; } // 格式错误,跳过 - *p = '\0'; char *f2 = trim(p + 1); - - // next comma - p = strchr(f2, ','); - if (!p) { line = strtok_r(NULL, "\n", &line_ctx); continue; } - *p = '\0'; char *f3 = trim(p + 1); - - // last comma - p = strchr(f3, ','); - if (!p) { line = strtok_r(NULL, "\n", &line_ctx); continue; } - *p = '\0'; char *f4 = trim(p + 1); - - f1 = trim(f1); - f2 = trim(f2); - f3 = trim(f3); - f4 = trim(f4); - - // 解析每个字段,任一失败则跳过该行 - int ok = 1; - - // field1 -> hook_code_addr[ijon_hooker_cnt] - target_ulong addr0; - if (parse_uint64_str(f1, &addr0) != 0) { ok = 0; } - - // field2 -> ijon_type[ijon_hooker_cnt] via str_to_ijon - uint32_t type2 = str_to_ijon(f2); - if (type2 == -1) { ok = 0; } - - // field3 -> g_var_addr[ijon_hooker_cnt] : numeric -> addr, string -> convert via str_to_ijon_reg (enum -> store as target_ulong) - target_ulong gaddr = 0; - if (ok) { - // 判断是否以数字开头或以 0x 开头或全数字(允许带负号? 这里假设不带) - int looks_like_number = 0; - const char *t = f3; - if (*t == '0' && (t[1]=='x' || t[1]=='X')) looks_like_number = 1; - else if (isdigit((unsigned char)*t)) looks_like_number = 1; - - if (looks_like_number) { - if (parse_uint64_str(f3, &gaddr) != 0) ok = 0; - } else { - uint32_t reg_enum = ijon_reg_to_addr(f3); - if (reg_enum == -1) ok = 0; - else { - CPUArchState* env = first_cpu->env_ptr; - gaddr = &env->regs[reg_enum]; - } - } - } + const char *path = getenv("AFL_QEMU_IJON"); + use_ijon = !!path; + if (!use_ijon) return; + + char *buf = NULL; + ssize_t buf_len = read_file_all(path, &buf); + if (buf_len < 0) { + fprintf(stderr, "ijon: cannot read '%s'\n", path ? path : "(null)"); + exit(-1); + } - // field4 -> g_var_len[ijon_hooker_cnt] - uint32_t len4 = 0; - if (ok) { - errno = 0; - char *endp = NULL; - unsigned long v = strtoul(f4, &endp, 0); - if (endp == f4 || errno == ERANGE) ok = 0; - else len4 = (uint32_t)v; - } + // strtok_r modifies the buffer, so we operate on a copy. + char *buf_copy = malloc(buf_len + 1); + if (!buf_copy) { + fprintf(stderr, "ijon: failed to allocate memory\n"); + free(buf); + exit(-1); + } + memcpy(buf_copy, buf, buf_len); + buf_copy[buf_len] = '\0'; + free(buf); // Original buffer no longer needed - if (ok) { - hook_code_addr[ijon_hooker_cnt] = addr0; - ijon_type[ijon_hooker_cnt] = type2; - g_var_addr[ijon_hooker_cnt] = gaddr; - g_var_len[ijon_hooker_cnt] = len4; - ijon_hooker_cnt++; - } else { - // skip - } + char *line_ctx; + char *line = strtok_r(buf_copy, "\n", &line_ctx); + int line_num = 1; - line = strtok_r(NULL, "\n", &line_ctx); + while (line && ijon_hooker_cnt < 0x1000) { + char *trimmed_line = trim(line); + if (*trimmed_line != '\0' && *trimmed_line != '#') { + parse_ijon_line(trimmed_line, line_num); } + line = strtok_r(NULL, "\n", &line_ctx); + line_num++; + } - free(buf_copy); + free(buf_copy); + + if (ijon_hooker_cnt == 0) { + fprintf(stderr, "ijon: You specified the AFL_QEMU_IJON file, but no valid entries were found.\n"); + exit(-1); + } fprintf(stderr, "ijon: loaded %u mappings\n", ijon_hooker_cnt); for (uint32_t i = 0; i < ijon_hooker_cnt; i++) { @@ -465,59 +490,64 @@ static void qemu_ijon_init(void) { (unsigned long)g_var_addr[i], (unsigned)g_var_len[i]); } - } -int ijon_reg_to_addr(const char *reg_str) { - if (reg_str == NULL) return -1; - - /* 可修改的拷贝以便 trim/upper 化 */ - size_t len = strlen(reg_str); - char *buf = (char *)malloc(len + 1); - if (!buf) return -1; - memcpy(buf, reg_str, len + 1); +void* ijon_reg_to_addr(const char *reg_str) { + if (reg_str == NULL) return NULL; - char *s = trim(buf); - if (!s || *s == '\0') { free(buf); return -1; } + /* copy for upper */ + char *s = strdup(reg_str); + if (!s || *s == '\0') { free(s); return NULL; } - /* 允许 AT&T 风格的前导 '%' */ + /* allow AT&T style '%' */ if (s[0] == '%') s++; - /* 统一转大写(就地)*/ + /* convert to uppercase */ for (char *p = s; *p; ++p) *p = (char)toupper((unsigned char)*p); - /* 常见寄存器别名(RAX/EAX -> R_EAX 等) */ - if (strcmp(s, "RAX") == 0 || strcmp(s, "EAX") == 0) { free(buf); return R_EAX; } - if (strcmp(s, "RCX") == 0 || strcmp(s, "ECX") == 0) { free(buf); return R_ECX; } - if (strcmp(s, "RDX") == 0 || strcmp(s, "EDX") == 0) { free(buf); return R_EDX; } - if (strcmp(s, "RBX") == 0 || strcmp(s, "EBX") == 0) { free(buf); return R_EBX; } - if (strcmp(s, "RSP") == 0 || strcmp(s, "ESP") == 0) { free(buf); return R_ESP; } - if (strcmp(s, "RBP") == 0 || strcmp(s, "EBP") == 0) { free(buf); return R_EBP; } - if (strcmp(s, "RSI") == 0 || strcmp(s, "ESI") == 0) { free(buf); return R_ESI; } - if (strcmp(s, "RDI") == 0 || strcmp(s, "EDI") == 0) { free(buf); return R_EDI; } - - /* 检查 R8..R15(允许后缀,如 R12D、R12W、R12B) */ + +#ifdef TARGET_X86_64 + CPUX86State* env = first_cpu->env_ptr; + if (strcmp(s, "RAX") == 0 || strcmp(s, "EAX") == 0) { return &env->regs[R_EAX]; } + if (strcmp(s, "RCX") == 0 || strcmp(s, "ECX") == 0) { return &env->regs[R_ECX]; } + if (strcmp(s, "RDX") == 0 || strcmp(s, "EDX") == 0) { return &env->regs[R_EDX]; } + if (strcmp(s, "RBX") == 0 || strcmp(s, "EBX") == 0) { return &env->regs[R_EBX]; } + if (strcmp(s, "RSP") == 0 || strcmp(s, "ESP") == 0) { return &env->regs[R_ESP]; } + if (strcmp(s, "RBP") == 0 || strcmp(s, "EBP") == 0) { return &env->regs[R_EBP]; } + if (strcmp(s, "RSI") == 0 || strcmp(s, "ESI") == 0) { return &env->regs[R_ESI]; } + if (strcmp(s, "RDI") == 0 || strcmp(s, "EDI") == 0) { return &env->regs[R_EDI]; } + + /* convert R8..R15 , allow R12D,R12W,R12 etc */ if (s[0] == 'R' && isdigit((unsigned char)s[1])) { char *endptr = NULL; long v = strtol(s + 1, &endptr, 10); - if (endptr != s + 1 && v >= 8 && v <= 15) { - free(buf); - return (int)v; /* enum R_R8..R_R15 对应数值 8..15 */ + if (v >= 8 && v <= 15) { + return &env->regs[v]; /* enum R_R8..R_R15 -> 8..15 */ } } - /* 直接数字形式 "0".."15" 也接受(有时配置中会用数字)*/ - if (isdigit((unsigned char)s[0])) { +#endif + +#if defined(TARGET_AARCH64) || defined(TARGET_ARM) + CPUARMState* env = first_cpu->env_ptr; + if ((s[0] == 'X' || s[0] == 'W') && isdigit((unsigned char)s[1])) { char *endptr = NULL; - long v = strtol(s, &endptr, 10); - if (endptr != s && v >= 0 && v <= 15) { - free(buf); - return (int)v; + long v = strtol(s + 1, &endptr, 10); + if (v >= 0 && v <= 31) { + return &env->xregs[v]; } } - free(buf); - return -1; + if (s[0] == 'R' && isdigit((unsigned char)s[1])) { + char *endptr = NULL; + long v = strtol(s + 1, &endptr, 10); + if (v >= 0 && v <= 15) { + return &env->regs[v]; + } + } +#endif + + return NULL; } const char* ijon_to_str(IJON v) { @@ -880,7 +910,7 @@ void afl_setup(void) { if (inst_r) afl_area_ptr[0] = 1; } - + disable_caching = getenv("AFL_QEMU_DISABLE_CACHE") != NULL; if (getenv("___AFL_EINS_ZWEI_POLIZEI___")) { // CmpLog forkserver @@ -905,7 +935,7 @@ void afl_setup(void) { afl_end_code = (abi_ulong)-1; } - + if (getenv("AFL_CODE_START")) afl_start_code = strtoll(getenv("AFL_CODE_START"), NULL, 16); if (getenv("AFL_CODE_END")) @@ -916,17 +946,17 @@ void afl_setup(void) { char *str = getenv("AFL_QEMU_INST_RANGES"); char *saveptr1, *saveptr2 = NULL, *save_pt1 = NULL; char *pt1, *pt2, *pt3 = NULL; - + while (1) { pt1 = strtok_r(str, ",", &saveptr1); if (pt1 == NULL) break; str = NULL; save_pt1 = strdup(pt1); - + pt2 = strtok_r(pt1, "-", &saveptr2); pt3 = strtok_r(NULL, "-", &saveptr2); - + struct vmrange* n = calloc(1, sizeof(struct vmrange)); n->next = afl_instr_code; @@ -948,7 +978,7 @@ void afl_setup(void) { n->name = save_pt1; } } - + afl_instr_code = n; } @@ -1061,7 +1091,7 @@ void afl_setup(void) { behaviour, and seems to work alright? */ rcu_disable_atfork(); - + if (getenv("AFL_QEMU_PERSISTENT_HOOK")) { #ifdef AFL_QEMU_STATIC_BUILD @@ -1104,9 +1134,9 @@ void afl_setup(void) { #endif } - + if (__afl_cmp_map) return; // no persistent for cmplog - + is_persistent = getenv("AFL_QEMU_PERSISTENT_ADDR") != NULL; if (is_persistent) @@ -1129,28 +1159,28 @@ void afl_setup(void) { afl_persistent_cnt = strtoll(getenv("AFL_QEMU_PERSISTENT_CNT"), NULL, 0); else afl_persistent_cnt = 0; - + if (getenv("AFL_QEMU_PERSISTENT_EXITS")) persistent_exits = 1; // TODO persistent exits for other archs not x86 // TODO persistent mode for other archs not x86 // TODO cmplog rtn for arm - + if (getenv("AFL_QEMU_SNAPSHOT")) { - + is_persistent = 1; persistent_save_gpr = 1; persistent_memory = 1; persistent_exits = 1; - + if (afl_persistent_addr == 0) afl_persistent_addr = strtoll(getenv("AFL_QEMU_SNAPSHOT"), NULL, 0); - + } - + if (persistent_memory && afl_snapshot_init() >= 0) lkm_snapshot = 1; - + if (getenv("AFL_DEBUG")) { if (is_persistent) fprintf(stderr, "Persistent: 0x%lx [0x%lx] %s%s%s\n", @@ -1358,15 +1388,15 @@ void afl_persistent_iter(CPUArchState *env) { static struct afl_tsl exit_cmd_tsl; if (!afl_persistent_cnt || --cycle_cnt) { - + if (persistent_memory) restore_memory_snapshot(); - + if (persistent_save_gpr && !afl_persistent_hook_ptr) { afl_restore_regs(&saved_regs, env); } if (!disable_caching) { - + memset(&exit_cmd_tsl, 0, sizeof(struct afl_tsl)); exit_cmd_tsl.tb.pc = (target_ulong)(-1); @@ -1378,16 +1408,16 @@ void afl_persistent_iter(CPUArchState *env) { exit(0); } - + } // TODO use only pipe raise(SIGSTOP); - + // now we have shared_buf updated and ready to use if (persistent_save_gpr && afl_persistent_hook_ptr) { - + struct api_regs hook_regs = saved_regs; afl_persistent_hook_ptr(&hook_regs, guest_base, shared_buf, *shared_buf_len); @@ -1425,15 +1455,15 @@ void afl_persistent_loop(CPUArchState *env) { afl_prev_loc = 0; } - + if (persistent_memory) collect_memory_snapshot(); - + if (persistent_save_gpr) { - + afl_save_regs(&saved_regs, env); - + if (afl_persistent_hook_ptr) { - + struct api_regs hook_regs = saved_regs; afl_persistent_hook_ptr(&hook_regs, guest_base, shared_buf, *shared_buf_len); @@ -1442,7 +1472,7 @@ void afl_persistent_loop(CPUArchState *env) { } } - + cycle_cnt = afl_persistent_cnt; persistent_first_pass = 0; persistent_stack_offset = TARGET_LONG_BITS / 8; @@ -1489,7 +1519,7 @@ static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags, t.chain.tb_exit = tb_exit; } - + if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) return; diff --git a/qemuafl/qemu-ijon-support.h b/qemuafl/qemu-ijon-support.h index a3427f71b96fb..0b02d6a5ea47f 100644 --- a/qemuafl/qemu-ijon-support.h +++ b/qemuafl/qemu-ijon-support.h @@ -32,9 +32,6 @@ X(ijon_inc, ijon_inc) \ X(ijon_xor_state, ijon_xor_state) \ X(ijon_reset_state, ijon_reset_state) -// -// 定义枚举(带 e_ 前缀) -// typedef enum { #define X(name, func) e_##name, IJON_LIST @@ -42,7 +39,7 @@ typedef enum { e_IJON_COUNT } IJON; -int ijon_reg_to_addr(const char *reg_str); +void* ijon_reg_to_addr(const char *reg_str); const char* ijon_to_str(IJON v); IJON str_to_ijon(const char* str); void ijon_dispatch(IJON v, uint32_t addr, u64 val); From e8c862f13b7cf44c6d9b71ef526f2e64f89271f1 Mon Sep 17 00:00:00 2001 From: "chenchen.wu" Date: Sat, 11 Oct 2025 16:16:48 +0800 Subject: [PATCH 6/7] Add ijon hook support for arm and aarch64 --- target/arm/translate-a64.c | 17 ++++++++++++++++- target/arm/translate.c | 19 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 2a2fe3942cdb8..be754f219a5bd 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -41,7 +41,7 @@ #include "qemuafl/cpu-translate.h" #include "qemuafl/qasan-qemu.h" - +#include "qemuafl/qemu-ijon-support.h" // SP = 31, LINK = 30 #define AFL_QEMU_TARGET_ARM64_SNIPPET \ @@ -14877,6 +14877,21 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; + for (int i = 0; i < ijon_hooker_cnt; i++) { + if (dc->base.pc_next == hook_code_addr[i]) { + TCGv var_addr = tcg_const_tl(g_var_addr[i]); + TCGv var_len = tcg_const_tl(g_var_len[i]); + TCGv itype = tcg_const_tl(ijon_type[i]); + TCGv idx = tcg_const_tl(i); + gen_helper_ijon_func_call(var_addr, var_len, itype, idx); + fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); + tcg_temp_free(var_addr); + tcg_temp_free(var_len); + tcg_temp_free(itype); + tcg_temp_free(idx); + } + } + if (dc->ss_active && !dc->pstate_ss) { /* Singlestep state is Active-pending. * If we're in this state at the start of a TB then either diff --git a/target/arm/translate.c b/target/arm/translate.c index 35f1747c2e6f0..bd8a0e557a779 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -39,6 +39,8 @@ #include "qemuafl/cpu-translate.h" +#include "qemuafl/qemu-ijon-support.h" + // TODO QASAN shadow stack //#include "qemuafl/qasan-qemu.h" @@ -9297,7 +9299,22 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) dc->pc_curr = dc->base.pc_next; AFL_QEMU_TARGET_ARM_SNIPPET - + + for (int i = 0; i < ijon_hooker_cnt; i++) { + if (dc->base.pc_next == hook_code_addr[i]) { + TCGv var_addr = tcg_const_tl(g_var_addr[i]); + TCGv var_len = tcg_const_tl(g_var_len[i]); + TCGv itype = tcg_const_tl(ijon_type[i]); + TCGv idx = tcg_const_tl(i); + gen_helper_ijon_func_call(var_addr, var_len, itype, idx); + fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); + tcg_temp_free(var_addr); + tcg_temp_free(var_len); + tcg_temp_free(itype); + tcg_temp_free(idx); + } + } + insn = arm_ldl_code(env, dc->base.pc_next, dc->sctlr_b); dc->insn = insn; dc->base.pc_next += 4; From 03f40b8e67be2f03f4a8aab3ad5000f7994c169c Mon Sep 17 00:00:00 2001 From: "chenchen.wu" Date: Sat, 11 Oct 2025 17:13:24 +0800 Subject: [PATCH 7/7] Using macros to simplify the hook code should make it easy to add IJON support to other architectures in the future. --- qemuafl/qemu-ijon-support.h | 18 ++++++++++++++++++ target/arm/translate-a64.c | 15 +-------------- target/arm/translate.c | 15 +-------------- target/i386/tcg/translate.c | 15 +-------------- 4 files changed, 21 insertions(+), 42 deletions(-) diff --git a/qemuafl/qemu-ijon-support.h b/qemuafl/qemu-ijon-support.h index 0b02d6a5ea47f..a9ac2d638d5f5 100644 --- a/qemuafl/qemu-ijon-support.h +++ b/qemuafl/qemu-ijon-support.h @@ -44,4 +44,22 @@ const char* ijon_to_str(IJON v); IJON str_to_ijon(const char* str); void ijon_dispatch(IJON v, uint32_t addr, u64 val); +#define INSTALL_IJON_HOOKS() \ +do { \ + for (int i = 0; i < ijon_hooker_cnt; i++) { \ + if (dc->base.pc_next == hook_code_addr[i]) { \ + TCGv var_addr = tcg_const_tl(g_var_addr[i]); \ + TCGv var_len = tcg_const_tl(g_var_len[i]); \ + TCGv itype = tcg_const_tl(ijon_type[i]); \ + TCGv idx = tcg_const_tl(i); \ + gen_helper_ijon_func_call(var_addr, var_len, itype, idx); \ + fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); \ + tcg_temp_free(var_addr); \ + tcg_temp_free(var_len); \ + tcg_temp_free(itype); \ + tcg_temp_free(idx); \ + } \ + } \ +} while (0) + #endif //QEMUAFL_QEMU_IJON_SUPPORT_H \ No newline at end of file diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index be754f219a5bd..9d82aaa76a2a4 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -14877,20 +14877,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; - for (int i = 0; i < ijon_hooker_cnt; i++) { - if (dc->base.pc_next == hook_code_addr[i]) { - TCGv var_addr = tcg_const_tl(g_var_addr[i]); - TCGv var_len = tcg_const_tl(g_var_len[i]); - TCGv itype = tcg_const_tl(ijon_type[i]); - TCGv idx = tcg_const_tl(i); - gen_helper_ijon_func_call(var_addr, var_len, itype, idx); - fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); - tcg_temp_free(var_addr); - tcg_temp_free(var_len); - tcg_temp_free(itype); - tcg_temp_free(idx); - } - } + INSTALL_IJON_HOOKS(); if (dc->ss_active && !dc->pstate_ss) { /* Singlestep state is Active-pending. diff --git a/target/arm/translate.c b/target/arm/translate.c index bd8a0e557a779..c5a2f0c55e6be 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -9300,20 +9300,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) AFL_QEMU_TARGET_ARM_SNIPPET - for (int i = 0; i < ijon_hooker_cnt; i++) { - if (dc->base.pc_next == hook_code_addr[i]) { - TCGv var_addr = tcg_const_tl(g_var_addr[i]); - TCGv var_len = tcg_const_tl(g_var_len[i]); - TCGv itype = tcg_const_tl(ijon_type[i]); - TCGv idx = tcg_const_tl(i); - gen_helper_ijon_func_call(var_addr, var_len, itype, idx); - fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); - tcg_temp_free(var_addr); - tcg_temp_free(var_len); - tcg_temp_free(itype); - tcg_temp_free(idx); - } - } + INSTALL_IJON_HOOKS(); insn = arm_ldl_code(env, dc->base.pc_next, dc->sctlr_b); dc->insn = insn; diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index dd8ce1e350254..966b11193b289 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -8717,20 +8717,7 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } #endif - for (int i = 0; i < ijon_hooker_cnt; i++) { - if (dc->base.pc_next == hook_code_addr[i]) { - TCGv var_addr = tcg_const_tl(g_var_addr[i]); - TCGv var_len = tcg_const_tl(g_var_len[i]); - TCGv itype = tcg_const_tl(ijon_type[i]); - TCGv idx = tcg_const_tl(i); - gen_helper_ijon_func_call(var_addr, var_len, itype, idx); - fprintf(stderr, "install ijon hook in %lx\n", hook_code_addr[i]); - tcg_temp_free(var_addr); - tcg_temp_free(var_len); - tcg_temp_free(itype); - tcg_temp_free(idx); - } - } + INSTALL_IJON_HOOKS(); pc_next = disas_insn(dc, cpu);