fix custom mutator C examples

This commit is contained in:
vanhauser-thc
2023-04-15 10:12:20 +02:00
parent 8f6d9d66ef
commit e12acaa203
7 changed files with 73 additions and 499 deletions

View File

@ -1,342 +0,0 @@
#ifndef CUSTOM_MUTATOR_HELPERS
#define CUSTOM_MUTATOR_HELPERS
#include "config.h"
#include "types.h"
#include <stdlib.h>
#define INITIAL_GROWTH_SIZE (64)
#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 fills in `&structptr->something_buf, &structptr->something_size`. */
#define BUF_PARAMS(struct, name) \
(void **)&struct->name##_buf, &struct->name##_size
typedef struct {
} afl_t;
static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
static s8 interesting_8[] = {INTERESTING_8};
static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
switch (RAND_BELOW(12)) {
case 0: {
/* Flip a single bit somewhere. Spooky! */
s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8);
out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7);
break;
}
case 1: {
/* Set byte to interesting value. */
u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))];
out_buf[(RAND_BELOW(end - begin) + begin)] = val;
break;
}
case 2: {
/* Set word to interesting value, randomly choosing endian. */
if (end - begin < 2) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 1) break;
switch (RAND_BELOW(2)) {
case 0:
*(u16 *)(out_buf + byte_idx) =
interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)];
break;
case 1:
*(u16 *)(out_buf + byte_idx) =
SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]);
break;
}
break;
}
case 3: {
/* Set dword to interesting value, randomly choosing endian. */
if (end - begin < 4) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 3) break;
switch (RAND_BELOW(2)) {
case 0:
*(u32 *)(out_buf + byte_idx) =
interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
break;
case 1:
*(u32 *)(out_buf + byte_idx) =
SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
break;
}
break;
}
case 4: {
/* Set qword to interesting value, randomly choosing endian. */
if (end - begin < 8) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 7) break;
switch (RAND_BELOW(2)) {
case 0:
*(u64 *)(out_buf + byte_idx) =
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
break;
case 1:
*(u64 *)(out_buf + byte_idx) = SWAP64(
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
break;
}
break;
}
case 5: {
/* Randomly subtract from byte. */
out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX);
break;
}
case 6: {
/* Randomly add to byte. */
out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX);
break;
}
case 7: {
/* Randomly subtract from word, random endian. */
if (end - begin < 2) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 1) break;
if (RAND_BELOW(2)) {
*(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
} else {
u16 num = 1 + RAND_BELOW(ARITH_MAX);
*(u16 *)(out_buf + byte_idx) =
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num);
}
break;
}
case 8: {
/* Randomly add to word, random endian. */
if (end - begin < 2) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 1) break;
if (RAND_BELOW(2)) {
*(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
} else {
u16 num = 1 + RAND_BELOW(ARITH_MAX);
*(u16 *)(out_buf + byte_idx) =
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num);
}
break;
}
case 9: {
/* Randomly subtract from dword, random endian. */
if (end - begin < 4) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 3) break;
if (RAND_BELOW(2)) {
*(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
} else {
u32 num = 1 + RAND_BELOW(ARITH_MAX);
*(u32 *)(out_buf + byte_idx) =
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num);
}
break;
}
case 10: {
/* Randomly add to dword, random endian. */
if (end - begin < 4) break;
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
if (byte_idx >= end - 3) break;
if (RAND_BELOW(2)) {
*(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
} else {
u32 num = 1 + RAND_BELOW(ARITH_MAX);
*(u32 *)(out_buf + byte_idx) =
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num);
}
break;
}
case 11: {
/* Just set a random byte to a random value. Because,
why not. We use XOR with 1-255 to eliminate the
possibility of a no-op. */
out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255);
break;
}
}
}
/* This function calculates the next power of 2 greater or equal its argument.
@return The rounded up power of 2 (if no overflow) or 0 on overflow.
*/
static inline size_t next_pow2(size_t in) {
if (in == 0 || in > (size_t)-1)
return 0; /* avoid undefined behaviour under-/overflow */
size_t out = in - 1;
out |= out >> 1;
out |= out >> 2;
out |= out >> 4;
out |= out >> 8;
out |= out >> 16;
return out + 1;
}
/* This function makes sure *size is > size_needed after call.
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 and free *buf if size_needed is <1 or realloc failed.
@return For convenience, this function returns *buf.
*/
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
/* No need to realloc */
if (likely(size_needed && *size >= size_needed)) return *buf;
/* No initial size was set */
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
/* grow exponentially */
size_t next_size = next_pow2(size_needed);
/* handle overflow */
if (!next_size) { next_size = size_needed; }
/* alloc */
*buf = realloc(*buf, next_size);
*size = *buf ? next_size : 0;
return *buf;
}
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
static inline void afl_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

View File

@ -10,21 +10,21 @@
// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo
//
#include "custom_mutator_helpers.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "afl-fuzz.h"
typedef struct my_mutator {
afl_t *afl;
afl_state_t *afl;
} my_mutator_t;
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
if (!data) {

View File

@ -7,7 +7,7 @@
*/
// You need to use -I/path/to/AFLplusplus/include -I.
#include "custom_mutator_helpers.h"
#include "afl-fuzz.h"
#include <stdint.h>
#include <stdlib.h>
@ -26,19 +26,14 @@ static const char *commands[] = {
typedef struct my_mutator {
afl_t *afl;
afl_state_t *afl;
// any additional data here!
size_t trim_size_current;
int trimmming_steps;
int cur_step;
// Reused buffers:
BUF_VAR(u8, fuzz);
BUF_VAR(u8, data);
BUF_VAR(u8, havoc);
BUF_VAR(u8, trim);
BUF_VAR(u8, post_process);
u8 *mutated_out, *post_process_buf, *trim_buf;
} my_mutator_t;
@ -53,7 +48,7 @@ typedef struct my_mutator {
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed); // needed also by surgical_havoc_mutate()
@ -65,6 +60,27 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
}
if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
perror("afl_custom_init malloc");
return NULL;
}
data->afl = afl;
return data;
@ -96,31 +112,14 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
// the fuzzer
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_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) {
*out_buf = NULL;
perror("custom mutator allocation (maybe_grow)");
return 0; /* afl-fuzz will very likely error out after this. */
}
memcpy(data->mutated_out, buf, buf_size);
// Randomly select a command string to add as a header to the packet
memcpy(mutated_out, commands[rand() % 3], 3);
memcpy(data->mutated_out, commands[rand() % 3], 3);
// Mutate the payload of the packet
int i;
for (i = 0; i < 8; ++i) {
if (mutated_size > max_size) { mutated_size = max_size; }
// Randomly perform one of the (no len modification) havoc mutations
surgical_havoc_mutate(mutated_out, 3, mutated_size);
}
if (max_size > mutated_size) { mutated_size = max_size; }
*out_buf = mutated_out;
*out_buf = data->mutated_out;
return mutated_size;
}
@ -144,24 +143,16 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf,
size_t buf_size, uint8_t **out_buf) {
uint8_t *post_process_buf =
maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
if (!post_process_buf) {
if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; }
perror("custom mutator realloc failed.");
*out_buf = NULL;
return 0;
memcpy(data->post_process_buf + 5, buf, buf_size);
data->post_process_buf[0] = 'A';
data->post_process_buf[1] = 'F';
data->post_process_buf[2] = 'L';
data->post_process_buf[3] = '+';
data->post_process_buf[4] = '+';
}
memcpy(post_process_buf + 5, buf, buf_size);
post_process_buf[0] = 'A';
post_process_buf[1] = 'F';
post_process_buf[2] = 'L';
post_process_buf[3] = '+';
post_process_buf[4] = '+';
*out_buf = post_process_buf;
*out_buf = data->post_process_buf;
return buf_size + 5;
@ -197,13 +188,6 @@ int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
data->cur_step = 0;
if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) {
perror("init_trim grow");
return -1;
}
memcpy(data->trim_buf, buf, buf_size);
data->trim_size_current = buf_size;
@ -284,27 +268,11 @@ int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
u8 **out_buf, size_t max_size) {
if (buf_size == 0) {
*out_buf = buf; // in-place mutation
*out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1);
if (!*out_buf) {
if (buf_size <= sizeof(size_t)) { return buf_size; }
perror("custom havoc: maybe_grow");
return 0;
}
**out_buf = rand() % 256;
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 - sizeof(size_t));
(*out_buf)[victim] += rand() % 10;
return buf_size;
@ -371,9 +339,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t *data,
void afl_custom_deinit(my_mutator_t *data) {
free(data->post_process_buf);
free(data->havoc_buf);
free(data->data_buf);
free(data->fuzz_buf);
free(data->mutated_out);
free(data->trim_buf);
free(data);

View File

@ -45,9 +45,8 @@
1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
and return the original `len`.
NOTE: the following is currently NOT true, we abort in this case!
2) If you want to skip this test case altogether and have AFL generate a
new one, return 0 or set `*out_buf = NULL`.
new one, return 0.
Use this sparingly - it's faster than running the target program
with patently useless inputs, but still wastes CPU time.
@ -59,8 +58,6 @@
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).
*** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
Alright. The example below shows a simple postprocessor that tries to make
sure that all input files start with "GIF89a".
@ -72,7 +69,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alloc-inl.h"
#include "afl-fuzz.h"
/* Header that must be present at the beginning of every test case: */
@ -80,8 +77,7 @@
typedef struct post_state {
unsigned char *buf;
size_t size;
size_t size;
} post_state_t;
@ -95,15 +91,6 @@ void *afl_custom_init(void *afl) {
}
state->buf = calloc(sizeof(unsigned char), 4096);
if (!state->buf) {
free(state);
perror("calloc");
return NULL;
}
return state;
}
@ -113,6 +100,10 @@ void *afl_custom_init(void *afl) {
size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
unsigned int len, unsigned char **out_buf) {
/* we do in-place modification as we do not increase the size */
*out_buf = in_buf;
/* Skip execution altogether for buffers shorter than 6 bytes (just to
show how it's done). We can trust len to be sane. */
@ -120,34 +111,7 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
/* Do nothing for buffers that already start with the expected header. */
if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
*out_buf = in_buf;
return len;
}
/* Allocate memory for new buffer, reusing previous allocation if
possible. Note we have to use afl-fuzz's own realloc!
We use afl_realloc because it is effective.
You can also work within in_buf, and assign it to *out_buf. */
*out_buf = afl_realloc(out_buf, len);
/* 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
own later on. */
if (!*out_buf) {
*out_buf = in_buf;
return len;
}
if (len > strlen(HEADER))
memcpy(*out_buf + strlen(HEADER), in_buf + strlen(HEADER),
len - strlen(HEADER));
if (!memcmp(in_buf, HEADER, strlen(HEADER))) { return len; }
/* Insert the new header. */
@ -162,7 +126,6 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
/* Gets called afterwards */
void afl_custom_deinit(post_state_t *data) {
free(data->buf);
free(data);
}

View File

@ -30,7 +30,7 @@
#include <string.h>
#include <zlib.h>
#include <arpa/inet.h>
#include "alloc-inl.h"
#include "afl-fuzz.h"
/* A macro to round an integer up to 4 kB. */
@ -53,7 +53,7 @@ void *afl_custom_init(void *afl) {
}
state->buf = calloc(sizeof(unsigned char), 4096);
state->buf = calloc(sizeof(unsigned char), MAX_FILE);
if (!state->buf) {
free(state);
@ -80,21 +80,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
}
/* This is not a good way to do it, if you do not need to grow the buffer
then just work with in_buf instead for speed reasons.
But we want to show how to grow a buffer, so this is how it's done: */
unsigned int pos = 8;
unsigned char *new_buf = afl_realloc(out_buf, UP4K(len));
if (!new_buf) {
*out_buf = in_buf;
return len;
}
memcpy(new_buf, in_buf, len);
unsigned int pos = 8;
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we
don't have that, we can bail out. */
@ -124,7 +110,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
if (real_cksum != file_cksum) {
*(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum;
*(uint32_t *)(data->buf + pos + 8 + chunk_len) = real_cksum;
}
@ -134,7 +120,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
}
*out_buf = new_buf;
*out_buf = data->buf;
return len;
}

View File

@ -1,6 +1,6 @@
// This simple example just creates random buffer <= 100 filled with 'A'
// needs -I /path/to/AFLplusplus/include
#include "custom_mutator_helpers.h"
#include "afl-fuzz.h"
#include <stdint.h>
#include <stdlib.h>
@ -13,14 +13,14 @@
typedef struct my_mutator {
afl_t *afl;
afl_state_t *afl;
// Reused buffers:
BUF_VAR(u8, fuzz);
u8 *fuzz_buf;
} my_mutator_t;
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed);
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
@ -31,6 +31,14 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
}
data->fuzz_buf = (u8 *)malloc(MAX_FILE);
if (!data->fuzz_buf) {
perror("afl_custom_init malloc");
return NULL;
}
data->afl = afl;
return data;
@ -44,18 +52,10 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
int size = (rand() % 100) + 1;
if (size > max_size) size = max_size;
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
if (!mutated_out) {
*out_buf = NULL;
perror("custom mutator allocation (maybe_grow)");
return 0; /* afl-fuzz will very likely error out after this. */
memset(data->fuzz_buf, _FIXED_CHAR, size);
}
memset(mutated_out, _FIXED_CHAR, size);
*out_buf = mutated_out;
*out_buf = data->fuzz_buf;
return size;
}

View File

@ -30,6 +30,7 @@
- unicorn_mode:
- updated and minor issues fixed
- new custom module: autotoken, a grammar free fuzzer for text inputs
- fixed custom mutator C examples
- better sanitizer default options support for all tools
- more minor fixes and cross-platform support