Skip to content

Commit 11b27cc

Browse files
authored
Refactor and fix edge-related code generation (#81)
* refactor and fix edge-related code generation. - fix sigsetjmp bug - fix tcg buffer overflow bug due to wrong icount report. - closer to original QEMU implementation to improve maintainability. - use the right TB icount / size semantic - report problems related errors during generation
1 parent 24abc2a commit 11b27cc

File tree

2 files changed

+182
-81
lines changed

2 files changed

+182
-81
lines changed

accel/tcg/translate-all.c

+181-81
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,66 @@ static target_ulong reverse_bits(target_ulong num)
331331
reverse_num <<= count;
332332
return reverse_num;
333333
}
334+
/*
335+
* Isolate the portion of code gen which can setjmp/longjmp.
336+
* Return the size of the generated code, or negative on error.
337+
*/
338+
static int libafl_setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
339+
vaddr pc, void *host_pc,
340+
int *max_insns, int64_t *ti)
341+
{
342+
int ret = sigsetjmp(tcg_ctx->jmp_trans, 0);
343+
if (unlikely(ret != 0)) {
344+
return ret;
345+
}
346+
347+
tcg_func_start(tcg_ctx);
348+
349+
tcg_ctx->cpu = env_cpu(env);
350+
351+
// -- start gen_intermediate_code
352+
const int num_insns = 1; // do "as-if" we were translating a single target instruction
353+
354+
#ifndef TARGET_INSN_START_EXTRA_WORDS
355+
tcg_gen_insn_start(pc);
356+
#elif TARGET_INSN_START_EXTRA_WORDS == 1
357+
tcg_gen_insn_start(pc, 0);
358+
#elif TARGET_INSN_START_EXTRA_WORDS == 2
359+
tcg_gen_insn_start(pc, 0, 0);
360+
#else
361+
#error Unhandled TARGET_INSN_START_EXTRA_WORDS value
362+
#endif
363+
364+
struct libafl_edge_hook* hook = libafl_edge_hooks;
365+
while (hook) {
366+
if (hook->cur_id != (uint64_t)-1 && hook->helper_info.func) {
367+
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
368+
TCGv_i64 tmp1 = tcg_constant_i64(hook->cur_id);
369+
TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1) };
370+
tcg_gen_callN(&hook->helper_info, NULL, tmp2);
371+
tcg_temp_free_i64(tmp0);
372+
tcg_temp_free_i64(tmp1);
373+
}
374+
if (hook->cur_id != (uint64_t)-1 && hook->jit) {
375+
hook->jit(hook->data, hook->cur_id);
376+
}
377+
hook = hook->next;
378+
}
379+
tcg_gen_goto_tb(0);
380+
tcg_gen_exit_tb(tb, 0);
381+
382+
// This is obviously wrong, but it is required that the number / size of target instruction translated
383+
// is at least 1. For now, we make it so that no problem occurs later on.
384+
tb->icount = num_insns; // number of target instructions translated in the TB.
385+
tb->size = num_insns; // size (in target bytes) of target instructions translated in the TB.
386+
// -- end gen_intermediate_code
387+
388+
assert(tb->size != 0);
389+
tcg_ctx->cpu = NULL;
390+
*max_insns = tb->icount;
391+
392+
return tcg_gen_code(tcg_ctx, tb, pc);
393+
}
334394

335395
/* Called with mmap_lock held for user mode emulation. */
336396
TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
@@ -339,25 +399,37 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
339399
int cflags)
340400
{
341401
CPUArchState *env = cpu_env(cpu);
342-
TranslationBlock *tb, *existing_tb;
343-
tb_page_addr_t phys_pc, phys_page2;
344-
target_ulong virt_page2;
402+
TranslationBlock *tb;
403+
tb_page_addr_t phys_pc;
345404
tcg_insn_unit *gen_code_buf;
346405
int gen_code_size, search_size, max_insns;
347-
#ifdef CONFIG_PROFILER
348-
TCGProfile *prof = &tcg_ctx->prof;
349406
int64_t ti;
350-
#endif
407+
void *host_pc;
351408

352409
target_ulong pc = src_block ^ reverse_bits((target_ulong)exit_n);
353410

354-
(void)virt_page2;
355-
(void)phys_page2;
356-
(void)phys_pc;
357-
(void)existing_tb;
358-
359411
assert_memory_lock();
412+
qemu_thread_jit_write();
360413

414+
phys_pc = get_page_addr_code_hostp(env, src_block, &host_pc);
415+
phys_pc ^= reverse_bits((tb_page_addr_t)exit_n);
416+
417+
// if (phys_pc == -1) {
418+
// /* Generate a one-shot TB with 1 insn in it */
419+
// cflags = (cflags & ~CF_COUNT_MASK) | 1;
420+
// }
421+
422+
/* Generate a one-shot TB with max 16 insn in it */
423+
cflags = (cflags & ~CF_COUNT_MASK) | LIBAFL_MAX_INSNS;
424+
QEMU_BUILD_BUG_ON(LIBAFL_MAX_INSNS > TCG_MAX_INSNS);
425+
426+
max_insns = cflags & CF_COUNT_MASK;
427+
if (max_insns == 0) {
428+
max_insns = TCG_MAX_INSNS;
429+
}
430+
QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS);
431+
432+
// edge hooks generation callbacks
361433
struct libafl_edge_hook* hook = libafl_edge_hooks;
362434
int no_exec_hook = 1;
363435
while (hook) {
@@ -371,21 +443,8 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
371443
if (no_exec_hook)
372444
return NULL;
373445

374-
qemu_thread_jit_write();
375-
376-
phys_pc = get_page_addr_code(env, src_block);
377-
phys_pc ^= reverse_bits((tb_page_addr_t)exit_n);
378-
379-
/* Generate a one-shot TB with max 8 insn in it */
380-
cflags = (cflags & ~CF_COUNT_MASK) | 8;
381-
382-
max_insns = cflags & CF_COUNT_MASK;
383-
if (max_insns == 0) {
384-
max_insns = TCG_MAX_INSNS;
385-
}
386-
QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS);
387-
388-
buffer_overflow1:
446+
buffer_overflow:
447+
assert_no_pages_locked();
389448
tb = tcg_tb_alloc(tcg_ctx);
390449
if (unlikely(!tb)) {
391450
/* flush must be done */
@@ -398,79 +457,116 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
398457

399458
gen_code_buf = tcg_ctx->code_gen_ptr;
400459
tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
460+
401461
if (!(cflags & CF_PCREL)) {
402462
tb->pc = pc;
403463
}
464+
404465
tb->cs_base = cs_base;
405466
tb->flags = flags;
406467
tb->cflags = cflags | CF_IS_EDGE;
407-
//tb_set_page_addr0(tb, phys_pc);
408-
//tb_set_page_addr1(tb, -1);
409-
tcg_ctx->gen_tb = tb;
468+
tb_set_page_addr0(tb, phys_pc);
469+
tb_set_page_addr1(tb, -1);
470+
// if (phys_pc != -1) {
471+
// tb_lock_page0(phys_pc);
472+
// }
410473

411-
#ifdef CONFIG_PROFILER
412-
/* includes aborted translations because of exceptions */
413-
qatomic_set(&prof->tb_count1, prof->tb_count1 + 1);
414-
ti = profile_getclock();
474+
tcg_ctx->gen_tb = tb;
475+
tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64;
476+
#ifdef CONFIG_SOFTMMU
477+
tcg_ctx->page_bits = TARGET_PAGE_BITS;
478+
tcg_ctx->page_mask = TARGET_PAGE_MASK;
479+
tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS;
480+
#endif
481+
tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS;
482+
#ifdef TCG_GUEST_DEFAULT_MO
483+
tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO;
484+
#else
485+
tcg_ctx->guest_mo = TCG_MO_ALL;
415486
#endif
416487

417-
tcg_func_start(tcg_ctx);
418-
419-
tcg_ctx->cpu = env_cpu(env);
420-
421-
hook = libafl_edge_hooks;
422-
size_t hcount = 0, hins = 0;
423-
while (hook) {
424-
if (hook->cur_id != (uint64_t)-1 && hook->helper_info.func) {
425-
hcount++;
426-
hins++;
427-
TCGv_i64 tmp0 = tcg_constant_i64(hook->data);
428-
TCGv_i64 tmp1 = tcg_constant_i64(hook->cur_id);
429-
TCGTemp *tmp2[2] = { tcgv_i64_temp(tmp0), tcgv_i64_temp(tmp1) };
430-
tcg_gen_callN(&hook->helper_info, NULL, tmp2);
431-
tcg_temp_free_i64(tmp0);
432-
tcg_temp_free_i64(tmp1);
433-
}
434-
if (hook->cur_id != (uint64_t)-1 && hook->jit) {
435-
hcount++;
436-
hins += hook->jit(hook->data, hook->cur_id);
437-
}
438-
hook = hook->next;
439-
}
440-
tcg_gen_goto_tb(0);
441-
tcg_gen_exit_tb(tb, 0);
442-
tb->size = hcount;
443-
tb->icount = hins;
444-
445-
assert(tb->size != 0);
446-
tcg_ctx->cpu = NULL;
447-
max_insns = tb->icount;
448-
488+
restart_translate:
449489
trace_translate_block(tb, pc, tb->tc.ptr);
450490

451-
#ifdef CONFIG_PROFILER
452-
qatomic_set(&prof->tb_count, prof->tb_count + 1);
453-
qatomic_set(&prof->interm_time,
454-
prof->interm_time + profile_getclock() - ti);
455-
ti = profile_getclock();
456-
#endif
457-
458-
gen_code_size = tcg_gen_code(tcg_ctx, tb, pc);
491+
gen_code_size = libafl_setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti);
459492
if (unlikely(gen_code_size < 0)) {
460-
goto buffer_overflow1;
493+
switch (gen_code_size) {
494+
case -1:
495+
/*
496+
* Overflow of code_gen_buffer, or the current slice of it.
497+
*
498+
* TODO: We don't need to re-do gen_intermediate_code, nor
499+
* should we re-do the tcg optimization currently hidden
500+
* inside tcg_gen_code. All that should be required is to
501+
* flush the TBs, allocate a new TB, re-initialize it per
502+
* above, and re-do the actual code generation.
503+
*/
504+
qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT,
505+
"Restarting code generation for "
506+
"code_gen_buffer overflow\n");
507+
tb_unlock_pages(tb);
508+
tcg_ctx->gen_tb = NULL;
509+
goto buffer_overflow;
510+
511+
case -2:
512+
assert(false && "This should never happen for edge code. There must be a bug.");
513+
/*
514+
* The code generated for the TranslationBlock is too large.
515+
* The maximum size allowed by the unwind info is 64k.
516+
* There may be stricter constraints from relocations
517+
* in the tcg backend.
518+
*
519+
* Try again with half as many insns as we attempted this time.
520+
* If a single insn overflows, there's a bug somewhere...
521+
*/
522+
assert(max_insns > 1);
523+
max_insns /= 2;
524+
qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT,
525+
"Restarting code generation with "
526+
"smaller translation block (max %d insns)\n",
527+
max_insns);
528+
529+
/*
530+
* The half-sized TB may not cross pages.
531+
* TODO: Fix all targets that cross pages except with
532+
* the first insn, at which point this can't be reached.
533+
*/
534+
// phys_p2 = tb_page_addr1(tb);
535+
// if (unlikely(phys_p2 != -1)) {
536+
// tb_unlock_page1(phys_pc, phys_p2);
537+
// tb_set_page_addr1(tb, -1);
538+
// }
539+
goto restart_translate;
540+
541+
case -3:
542+
/*
543+
* We had a page lock ordering problem. In order to avoid
544+
* deadlock we had to drop the lock on page0, which means
545+
* that everything we translated so far is compromised.
546+
* Restart with locks held on both pages.
547+
*/
548+
qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT,
549+
"Restarting code generation with re-locked pages");
550+
goto restart_translate;
551+
552+
default:
553+
g_assert_not_reached();
554+
}
461555
}
556+
tcg_ctx->gen_tb = NULL;
557+
462558
search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
463559
if (unlikely(search_size < 0)) {
464-
goto buffer_overflow1;
560+
tb_unlock_pages(tb);
561+
goto buffer_overflow;
465562
}
466563
tb->tc.size = gen_code_size;
467564

468-
#ifdef CONFIG_PROFILER
469-
qatomic_set(&prof->code_time, prof->code_time + profile_getclock() - ti);
470-
qatomic_set(&prof->code_in_len, prof->code_in_len + tb->size);
471-
qatomic_set(&prof->code_out_len, prof->code_out_len + gen_code_size);
472-
qatomic_set(&prof->search_out_len, prof->search_out_len + search_size);
473-
#endif
565+
/*
566+
* For CF_PCREL, attribute all executions of the generated code
567+
* to its first mapping.
568+
*/
569+
perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf));
474570

475571
qatomic_set(&tcg_ctx->code_gen_ptr, (void *)
476572
ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size,
@@ -492,6 +588,8 @@ TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
492588
tb_reset_jump(tb, 1);
493589
}
494590

591+
assert_no_pages_locked();
592+
495593
#ifndef CONFIG_USER_ONLY
496594
tb->page_addr[0] = tb->page_addr[1] = -1;
497595
#endif
@@ -543,12 +641,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
543641

544642
gen_code_buf = tcg_ctx->code_gen_ptr;
545643
tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
644+
546645
//// --- Begin LibAFL code ---
547646
// Always include pc for edge hooks
548647
//if (!(cflags & CF_PCREL)) {
549648
tb->pc = pc;
550649
//}
551650
//// --- End LibAFL code ---
651+
552652
tb->cs_base = cs_base;
553653
tb->flags = flags;
554654
tb->cflags = cflags;

include/libafl/hook.h

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#define LIBAFL_TABLES_SIZE 16384
1111
#define LIBAFL_TABLES_HASH(p) (((13*((size_t)(p))) ^ (((size_t)(p)) >> 15)) % LIBAFL_TABLES_SIZE)
12+
#define LIBAFL_MAX_INSNS 16
1213

1314
void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args);
1415

0 commit comments

Comments
 (0)