This commit is contained in:
Romain Malmain 2025-04-29 14:29:00 +02:00
parent 7633d5fce5
commit 9366380915
No known key found for this signature in database
GPG Key ID: 02E4A13F7415468A
4 changed files with 103 additions and 94 deletions

View File

@ -1,4 +1,4 @@
DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_2(libafl_qemu_handle_breakpoint, TCG_CALL_NO_RWG, void, env,
void, env, i64) i64)
DEF_HELPER_FLAGS_3(libafl_qemu_handle_custom_insn, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_3(libafl_qemu_handle_custom_insn, TCG_CALL_NO_RWG, void, env,
void, env, i64, i32) i64, i32)

View File

@ -5,4 +5,3 @@
void tcg_gen_callN(void* func, TCGHelperInfo* info, TCGTemp* ret, void tcg_gen_callN(void* func, TCGHelperInfo* info, TCGTemp* ret,
TCGTemp** args); TCGTemp** args);

View File

@ -5,18 +5,20 @@
#include "libafl/exit.h" #include "libafl/exit.h"
#define HELPER_H "libafl/tcg-helper.h" #define HELPER_H "libafl/tcg-helper.h"
#include "exec/helper-info.c.inc" #include "exec/helper-info.c.inc"
#undef HELPER_H #undef HELPER_H
void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState *env, uint64_t pc) void HELPER(libafl_qemu_handle_breakpoint)(CPUArchState* env, uint64_t pc)
{ {
CPUState* cpu = env_cpu(env); CPUState* cpu = env_cpu(env);
libafl_exit_request_breakpoint(cpu, (target_ulong) pc); libafl_exit_request_breakpoint(cpu, (target_ulong)pc);
} }
void HELPER(libafl_qemu_handle_custom_insn)(CPUArchState *env, uint64_t pc, uint32_t kind) void HELPER(libafl_qemu_handle_custom_insn)(CPUArchState* env, uint64_t pc,
uint32_t kind)
{ {
CPUState* cpu = env_cpu(env); CPUState* cpu = env_cpu(env);
libafl_exit_request_custom_insn(cpu, (target_ulong) pc, (enum libafl_custom_insn_kind) kind); libafl_exit_request_custom_insn(cpu, (target_ulong)pc,
(enum libafl_custom_insn_kind)kind);
} }

View File

@ -19,12 +19,11 @@ static target_ulong reverse_bits(target_ulong num)
target_ulong reverse_num = num; target_ulong reverse_num = num;
num >>= 1; num >>= 1;
while(num) while (num) {
{ reverse_num <<= 1;
reverse_num <<= 1; reverse_num |= num & 1;
reverse_num |= num & 1; num >>= 1;
num >>= 1; count--;
count--;
} }
reverse_num <<= count; reverse_num <<= count;
return reverse_num; return reverse_num;
@ -34,9 +33,9 @@ static target_ulong reverse_bits(target_ulong num)
* Isolate the portion of code gen which can setjmp/longjmp. * Isolate the portion of code gen which can setjmp/longjmp.
* Return the size of the generated code, or negative on error. * Return the size of the generated code, or negative on error.
*/ */
static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, static int libafl_setjmp_gen_code(CPUArchState* env, TranslationBlock* tb,
vaddr pc, void *host_pc, vaddr pc, void* host_pc, int* max_insns,
int *max_insns, int64_t *ti) int64_t* ti)
{ {
int ret = sigsetjmp(tcg_ctx->jmp_trans, 0); int ret = sigsetjmp(tcg_ctx->jmp_trans, 0);
if (unlikely(ret != 0)) { if (unlikely(ret != 0)) {
@ -48,7 +47,8 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
tcg_ctx->cpu = env_cpu(env); tcg_ctx->cpu = env_cpu(env);
// -- start gen_intermediate_code // -- start gen_intermediate_code
const int num_insns = 1; // do "as-if" we were translating a single target instruction const int num_insns =
1; // do "as-if" we were translating a single target instruction
#ifndef TARGET_INSN_START_EXTRA_WORDS #ifndef TARGET_INSN_START_EXTRA_WORDS
tcg_gen_insn_start(pc); tcg_gen_insn_start(pc);
@ -66,10 +66,13 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
tcg_gen_goto_tb(0); tcg_gen_goto_tb(0);
tcg_gen_exit_tb(tb, 0); tcg_gen_exit_tb(tb, 0);
// This is obviously wrong, but it is required that the number / size of target instruction translated // This is obviously wrong, but it is required that the number / size of
// is at least 1. For now, we make it so that no problem occurs later on. // target instruction translated is at least 1. For now, we make it so that
tb->icount = num_insns; // number of target instructions translated in the TB. // no problem occurs later on.
tb->size = num_insns; // size (in target bytes) of target instructions translated in the TB. tb->icount =
num_insns; // number of target instructions translated in the TB.
tb->size = num_insns; // size (in target bytes) of target instructions
// translated in the TB.
// -- end gen_intermediate_code // -- end gen_intermediate_code
assert(tb->size != 0); assert(tb->size != 0);
@ -80,18 +83,18 @@ static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
} }
/* Called with mmap_lock held for user mode emulation. */ /* Called with mmap_lock held for user mode emulation. */
TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block, TranslationBlock* libafl_gen_edge(CPUState* cpu, target_ulong src_block,
target_ulong dst_block, int exit_n, target_ulong dst_block, int exit_n,
target_ulong cs_base, uint32_t flags, target_ulong cs_base, uint32_t flags,
int cflags) int cflags)
{ {
CPUArchState *env = cpu_env(cpu); CPUArchState* env = cpu_env(cpu);
TranslationBlock *tb; TranslationBlock* tb;
tb_page_addr_t phys_pc; tb_page_addr_t phys_pc;
tcg_insn_unit *gen_code_buf; tcg_insn_unit* gen_code_buf;
int gen_code_size, search_size, max_insns; int gen_code_size, search_size, max_insns;
int64_t ti; int64_t ti;
void *host_pc; void* host_pc;
// edge hooks generation callbacks // edge hooks generation callbacks
// early check if it should be skipped or not // early check if it should be skipped or not
@ -106,7 +109,8 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
assert_memory_lock(); assert_memory_lock();
qemu_thread_jit_write(); qemu_thread_jit_write();
// TODO: this (get_page_addr_code_hostp) is a bottleneck in systemmode, investigate why // TODO: this (get_page_addr_code_hostp) is a bottleneck in systemmode,
// investigate why
phys_pc = get_page_addr_code_hostp(env, src_block, &host_pc); phys_pc = get_page_addr_code_hostp(env, src_block, &host_pc);
phys_pc ^= reverse_bits((tb_page_addr_t)exit_n); phys_pc ^= reverse_bits((tb_page_addr_t)exit_n);
@ -125,7 +129,7 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
} }
QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS); QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS);
buffer_overflow: buffer_overflow:
assert_no_pages_locked(); assert_no_pages_locked();
tb = tcg_tb_alloc(tcg_ctx); tb = tcg_tb_alloc(tcg_ctx);
if (unlikely(!tb)) { if (unlikely(!tb)) {
@ -167,77 +171,80 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
tcg_ctx->guest_mo = TCG_MO_ALL; tcg_ctx->guest_mo = TCG_MO_ALL;
#endif #endif
restart_translate: restart_translate:
trace_translate_block(tb, pc, tb->tc.ptr); trace_translate_block(tb, pc, tb->tc.ptr);
gen_code_size = libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); gen_code_size =
libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti);
if (unlikely(gen_code_size < 0)) { if (unlikely(gen_code_size < 0)) {
switch (gen_code_size) { switch (gen_code_size) {
case -1: case -1:
/* /*
* Overflow of code_gen_buffer, or the current slice of it. * Overflow of code_gen_buffer, or the current slice of it.
* *
* TODO: We don't need to re-do gen_intermediate_code, nor * TODO: We don't need to re-do gen_intermediate_code, nor
* should we re-do the tcg optimization currently hidden * should we re-do the tcg optimization currently hidden
* inside tcg_gen_code. All that should be required is to * inside tcg_gen_code. All that should be required is to
* flush the TBs, allocate a new TB, re-initialize it per * flush the TBs, allocate a new TB, re-initialize it per
* above, and re-do the actual code generation. * above, and re-do the actual code generation.
*/ */
qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT,
"Restarting code generation for " "Restarting code generation for "
"code_gen_buffer overflow\n"); "code_gen_buffer overflow\n");
tb_unlock_pages(tb); tb_unlock_pages(tb);
tcg_ctx->gen_tb = NULL; tcg_ctx->gen_tb = NULL;
goto buffer_overflow; goto buffer_overflow;
case -2: case -2:
assert(false && "This should never happen for edge code. There must be a bug."); assert(
/* false &&
* The code generated for the TranslationBlock is too large. "This should never happen for edge code. There must be a bug.");
* The maximum size allowed by the unwind info is 64k. /*
* There may be stricter constraints from relocations * The code generated for the TranslationBlock is too large.
* in the tcg backend. * The maximum size allowed by the unwind info is 64k.
* * There may be stricter constraints from relocations
* Try again with half as many insns as we attempted this time. * in the tcg backend.
* If a single insn overflows, there's a bug somewhere... *
*/ * Try again with half as many insns as we attempted this time.
assert(max_insns > 1); * If a single insn overflows, there's a bug somewhere...
max_insns /= 2; */
qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, assert(max_insns > 1);
"Restarting code generation with " max_insns /= 2;
"smaller translation block (max %d insns)\n", qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT,
max_insns); "Restarting code generation with "
"smaller translation block (max %d insns)\n",
max_insns);
/* /*
* The half-sized TB may not cross pages. * The half-sized TB may not cross pages.
* TODO: Fix all targets that cross pages except with * TODO: Fix all targets that cross pages except with
* the first insn, at which point this can't be reached. * the first insn, at which point this can't be reached.
*/ */
// phys_p2 = tb_page_addr1(tb); // phys_p2 = tb_page_addr1(tb);
// if (unlikely(phys_p2 != -1)) { // if (unlikely(phys_p2 != -1)) {
// tb_unlock_page1(phys_pc, phys_p2); // tb_unlock_page1(phys_pc, phys_p2);
// tb_set_page_addr1(tb, -1); // tb_set_page_addr1(tb, -1);
// } // }
goto restart_translate; goto restart_translate;
case -3: case -3:
/* /*
* We had a page lock ordering problem. In order to avoid * We had a page lock ordering problem. In order to avoid
* deadlock we had to drop the lock on page0, which means * deadlock we had to drop the lock on page0, which means
* that everything we translated so far is compromised. * that everything we translated so far is compromised.
* Restart with locks held on both pages. * Restart with locks held on both pages.
*/ */
qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT,
"Restarting code generation with re-locked pages"); "Restarting code generation with re-locked pages");
goto restart_translate; goto restart_translate;
default: default:
g_assert_not_reached(); g_assert_not_reached();
} }
} }
tcg_ctx->gen_tb = NULL; tcg_ctx->gen_tb = NULL;
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size); search_size = encode_search(tb, (void*)gen_code_buf + gen_code_size);
if (unlikely(search_size < 0)) { if (unlikely(search_size < 0)) {
tb_unlock_pages(tb); tb_unlock_pages(tb);
goto buffer_overflow; goto buffer_overflow;
@ -250,9 +257,10 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
*/ */
perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf)); perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf));
qatomic_set(&tcg_ctx->code_gen_ptr, (void *) qatomic_set(
ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, &tcg_ctx->code_gen_ptr,
CODE_GEN_ALIGN)); (void*)ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size,
CODE_GEN_ALIGN));
/* init jump list */ /* init jump list */
qemu_spin_init(&tb->jmp_lock); qemu_spin_init(&tb->jmp_lock);