Skip to content

Commit

Permalink
Strip down fuzz test code and simplify.
Browse files Browse the repository at this point in the history
Genetic algorithm stuff is unnecessary now that the bit stream reader
bug has been fixed. Just generate a block of random data and feed it
into the decompressor.
  • Loading branch information
fragglet committed Apr 9, 2012
1 parent 2d6573a commit ab82546
Showing 1 changed file with 29 additions and 175 deletions.
204 changes: 29 additions & 175 deletions test/fuzzer.c
Expand Up @@ -20,13 +20,7 @@ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// Fuzz testing system for stress-testing the decompressors.
// This works by repeatedly generating new random streams of
// data and feeding them to the decompressor. This on its own
// works for the majority of decompressors (-lh1-, -lh5-, etc.)
//
// Some decompressors (eg -pm2-) are more particular about
// input checking, and will fail if given random data. To
// cope with these, a genetic algorithm is used to generate
// progressively longer valid input streams.
// data and feeding them to the decompressor.

#include <stdio.h>
#include <stdlib.h>
Expand All @@ -37,19 +31,14 @@ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include "lha_decoder.h"

// Maximum signature length before stopping.
// Maximum amount of data to read before stopping.

#define MAX_FUZZ_LEN 2000000
#define MAX_FUZZ_LEN (2 * 1024 * 1024)

typedef struct {
uint8_t *data;
unsigned int data_len;
} FuzzSignature;

typedef struct {
FuzzSignature *signature;
size_t data_len;
unsigned int read;
unsigned int max_len;
} ReadCallbackData;

// Contents of "canary buffer" that is put around allocated blocks to
Expand Down Expand Up @@ -113,117 +102,23 @@ static void fuzz_block(uint8_t *data, unsigned int data_len)
}
}

// Create an empty signature.

static FuzzSignature *empty_signature(void)
{
FuzzSignature *result;

result = malloc(sizeof(FuzzSignature));
assert(result != NULL);
result->data = NULL;
result->data_len = 0;

return result;
}

// Copy a signature to create a new one.

static FuzzSignature *dup_signature(FuzzSignature *signature)
{
FuzzSignature *result;

result = malloc(sizeof(FuzzSignature));
assert(result != NULL);
result->data = malloc(signature->data_len);
assert(result->data != NULL);
memcpy(result->data, signature->data, signature->data_len);
result->data_len = signature->data_len;

return result;
}

// Create a new "child" signature, derived from an existing
// signature with the last few bytes changed.

static FuzzSignature *child_signature(FuzzSignature *signature,
unsigned int nbytes)
{
FuzzSignature *child;

child = dup_signature(signature);

if (nbytes > child->data_len) {
nbytes = child->data_len;
}

fuzz_block(child->data + child->data_len - nbytes, nbytes);

return child;
}

// Free a signature.

static void free_signature(FuzzSignature *signature)
{
if (signature != NULL) {
free(signature->data);
free(signature);
}
}

// "Extend" a signature, adding some more random data to the end.

static void extend_signature(FuzzSignature *signature)
{
unsigned int new_len;

new_len = signature->data_len + 16;
signature->data = realloc(signature->data, new_len);
assert(signature->data != NULL);

fuzz_block(signature->data + signature->data_len, 16);
signature->data_len = new_len;
}

static void *init_decoder(LHADecoderType *dtype,
LHADecoderCallback read_callback,
void *callback_data)
{
void *result;

result = canary_malloc(dtype->extra_size);

assert(dtype->init(result, read_callback, callback_data));

return result;
}

// Callback function used to read more data from the signature being
// processed. If the end of the signature is reached, new data is
// randomly generated and the length of the signature extended.
// processed.

static size_t read_more_data(void *buf, size_t buf_len, void *user_data)
{
ReadCallbackData *cb_data = user_data;

// Limit how much input data is read, and return end of file
// when we hit the limit.
// Return end of file when we reach the end of the data.

if (cb_data->read >= cb_data->max_len) {
if (cb_data->read >= cb_data->data_len) {
return 0;
}

// If we reach the end of the signature, extend it.

if (cb_data->read >= cb_data->signature->data_len) {
extend_signature(cb_data->signature);
}

// Only copy a single byte at a time. This allows us to
// accurately track how much of the signature is valid.

memcpy(buf, cb_data->signature->data + cb_data->read, 1);
memcpy(buf, cb_data->data + cb_data->read, 1);
++cb_data->read;

return 1;
Expand All @@ -233,8 +128,8 @@ static size_t read_more_data(void *buf, size_t buf_len, void *user_data)
// of the specified type.

static unsigned int run_fuzz_test(LHADecoderType *dtype,
FuzzSignature *signature,
unsigned int max_len)
uint8_t *data,
size_t data_len)
{
ReadCallbackData cb_data;
uint8_t *read_buf;
Expand All @@ -243,11 +138,12 @@ static unsigned int run_fuzz_test(LHADecoderType *dtype,

// Init decoder.

cb_data.signature = signature;
cb_data.data = data;
cb_data.data_len = data_len;
cb_data.read = 0;
cb_data.max_len = max_len;

handle = init_decoder(dtype, read_more_data, &cb_data);
handle = canary_malloc(dtype->extra_size);
assert(dtype->init(handle, read_more_data, &cb_data));

// Create a buffer into which to decompress data.

Expand Down Expand Up @@ -280,70 +176,28 @@ static unsigned int run_fuzz_test(LHADecoderType *dtype,
return cb_data.read;
}

static void fuzz_test(LHADecoderType *dtype, unsigned int iterations,
unsigned int max_len)
static void fuzz_test(LHADecoderType *dtype, size_t data_len)
{
FuzzSignature *signature;
FuzzSignature *new_signature;
FuzzSignature *best;
unsigned int len;
unsigned int i;

signature = empty_signature();

for (; iterations > 0; --iterations) {
// Generate several new signatures, based on the current
// signature, but with the last few bytes changed.
// Then run each and see how far they get, cropping
// them at the number of bytes that are read. The best
// signature wins and becomes the new signature.

best = NULL;

for (i = 0; i < 4; ++i) {
new_signature = child_signature(signature, 4);
len = run_fuzz_test(dtype, new_signature, max_len);
new_signature->data_len = len;

if (len > signature->data_len
&& (best == NULL || len > best->data_len)) {
free_signature(best);
best = new_signature;
} else {
free_signature(new_signature);
}

if (len >= max_len) {
printf("\tReached limit.\n");
free_signature(best);
goto finished;
}
}
unsigned int count;
void *data;

// If one of the current signatures did better than
// the current signature, replace it.
// Generate a block of random data.

if (best != NULL) {
printf("\tNew signature: %i bytes\n", best->data_len);
data = malloc(data_len);
assert(data != NULL);
fuzz_block(data, data_len);

free_signature(signature);
signature = best;
}
// Run the decoder with the data as input.

// If none of the new signatures succeeded, there may
// be something in the existing signature preventing
// us getting any further. Back up a bit and try again.
count = run_fuzz_test(dtype, data, data_len);

else {
printf("\tFailed to generate new signature.\n");
if (signature->data_len >= 16) {
signature->data_len -= 16;
}
}
if (count >= data_len) {
printf("\tTest complete (end of file)\n");
} else {
printf("\tTest complete (read %i bytes)\n", count);
}

finished:
free_signature(signature);
free(data);
}

int main(int argc, char *argv[])
Expand All @@ -367,7 +221,7 @@ int main(int argc, char *argv[])

for (i = 0; ; ++i) {
printf("Iteration %i:\n", i);
fuzz_test(dtype, 100, MAX_FUZZ_LEN);
fuzz_test(dtype, MAX_FUZZ_LEN);
}

return 0;
Expand Down

0 comments on commit ab82546

Please sign in to comment.