This commit is contained in:
h1994st
2020-03-07 10:29:14 -05:00
10 changed files with 318 additions and 47 deletions

View File

@ -16,6 +16,8 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
- afl-fuzz basic tools now report on the environment variables picked up - afl-fuzz basic tools now report on the environment variables picked up
- more tools get environment variable usage info in the help output - more tools get environment variable usage info in the help output
- AFL_AUTORESUME will resume execution without the need to specify `-i -` - AFL_AUTORESUME will resume execution without the need to specify `-i -`
- afl-tmin now supports hang mode `-H` to minimize hangs
- fixed potential afl-tmin missbehavior for targets with multiple hangs
### Version ++2.62c (release): ### Version ++2.62c (release):

View File

@ -28,13 +28,14 @@ performed with the custom mutator.
C/C++: C/C++:
```c ```c
void afl_custom_init(unsigned int seed); void afl_custom_init(unsigned int seed);
size_t afl_custom_fuzz(u8* buf, size_t buf_size, size_t afl_custom_fuzz(u8** buf, size_t buf_size, u8* add_buf,
u8* add_buf, size_t add_buf_size, size_t add_buf_size, size_t max_size);
u8* mutated_out, size_t max_size);
size_t afl_custom_pre_save(u8* buf, size_t buf_size, u8** out_buf); size_t afl_custom_pre_save(u8* buf, size_t buf_size, u8** out_buf);
u32 afl_custom_init_trim(u8* buf, size_t buf_size); u32 afl_custom_init_trim(u8* buf, size_t buf_size);
void afl_custom_trim(u8** out_buf, size_t* out_buf_size); void afl_custom_trim(u8** out_buf, size_t* out_buf_size);
u32 afl_custom_post_trim(u8 success); u32 afl_custom_post_trim(u8 success);
size_t afl_custom_havoc_mutation(uint8_t** buf, size_t buf_size, size_t max_size);
uint8_t afl_custom_havoc_mutation_probability(void);
``` ```
Python: Python:
@ -56,6 +57,12 @@ def trim():
def post_trim(success): def post_trim(success):
return next_index return next_index
def havoc_mutation(buf, max_size):
return mutated_out
def havoc_mutation_probability():
return probability # int in [0, 100]
``` ```
### Custom Mutation ### Custom Mutation

View File

@ -286,8 +286,9 @@ operation of `afl-tmin` is as follows.
First, the tool automatically selects the operating mode. If the initial input First, the tool automatically selects the operating mode. If the initial input
crashes the target binary, afl-tmin will run in non-instrumented mode, simply crashes the target binary, afl-tmin will run in non-instrumented mode, simply
keeping any tweaks that produce a simpler file but still crash the target. If keeping any tweaks that produce a simpler file but still crash the target.
the target is non-crashing, the tool uses an instrumented mode and keeps only The same mode is used for hangs, if `-H` (hang mode) is specified.
If the target is non-crashing, the tool uses an instrumented mode and keeps only
the tweaks that produce exactly the same execution path. the tweaks that produce exactly the same execution path.
The actual minimization algorithm is: The actual minimization algorithm is:

View File

@ -30,23 +30,27 @@ void afl_custom_init(unsigned int seed) {
* *
* (Optional for now. Required in the future) * (Optional for now. Required in the future)
* *
* @param[in] buf Input data to be mutated * @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data * @param[in] buf_size Size of input data
* @param[in] add_buf Buffer containing the additional test case * @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case * @param[in] add_buf_size Size of the additional test case
* @param[out] mutated_out Buffer to store the mutated input
* @param[in] max_size Maximum size of the mutated output. The mutation must not * @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size. * produce data larger than max_size.
* @return Size of the mutated output. * @return Size of the mutated output.
*/ */
size_t afl_custom_fuzz(uint8_t *buf, size_t buf_size, size_t afl_custom_fuzz(uint8_t **buf, size_t buf_size,
uint8_t *add_buf,size_t add_buf_size, uint8_t *add_buf,size_t add_buf_size, // add_buf can be NULL
uint8_t *mutated_out, size_t max_size) { size_t max_size) {
// Make sure that the packet size does not exceed the maximum size expected by // Make sure that the packet size does not exceed the maximum size expected by
// the fuzzer // the fuzzer
size_t mutated_size = data_size <= max_size ? data_size : max_size; size_t mutated_size = data_size <= max_size ? data_size : max_size;
if (mutated_size > buf_size)
*buf = realloc(*buf, mutated_size);
uint8_t* mutated_out = *buf;
// Randomly select a command string to add as a header to the packet // Randomly select a command string to add as a header to the packet
memcpy(mutated_out, commands[rand() % 3], 3); memcpy(mutated_out, commands[rand() % 3], 3);
@ -175,3 +179,45 @@ int afl_custom_post_trim(int success) {
return trimmming_steps; return trimmming_steps;
} }
/**
* Perform a single custom mutation on a given input.
* This mutation is stacked with the other muatations in havoc.
*
* (Optional)
*
* @param[in] buf Pointer to the input data to be mutated
* @param[in] buf_size Size of input data
* @param[in] max_size Maximum size of the mutated output. The mutation must not produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t afl_custom_havoc_mutation(uint8_t** buf, size_t buf_size, size_t max_size) {
if (buf_size == 0) {
*buf = realloc(*buf, 1);
**buf = rand() % 256;
buf_size = 1;
}
size_t victim = rand() % buf_size;
(*buf)[victim] += rand() % 10;
return buf_size;
}
/**
* Return the probability (in percentage) that afl_custom_havoc_mutation
* is called in havoc. By default it is 6 %.
*
* (Optional)
*
* @return The probability (0-100).
*/
uint8_t afl_custom_havoc_mutation_probability(void) {
return 5; // 5 %
}

View File

@ -480,18 +480,16 @@ struct custom_mutator {
* *
* (Optional for now. Required in the future) * (Optional for now. Required in the future)
* *
* @param[in] buf Input data to be mutated * @param[in] buf Pointer to input data to be mutated
* @param[in] buf_size Size of input data * @param[in] buf_size Size of input data
* @param[in] add_buf Buffer containing the additional test case * @param[in] add_buf Buffer containing the additional test case
* @param[in] add_buf_size Size of the additional test case * @param[in] add_buf_size Size of the additional test case
* @param[out] mutated_out Buffer to store the mutated input
* @param[in] max_size Maximum size of the mutated output. The mutation must not * @param[in] max_size Maximum size of the mutated output. The mutation must not
* produce data larger than max_size. * produce data larger than max_size.
* @return Size of the mutated output. * @return Size of the mutated output.
*/ */
size_t (*afl_custom_fuzz)(u8* buf, size_t buf_size, size_t (*afl_custom_fuzz)(u8** buf, size_t buf_size, u8* add_buf,
u8* add_buf, size_t add_buf_size, size_t add_buf_size, size_t max_size);
u8* mutated_out, size_t max_size);
/** /**
* A post-processing function to use right before AFL writes the test case to * A post-processing function to use right before AFL writes the test case to
@ -561,6 +559,30 @@ struct custom_mutator {
* steps returned in init_trim) * steps returned in init_trim)
*/ */
u32 (*afl_custom_post_trim)(u8 success); u32 (*afl_custom_post_trim)(u8 success);
/**
* Perform a single custom mutation on a given input.
* This mutation is stacked with the other muatations in havoc.
*
* (Optional)
*
* @param[in] buf Pointer to the input data to be mutated
* @param[in] buf_size Size of input data
* @param[in] max_size Maximum size of the mutated output. The mutation must not produce data larger than max_size.
* @return Size of the mutated output.
*/
size_t (*afl_custom_havoc_mutation)(u8** buf, size_t buf_size, size_t max_size);
/**
* Return the probability (in percentage) that afl_custom_havoc_mutation
* is called in havoc. By default it is 6 %.
*
* (Optional)
*
* @return The probability (0-100).
*/
u8 (*afl_custom_havoc_mutation_probability)(void);
}; };
extern struct custom_mutator* mutator; extern struct custom_mutator* mutator;
@ -610,6 +632,8 @@ enum {
/* 03 */ PY_FUNC_INIT_TRIM, /* 03 */ PY_FUNC_INIT_TRIM,
/* 04 */ PY_FUNC_POST_TRIM, /* 04 */ PY_FUNC_POST_TRIM,
/* 05 */ PY_FUNC_TRIM, /* 05 */ PY_FUNC_TRIM,
/* 06 */ PY_FUNC_HAVOC_MUTATION,
/* 07 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY,
PY_FUNC_COUNT PY_FUNC_COUNT
}; };
@ -623,23 +647,23 @@ extern PyObject* py_functions[PY_FUNC_COUNT];
/* Custom mutators */ /* Custom mutators */
void setup_custom_mutator(void); void setup_custom_mutator(void);
void destroy_custom_mutator(void); void destroy_custom_mutator(void);
void load_custom_mutator(const char*);
void load_custom_mutator_py(const char*);
u8 trim_case_custom(char** argv, struct queue_entry* q, u8* in_buf); u8 trim_case_custom(char** argv, struct queue_entry* q, u8* in_buf);
/* Python */ /* Python */
#ifdef USE_PYTHON #ifdef USE_PYTHON
int init_py_module(u8*); int init_py_module(u8*);
void finalize_py_module(); void finalize_py_module();
void init_py(unsigned int seed); void init_py(unsigned int);
size_t fuzz_py(u8* buf, size_t buf_size, size_t fuzz_py(u8**, size_t, u8*, size_t, size_t);
u8* add_buf, size_t add_buf_size, size_t pre_save_py(u8*, size_t, u8**);
u8* mutated_out, size_t max_size);
size_t pre_save_py(u8* data, size_t size, u8** new_data);
u32 init_trim_py(u8*, size_t); u32 init_trim_py(u8*, size_t);
u32 post_trim_py(u8); u32 post_trim_py(u8);
void trim_py(u8**, size_t*); void trim_py(u8**, size_t*);
size_t havoc_mutation_py(u8**, size_t, size_t);
u8 havoc_mutation_probability_py(void);
#endif #endif
/* Queue */ /* Queue */

View File

@ -34,6 +34,8 @@
#include "types.h" #include "types.h"
#include "debug.h" #include "debug.h"
// Be careful! _WANT_ORIGINAL_AFL_ALLOC is not compatible with custom mutators
#ifndef _WANT_ORIGINAL_AFL_ALLOC #ifndef _WANT_ORIGINAL_AFL_ALLOC
// afl++ stuff without memory corruption checks - for speed // afl++ stuff without memory corruption checks - for speed

View File

@ -25,6 +25,11 @@
#include "afl-fuzz.h" #include "afl-fuzz.h"
void load_custom_mutator(const char*);
#ifdef USE_PYTHON
void load_custom_mutator_py(const char*);
#endif
void setup_custom_mutator(void) { void setup_custom_mutator(void) {
/* Try mutator library first */ /* Try mutator library first */
@ -146,6 +151,16 @@ void load_custom_mutator(const char* fn) {
"trimming will be used."); "trimming will be used.");
} }
/* "afl_custom_havoc_mutation", optional */
mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation");
if (!mutator->afl_custom_havoc_mutation)
WARNF("Symbol 'afl_custom_havoc_mutation' not found.");
/* "afl_custom_havoc_mutation", optional */
mutator->afl_custom_havoc_mutation_probability = dlsym(dh, "afl_custom_havoc_mutation_probability");
if (!mutator->afl_custom_havoc_mutation_probability)
WARNF("Symbol 'afl_custom_havoc_mutation_probability' not found.");
OKF("Custom mutator '%s' installed successfully.", fn); OKF("Custom mutator '%s' installed successfully.", fn);
@ -276,6 +291,7 @@ abort_trimming:
} }
#ifdef USE_PYTHON
void load_custom_mutator_py(const char* module_name) { void load_custom_mutator_py(const char* module_name) {
mutator = ck_alloc(sizeof(struct custom_mutator)); mutator = ck_alloc(sizeof(struct custom_mutator));
@ -301,6 +317,12 @@ void load_custom_mutator_py(const char* module_name) {
if (py_functions[PY_FUNC_TRIM]) if (py_functions[PY_FUNC_TRIM])
mutator->afl_custom_trim = trim_py; mutator->afl_custom_trim = trim_py;
if (py_functions[PY_FUNC_HAVOC_MUTATION])
mutator->afl_custom_havoc_mutation = havoc_mutation_py;
if (py_functions[PY_FUNC_HAVOC_MUTATION_PROBABILITY])
mutator->afl_custom_havoc_mutation_probability = havoc_mutation_probability_py;
OKF("Python mutator '%s' installed successfully.", module_name); OKF("Python mutator '%s' installed successfully.", module_name);
@ -309,3 +331,4 @@ void load_custom_mutator_py(const char* module_name) {
mutator->afl_custom_init(UR(0xFFFFFFFF)); mutator->afl_custom_init(UR(0xFFFFFFFF));
} }
#endif

View File

@ -1540,11 +1540,10 @@ custom_mutator_stage:
if (stage_max < HAVOC_MIN) stage_max = HAVOC_MIN; if (stage_max < HAVOC_MIN) stage_max = HAVOC_MIN;
const u32 max_seed_size = 4096 * 4096; const u32 max_seed_size = MAX_FILE;
u8* mutated_buf = ck_alloc(max_seed_size);
orig_hit_cnt = queued_paths + unique_crashes; orig_hit_cnt = queued_paths + unique_crashes;
for (stage_cur = 0; stage_cur < stage_max; ++stage_cur) { for (stage_cur = 0; stage_cur < stage_max; ++stage_cur) {
struct queue_entry* target; struct queue_entry* target;
@ -1589,21 +1588,17 @@ custom_mutator_stage:
new_buf = ck_alloc_nozero(target->len); new_buf = ck_alloc_nozero(target->len);
ck_read(fd, new_buf, target->len, target->fname); ck_read(fd, new_buf, target->len, target->fname);
close(fd); close(fd);
size_t mutated_size = mutator->afl_custom_fuzz(out_buf, len, size_t mutated_size = mutator->afl_custom_fuzz(&out_buf, len,
new_buf, target->len, new_buf, target->len,
mutated_buf, max_seed_size); max_seed_size);
ck_free(new_buf); ck_free(new_buf);
if (mutated_size > 0) { if (mutated_size > 0) {
out_buf = ck_realloc(out_buf, mutated_size);
memcpy(out_buf, mutated_buf, mutated_size);
if (common_fuzz_stuff(argv, out_buf, (u32)mutated_size)) { if (common_fuzz_stuff(argv, out_buf, (u32)mutated_size)) {
ck_free(mutated_buf);
goto abandon_entry; goto abandon_entry;
} }
@ -1625,10 +1620,12 @@ custom_mutator_stage:
} }
} }
if (mutated_size < len) out_buf = ck_realloc(out_buf, len);
memcpy(out_buf, in_buf, len);
} }
ck_free(mutated_buf);
new_hit_cnt = queued_paths + unique_crashes; new_hit_cnt = queued_paths + unique_crashes;
stage_finds[STAGE_CUSTOM_MUTATOR] += new_hit_cnt - orig_hit_cnt; stage_finds[STAGE_CUSTOM_MUTATOR] += new_hit_cnt - orig_hit_cnt;
@ -1681,6 +1678,17 @@ havoc_stage:
havoc_queued = queued_paths; havoc_queued = queued_paths;
u8 stacked_custom = (mutator && mutator->afl_custom_havoc_mutation);
u8 stacked_custom_prob = 6; // like one of the default mutations in havoc
if (stacked_custom && mutator->afl_custom_havoc_mutation_probability) {
stacked_custom_prob = mutator->afl_custom_havoc_mutation_probability();
if (stacked_custom_prob > 100)
FATAL("The probability returned by afl_custom_havoc_mutation_propability has to be in the range 0-100.");
}
/* We essentially just do several thousand runs (depending on perf_score) /* We essentially just do several thousand runs (depending on perf_score)
where we take the input file and make random stacked tweaks. */ where we take the input file and make random stacked tweaks. */
@ -1691,6 +1699,13 @@ havoc_stage:
stage_cur_val = use_stacking; stage_cur_val = use_stacking;
for (i = 0; i < use_stacking; ++i) { for (i = 0; i < use_stacking; ++i) {
if (stacked_custom && UR(100) < stacked_custom_prob) {
temp_len = mutator->afl_custom_havoc_mutation(&out_buf, temp_len,
MAX_FILE);
}
switch (UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))) { switch (UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))) {

View File

@ -55,6 +55,8 @@ int init_py_module(u8* module_name) {
py_functions[PY_FUNC_POST_TRIM] = py_functions[PY_FUNC_POST_TRIM] =
PyObject_GetAttrString(py_module, "post_trim"); PyObject_GetAttrString(py_module, "post_trim");
py_functions[PY_FUNC_TRIM] = PyObject_GetAttrString(py_module, "trim"); py_functions[PY_FUNC_TRIM] = PyObject_GetAttrString(py_module, "trim");
py_functions[PY_FUNC_HAVOC_MUTATION] = PyObject_GetAttrString(py_module, "havoc_mutation");
py_functions[PY_FUNC_HAVOC_MUTATION_PROBABILITY] = PyObject_GetAttrString(py_module, "havoc_mutation_probability");
for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) { for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) {
@ -159,16 +161,15 @@ void init_py(unsigned int seed) {
} }
} }
size_t fuzz_py(u8* buf, size_t buf_size, size_t fuzz_py(u8** buf, size_t buf_size, u8* add_buf, size_t add_buf_size,
u8* add_buf, size_t add_buf_size, size_t max_size) {
u8* mutated_out, size_t max_size) {
size_t mutated_size; size_t mutated_size;
PyObject *py_args, *py_value; PyObject *py_args, *py_value;
py_args = PyTuple_New(3); py_args = PyTuple_New(3);
/* buf */ /* buf */
py_value = PyByteArray_FromStringAndSize(buf, buf_size); py_value = PyByteArray_FromStringAndSize(*buf, buf_size);
if (!py_value) { if (!py_value) {
Py_DECREF(py_args); Py_DECREF(py_args);
@ -211,7 +212,10 @@ size_t fuzz_py(u8* buf, size_t buf_size,
if (py_value != NULL) { if (py_value != NULL) {
mutated_size = PyByteArray_Size(py_value); mutated_size = PyByteArray_Size(py_value);
memcpy(mutated_out, PyByteArray_AsString(py_value), mutated_size); if (buf_size < mutated_size)
*buf = ck_realloc(*buf, mutated_size);
memcpy(*buf, PyByteArray_AsString(py_value), mutated_size);
Py_DECREF(py_value); Py_DECREF(py_value);
return mutated_size; return mutated_size;
@ -359,5 +363,84 @@ void trim_py(u8** out_buf, size_t* out_buf_size) {
} }
size_t havoc_mutation_py(u8** buf, size_t buf_size, size_t max_size) {
size_t mutated_size;
PyObject *py_args, *py_value;
py_args = PyTuple_New(2);
/* buf */
py_value = PyByteArray_FromStringAndSize(*buf, buf_size);
if (!py_value) {
Py_DECREF(py_args);
FATAL("Failed to convert arguments");
}
PyTuple_SetItem(py_args, 0, py_value);
/* max_size */
#if PY_MAJOR_VERSION >= 3
py_value = PyLong_FromLong(max_size);
#else
py_value = PyInt_FromLong(max_size);
#endif
if (!py_value) {
Py_DECREF(py_args);
FATAL("Failed to convert arguments");
}
PyTuple_SetItem(py_args, 1, py_value);
py_value = PyObject_CallObject(py_functions[PY_FUNC_HAVOC_MUTATION], py_args);
Py_DECREF(py_args);
if (py_value != NULL) {
mutated_size = PyByteArray_Size(py_value);
if (buf_size < mutated_size)
*buf = ck_realloc(*buf, mutated_size);
memcpy(*buf, PyByteArray_AsString(py_value), mutated_size);
Py_DECREF(py_value);
return mutated_size;
} else {
PyErr_Print();
FATAL("Call failed");
}
}
u8 havoc_mutation_probability_py(void) {
PyObject *py_args, *py_value;
py_args = PyTuple_New(0);
py_value = PyObject_CallObject(py_functions[PY_FUNC_HAVOC_MUTATION_PROBABILITY], py_args);
Py_DECREF(py_args);
if (py_value != NULL) {
long prob = PyLong_AsLong(py_value);
Py_DECREF(py_value);
return (u8)prob;
} else {
PyErr_Print();
FATAL("Call failed");
}
}
#endif /* USE_PYTHON */ #endif /* USE_PYTHON */

View File

@ -89,6 +89,7 @@ u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
s32 dev_null_fd = -1; /* FD to /dev/null */ s32 dev_null_fd = -1; /* FD to /dev/null */
u8 crash_mode, /* Crash-centric mode? */ u8 crash_mode, /* Crash-centric mode? */
hang_mode, /* Minimize as long as it hangs */
exit_crash, /* Treat non-zero exit as crash? */ exit_crash, /* Treat non-zero exit as crash? */
edges_only, /* Ignore hit counts? */ edges_only, /* Ignore hit counts? */
exact_mode, /* Require path match for crashes? */ exact_mode, /* Require path match for crashes? */
@ -98,6 +99,7 @@ static volatile u8 stop_soon; /* Ctrl-C pressed? */
static u8 qemu_mode; static u8 qemu_mode;
/* /*
* forkserver section * forkserver section
*/ */
@ -427,6 +429,8 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
u32 cksum; u32 cksum;
child_timed_out = 0;
memset(trace_bits, 0, MAP_SIZE); memset(trace_bits, 0, MAP_SIZE);
MEM_BARRIER(); MEM_BARRIER();
@ -484,8 +488,13 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
if (*(u32*)trace_bits == EXEC_FAIL_SIG) if (*(u32*)trace_bits == EXEC_FAIL_SIG)
FATAL("Unable to execute '%s'", argv[0]); FATAL("Unable to execute '%s'", argv[0]);
classify_counts(trace_bits); if (!hang_mode) {
apply_mask((u32*)trace_bits, (u32*)mask_bitmap);
classify_counts(trace_bits);
apply_mask((u32*)trace_bits, (u32*)mask_bitmap);
}
total_execs++; total_execs++;
if (stop_soon) { if (stop_soon) {
@ -496,7 +505,27 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
} }
/* Always discard inputs that time out. */ /* Always discard inputs that time out, unless we are in hang mode */
if (hang_mode) {
if (child_timed_out) return 1;
if (WIFSIGNALED(status) ||
(WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) ||
(WIFEXITED(status) && WEXITSTATUS(status) && exit_crash)) {
missed_crashes++;
} else {
missed_hangs++;
}
return 0;
}
if (child_timed_out) { if (child_timed_out) {
@ -504,7 +533,7 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
return 0; return 0;
} }
/* Handle crashing inputs depending on current mode. */ /* Handle crashing inputs depending on current mode. */
if (WIFSIGNALED(status) || if (WIFSIGNALED(status) ||
@ -791,6 +820,19 @@ next_del_blksize:
finalize_all: finalize_all:
if (hang_mode) {
SAYF("\n" cGRA " File size reduced by : " cRST
"%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST
"%0.02f%%\n" cGRA " Number of execs done : " cRST "%u\n" cGRA
" Fruitless execs : " cRST "termination=%u crash=%u\n\n",
100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs,
missed_paths, missed_crashes);
return;
}
SAYF("\n" cGRA " File size reduced by : " cRST SAYF("\n" cGRA " File size reduced by : " cRST
"%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST "%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST
"%0.02f%%\n" cGRA " Number of execs done : " cRST "%u\n" cGRA "%0.02f%%\n" cGRA " Number of execs done : " cRST "%u\n" cGRA
@ -799,7 +841,7 @@ finalize_all:
((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs, ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs,
missed_paths, missed_crashes, missed_hangs ? cLRD : "", missed_hangs); missed_paths, missed_crashes, missed_hangs ? cLRD : "", missed_hangs);
if (total_execs > 50 && missed_hangs * 10 > total_execs) if (total_execs > 50 && missed_hangs * 10 > total_execs && !hang_mode)
WARNF(cLRD "Frequent timeouts - results may be skewed." cRST); WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);
} }
@ -978,6 +1020,7 @@ static void usage(u8* argv0) {
" -e - solve for edge coverage only, ignore hit counts\n" " -e - solve for edge coverage only, ignore hit counts\n"
" -x - treat non-zero exit codes as crashes\n\n" " -x - treat non-zero exit codes as crashes\n\n"
" -H - minimize a hang (hang mode)\n"
"For additional tips, please consult %s/README.md.\n\n" "For additional tips, please consult %s/README.md.\n\n"
@ -1077,7 +1120,7 @@ int main(int argc, char** argv, char** envp) {
SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n"); SAYF(cCYA "afl-tmin" VERSION cRST " by Michal Zalewski\n");
while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeQUWh")) > 0) while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeQUWHh")) > 0)
switch (opt) { switch (opt) {
@ -1103,6 +1146,7 @@ int main(int argc, char** argv, char** envp) {
case 'e': case 'e':
if (edges_only) FATAL("Multiple -e options not supported"); if (edges_only) FATAL("Multiple -e options not supported");
if (hang_mode) FATAL("Edges only and hang mode are mutually exclusive.");
edges_only = 1; edges_only = 1;
break; break;
@ -1188,6 +1232,15 @@ int main(int argc, char** argv, char** envp) {
break; break;
case 'H': /* Hang Mode */
/* Minimizes a testcase to the minimum that still times out */
if (hang_mode) FATAL("Multipe -H options not supported");
if (edges_only) FATAL("Edges only and hang mode are mutually exclusive.");
hang_mode = 1;
break;
case 'B': /* load bitmap */ case 'B': /* load bitmap */
/* This is a secret undocumented option! It is speculated to be useful /* This is a secret undocumented option! It is speculated to be useful
@ -1242,6 +1295,13 @@ int main(int argc, char** argv, char** envp) {
exact_mode = !!get_afl_env("AFL_TMIN_EXACT"); exact_mode = !!get_afl_env("AFL_TMIN_EXACT");
if (hang_mode && exact_mode) {
SAYF("AFL_TMIN_EXACT won't work for loops in hang mode, ignoring.");
exact_mode = 0;
}
SAYF("\n"); SAYF("\n");
read_initial_file(); read_initial_file();
@ -1253,10 +1313,18 @@ int main(int argc, char** argv, char** envp) {
run_target(use_argv, in_data, in_len, 1); run_target(use_argv, in_data, in_len, 1);
if (child_timed_out) if (hang_mode && !child_timed_out)
FATAL("Target binary times out (adjusting -t may help)."); FATAL("Target binary did not time out but hang minimization mode "
"(-H) was set (-t %u).", exec_tmout);
if (!crash_mode) { if (child_timed_out && !hang_mode)
FATAL("Target binary times out (adjusting -t may help). Use -H to minimize a hang.");
if (hang_mode) {
OKF("Program hangs as expected, minimizing in " cCYA "hang" cRST " mode.");
} else if (!crash_mode) {
OKF("Program terminates normally, minimizing in " cCYA "instrumented" cRST OKF("Program terminates normally, minimizing in " cCYA "instrumented" cRST
" mode."); " mode.");