Files
xen/tools/fuzz/x86_instruction_emulator/fuzz-emul.c
T
George Dunlap e03583ac44 fuzz/x86_emulate: Improve failure descriptions in x86_emulate harness
- Print the symbolic name rather than the number
- Explicitly state when data_read() fails due to EOI

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
2017-10-11 23:35:23 +01:00

859 lines
21 KiB
C

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xen/xen.h>
#include "x86_emulate.h"
#define MSR_INDEX_MAX 16
#define SEG_NUM x86_seg_none
/* Layout of data expected as fuzzing input. */
struct fuzz_corpus
{
unsigned long cr[5];
uint64_t msr[MSR_INDEX_MAX];
struct cpu_user_regs regs;
struct segment_register segments[SEG_NUM];
unsigned long options;
unsigned char data[4096];
} input;
#define DATA_OFFSET offsetof(struct fuzz_corpus, data)
/*
* Internal state of the fuzzing harness. Calculated initially from the input
* corpus, and later mutates by the emulation callbacks.
*/
struct fuzz_state
{
/* Fuzzer's input data. */
struct fuzz_corpus *corpus;
/* Real amount of data backing corpus->data[]. */
size_t data_num;
/* Amount of corpus->data[] consumed thus far. */
size_t data_index;
/* Emulation ops, some of which are disabled based on corpus->options. */
struct x86_emulate_ops ops;
};
static const char* const x86emul_return_string[] = {
[X86EMUL_OKAY] = "X86EMUL_OKAY",
[X86EMUL_UNHANDLEABLE] = "X86EMUL_UNHANDLEABLE",
[X86EMUL_EXCEPTION] = "X86EMUL_EXCEPTION",
[X86EMUL_RETRY] = "X86EMUL_RETRY",
[X86EMUL_DONE] = "X86EMUL_DONE",
};
/*
* Randomly return success or failure when processing data. If
* `exception` is false, this function turns _EXCEPTION to _OKAY.
*/
static int maybe_fail(struct x86_emulate_ctxt *ctxt,
const char *why, bool exception)
{
struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
int rc;
if ( s->data_index >= s->data_num )
rc = X86EMUL_EXCEPTION;
else
{
/* Randomly returns value:
* 50% okay
* 25% unhandlable
* 25% exception
*/
if ( c->data[s->data_index] > 0xc0 )
rc = X86EMUL_EXCEPTION;
else if ( c->data[s->data_index] > 0x80 )
rc = X86EMUL_UNHANDLEABLE;
else
rc = X86EMUL_OKAY;
s->data_index++;
}
if ( rc == X86EMUL_EXCEPTION && !exception )
rc = X86EMUL_OKAY;
printf("maybe_fail %s: %s\n", why, x86emul_return_string[rc]);
if ( rc == X86EMUL_EXCEPTION )
/* Fake up a pagefault. */
x86_emul_pagefault(0, 0, ctxt);
return rc;
}
static int data_read(struct x86_emulate_ctxt *ctxt,
enum x86_segment seg,
const char *why, void *dst, unsigned int bytes)
{
struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
unsigned int i;
int rc;
if ( s->data_index + bytes > s->data_num )
{
/*
* Fake up a segment limit violation. System segment limit volations
* are reported by X86EMUL_EXCEPTION alone, so the emulator can fill
* in the correct context.
*/
if ( !is_x86_system_segment(seg) )
x86_emul_hw_exception(13, 0, ctxt);
rc = X86EMUL_EXCEPTION;
printf("data_read %s: X86EMUL_EXCEPTION (end of input)\n", why);
}
else
rc = maybe_fail(ctxt, why, true);
if ( rc == X86EMUL_OKAY )
{
memcpy(dst, &c->data[s->data_index], bytes);
s->data_index += bytes;
printf("%s: ", why);
for ( i = 0; i < bytes; i++ )
printf(" %02x", *(unsigned char *)(dst + i));
printf("\n");
}
return rc;
}
static int fuzz_read(
enum x86_segment seg,
unsigned long offset,
void *p_data,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
/* Reads expected for all user and system segments. */
if ( is_x86_user_segment(seg) )
assert(ctxt->addr_size == 64 || !(offset >> 32));
else if ( seg == x86_seg_tr )
/*
* The TSS is special in that accesses below the segment base are
* possible, as the Interrupt Redirection Bitmap starts 32 bytes
* ahead of the I/O Bitmap, regardless of the value of the latter.
*/
assert((long)offset < 0 ? (long)offset > -32 : !(offset >> 17));
else
assert(is_x86_system_segment(seg) &&
(ctxt->lma ? offset <= 0x10007 : !(offset >> 16)));
return data_read(ctxt, seg, "read", p_data, bytes);
}
static int fuzz_read_io(
unsigned int port,
unsigned int bytes,
unsigned long *val,
struct x86_emulate_ctxt *ctxt)
{
return data_read(ctxt, x86_seg_none, "read_io", val, bytes);
}
static int fuzz_insn_fetch(
enum x86_segment seg,
unsigned long offset,
void *p_data,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
assert(seg == x86_seg_cs);
/* Minimal segment limit checking, until full one is being put in place. */
if ( ctxt->addr_size < 64 && (offset >> 32) )
{
x86_emul_hw_exception(13, 0, ctxt);
return X86EMUL_EXCEPTION;
}
/*
* Zero-length instruction fetches are made at the destination of jumps,
* to perform segmentation checks. No data needs returning.
*/
if ( bytes == 0 )
{
assert(p_data == NULL);
return maybe_fail(ctxt, "insn_fetch", true);
}
return data_read(ctxt, seg, "insn_fetch", p_data, bytes);
}
static int _fuzz_rep_read(struct x86_emulate_ctxt *ctxt,
const char *why, unsigned long *reps)
{
int rc;
unsigned long bytes_read = 0;
rc = data_read(ctxt, x86_seg_none, why, &bytes_read, sizeof(bytes_read));
if ( bytes_read <= *reps )
*reps = bytes_read;
switch ( rc )
{
case X86EMUL_UNHANDLEABLE:
/* No work is done in this case */
*reps = 0;
break;
case X86EMUL_EXCEPTION:
case X86EMUL_RETRY:
/* Halve the amount in this case */
*reps /= 2;
break;
}
return rc;
}
static int _fuzz_rep_write(struct x86_emulate_ctxt *ctxt,
const char *why, unsigned long *reps)
{
int rc = maybe_fail(ctxt, why, true);
switch ( rc )
{
case X86EMUL_UNHANDLEABLE:
/* No work is done in this case */
*reps = 0;
break;
case X86EMUL_EXCEPTION:
case X86EMUL_RETRY:
/* Halve the amount in this case */
*reps /= 2;
break;
}
return rc;
}
static int fuzz_rep_ins(
uint16_t src_port,
enum x86_segment dst_seg,
unsigned long dst_offset,
unsigned int bytes_per_rep,
unsigned long *reps,
struct x86_emulate_ctxt *ctxt)
{
assert(dst_seg == x86_seg_es);
assert(ctxt->addr_size == 64 || !(dst_offset >> 32));
return _fuzz_rep_read(ctxt, "rep_ins", reps);
}
static int fuzz_rep_movs(
enum x86_segment src_seg,
unsigned long src_offset,
enum x86_segment dst_seg,
unsigned long dst_offset,
unsigned int bytes_per_rep,
unsigned long *reps,
struct x86_emulate_ctxt *ctxt)
{
assert(is_x86_user_segment(src_seg));
assert(dst_seg == x86_seg_es);
assert(ctxt->addr_size == 64 || !((src_offset | dst_offset) >> 32));
return _fuzz_rep_read(ctxt, "rep_movs", reps);
}
static int fuzz_rep_outs(
enum x86_segment src_seg,
unsigned long src_offset,
uint16_t dst_port,
unsigned int bytes_per_rep,
unsigned long *reps,
struct x86_emulate_ctxt *ctxt)
{
assert(is_x86_user_segment(src_seg));
assert(ctxt->addr_size == 64 || !(src_offset >> 32));
return _fuzz_rep_write(ctxt, "rep_outs", reps);
}
static int fuzz_rep_stos(
void *p_data,
enum x86_segment seg,
unsigned long offset,
unsigned int bytes_per_rep,
unsigned long *reps,
struct x86_emulate_ctxt *ctxt)
{
/*
* STOS itself may only have an %es segment, but the stos() hook is reused
* for CLZERO.
*/
assert(is_x86_user_segment(seg));
assert(ctxt->addr_size == 64 || !(offset >> 32));
return _fuzz_rep_write(ctxt, "rep_stos", reps);
}
static int fuzz_write(
enum x86_segment seg,
unsigned long offset,
void *p_data,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
/* Writes not expected for any system segments. */
assert(is_x86_user_segment(seg));
assert(ctxt->addr_size == 64 || !(offset >> 32));
return maybe_fail(ctxt, "write", true);
}
static int fuzz_cmpxchg(
enum x86_segment seg,
unsigned long offset,
void *old,
void *new,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt)
{
/*
* Cmpxchg expected for user segments, and setting accessed/busy bits in
* GDT/LDT enties, but not expected for any IDT or TR accesses.
*/
if ( is_x86_user_segment(seg) )
assert(ctxt->addr_size == 64 || !(offset >> 32));
else
assert((seg == x86_seg_gdtr || seg == x86_seg_ldtr) && !(offset >> 16));
return maybe_fail(ctxt, "cmpxchg", true);
}
static int fuzz_invlpg(
enum x86_segment seg,
unsigned long offset,
struct x86_emulate_ctxt *ctxt)
{
/* invlpg(), unlike all other hooks, may be called with x86_seg_none. */
assert(is_x86_user_segment(seg) || seg == x86_seg_none);
assert(ctxt->addr_size == 64 || !(offset >> 32));
return maybe_fail(ctxt, "invlpg", false);
}
static int fuzz_wbinvd(
struct x86_emulate_ctxt *ctxt)
{
return maybe_fail(ctxt, "wbinvd", true);
}
static int fuzz_write_io(
unsigned int port,
unsigned int bytes,
unsigned long val,
struct x86_emulate_ctxt *ctxt)
{
return maybe_fail(ctxt, "write_io", true);
}
static int fuzz_read_segment(
enum x86_segment seg,
struct segment_register *reg,
struct x86_emulate_ctxt *ctxt)
{
const struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
assert(is_x86_user_segment(seg) || is_x86_system_segment(seg));
*reg = c->segments[seg];
return X86EMUL_OKAY;
}
static int fuzz_write_segment(
enum x86_segment seg,
const struct segment_register *reg,
struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
struct fuzz_corpus *c = s->corpus;
int rc;
assert(is_x86_user_segment(seg) || is_x86_system_segment(seg));
rc = maybe_fail(ctxt, "write_segment", true);
if ( rc == X86EMUL_OKAY )
c->segments[seg] = *reg;
return rc;
}
static int fuzz_read_cr(
unsigned int reg,
unsigned long *val,
struct x86_emulate_ctxt *ctxt)
{
const struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
if ( reg >= ARRAY_SIZE(c->cr) )
return X86EMUL_UNHANDLEABLE;
*val = c->cr[reg];
return X86EMUL_OKAY;
}
static int fuzz_write_cr(
unsigned int reg,
unsigned long val,
struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
struct fuzz_corpus *c = s->corpus;
int rc;
if ( reg >= ARRAY_SIZE(c->cr) )
return X86EMUL_UNHANDLEABLE;
rc = maybe_fail(ctxt, "write_cr", true);
if ( rc != X86EMUL_OKAY )
return rc;
c->cr[reg] = val;
return X86EMUL_OKAY;
}
enum {
MSRI_IA32_SYSENTER_CS,
MSRI_IA32_SYSENTER_ESP,
MSRI_IA32_SYSENTER_EIP,
MSRI_EFER,
MSRI_STAR,
MSRI_LSTAR,
MSRI_CSTAR,
MSRI_SYSCALL_MASK,
MSRI_IA32_DEBUGCTLMSR,
};
static const unsigned int msr_index[MSR_INDEX_MAX] = {
[MSRI_IA32_SYSENTER_CS] = MSR_IA32_SYSENTER_CS,
[MSRI_IA32_SYSENTER_ESP] = MSR_IA32_SYSENTER_ESP,
[MSRI_IA32_SYSENTER_EIP] = MSR_IA32_SYSENTER_EIP,
[MSRI_EFER] = MSR_EFER,
[MSRI_STAR] = MSR_STAR,
[MSRI_LSTAR] = MSR_LSTAR,
[MSRI_CSTAR] = MSR_CSTAR,
[MSRI_SYSCALL_MASK] = MSR_SYSCALL_MASK,
[MSRI_IA32_DEBUGCTLMSR] = MSR_IA32_DEBUGCTLMSR,
};
static int fuzz_read_msr(
unsigned int reg,
uint64_t *val,
struct x86_emulate_ctxt *ctxt)
{
const struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
unsigned int idx;
switch ( reg )
{
case MSR_TSC_AUX:
case MSR_IA32_TSC:
/*
* TSC should return monotonically increasing values, TSC_AUX
* should preferably return consistent values, but returning
* random values is fine in fuzzer.
*/
return data_read(ctxt, x86_seg_none, "read_msr", val, sizeof(*val));
case MSR_EFER:
*val = c->msr[MSRI_EFER];
*val &= ~EFER_LMA;
if ( (*val & EFER_LME) && (c->cr[4] & X86_CR4_PAE) &&
(c->cr[0] & X86_CR0_PG) )
{
printf("Setting EFER_LMA\n");
*val |= EFER_LMA;
}
return X86EMUL_OKAY;
}
for ( idx = 0; idx < MSR_INDEX_MAX; idx++ )
{
if ( msr_index[idx] == reg )
{
*val = c->msr[idx];
return X86EMUL_OKAY;
}
}
x86_emul_hw_exception(13, 0, ctxt);
return X86EMUL_EXCEPTION;
}
static int fuzz_write_msr(
unsigned int reg,
uint64_t val,
struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
struct fuzz_corpus *c = s->corpus;
unsigned int idx;
int rc;
rc = maybe_fail(ctxt, "write_msr", true);
if ( rc != X86EMUL_OKAY )
return rc;
switch ( reg )
{
case MSR_TSC_AUX:
case MSR_IA32_TSC:
return X86EMUL_OKAY;
}
for ( idx = 0; idx < MSR_INDEX_MAX; idx++ )
{
if ( msr_index[idx] == reg )
{
c->msr[idx] = val;
return X86EMUL_OKAY;
}
}
x86_emul_hw_exception(13, 0, ctxt);
return X86EMUL_EXCEPTION;
}
#define SET(h) .h = fuzz_##h
static const struct x86_emulate_ops all_fuzzer_ops = {
SET(read),
SET(insn_fetch),
SET(write),
SET(cmpxchg),
SET(rep_ins),
SET(rep_outs),
SET(rep_movs),
SET(rep_stos),
SET(read_segment),
SET(write_segment),
SET(read_io),
SET(write_io),
SET(read_cr),
SET(write_cr),
SET(read_msr),
SET(write_msr),
SET(wbinvd),
SET(invlpg),
.get_fpu = emul_test_get_fpu,
.put_fpu = emul_test_put_fpu,
.cpuid = emul_test_cpuid,
};
#undef SET
static void setup_fpu_exception_handler(void)
{
/* FIXME - just disable exceptions for now */
unsigned long a;
asm volatile ( "fnclex");
a = 0x37f; /* FCW_DEFAULT in Xen */
asm volatile ( "fldcw %0" :: "m" (a));
a = 0x1f80; /* MXCSR_DEFAULT in Xen */
asm volatile ( "ldmxcsr %0" :: "m" (a) );
}
static void dump_state(struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
struct cpu_user_regs *regs = ctxt->regs;
uint64_t val = 0;
printf(" -- State -- \n");
printf("addr / sp size: %d / %d\n", ctxt->addr_size, ctxt->sp_size);
printf(" cr0: %lx\n", c->cr[0]);
printf(" cr3: %lx\n", c->cr[3]);
printf(" cr4: %lx\n", c->cr[4]);
printf(" rip: %"PRIx64"\n", regs->rip);
fuzz_read_msr(MSR_EFER, &val, ctxt);
printf("EFER: %"PRIx64"\n", val);
}
static bool long_mode_active(struct x86_emulate_ctxt *ctxt)
{
uint64_t val;
if ( fuzz_read_msr(MSR_EFER, &val, ctxt) != X86EMUL_OKAY )
return false;
return val & EFER_LMA;
}
static bool in_longmode(struct x86_emulate_ctxt *ctxt)
{
const struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
return long_mode_active(ctxt) && c->segments[x86_seg_cs].l;
}
static void set_sizes(struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
ctxt->lma = long_mode_active(ctxt);
if ( in_longmode(ctxt) )
ctxt->addr_size = ctxt->sp_size = 64;
else
{
ctxt->addr_size = c->segments[x86_seg_cs].db ? 32 : 16;
ctxt->sp_size = c->segments[x86_seg_ss].db ? 32 : 16;
}
}
#define CANONICALIZE(x) \
do { \
uint64_t _y = (x); \
if ( _y & (1ULL << 47) ) \
_y |= (~0ULL) << 48; \
else \
_y &= (1ULL << 48)-1; \
printf("Canonicalized %" PRIx64 " to %" PRIx64 "\n", x, _y); \
(x) = _y; \
} while( 0 )
/* Expects bitmap and regs to be defined */
#define CANONICALIZE_MAYBE(reg) \
if ( !(bitmap & (1 << CANONICALIZE_##reg)) ) \
CANONICALIZE(regs->reg); \
enum {
HOOK_read,
HOOK_insn_fetch,
HOOK_write,
HOOK_cmpxchg,
HOOK_rep_ins,
HOOK_rep_outs,
HOOK_rep_movs,
HOOK_rep_stos,
HOOK_read_segment,
HOOK_write_segment,
HOOK_read_io,
HOOK_write_io,
HOOK_read_cr,
HOOK_write_cr,
HOOK_read_dr,
HOOK_write_dr,
HOOK_read_msr,
HOOK_write_msr,
HOOK_wbinvd,
HOOK_cpuid,
HOOK_inject_hw_exception,
HOOK_inject_sw_interrupt,
HOOK_get_fpu,
HOOK_put_fpu,
HOOK_invlpg,
HOOK_vmfunc,
CANONICALIZE_rip,
CANONICALIZE_rsp,
CANONICALIZE_rbp
};
/* Expects bitmap to be defined */
#define MAYBE_DISABLE_HOOK(h) \
if ( bitmap & (1 << HOOK_##h) ) \
{ \
s->ops.h = NULL; \
printf("Disabling hook "#h"\n"); \
}
static void disable_hooks(struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
const struct fuzz_corpus *c = s->corpus;
unsigned long bitmap = c->options;
/* See also sanitize_input, some hooks can't be disabled. */
MAYBE_DISABLE_HOOK(read);
MAYBE_DISABLE_HOOK(insn_fetch);
MAYBE_DISABLE_HOOK(write);
MAYBE_DISABLE_HOOK(cmpxchg);
MAYBE_DISABLE_HOOK(rep_ins);
MAYBE_DISABLE_HOOK(rep_outs);
MAYBE_DISABLE_HOOK(rep_movs);
MAYBE_DISABLE_HOOK(rep_stos);
MAYBE_DISABLE_HOOK(read_segment);
MAYBE_DISABLE_HOOK(write_segment);
MAYBE_DISABLE_HOOK(read_io);
MAYBE_DISABLE_HOOK(write_io);
MAYBE_DISABLE_HOOK(read_cr);
MAYBE_DISABLE_HOOK(write_cr);
MAYBE_DISABLE_HOOK(read_msr);
MAYBE_DISABLE_HOOK(write_msr);
MAYBE_DISABLE_HOOK(wbinvd);
MAYBE_DISABLE_HOOK(cpuid);
MAYBE_DISABLE_HOOK(get_fpu);
MAYBE_DISABLE_HOOK(invlpg);
}
/*
* Constrain input to architecturally-possible states where
* the emulator relies on these
*
* In general we want the emulator to be as absolutely robust as
* possible; which means that we want to minimize the number of things
* it assumes about the input state. Tesing this means minimizing and
* removing as much of the input constraints as possible.
*
* So we only add constraints that (in general) have been proven to
* cause crashes in the emulator.
*
* For future reference: other constraints which might be necessary at
* some point:
*
* - EFER.LMA => !EFLAGS.NT
* - In VM86 mode, force segment...
* - ...access rights to 0xf3
* - ...limits to 0xffff
* - ...bases to below 1Mb, 16-byte aligned
* - ...selectors to (base >> 4)
*/
static void sanitize_input(struct x86_emulate_ctxt *ctxt)
{
struct fuzz_state *s = ctxt->data;
struct fuzz_corpus *c = s->corpus;
struct cpu_user_regs *regs = &c->regs;
unsigned long bitmap = c->options;
/* Some hooks can't be disabled. */
c->options &= ~((1<<HOOK_read)|(1<<HOOK_insn_fetch));
/* Zero 'private' entries */
regs->error_code = 0;
regs->entry_vector = 0;
CANONICALIZE_MAYBE(rip);
CANONICALIZE_MAYBE(rsp);
CANONICALIZE_MAYBE(rbp);
/*
* CR0.PG can't be set if CR0.PE isn't set. Set is more interesting, so
* set PE if PG is set.
*/
if ( c->cr[0] & X86_CR0_PG )
c->cr[0] |= X86_CR0_PE;
/* EFLAGS.VM not available in long mode */
if ( long_mode_active(ctxt) )
regs->rflags &= ~X86_EFLAGS_VM;
/* EFLAGS.VM implies 16-bit mode */
if ( regs->rflags & X86_EFLAGS_VM )
{
c->segments[x86_seg_cs].db = 0;
c->segments[x86_seg_ss].db = 0;
}
}
int LLVMFuzzerInitialize(int *argc, char ***argv)
{
if ( !emul_test_init() )
{
printf("Warning: Stack could not be made executable (%d).\n", errno);
return 1;
}
return 0;
}
int LLVMFuzzerTestOneInput(const uint8_t *data_p, size_t size)
{
struct fuzz_state state = {
.ops = all_fuzzer_ops,
};
struct x86_emulate_ctxt ctxt = {
.data = &state,
.regs = &input.regs,
.addr_size = 8 * sizeof(void *),
.sp_size = 8 * sizeof(void *),
};
int rc;
/* Reset all global state variables */
memset(&input, 0, sizeof(input));
if ( size <= DATA_OFFSET )
{
printf("Input too small\n");
return 1;
}
if ( size > sizeof(input) )
{
printf("Input too large\n");
return 1;
}
memcpy(&input, data_p, size);
state.corpus = &input;
state.data_num = size - DATA_OFFSET;
sanitize_input(&ctxt);
disable_hooks(&ctxt);
do {
/* FIXME: Until we actually implement SIGFPE handling properly */
setup_fpu_exception_handler();
set_sizes(&ctxt);
dump_state(&ctxt);
rc = x86_emulate(&ctxt, &state.ops);
printf("Emulation result: %d\n", rc);
} while ( rc == X86EMUL_OKAY );
return 0;
}
unsigned int fuzz_minimal_input_size(void)
{
return DATA_OFFSET + 1;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/