more custom mutator remodelling

This commit is contained in:
Dominik Maier
2020-03-28 04:57:44 +01:00
parent 0059d16731
commit 53fd8fe6ea
12 changed files with 370 additions and 151 deletions

View File

@ -5,8 +5,18 @@
#include "types.h" #include "types.h"
#include <stdlib.h> #include <stdlib.h>
#define INITIAL_GROWTH_SIZE (64)
#define RAND_BELOW(limit) (rand() % (limit)) #define RAND_BELOW(limit) (rand() % (limit))
/* Use in a struct: creates a name_buf and a name_size variable. */
#define BUF_VAR(type, name) \
type * name##_buf; \
size_t name##_size;
/* this filles in `&structptr->something_buf, &structptr->something_size`. */
#define BUF_PARAMS(struct, name) \
(void **)&struct->name##_buf, &struct->name##_size
typedef struct { typedef struct {
} afl_t; } afl_t;
@ -267,5 +277,56 @@ static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
} }
/* This function makes sure *size is > size_needed after call.
It changes buf and size in-place, if needed.
It will realloc *buf otherwise.
*size will grow exponentially as per:
https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
Will return NULL if size_needed is <1 or *size is negative or malloc Failed.
@return For convenience, this function returns *buf. NULL on error.
*/
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
/* Oops. found a bug? */
if (unlikely(size_needed < 1)) return NULL;
/* No need to realloc */
if (likely(*size >= size_needed)) return *buf;
if (unlikely(*size < 0)) return NULL;
/* No inital size was set */
if (*size == 0) *size = INITIAL_GROWTH_SIZE;
while (*size < size_needed) {
*size *= 2;
if ((*size) < 0) {
/* An overflow occurred. Fall back to size_needed */
*size = size_needed;
}
}
*buf = realloc(*buf, *size);
return *buf;
}
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
static inline void swap_bufs(void **buf1, size_t *size1, void **buf2,
size_t *size2) {
void * scratch_buf = *buf1;
size_t scratch_size = *size1;
*buf1 = *buf2;
*size1 = *size2;
*buf2 = scratch_buf;
*size2 = scratch_size;
}
#undef INITIAL_GROWTH_SIZE
#endif #endif

View File

@ -15,7 +15,6 @@
#include <stdio.h> #include <stdio.h>
#define DATA_SIZE (100) #define DATA_SIZE (100)
#define INITIAL_BUF_SIZE (16384)
static const char *commands[] = { static const char *commands[] = {
@ -28,9 +27,19 @@ static const char *commands[] = {
typedef struct my_mutator { typedef struct my_mutator {
afl_t *afl; afl_t *afl;
// any additional data here! // any additional data here!
size_t pre_save_size; uint8_t *trim_buf;
u8 * pre_save_buf; size_t trim_buf_size;
int trimmming_steps;
int cur_step;
// Reused buffers:
BUF_VAR(u8, fuzz);
BUF_VAR(u8, data);
BUF_VAR(u8, havoc);
BUF_VAR(u8, trim_out);
BUF_VAR(u8, pre_save);
} my_mutator_t; } my_mutator_t;
@ -59,16 +68,6 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
data->afl = afl; data->afl = afl;
data->pre_save_buf = malloc(INITIAL_BUF_SIZE);
if (!data->pre_save_buf) {
free(data);
return NULL;
}
data->pre_save_size = INITIAL_BUF_SIZE;
return data; return data;
} }
@ -85,20 +84,25 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
* @param[in] add_buf_size Size of the additional test case * @param[in] add_buf_size Size of the additional test case
* @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. Negative return will abort fuzzing.
*/ */
size_t afl_custom_fuzz(my_mutator_t *data, uint8_t **buf, size_t buf_size, size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
uint8_t *add_buf, u8 **out_buf, uint8_t *add_buf,
size_t add_buf_size, // add_buf can be NULL size_t add_buf_size, // add_buf can be NULL
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); // maybe_grow is optimized to be quick for reused buffers.
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size);
if (!mutated_out) {
uint8_t *mutated_out = *buf; perror("custom mutator allocation (maybe_grow)");
return -1; /* afl-fuzz will very likely error out after this. */
}
// 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);
@ -112,6 +116,7 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t **buf, size_t buf_size,
} }
*out_buf = mutated_out;
return mutated_size; return mutated_size;
} }
@ -137,11 +142,10 @@ size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size,
if (data->pre_save_size < buf_size + 5) { if (data->pre_save_size < buf_size + 5) {
data->pre_save_buf = realloc(data->pre_save_buf, buf_size + 5); data->pre_save_buf = maybe_grow(BUF_PARAMS(data, pre_save), buf_size + 5);
if (!data->pre_save_buf) { if (!data->pre_save_buf) {
perror("custom mutator realloc"); perror("custom mutator realloc failed.");
free(data);
return -1; return -1;
} }
@ -164,11 +168,6 @@ size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size,
} }
static uint8_t *trim_buf;
static size_t trim_buf_size;
static int trimmming_steps;
static int cur_step;
/** /**
* This method is called at the start of each trimming operation and receives * 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 * the initial buffer. It should return the amount of iteration steps possible
@ -193,20 +192,20 @@ static int cur_step;
int afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, size_t buf_size) { int afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, size_t buf_size) {
// We simply trim once // We simply trim once
trimmming_steps = 1; data->trimmming_steps = 1;
cur_step = 0; data->cur_step = 0;
trim_buf = buf; data->trim_buf = buf;
trim_buf_size = buf_size; data->trim_buf_size = buf_size;
return trimmming_steps; return data->trimmming_steps;
} }
/** /**
* This method is called for each trimming operation. It doesn't have any * 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 * 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 * can memorize the current state in *data. This can also save
* reparsing steps for each iteration. It should return the trimmed input * reparsing steps for each iteration. It should return the trimmed input
* buffer, where the returned data must not exceed the initial input data in * buffer, where the returned data must not exceed the initial input data in
* length. Returning anything that is larger than the original data (passed * length. Returning anything that is larger than the original data (passed
@ -216,19 +215,18 @@ int afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, size_t buf_size) {
* *
* @param[in] data pointer returned in afl_custom_init for this fuzz case * @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[out] out_buf Pointer to the buffer containing the trimmed test case. * @param[out] out_buf Pointer to the buffer containing the trimmed test case.
* External library should allocate memory for out_buf. AFL++ will release * External library should allocate memory for out_buf.
* the memory after saving the test case. * AFL++ will not release the memory after saving the test case.
* @param[out] out_buf_size Pointer to the size of the trimmed test case * Keep a ref in *data.
* @return Pointer to the size of the trimmed test case
*/ */
void afl_custom_trim(my_mutator_t *data, uint8_t **out_buf, size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) {
size_t *out_buf_size) {
*out_buf_size = trim_buf_size - 1; size_t ret = data->trim_buf_size - 1;
// External mutator should allocate memory for `out_buf` *out_buf = maybe_grow(BUF_PARAMS(data, trim_out), ret);
*out_buf = malloc(*out_buf_size);
// Remove the last byte of the trimming input // Remove the last byte of the trimming input
memcpy(*out_buf, trim_buf, *out_buf_size); memcpy(*out_buf, data->trim_buf, ret);
} }
@ -248,12 +246,12 @@ int afl_custom_post_trim(my_mutator_t *data, int success) {
if (success) { if (success) {
++cur_step; ++data->cur_step;
return cur_step; return data->cur_step;
} }
return trimmming_steps; return data->trimmming_steps;
} }
@ -264,26 +262,33 @@ int afl_custom_post_trim(my_mutator_t *data, int success) {
* (Optional) * (Optional)
* *
* @param[in] data pointer returned in afl_custom_init for this fuzz case * @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[inout] buf Pointer to the input data to be mutated and the mutated * @param[in] buf Pointer to the input data to be mutated and the mutated
* output * output
* @param[in] buf_size Size of input data * @param[in] buf_size Size of input data
* @param[out] out_buf The output buffer. buf can be reused, if the content
* fits.
* @param[in] max_size Maximum size of the mutated output. The mutation must * @param[in] max_size Maximum size of the mutated output. The mutation must
* not produce data larger than max_size. * not produce data larger than max_size.
* @return Size of the mutated output. * @return Size of the mutated output.
*/ */
size_t afl_custom_havoc_mutation(my_mutator_t *data, uint8_t **buf, size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
size_t buf_size, size_t max_size) { u8 **out_buf, size_t max_size) {
if (buf_size == 0) { if (buf_size == 0) {
*buf = realloc(*buf, 1); *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1);
**buf = rand() % 256; **out_buf = rand() % 256;
buf_size = 1; buf_size = 1;
} else {
// We reuse buf here. It's legal and faster.
*out_buf = buf;
} }
size_t victim = rand() % buf_size; size_t victim = rand() % buf_size;
(*buf)[victim] += rand() % 10; (*out_buf)[victim] += rand() % 10;
return buf_size; return buf_size;
@ -346,6 +351,10 @@ void afl_custom_queue_new_entry(my_mutator_t * data,
void afl_custom_deinit(my_mutator_t *data) { void afl_custom_deinit(my_mutator_t *data) {
free(data->pre_save_buf); free(data->pre_save_buf);
free(data->havoc_buf);
free(data->data_buf);
free(data->fuzz_buf);
free(data->trim_out_buf);
free(data); free(data);
} }

View File

@ -3,6 +3,7 @@
-------------------------------------------------- --------------------------------------------------
Originally written by Michal Zalewski Originally written by Michal Zalewski
Edited by Dominik Maier, 2020
Copyright 2015 Google Inc. All rights reserved. Copyright 2015 Google Inc. All rights reserved.
@ -41,22 +42,23 @@
AFL will call the afl_postprocess() function for every mutated output buffer. AFL will call the afl_postprocess() function for every mutated output buffer.
From there, you have three choices: From there, you have three choices:
1) If you don't want to modify the test case, simply return the original 1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
buffer pointer ('in_buf'). and return the original `len`.
2) If you want to skip this test case altogether and have AFL generate a 2) If you want to skip this test case altogether and have AFL generate a
new one, return NULL. Use this sparingly - it's faster than running new one, return 0 or set `*out_buf = NULL`.
the target program with patently useless inputs, but still wastes CPU Use this sparingly - it's faster than running the target program
time. with patently useless inputs, but still wastes CPU time.
3) If you want to modify the test case, allocate an appropriately-sized 3) If you want to modify the test case, allocate an appropriately-sized
buffer, move the data into that buffer, make the necessary changes, and buffer, move the data into that buffer, make the necessary changes, and
then return the new pointer. You can update *len if necessary, too. then return the new pointer as out_buf. Return an appropriate len
afterwards.
Note that the buffer will *not* be freed for you. To avoid memory leaks, Note that the buffer will *not* be freed for you. To avoid memory leaks,
you need to free it or reuse it on subsequent calls (as shown below). you need to free it or reuse it on subsequent calls (as shown below).
*** DO NOT MODIFY THE ORIGINAL 'in_buf' BUFFER. *** *** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
Aight. The example below shows a simple postprocessor that tries to make Aight. The example below shows a simple postprocessor that tries to make
sure that all input files start with "GIF89a". sure that all input files start with "GIF89a".
@ -74,47 +76,83 @@
#define HEADER "GIF89a" #define HEADER "GIF89a"
typedef struct post_state {
unsigned char *buf;
size_t size;
} post_state_t;
void *afl_postprocess_init(void *afl) {
post_state_t *state = malloc(sizeof(post_state_t));
if (!state) {
perror("malloc");
return NULL;
}
state->buf = calloc(sizeof(unsigned char), 4096);
if (!state->buf) { return NULL; }
return state;
}
/* The actual postprocessor routine called by afl-fuzz: */ /* The actual postprocessor routine called by afl-fuzz: */
const unsigned char *afl_postprocess(const unsigned char *in_buf, size_t afl_postprocess(post_state_t *data, unsigned char *in_buf,
unsigned int * len) { unsigned int len, unsigned char **out_buf) {
static unsigned char *saved_buf;
unsigned char * new_buf;
/* Skip execution altogether for buffers shorter than 6 bytes (just to /* Skip execution altogether for buffers shorter than 6 bytes (just to
show how it's done). We can trust *len to be sane. */ show how it's done). We can trust len to be sane. */
if (*len < strlen(HEADER)) return NULL; if (len < strlen(HEADER)) return 0;
/* Do nothing for buffers that already start with the expected header. */ /* Do nothing for buffers that already start with the expected header. */
if (!memcmp(in_buf, HEADER, strlen(HEADER))) return in_buf; if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
*out_buf = in_buf;
return len;
}
/* Allocate memory for new buffer, reusing previous allocation if /* Allocate memory for new buffer, reusing previous allocation if
possible. */ possible. */
new_buf = realloc(saved_buf, *len); *out_buf = realloc(data->buf, len);
/* If we're out of memory, the most graceful thing to do is to return the /* If we're out of memory, the most graceful thing to do is to return the
original buffer and give up on modifying it. Let AFL handle OOM on its original buffer and give up on modifying it. Let AFL handle OOM on its
own later on. */ own later on. */
if (!new_buf) return in_buf; if (!*out_buf) {
saved_buf = new_buf;
*out_buf = in_buf;
return len;
}
/* Copy the original data to the new location. */ /* Copy the original data to the new location. */
memcpy(new_buf, in_buf, *len); memcpy(*out_buf, in_buf, len);
/* Insert the new header. */ /* Insert the new header. */
memcpy(new_buf, HEADER, strlen(HEADER)); memcpy(*out_buf, HEADER, strlen(HEADER));
/* Return modified buffer. No need to update *len in this particular case, /* Return the new len. It hasn't changed, so it's just len. */
as we're not changing it. */
return new_buf; return len;
} }
/* Gets called afterwards */
void afl_postprocess_deinit(post_state_t *data) {
free(data->buf);
free(data);
}

View File

@ -5,6 +5,7 @@
Originally written by Michal Zalewski Originally written by Michal Zalewski
Copyright 2015 Google Inc. All rights reserved. Copyright 2015 Google Inc. All rights reserved.
Adapted to the new API, 2020 by Dominik Maier
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -35,11 +36,32 @@
#define UP4K(_i) ((((_i) >> 12) + 1) << 12) #define UP4K(_i) ((((_i) >> 12) + 1) << 12)
const unsigned char *afl_postprocess(const unsigned char *in_buf, typedef struct post_state {
unsigned int * len) {
static unsigned char *saved_buf; unsigned char *buf;
static unsigned int saved_len; size_t size;
} post_state_t;
void *afl_postprocess_init(void *afl) {
post_state_t *state = malloc(sizeof(post_state_t));
if (!state) {
perror("malloc");
return NULL;
}
state->buf = calloc(sizeof(unsigned char), 4096);
if (!state->buf) { return NULL; }
return state;
}
size_t afl_postprocess(post_state_t *data, const unsigned char *in_buf,
unsigned int len, const unsigned char **out_buf) {
unsigned char *new_buf = (unsigned char *)in_buf; unsigned char *new_buf = (unsigned char *)in_buf;
unsigned int pos = 8; unsigned int pos = 8;
@ -47,12 +69,17 @@ const unsigned char *afl_postprocess(const unsigned char *in_buf,
/* Don't do anything if there's not enough room for the PNG header /* Don't do anything if there's not enough room for the PNG header
(8 bytes). */ (8 bytes). */
if (*len < 8) return in_buf; if (len < 8) {
*out_buf = in_buf;
return len;
}
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we /* Minimum size of a zero-length PNG chunk is 12 bytes; if we
don't have that, we can bail out. */ don't have that, we can bail out. */
while (pos + 12 <= *len) { while (pos + 12 <= len) {
unsigned int chunk_len, real_cksum, file_cksum; unsigned int chunk_len, real_cksum, file_cksum;
@ -62,7 +89,7 @@ const unsigned char *afl_postprocess(const unsigned char *in_buf,
/* Bail out if chunk size is too big or goes past EOF. */ /* Bail out if chunk size is too big or goes past EOF. */
if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > *len) break; if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break;
/* Chunk checksum is calculated for chunk ID (dword) and the actual /* Chunk checksum is calculated for chunk ID (dword) and the actual
payload. */ payload. */
@ -82,17 +109,23 @@ const unsigned char *afl_postprocess(const unsigned char *in_buf,
if (new_buf == in_buf) { if (new_buf == in_buf) {
if (*len <= saved_len) { if (len <= data->size) {
new_buf = saved_buf; new_buf = data->buf;
} else { } else {
new_buf = realloc(saved_buf, UP4K(*len)); new_buf = realloc(data->buf, UP4K(len));
if (!new_buf) return in_buf; if (!new_buf) {
saved_buf = new_buf;
saved_len = UP4K(*len); *out_buf = in_buf;
memcpy(new_buf, in_buf, *len); return len;
}
data->buf = new_buf;
data->size = UP4K(len);
memcpy(new_buf, in_buf, len);
} }
@ -108,7 +141,15 @@ const unsigned char *afl_postprocess(const unsigned char *in_buf,
} }
return new_buf; *out_buf = new_buf;
return len;
} }
/* Gets called afterwards */
void afl_postprocess_deinit(post_state_t *data) {
free(data->buf);
free(data);
}

View File

@ -293,9 +293,18 @@ typedef struct py_mutator {
void * afl_state; void * afl_state;
void * py_data; void * py_data;
u8 * fuzz_buf;
size_t fuzz_size;
u8 * pre_save_buf; u8 * pre_save_buf;
size_t pre_save_size; size_t pre_save_size;
u8 * trim_buf;
size_t trim_size;
u8 * havoc_buf;
size_t havoc_size;
} py_mutator_t; } py_mutator_t;
#endif #endif
@ -544,7 +553,11 @@ typedef struct afl_state {
struct extra_data *a_extras; /* Automatically selected extras */ struct extra_data *a_extras; /* Automatically selected extras */
u32 a_extras_cnt; /* Total number of tokens available */ u32 a_extras_cnt; /* Total number of tokens available */
u8 *(*post_handler)(u8 *buf, u32 *len); /* afl_postprocess API */
void *(*post_init)(struct afl_state *afl);
size_t (*post_handler)(void *data, u8 *buf, u32 len, u8 **out_buf);
void *(*post_deinit)(void *data);
void *post_data;
/* CmpLog */ /* CmpLog */
@ -643,10 +656,10 @@ struct custom_mutator {
* @param[in] add_buf_size Size of the additional test case * @param[in] add_buf_size Size of the additional test case
* @param[in] max_size Maximum size of the mutated output. The mutation must * @param[in] max_size Maximum size of the mutated output. The mutation must
* not produce data larger than max_size. * not produce data larger than max_size.
* @return Size of the mutated output. * @return Size of the mutated output. Negative on error will abort exeuction.
*/ */
size_t (*afl_custom_fuzz)(void *data, u8 **buf, size_t buf_size, u8 *add_buf, size_t (*afl_custom_fuzz)(void *data, u8 *buf, size_t buf_size, u8 **out_buf,
size_t add_buf_size, size_t max_size); u8 *add_buf, size_t add_buf_size, 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
@ -704,9 +717,9 @@ struct custom_mutator {
* @param[out] out_buf Pointer to the buffer containing the trimmed test case. * @param[out] out_buf Pointer to the buffer containing the trimmed test case.
* External library should allocate memory for out_buf. AFL++ will release * External library should allocate memory for out_buf. AFL++ will release
* the memory after saving the test case. * the memory after saving the test case.
* @param[out] out_buf_size Pointer to the size of the trimmed test case * @return the size of the trimmed test case
*/ */
void (*afl_custom_trim)(void *data, u8 **out_buf, size_t *out_buf_size); size_t (*afl_custom_trim)(void *data, u8 **out_buf);
/** /**
* This method is called after each trim operation to inform you if your * This method is called after each trim operation to inform you if your
@ -728,16 +741,18 @@ struct custom_mutator {
* *
* (Optional) * (Optional)
* *
* @param data pointer returned in afl_custom_init for this fuzz case * @param[in] data pointer returned in afl_custom_init for this fuzz case
* @param[inout] buf Pointer to the input data to be mutated and the mutated * @param[in] buf Pointer to the input data to be mutated and the mutated
* output * output
* @param[in] buf_size Size of input data * @param[in] buf_size Size of input data
* @param[out] out_buf The new buffer. It's legal to reuse *buf if it's <
* buf_size.
* @param[in] max_size Maximum size of the mutated output. The mutation must * @param[in] max_size Maximum size of the mutated output. The mutation must
* not produce data larger than max_size. * not produce data larger than max_size.
* @return Size of the mutated output. * @return Size of the mutated output (out_size).
*/ */
size_t (*afl_custom_havoc_mutation)(void *data, u8 **buf, size_t buf_size, size_t (*afl_custom_havoc_mutation)(void *data, u8 *buf, size_t buf_size,
size_t max_size); u8 **out_buf, size_t max_size);
/** /**
* Return the probability (in percentage) that afl_custom_havoc_mutation * Return the probability (in percentage) that afl_custom_havoc_mutation
@ -803,8 +818,8 @@ void finalize_py_module(void *);
size_t pre_save_py(void *, u8 *, size_t, u8 **); size_t pre_save_py(void *, u8 *, size_t, u8 **);
u32 init_trim_py(void *, u8 *, size_t); u32 init_trim_py(void *, u8 *, size_t);
u32 post_trim_py(void *, u8); u32 post_trim_py(void *, u8);
void trim_py(void *, u8 **, size_t *); size_t trim_py(void *, u8 **);
size_t havoc_mutation_py(void *, u8 **, size_t, size_t); size_t havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t);
u8 havoc_mutation_probability_py(void *); u8 havoc_mutation_probability_py(void *);
u8 queue_get_py(void *, const u8 *); u8 queue_get_py(void *, const u8 *);
void queue_new_entry_py(void *, const u8 *, const u8 *); void queue_new_entry_py(void *, const u8 *, const u8 *);

View File

@ -527,8 +527,13 @@ u8 common_fuzz_cmplog_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
if (afl->post_handler) { if (afl->post_handler) {
out_buf = afl->post_handler(out_buf, &len); u8 *post_buf = NULL;
if (!out_buf || !len) return 0;
size_t post_len =
afl->post_handler(afl->post_data, out_buf, len, &post_buf);
if (!post_buf || !post_len) return 0;
out_buf = post_buf;
len = post_len;
} }

View File

@ -349,6 +349,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
void afl_state_deinit(afl_state_t *afl) { void afl_state_deinit(afl_state_t *afl) {
if (afl->post_deinit) afl->post_deinit(afl->post_data);
free(afl->out_buf); free(afl->out_buf);
free(afl->out_scratch_buf); free(afl->out_scratch_buf);
free(afl->eff_buf); free(afl->eff_buf);

View File

@ -270,7 +270,9 @@ void setup_post(afl_state_t *afl) {
void *dh; void *dh;
u8 * fn = afl->afl_env.afl_post_library; u8 * fn = afl->afl_env.afl_post_library;
u8 tbuf[6];
u32 tlen = 6; u32 tlen = 6;
strncpy(tbuf, "hello", tlen);
if (!fn) return; if (!fn) return;
@ -281,10 +283,20 @@ void setup_post(afl_state_t *afl) {
afl->post_handler = dlsym(dh, "afl_postprocess"); afl->post_handler = dlsym(dh, "afl_postprocess");
if (!afl->post_handler) FATAL("Symbol 'afl_postprocess' not found."); if (!afl->post_handler) FATAL("Symbol 'afl_postprocess' not found.");
afl->post_init = dlsym(dh, "afl_postprocess_init");
if (!afl->post_init) FATAL("Symbol 'afl_postprocess_init' not found.");
afl->post_deinit = dlsym(dh, "afl_postprocess_deinit");
if (!afl->post_deinit) FATAL("Symbol 'afl_postprocess_deinit' not found.");
/* Do a quick test. It's better to segfault now than later =) */ /* Do a quick test. It's better to segfault now than later =) */
afl->post_handler("hello", &tlen); u8 *post_buf = NULL;
afl->post_data = afl->post_init(afl);
if (!afl->post_data) FATAL("Could not initialize post handler.");
size_t post_len = afl->post_handler(afl->post_data, tbuf, tlen, &post_buf);
if (!post_len || !post_buf)
SAYF("Empty return in test post handler for buf=\"hello\\0\".");
OKF("Postprocessor installed successfully."); OKF("Postprocessor installed successfully.");

View File

@ -7,6 +7,7 @@
Now maintained by Marc Heuse <mh@mh-sec.de>, Now maintained by Marc Heuse <mh@mh-sec.de>,
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
Andrea Fioraldi <andreafioraldi@gmail.com> Andrea Fioraldi <andreafioraldi@gmail.com>
Dominik Maier <mail@dmnk.co>
Copyright 2016, 2017 Google Inc. All rights reserved. Copyright 2016, 2017 Google Inc. All rights reserved.
Copyright 2019-2020 AFLplusplus Project. All rights reserved. Copyright 2019-2020 AFLplusplus Project. All rights reserved.
@ -192,7 +193,8 @@ void load_custom_mutator(afl_state_t *afl, const char *fn) {
/* Initialize the custom mutator */ /* Initialize the custom mutator */
if (afl->mutator->afl_custom_init) if (afl->mutator->afl_custom_init)
afl->mutator->data = afl->mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); afl->mutator->data =
afl->mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF));
} }
@ -218,17 +220,18 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
while (afl->stage_cur < afl->stage_max) { while (afl->stage_cur < afl->stage_max) {
u8 *retbuf = NULL;
sprintf(afl->stage_name_buf, "ptrim %s", sprintf(afl->stage_name_buf, "ptrim %s",
u_stringify_int(val_buf, trim_exec)); u_stringify_int(val_buf, trim_exec));
u32 cksum; u32 cksum;
u8 * retbuf = NULL; size_t retlen = afl->mutator->afl_custom_trim(afl->mutator->data, &retbuf);
size_t retlen = 0;
afl->mutator->afl_custom_trim(afl->mutator->data, &retbuf, &retlen); if (unlikely(retlen < 0 || !retbuf))
FATAL("custom_trim failed (ret %zd)", retlen);
if (retlen > orig_len) else if (unlikely(retlen > orig_len))
FATAL( FATAL(
"Trimmed data returned by custom mutator is larger than original " "Trimmed data returned by custom mutator is larger than original "
"data"); "data");
@ -238,12 +241,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
fault = run_target(afl, afl->fsrv.exec_tmout); fault = run_target(afl, afl->fsrv.exec_tmout);
++afl->trim_execs; ++afl->trim_execs;
if (afl->stop_soon || fault == FAULT_ERROR) { if (afl->stop_soon || fault == FAULT_ERROR) { goto abort_trimming; }
ck_free(retbuf);
goto abort_trimming;
}
cksum = hash32(afl->fsrv.trace_bits, MAP_SIZE, HASH_CONST); cksum = hash32(afl->fsrv.trace_bits, MAP_SIZE, HASH_CONST);
@ -281,8 +279,6 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
} }
ck_free(retbuf);
/* Since this can be slow, update the screen every now and then. */ /* Since this can be slow, update the screen every now and then. */
if (!(trim_exec++ % afl->stats_update_freq)) show_stats(afl); if (!(trim_exec++ % afl->stats_update_freq)) show_stats(afl);

View File

@ -1612,15 +1612,20 @@ custom_mutator_stage:
ck_read(fd, new_buf, target->len, target->fname); ck_read(fd, new_buf, target->len, target->fname);
close(fd); close(fd);
// TODO: clean up this mess. u8 *mutated_buf = NULL;
size_t mutated_size = afl->mutator->afl_custom_fuzz( size_t mutated_size = afl->mutator->afl_custom_fuzz(
afl->mutator->data, &out_buf, len, new_buf, target->len, max_seed_size); afl->mutator->data, out_buf, len, &mutated_buf, new_buf, target->len,
max_seed_size);
if (unlikely(mutated_size < 0))
FATAL("custom_fuzz returned %zd", mutated_size);
if (mutated_size > len) afl->out_size = mutated_size; if (mutated_size > len) afl->out_size = mutated_size;
if (mutated_size > 0) { if (mutated_size > 0) {
if (common_fuzz_stuff(afl, out_buf, (u32)mutated_size)) { if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) {
goto abandon_entry; goto abandon_entry;
@ -1726,8 +1731,22 @@ havoc_stage:
if (stacked_custom && rand_below(afl, 100) < stacked_custom_prob) { if (stacked_custom && rand_below(afl, 100) < stacked_custom_prob) {
temp_len = afl->mutator->afl_custom_havoc_mutation( u8 * custom_havoc_buf = NULL;
afl->mutator->data, &out_buf, temp_len, MAX_FILE); size_t new_len = afl->mutator->afl_custom_havoc_mutation(
afl->mutator->data, out_buf, temp_len, &custom_havoc_buf, MAX_FILE);
if (unlikely(new_len < 0))
FATAL("Error in custom_havoc (return %zd)", new_len);
if (likely(new_len > 0 && custom_havoc_buf)) {
temp_len = new_len;
if (out_buf != custom_havoc_buf) {
ck_maybe_grow(BUF_PARAMS(out), temp_len);
memcpy(out_buf, custom_havoc_buf, temp_len);
}
}
} }

View File

@ -35,15 +35,22 @@ static void *unsupported(afl_state_t *afl, unsigned int seed) {
} }
size_t fuzz_py(void *py_mutator, u8 **buf, size_t buf_size, u8 *add_buf, /* sorry for this makro...
size_t add_buf_size, size_t max_size) { it just filles in `&py_mutator->something_buf, &py_mutator->something_size`. */
#define BUF_PARAMS(name) \
(void **)&((py_mutator_t *)py_mutator)->name##_buf, \
&((py_mutator_t *)py_mutator)->name##_size
size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf,
u8 *add_buf, size_t add_buf_size, 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);
py_mutator_t *py = (py_mutator_t *)py_mutator;
/* 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);
@ -79,17 +86,17 @@ size_t fuzz_py(void *py_mutator, u8 **buf, size_t buf_size, u8 *add_buf,
PyTuple_SetItem(py_args, 2, py_value); PyTuple_SetItem(py_args, 2, py_value);
py_value = PyObject_CallObject( py_value = PyObject_CallObject(py->py_functions[PY_FUNC_FUZZ], py_args);
((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_FUZZ], py_args);
Py_DECREF(py_args); Py_DECREF(py_args);
if (py_value != NULL) { if (py_value != NULL) {
mutated_size = PyByteArray_Size(py_value); 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); *out_buf = ck_maybe_grow(BUF_PARAMS(fuzz), mutated_size);
memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size);
Py_DECREF(py_value); Py_DECREF(py_value);
return mutated_size; return mutated_size;
@ -364,14 +371,8 @@ size_t pre_save_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf) {
py_out_buf_size = PyByteArray_Size(py_value); py_out_buf_size = PyByteArray_Size(py_value);
if (py_out_buf_size > py->pre_save_size) { ck_maybe_grow((void **)&py->pre_save_buf, &py->pre_save_size,
py_out_buf_size);
/* Not enough space!
Let's resize our buf */
py->pre_save_buf = ck_realloc(py->pre_save_buf, py_out_buf_size);
py->pre_save_size = py_out_buf_size;
}
memcpy(py->pre_save_buf, PyByteArray_AsString(py_value), py_out_buf_size); memcpy(py->pre_save_buf, PyByteArray_AsString(py_value), py_out_buf_size);
Py_DECREF(py_value); Py_DECREF(py_value);
@ -465,9 +466,10 @@ u32 post_trim_py(void *py_mutator, u8 success) {
} }
void trim_py(void *py_mutator, u8 **out_buf, size_t *out_buf_size) { size_t trim_py(void *py_mutator, u8 **out_buf) {
PyObject *py_args, *py_value; PyObject *py_args, *py_value;
size_t ret;
py_args = PyTuple_New(0); py_args = PyTuple_New(0);
py_value = PyObject_CallObject( py_value = PyObject_CallObject(
@ -476,9 +478,9 @@ void trim_py(void *py_mutator, u8 **out_buf, size_t *out_buf_size) {
if (py_value != NULL) { if (py_value != NULL) {
*out_buf_size = PyByteArray_Size(py_value); ret = PyByteArray_Size(py_value);
*out_buf = malloc(*out_buf_size); *out_buf = ck_maybe_grow(BUF_PARAMS(trim), ret);
memcpy(*out_buf, PyByteArray_AsString(py_value), *out_buf_size); memcpy(*out_buf, PyByteArray_AsString(py_value), ret);
Py_DECREF(py_value); Py_DECREF(py_value);
} else { } else {
@ -488,17 +490,19 @@ void trim_py(void *py_mutator, u8 **out_buf, size_t *out_buf_size) {
} }
return ret;
} }
size_t havoc_mutation_py(void *py_mutator, u8 **buf, size_t buf_size, size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size,
size_t max_size) { u8 **out_buf, 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(2); py_args = PyTuple_New(2);
/* 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);
@ -532,9 +536,19 @@ size_t havoc_mutation_py(void *py_mutator, 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);
if (buf_size < mutated_size) *buf = ck_realloc(*buf, mutated_size); if (mutated_size <= buf_size) {
memcpy(*buf, PyByteArray_AsString(py_value), mutated_size); /* We reuse the input buf here. */
*out_buf = buf;
} else {
/* A new buf is needed... */
*out_buf = ck_maybe_grow(BUF_PARAMS(havoc), mutated_size);
}
memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size);
Py_DECREF(py_value); Py_DECREF(py_value);
return mutated_size; return mutated_size;
@ -680,5 +694,7 @@ void queue_new_entry_py(void *py_mutator, const u8 *filename_new_queue,
} }
#undef BUF_PARAMS
#endif /* USE_PYTHON */ #endif /* USE_PYTHON */

View File

@ -741,8 +741,13 @@ u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
if (afl->post_handler) { if (afl->post_handler) {
out_buf = afl->post_handler(out_buf, &len); u8 *post_buf = NULL;
if (!out_buf || !len) return 0;
size_t post_len =
afl->post_handler(afl->post_data, out_buf, len, &post_buf);
if (!post_buf || !post_len) return 0;
out_buf = post_buf;
len = post_len;
} }