mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-18 04:38:08 +00:00
Uniform API for both Python and custom mutator
This commit is contained in:
@ -472,12 +472,12 @@ struct custom_mutator {
|
|||||||
*
|
*
|
||||||
* (Optional)
|
* (Optional)
|
||||||
*/
|
*/
|
||||||
u32 (*afl_custom_init)(void);
|
void (*afl_custom_init)(unsigned int seed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform custom mutations on a given input
|
* Perform custom mutations on a given input
|
||||||
*
|
*
|
||||||
* (Required)
|
* (Optional for now. Required in the future)
|
||||||
*
|
*
|
||||||
* @param[in] data Input data to be mutated
|
* @param[in] data Input data to be mutated
|
||||||
* @param[in] size Size of input data
|
* @param[in] size Size of input data
|
||||||
@ -498,46 +498,67 @@ struct custom_mutator {
|
|||||||
* (Optional) If this functionality is not needed, simply don't define this
|
* (Optional) If this functionality is not needed, simply don't define this
|
||||||
* function.
|
* function.
|
||||||
*
|
*
|
||||||
* @param[in] data Buffer containing the test case to be executed.
|
* @param[in] data Buffer containing the test case to be executed
|
||||||
* @param[in] size Size of the test case.
|
* @param[in] size Size of the test case
|
||||||
* @param[out] new_data Buffer to store the test case after processing
|
* @param[out] new_data Buffer to store the test case after processing
|
||||||
* @return Size of data after processing.
|
* @return Size of data after processing
|
||||||
*/
|
*/
|
||||||
size_t (*afl_custom_pre_save)(u8* data, size_t size, u8** new_data);
|
size_t (*afl_custom_pre_save)(u8* data, size_t size, u8** new_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: figure out what `trim` is
|
* This method is called at the start of each trimming operation and receives
|
||||||
|
* the initial buffer. It should return the amount of iteration steps possible
|
||||||
|
* on this input (e.g. if your input has n elements and you want to remove
|
||||||
|
* them one by one, return n, if you do a binary search, return log(n),
|
||||||
|
* and so on...).
|
||||||
|
*
|
||||||
|
* If your trimming algorithm doesn't allow you to determine the amount of
|
||||||
|
* (remaining) steps easily (esp. while running), then you can alternatively
|
||||||
|
* return 1 here and always return 0 in post_trim until you are finished and
|
||||||
|
* no steps remain. In that case, returning 1 in post_trim will end the
|
||||||
|
* trimming routine. The whole current index/max iterations stuff is only used
|
||||||
|
* to show progress.
|
||||||
*
|
*
|
||||||
* (Optional)
|
* (Optional)
|
||||||
|
*
|
||||||
|
* @param data Buffer containing the test case
|
||||||
|
* @param size Size of the test case
|
||||||
|
* @return The amount of possible iteration steps to trim the input
|
||||||
*/
|
*/
|
||||||
u32 (*afl_custom_init_trim)(u8*, size_t);
|
u32 (*afl_custom_init_trim)(u8* data, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: figure out how `trim` works
|
* This method is called for each trimming operation. It doesn't have any
|
||||||
|
* arguments because we already have the initial buffer from init_trim and we
|
||||||
|
* can memorize the current state in global variables. This can also save
|
||||||
|
* reparsing steps for each iteration. It should return the trimmed input
|
||||||
|
* buffer, where the returned data must not exceed the initial input data in
|
||||||
|
* length. Returning anything that is larger than the original data (passed
|
||||||
|
* to init_trim) will result in a fatal abort of AFLFuzz.
|
||||||
*
|
*
|
||||||
* (Optional)
|
* (Optional)
|
||||||
*
|
*
|
||||||
* @param[out] ret (TODO: finish here)
|
* @param[out] ret Buffer containing the trimmed test case
|
||||||
* @param[out] ret_len (TODO: finish here)
|
* @param[out] ret_len Size of the trimmed test case
|
||||||
*/
|
*/
|
||||||
void (*afl_custom_trim)(u8** ret, size_t* ret_len);
|
void (*afl_custom_trim)(u8** ret, size_t* ret_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A post-processing function for the last trim operation.
|
* This method is called after each trim operation to inform you if your
|
||||||
|
* trimming step was successful or not (in terms of coverage). If you receive
|
||||||
|
* a failure here, you should reset your input to the last known good state.
|
||||||
*
|
*
|
||||||
* (Optional)
|
* (Optional)
|
||||||
*
|
*
|
||||||
* @param success Indicates if the last trim operation was successful.
|
* @param success Indicates if the last trim operation was successful.
|
||||||
|
* @return The next trim iteration index (from 0 to the maximum amount of
|
||||||
|
* steps returned in init_trim)
|
||||||
*/
|
*/
|
||||||
u32 (*afl_custom_post_trim)(u8 success);
|
u32 (*afl_custom_post_trim)(u8 success);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct custom_mutator* mutator;
|
extern struct custom_mutator* mutator;
|
||||||
|
|
||||||
size_t (*custom_mutator)(u8* data, size_t size, u8* mutated_out,
|
|
||||||
size_t max_size, unsigned int seed);
|
|
||||||
size_t (*pre_save_handler)(u8* data, size_t size, u8** new_data);
|
|
||||||
|
|
||||||
/* Interesting values, as per config.h */
|
/* Interesting values, as per config.h */
|
||||||
|
|
||||||
extern s8 interesting_8[INTERESTING_8_LEN];
|
extern s8 interesting_8[INTERESTING_8_LEN];
|
||||||
@ -598,17 +619,19 @@ 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(const char*);
|
||||||
void load_custom_mutator_py(const char*);
|
void load_custom_mutator_py(const char*);
|
||||||
|
u8 trim_case_custom(char** argv, struct queue_entry* q, u8* in_buf);
|
||||||
|
|
||||||
/* Python */
|
/* Python */
|
||||||
#ifdef USE_PYTHON
|
#ifdef USE_PYTHON
|
||||||
int init_py();
|
int init_py_module(u8*);
|
||||||
void finalize_py();
|
void finalize_py_module();
|
||||||
void fuzz_py(char*, size_t, char*, size_t, char**, size_t*);
|
|
||||||
|
void init_py(unsigned int seed);
|
||||||
|
void fuzz_py(char*, size_t, char*, size_t, char**, size_t*);
|
||||||
size_t pre_save_py(u8* data, size_t size, u8** new_data);
|
size_t pre_save_py(u8* data, size_t size, u8** new_data);
|
||||||
u32 init_trim_py(char*, size_t);
|
u32 init_trim_py(u8*, size_t);
|
||||||
u32 post_trim_py(char);
|
u32 post_trim_py(u8);
|
||||||
void trim_py(char**, size_t*);
|
void trim_py(u8**, size_t*);
|
||||||
u8 trim_case_python(char**, struct queue_entry*, u8*);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Queue */
|
/* Queue */
|
||||||
|
@ -259,11 +259,6 @@ s32 cmplog_child_pid, cmplog_forksrv_pid;
|
|||||||
/* Custom mutator */
|
/* Custom mutator */
|
||||||
struct custom_mutator* mutator;
|
struct custom_mutator* mutator;
|
||||||
|
|
||||||
/* hooks for the custom mutator function */
|
|
||||||
size_t (*custom_mutator)(u8 *data, size_t size, u8 *mutated_out,
|
|
||||||
size_t max_size, unsigned int seed);
|
|
||||||
size_t (*pre_save_handler)(u8 *data, size_t size, u8 **new_data);
|
|
||||||
|
|
||||||
/* Interesting values, as per config.h */
|
/* Interesting values, as per config.h */
|
||||||
|
|
||||||
s8 interesting_8[] = {INTERESTING_8};
|
s8 interesting_8[] = {INTERESTING_8};
|
||||||
|
@ -42,11 +42,22 @@ void setup_custom_mutator(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PYTHON
|
#ifdef USE_PYTHON
|
||||||
if (init_py()) FATAL("Failed to initialize Python module");
|
u8* module_name = getenv("AFL_PYTHON_MODULE");
|
||||||
|
|
||||||
// u8* module_name = getenv("AFL_PYTHON_MODULE");
|
if (module_name) {
|
||||||
// if (py_module && module_name)
|
|
||||||
// load_custom_mutator_py(module_name);
|
if (limit_time_sig)
|
||||||
|
FATAL(
|
||||||
|
"MOpt and Python mutator are mutually exclusive. We accept pull "
|
||||||
|
"requests that integrates MOpt with the optional mutators "
|
||||||
|
"(custom/radamsa/redquenn/...).");
|
||||||
|
|
||||||
|
if (init_py_module(module_name))
|
||||||
|
FATAL("Failed to initialize Python module");
|
||||||
|
|
||||||
|
load_custom_mutator_py(module_name);
|
||||||
|
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (getenv("AFL_PYTHON_MODULE"))
|
if (getenv("AFL_PYTHON_MODULE"))
|
||||||
FATAL("Your AFL binary was built without Python support");
|
FATAL("Your AFL binary was built without Python support");
|
||||||
@ -62,7 +73,7 @@ void destroy_custom_mutator(void) {
|
|||||||
else {
|
else {
|
||||||
/* Python mutator */
|
/* Python mutator */
|
||||||
#ifdef USE_PYTHON
|
#ifdef USE_PYTHON
|
||||||
finalize_py();
|
finalize_py_module();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,11 +91,11 @@ void load_custom_mutator(const char* fn) {
|
|||||||
ACTF("Loading custom mutator library from '%s'...", fn);
|
ACTF("Loading custom mutator library from '%s'...", fn);
|
||||||
|
|
||||||
dh = dlopen(fn, RTLD_NOW);
|
dh = dlopen(fn, RTLD_NOW);
|
||||||
if (!mutator->dh) FATAL("%s", dlerror());
|
if (!dh) FATAL("%s", dlerror());
|
||||||
mutator->dh = dh;
|
mutator->dh = dh;
|
||||||
|
|
||||||
/* Mutator */
|
/* Mutator */
|
||||||
/* "afl_custom_init", optional */
|
/* "afl_custom_init", optional for backward compatibility */
|
||||||
mutator->afl_custom_init = dlsym(dh, "afl_custom_init");
|
mutator->afl_custom_init = dlsym(dh, "afl_custom_init");
|
||||||
if (!mutator->afl_custom_init)
|
if (!mutator->afl_custom_init)
|
||||||
WARNF("Symbol 'afl_custom_init' not found.");
|
WARNF("Symbol 'afl_custom_init' not found.");
|
||||||
@ -92,13 +103,14 @@ void load_custom_mutator(const char* fn) {
|
|||||||
/* "afl_custom_fuzz" or "afl_custom_mutator", required */
|
/* "afl_custom_fuzz" or "afl_custom_mutator", required */
|
||||||
mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz");
|
mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz");
|
||||||
if (!mutator->afl_custom_fuzz) {
|
if (!mutator->afl_custom_fuzz) {
|
||||||
|
|
||||||
/* Try "afl_custom_mutator" for backward compatibility */
|
/* Try "afl_custom_mutator" for backward compatibility */
|
||||||
WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'.");
|
WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'.");
|
||||||
|
|
||||||
mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator");
|
mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator");
|
||||||
if (!mutator->afl_custom_fuzz) {
|
if (!mutator->afl_custom_fuzz)
|
||||||
FATAL("Symbol 'afl_custom_mutator' not found.");
|
FATAL("Symbol 'afl_custom_mutator' not found.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "afl_custom_pre_save", optional */
|
/* "afl_custom_pre_save", optional */
|
||||||
@ -106,6 +118,7 @@ void load_custom_mutator(const char* fn) {
|
|||||||
if (!mutator->afl_custom_pre_save)
|
if (!mutator->afl_custom_pre_save)
|
||||||
WARNF("Symbol 'afl_custom_pre_save' not found.");
|
WARNF("Symbol 'afl_custom_pre_save' not found.");
|
||||||
|
|
||||||
|
u8 notrim = 0;
|
||||||
/* "afl_custom_init_trim", optional */
|
/* "afl_custom_init_trim", optional */
|
||||||
mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim");
|
mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim");
|
||||||
if (!mutator->afl_custom_init_trim)
|
if (!mutator->afl_custom_init_trim)
|
||||||
@ -121,29 +134,177 @@ void load_custom_mutator(const char* fn) {
|
|||||||
if (!mutator->afl_custom_post_trim)
|
if (!mutator->afl_custom_post_trim)
|
||||||
WARNF("Symbol 'afl_custom_post_trim' not found.");
|
WARNF("Symbol 'afl_custom_post_trim' not found.");
|
||||||
|
|
||||||
|
if (notrim) {
|
||||||
|
|
||||||
|
mutator->afl_custom_init_trim = NULL;
|
||||||
|
mutator->afl_custom_trim = NULL;
|
||||||
|
mutator->afl_custom_post_trim = NULL;
|
||||||
|
WARNF(
|
||||||
|
"Custom mutator does not implement all three trim APIs, standard "
|
||||||
|
"trimming will be used.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
OKF("Custom mutator '%s' installed successfully.", fn);
|
OKF("Custom mutator '%s' installed successfully.", fn);
|
||||||
|
|
||||||
/* Initialize the custom mutator */
|
/* Initialize the custom mutator */
|
||||||
if (mutator->afl_custom_init)
|
if (mutator->afl_custom_init)
|
||||||
mutator->afl_custom_init();
|
mutator->afl_custom_init(UR(0xFFFFFFFF));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// void load_custom_mutator_py(const char* module_name) {
|
u8 trim_case_custom(char** argv, struct queue_entry* q, u8* in_buf) {
|
||||||
|
|
||||||
// mutator = ck_alloc(sizeof(struct custom_mutator));
|
static u8 tmp[64];
|
||||||
|
static u8 clean_trace[MAP_SIZE];
|
||||||
|
|
||||||
// mutator->name = module_name;
|
u8 needs_write = 0, fault = 0;
|
||||||
// ACTF("Loading Python mutator library from '%s'...", module_name);
|
u32 trim_exec = 0;
|
||||||
|
u32 orig_len = q->len;
|
||||||
|
|
||||||
// /* Initialize of the Python mutator has been invoked in "init_py()" */
|
stage_name = tmp;
|
||||||
// mutator->afl_custom_init = NULL;
|
bytes_trim_in += q->len;
|
||||||
// mutator->afl_custom_fuzz = fuzz_py;
|
|
||||||
// mutator->afl_custom_pre_save = pre_save_py;
|
|
||||||
// mutator->afl_custom_init_trim = init_trim_py;
|
|
||||||
// mutator->afl_custom_trim = trim_py;
|
|
||||||
// mutator->afl_custom_post_trim = post_trim_py;
|
|
||||||
|
|
||||||
// OKF("Python mutator '%s' installed successfully.", module_name);
|
/* Initialize trimming in the custom mutator */
|
||||||
|
stage_cur = 0;
|
||||||
|
stage_max = mutator->afl_custom_init_trim(in_buf, q->len);
|
||||||
|
|
||||||
// }
|
if (not_on_tty && debug)
|
||||||
|
SAYF("[Custom Trimming] START: Max %d iterations, %u bytes", stage_max,
|
||||||
|
q->len);
|
||||||
|
|
||||||
|
while (stage_cur < stage_max) {
|
||||||
|
|
||||||
|
sprintf(tmp, "ptrim %s", DI(trim_exec));
|
||||||
|
|
||||||
|
u32 cksum;
|
||||||
|
|
||||||
|
u8* retbuf = NULL;
|
||||||
|
size_t retlen = 0;
|
||||||
|
|
||||||
|
mutator->afl_custom_trim(&retbuf, &retlen);
|
||||||
|
|
||||||
|
if (retlen > orig_len)
|
||||||
|
FATAL(
|
||||||
|
"Trimmed data returned by custom mutator is larger than original "
|
||||||
|
"data");
|
||||||
|
|
||||||
|
write_to_testcase(retbuf, retlen);
|
||||||
|
|
||||||
|
fault = run_target(argv, exec_tmout);
|
||||||
|
++trim_execs;
|
||||||
|
|
||||||
|
if (stop_soon || fault == FAULT_ERROR) {
|
||||||
|
|
||||||
|
free(retbuf);
|
||||||
|
goto abort_trimming;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
|
||||||
|
|
||||||
|
if (cksum == q->exec_cksum) {
|
||||||
|
|
||||||
|
q->len = retlen;
|
||||||
|
memcpy(in_buf, retbuf, retlen);
|
||||||
|
|
||||||
|
/* Let's save a clean trace, which will be needed by
|
||||||
|
update_bitmap_score once we're done with the trimming stuff. */
|
||||||
|
|
||||||
|
if (!needs_write) {
|
||||||
|
|
||||||
|
needs_write = 1;
|
||||||
|
memcpy(clean_trace, trace_bits, MAP_SIZE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tell the custom mutator that the trimming was successful */
|
||||||
|
stage_cur = mutator->afl_custom_post_trim(1);
|
||||||
|
|
||||||
|
if (not_on_tty && debug)
|
||||||
|
SAYF("[Custom Trimming] SUCCESS: %d/%d iterations (now at %u bytes)",
|
||||||
|
stage_cur, stage_max, q->len);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Tell the custom mutator that the trimming was unsuccessful */
|
||||||
|
stage_cur = mutator->afl_custom_post_trim(0);
|
||||||
|
if (not_on_tty && debug)
|
||||||
|
SAYF("[Custom Trimming] FAILURE: %d/%d iterations", stage_cur,
|
||||||
|
stage_max);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(retbuf);
|
||||||
|
|
||||||
|
/* Since this can be slow, update the screen every now and then. */
|
||||||
|
|
||||||
|
if (!(trim_exec++ % stats_update_freq)) show_stats();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not_on_tty && debug)
|
||||||
|
SAYF("[Custom Trimming] DONE: %u bytes -> %u bytes", orig_len, q->len);
|
||||||
|
|
||||||
|
/* If we have made changes to in_buf, we also need to update the on-disk
|
||||||
|
version of the test case. */
|
||||||
|
|
||||||
|
if (needs_write) {
|
||||||
|
|
||||||
|
s32 fd;
|
||||||
|
|
||||||
|
unlink(q->fname); /* ignore errors */
|
||||||
|
|
||||||
|
fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||||
|
|
||||||
|
if (fd < 0) PFATAL("Unable to create '%s'", q->fname);
|
||||||
|
|
||||||
|
ck_write(fd, in_buf, q->len, q->fname);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
memcpy(trace_bits, clean_trace, MAP_SIZE);
|
||||||
|
update_bitmap_score(q);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abort_trimming:
|
||||||
|
|
||||||
|
bytes_trim_out += q->len;
|
||||||
|
return fault;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_custom_mutator_py(const char* module_name) {
|
||||||
|
|
||||||
|
mutator = ck_alloc(sizeof(struct custom_mutator));
|
||||||
|
|
||||||
|
mutator->name = module_name;
|
||||||
|
ACTF("Loading Python mutator library from '%s'...", module_name);
|
||||||
|
|
||||||
|
/* TODO: unify "init" and "fuzz" */
|
||||||
|
if (py_functions[PY_FUNC_INIT])
|
||||||
|
mutator->afl_custom_init = init_py;
|
||||||
|
|
||||||
|
/* "afl_custom_fuzz" should not be NULL, but the interface of Python mutator
|
||||||
|
is quite different from the custom mutator. */
|
||||||
|
mutator->afl_custom_fuzz = NULL;
|
||||||
|
|
||||||
|
if (py_functions[PY_FUNC_PRE_SAVE])
|
||||||
|
mutator->afl_custom_pre_save = pre_save_py;
|
||||||
|
|
||||||
|
if (py_functions[PY_FUNC_INIT_TRIM])
|
||||||
|
mutator->afl_custom_init_trim = init_trim_py;
|
||||||
|
|
||||||
|
if (py_functions[PY_FUNC_POST_TRIM])
|
||||||
|
mutator->afl_custom_post_trim = post_trim_py;
|
||||||
|
|
||||||
|
if (py_functions[PY_FUNC_TRIM])
|
||||||
|
mutator->afl_custom_trim = trim_py;
|
||||||
|
|
||||||
|
OKF("Python mutator '%s' installed successfully.", module_name);
|
||||||
|
|
||||||
|
/* Initialize the custom mutator */
|
||||||
|
if (mutator->afl_custom_init)
|
||||||
|
mutator->afl_custom_init(UR(0xFFFFFFFF));
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -449,7 +449,7 @@ u8 fuzz_one_original(char** argv) {
|
|||||||
* TRIMMING *
|
* TRIMMING *
|
||||||
************/
|
************/
|
||||||
|
|
||||||
if (!dumb_mode && !queue_cur->trim_done && !custom_mutator && !disable_trim) {
|
if (!dumb_mode && !queue_cur->trim_done && !disable_trim) {
|
||||||
|
|
||||||
u8 res = trim_case(argv, queue_cur, in_buf);
|
u8 res = trim_case(argv, queue_cur, in_buf);
|
||||||
|
|
||||||
@ -484,7 +484,7 @@ u8 fuzz_one_original(char** argv) {
|
|||||||
|
|
||||||
// custom_stage: // not used - yet
|
// custom_stage: // not used - yet
|
||||||
|
|
||||||
if (custom_mutator) {
|
if (mutator->afl_custom_fuzz) {
|
||||||
|
|
||||||
stage_short = "custom";
|
stage_short = "custom";
|
||||||
stage_name = "custom mutator";
|
stage_name = "custom mutator";
|
||||||
@ -499,8 +499,9 @@ u8 fuzz_one_original(char** argv) {
|
|||||||
for (stage_cur = 0; stage_cur < stage_max; ++stage_cur) {
|
for (stage_cur = 0; stage_cur < stage_max; ++stage_cur) {
|
||||||
|
|
||||||
size_t orig_size = (size_t)len;
|
size_t orig_size = (size_t)len;
|
||||||
size_t mutated_size = custom_mutator(in_buf, orig_size, mutated_buf,
|
size_t mutated_size = mutator->afl_custom_fuzz(in_buf, orig_size,
|
||||||
max_seed_size, UR(UINT32_MAX));
|
mutated_buf, max_seed_size,
|
||||||
|
UR(UINT32_MAX));
|
||||||
if (mutated_size > 0) {
|
if (mutated_size > 0) {
|
||||||
|
|
||||||
out_buf = ck_realloc(out_buf, mutated_size);
|
out_buf = ck_realloc(out_buf, mutated_size);
|
||||||
|
@ -28,124 +28,81 @@
|
|||||||
/* Python stuff */
|
/* Python stuff */
|
||||||
#ifdef USE_PYTHON
|
#ifdef USE_PYTHON
|
||||||
|
|
||||||
int init_py() {
|
int init_py_module(u8* module_name) {
|
||||||
|
|
||||||
Py_Initialize();
|
if (!module_name) return 1;
|
||||||
u8* module_name = getenv("AFL_PYTHON_MODULE");
|
|
||||||
|
|
||||||
if (module_name) {
|
|
||||||
|
|
||||||
if (limit_time_sig)
|
|
||||||
FATAL(
|
|
||||||
"MOpt and Python mutator are mutually exclusive. We accept pull "
|
|
||||||
"requests that integrates MOpt with the optional mutators "
|
|
||||||
"(custom/radamsa/redquenn/...).");
|
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
PyObject* py_name = PyUnicode_FromString(module_name);
|
PyObject* py_name = PyUnicode_FromString(module_name);
|
||||||
#else
|
#else
|
||||||
PyObject* py_name = PyString_FromString(module_name);
|
PyObject* py_name = PyString_FromString(module_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
py_module = PyImport_Import(py_name);
|
py_module = PyImport_Import(py_name);
|
||||||
Py_DECREF(py_name);
|
Py_DECREF(py_name);
|
||||||
|
|
||||||
if (py_module != NULL) {
|
if (py_module != NULL) {
|
||||||
|
|
||||||
u8 py_notrim = 0, py_idx;
|
u8 py_notrim = 0, py_idx;
|
||||||
py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init");
|
py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init");
|
||||||
py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz");
|
py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz");
|
||||||
py_functions[PY_FUNC_PRE_SAVE] =
|
py_functions[PY_FUNC_PRE_SAVE] =
|
||||||
PyObject_GetAttrString(py_module, "pre_save");
|
PyObject_GetAttrString(py_module, "pre_save");
|
||||||
py_functions[PY_FUNC_INIT_TRIM] =
|
py_functions[PY_FUNC_INIT_TRIM] =
|
||||||
PyObject_GetAttrString(py_module, "init_trim");
|
PyObject_GetAttrString(py_module, "init_trim");
|
||||||
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");
|
||||||
|
|
||||||
for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) {
|
for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) {
|
||||||
|
|
||||||
if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) {
|
if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) {
|
||||||
|
|
||||||
if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) {
|
if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) {
|
||||||
|
|
||||||
// Implementing the trim API is optional for now
|
// Implementing the trim API is optional for now
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
py_notrim = 1;
|
py_notrim = 1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Cannot find/call function with index %d in external "
|
"Cannot find/call function with index %d in external "
|
||||||
"Python module.\n",
|
"Python module.\n",
|
||||||
py_idx);
|
py_idx);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (py_notrim) {
|
}
|
||||||
|
|
||||||
py_functions[PY_FUNC_INIT_TRIM] = NULL;
|
if (py_notrim) {
|
||||||
py_functions[PY_FUNC_POST_TRIM] = NULL;
|
|
||||||
py_functions[PY_FUNC_TRIM] = NULL;
|
|
||||||
WARNF(
|
|
||||||
"Python module does not implement trim API, standard trimming will "
|
|
||||||
"be used.");
|
|
||||||
|
|
||||||
}
|
py_functions[PY_FUNC_INIT_TRIM] = NULL;
|
||||||
|
py_functions[PY_FUNC_POST_TRIM] = NULL;
|
||||||
PyObject *py_args, *py_value;
|
py_functions[PY_FUNC_TRIM] = NULL;
|
||||||
|
WARNF(
|
||||||
/* Provide the init function a seed for the Python RNG */
|
"Python module does not implement trim API, standard trimming will "
|
||||||
py_args = PyTuple_New(1);
|
"be used.");
|
||||||
#if PY_MAJOR_VERSION >= 3
|
|
||||||
py_value = PyLong_FromLong(UR(0xFFFFFFFF));
|
|
||||||
#else
|
|
||||||
py_value = PyInt_FromLong(UR(0xFFFFFFFF));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!py_value) {
|
|
||||||
|
|
||||||
Py_DECREF(py_args);
|
|
||||||
fprintf(stderr, "Cannot convert argument\n");
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTuple_SetItem(py_args, 0, py_value);
|
|
||||||
|
|
||||||
py_value = PyObject_CallObject(py_functions[PY_FUNC_INIT], py_args);
|
|
||||||
|
|
||||||
Py_DECREF(py_args);
|
|
||||||
|
|
||||||
if (py_value == NULL) {
|
|
||||||
|
|
||||||
PyErr_Print();
|
|
||||||
fprintf(stderr, "Call failed\n");
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
PyErr_Print();
|
|
||||||
fprintf(stderr, "Failed to load \"%s\"\n", module_name);
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
PyErr_Print();
|
||||||
|
fprintf(stderr, "Failed to load \"%s\"\n", module_name);
|
||||||
|
return 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void finalize_py() {
|
void finalize_py_module() {
|
||||||
|
|
||||||
if (py_module != NULL) {
|
if (py_module != NULL) {
|
||||||
|
|
||||||
@ -213,7 +170,43 @@ void fuzz_py(char* buf, size_t buflen, char* add_buf, size_t add_buflen,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 init_trim_py(char* buf, size_t buflen) {
|
size_t pre_save_py(u8* data, size_t size, u8** new_data) {
|
||||||
|
|
||||||
|
size_t new_size;
|
||||||
|
PyObject *py_args, *py_value;
|
||||||
|
py_args = PyTuple_New(2);
|
||||||
|
py_value = PyByteArray_FromStringAndSize(data, size);
|
||||||
|
if (!py_value) {
|
||||||
|
|
||||||
|
Py_DECREF(py_args);
|
||||||
|
FATAL("Failed to convert arguments");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PyTuple_SetItem(py_args, 0, py_value);
|
||||||
|
|
||||||
|
py_value = PyObject_CallObject(py_functions[PY_FUNC_PRE_SAVE], py_args);
|
||||||
|
|
||||||
|
Py_DECREF(py_args);
|
||||||
|
|
||||||
|
if (py_value != NULL) {
|
||||||
|
|
||||||
|
new_size = PyByteArray_Size(py_value);
|
||||||
|
*new_data = malloc(new_size);
|
||||||
|
memcpy(*new_data, PyByteArray_AsString(py_value), new_size);
|
||||||
|
Py_DECREF(py_value);
|
||||||
|
return new_size;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
PyErr_Print();
|
||||||
|
FATAL("Call failed");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 init_trim_py(u8* buf, size_t buflen) {
|
||||||
|
|
||||||
PyObject *py_args, *py_value;
|
PyObject *py_args, *py_value;
|
||||||
|
|
||||||
@ -250,7 +243,7 @@ u32 init_trim_py(char* buf, size_t buflen) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 post_trim_py(char success) {
|
u32 post_trim_py(u8 success) {
|
||||||
|
|
||||||
PyObject *py_args, *py_value;
|
PyObject *py_args, *py_value;
|
||||||
|
|
||||||
@ -288,7 +281,7 @@ u32 post_trim_py(char success) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void trim_py(char** ret, size_t* retlen) {
|
void trim_py(u8** ret, size_t* retlen) {
|
||||||
|
|
||||||
PyObject *py_args, *py_value;
|
PyObject *py_args, *py_value;
|
||||||
|
|
||||||
@ -312,126 +305,5 @@ void trim_py(char** ret, size_t* retlen) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 trim_case_python(char** argv, struct queue_entry* q, u8* in_buf) {
|
|
||||||
|
|
||||||
static u8 tmp[64];
|
|
||||||
static u8 clean_trace[MAP_SIZE];
|
|
||||||
|
|
||||||
u8 needs_write = 0, fault = 0;
|
|
||||||
u32 trim_exec = 0;
|
|
||||||
u32 orig_len = q->len;
|
|
||||||
|
|
||||||
stage_name = tmp;
|
|
||||||
bytes_trim_in += q->len;
|
|
||||||
|
|
||||||
/* Initialize trimming in the Python module */
|
|
||||||
stage_cur = 0;
|
|
||||||
stage_max = init_trim_py(in_buf, q->len);
|
|
||||||
|
|
||||||
if (not_on_tty && debug)
|
|
||||||
SAYF("[Python Trimming] START: Max %d iterations, %u bytes", stage_max,
|
|
||||||
q->len);
|
|
||||||
|
|
||||||
while (stage_cur < stage_max) {
|
|
||||||
|
|
||||||
sprintf(tmp, "ptrim %s", DI(trim_exec));
|
|
||||||
|
|
||||||
u32 cksum;
|
|
||||||
|
|
||||||
char* retbuf = NULL;
|
|
||||||
size_t retlen = 0;
|
|
||||||
|
|
||||||
trim_py(&retbuf, &retlen);
|
|
||||||
|
|
||||||
if (retlen > orig_len)
|
|
||||||
FATAL(
|
|
||||||
"Trimmed data returned by Python module is larger than original "
|
|
||||||
"data");
|
|
||||||
|
|
||||||
write_to_testcase(retbuf, retlen);
|
|
||||||
|
|
||||||
fault = run_target(argv, exec_tmout);
|
|
||||||
++trim_execs;
|
|
||||||
|
|
||||||
if (stop_soon || fault == FAULT_ERROR) {
|
|
||||||
|
|
||||||
free(retbuf);
|
|
||||||
goto abort_trimming;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
|
|
||||||
|
|
||||||
if (cksum == q->exec_cksum) {
|
|
||||||
|
|
||||||
q->len = retlen;
|
|
||||||
memcpy(in_buf, retbuf, retlen);
|
|
||||||
|
|
||||||
/* Let's save a clean trace, which will be needed by
|
|
||||||
update_bitmap_score once we're done with the trimming stuff. */
|
|
||||||
|
|
||||||
if (!needs_write) {
|
|
||||||
|
|
||||||
needs_write = 1;
|
|
||||||
memcpy(clean_trace, trace_bits, MAP_SIZE);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tell the Python module that the trimming was successful */
|
|
||||||
stage_cur = post_trim_py(1);
|
|
||||||
|
|
||||||
if (not_on_tty && debug)
|
|
||||||
SAYF("[Python Trimming] SUCCESS: %d/%d iterations (now at %u bytes)",
|
|
||||||
stage_cur, stage_max, q->len);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* Tell the Python module that the trimming was unsuccessful */
|
|
||||||
stage_cur = post_trim_py(0);
|
|
||||||
if (not_on_tty && debug)
|
|
||||||
SAYF("[Python Trimming] FAILURE: %d/%d iterations", stage_cur,
|
|
||||||
stage_max);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
free(retbuf);
|
|
||||||
|
|
||||||
/* Since this can be slow, update the screen every now and then. */
|
|
||||||
|
|
||||||
if (!(trim_exec++ % stats_update_freq)) show_stats();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not_on_tty && debug)
|
|
||||||
SAYF("[Python Trimming] DONE: %u bytes -> %u bytes", orig_len, q->len);
|
|
||||||
|
|
||||||
/* If we have made changes to in_buf, we also need to update the on-disk
|
|
||||||
version of the test case. */
|
|
||||||
|
|
||||||
if (needs_write) {
|
|
||||||
|
|
||||||
s32 fd;
|
|
||||||
|
|
||||||
unlink(q->fname); /* ignore errors */
|
|
||||||
|
|
||||||
fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
|
||||||
|
|
||||||
if (fd < 0) PFATAL("Unable to create '%s'", q->fname);
|
|
||||||
|
|
||||||
ck_write(fd, in_buf, q->len, q->fname);
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
memcpy(trace_bits, clean_trace, MAP_SIZE);
|
|
||||||
update_bitmap_score(q);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
abort_trimming:
|
|
||||||
|
|
||||||
bytes_trim_out += q->len;
|
|
||||||
return fault;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* USE_PYTHON */
|
#endif /* USE_PYTHON */
|
||||||
|
|
||||||
|
@ -309,10 +309,10 @@ void write_to_testcase(void* mem, u32 len) {
|
|||||||
|
|
||||||
lseek(fd, 0, SEEK_SET);
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
if (pre_save_handler) {
|
if (mutator->afl_custom_pre_save) {
|
||||||
|
|
||||||
u8* new_data;
|
u8* new_data;
|
||||||
size_t new_size = pre_save_handler(mem, len, &new_data);
|
size_t new_size = mutator->afl_custom_pre_save(mem, len, &new_data);
|
||||||
ck_write(fd, new_data, new_size, out_file);
|
ck_write(fd, new_data, new_size, out_file);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -678,9 +678,8 @@ void sync_fuzzers(char** argv) {
|
|||||||
|
|
||||||
u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {
|
u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {
|
||||||
|
|
||||||
#ifdef USE_PYTHON
|
/* Custom mutator trimmer */
|
||||||
if (py_functions[PY_FUNC_TRIM]) return trim_case_python(argv, q, in_buf);
|
if (mutator->afl_custom_trim) return trim_case_custom(argv, q, in_buf);
|
||||||
#endif
|
|
||||||
|
|
||||||
static u8 tmp[64];
|
static u8 tmp[64];
|
||||||
static u8 clean_trace[MAP_SIZE];
|
static u8 clean_trace[MAP_SIZE];
|
||||||
|
@ -655,7 +655,7 @@ void show_stats(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (custom_mutator) {
|
if (mutator) {
|
||||||
|
|
||||||
sprintf(tmp, "%s/%s", DI(stage_finds[STAGE_CUSTOM_MUTATOR]),
|
sprintf(tmp, "%s/%s", DI(stage_finds[STAGE_CUSTOM_MUTATOR]),
|
||||||
DI(stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
DI(stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
||||||
|
Reference in New Issue
Block a user