mirror of
https://github.com/openssh/libopenssh
synced 2026-04-15 17:26:14 +00:00
Basic fuzzing framework
This commit is contained in:
committed by
Markus Friedl
parent
30c2bb48b9
commit
0bececbb60
@@ -1,7 +1,7 @@
|
||||
# $OpenBSD$
|
||||
|
||||
LIB= test_helper
|
||||
SRCS= test_helper.c
|
||||
SRCS= test_helper.c fuzz.c
|
||||
|
||||
DEBUGLIBS= no
|
||||
NOPROFILE= yes
|
||||
|
||||
255
unittests/test_helper/fuzz.c
Normal file
255
unittests/test_helper/fuzz.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/* $OpenBSD */
|
||||
/*
|
||||
* Copyright (c) 2011 Damien Miller <djm@mindrot.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Utility functions/framework for fuzz tests */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "test_helper.h"
|
||||
|
||||
struct fuzz {
|
||||
/* Fuzz method in use */
|
||||
int strategy;
|
||||
|
||||
/* Original seed data blob */
|
||||
void *seed;
|
||||
size_t slen;
|
||||
|
||||
/* Current working copy of seed with fuzz mutations applied */
|
||||
u_char *fuzzed;
|
||||
size_t flen;
|
||||
|
||||
/* Used by fuzz methods */
|
||||
size_t o1, o2;
|
||||
};
|
||||
|
||||
void
|
||||
fuzz_dump(struct fuzz *fuzz)
|
||||
{
|
||||
u_char *p = fuzz_buf(fuzz);
|
||||
size_t i, j, len = fuzz_len(fuzz);
|
||||
|
||||
switch (fuzz->strategy) {
|
||||
case FUZZ_1_BIT_FLIP:
|
||||
fprintf(stderr, "FUZZ_1_BIT_FLIP case %zu of %zu\n",
|
||||
fuzz->o1, fuzz->flen * 8);
|
||||
break;
|
||||
case FUZZ_2_BIT_FLIP:
|
||||
fprintf(stderr, "FUZZ_2_BIT_FLIP case %llu of %llu\n",
|
||||
((unsigned long long)fuzz->o1) * fuzz->o2,
|
||||
((unsigned long long)fuzz->flen * 8) * fuzz->flen * 8);
|
||||
break;
|
||||
case FUZZ_1_BYTE_FLIP:
|
||||
fprintf(stderr, "FUZZ_1_BYTE_FLIP case %zu of %zu\n",
|
||||
fuzz->o1, fuzz->flen);
|
||||
break;
|
||||
case FUZZ_2_BYTE_FLIP:
|
||||
fprintf(stderr, "FUZZ_2_BYTE_FLIP case %llu of %llu\n",
|
||||
((unsigned long long)fuzz->o1) * fuzz->o2,
|
||||
((unsigned long long)fuzz->flen) * fuzz->flen);
|
||||
break;
|
||||
case FUZZ_TRUNCATE_START:
|
||||
fprintf(stderr, "FUZZ_TRUNCATE_START case %zu of %zu\n",
|
||||
fuzz->o1, fuzz->flen);
|
||||
break;
|
||||
case FUZZ_TRUNCATE_END:
|
||||
fprintf(stderr, "FUZZ_TRUNCATE_END case %zu of %zu\n",
|
||||
fuzz->o1, fuzz->flen);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, len);
|
||||
for (i = 0; i < len; i += 16) {
|
||||
fprintf(stderr, "%.4zd: ", i);
|
||||
for (j = i; j < i + 16; j++) {
|
||||
if (j < len)
|
||||
fprintf(stderr, "%02x ", p[j]);
|
||||
else
|
||||
fprintf(stderr, " ");
|
||||
}
|
||||
fprintf(stderr, " ");
|
||||
for (j = i; j < i + 16; j++) {
|
||||
if (j < len) {
|
||||
if (isascii(p[j]) && isprint(p[j]))
|
||||
fprintf(stderr, "%c", p[j]);
|
||||
else
|
||||
fprintf(stderr, ".");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct fuzz *
|
||||
fuzz_begin(int strategy, void *p, size_t l)
|
||||
{
|
||||
struct fuzz *ret = calloc(sizeof(ret), 1);
|
||||
|
||||
assert(ret != NULL);
|
||||
ret->strategy = strategy;
|
||||
ret->seed = p;
|
||||
ret->slen = l;
|
||||
|
||||
switch (ret->strategy) {
|
||||
case FUZZ_1_BIT_FLIP:
|
||||
case FUZZ_2_BIT_FLIP:
|
||||
assert(ret->slen < SIZE_MAX / 8);
|
||||
/* FALLTHROUGH */
|
||||
case FUZZ_1_BYTE_FLIP:
|
||||
case FUZZ_2_BYTE_FLIP:
|
||||
case FUZZ_TRUNCATE_START:
|
||||
case FUZZ_TRUNCATE_END:
|
||||
ret->fuzzed = calloc(ret->slen, 1);
|
||||
assert(ret->fuzzed != NULL);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
fuzz_next(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
fuzz_next(struct fuzz *fuzz)
|
||||
{
|
||||
switch (fuzz->strategy) {
|
||||
case FUZZ_1_BIT_FLIP:
|
||||
assert(fuzz->flen == fuzz->slen);
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
assert(fuzz->o1 / 8 < fuzz->flen);
|
||||
memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen);
|
||||
fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
|
||||
fuzz->o1++;
|
||||
break;
|
||||
case FUZZ_2_BIT_FLIP:
|
||||
assert(fuzz->flen == fuzz->slen);
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
assert(fuzz->o1 / 8 < fuzz->flen);
|
||||
assert(fuzz->o2 / 8 < fuzz->flen);
|
||||
memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen);
|
||||
fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
|
||||
fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8);
|
||||
fuzz->o1++;
|
||||
if (fuzz->o1 >= fuzz->flen * 8) {
|
||||
fuzz->o1 = 0;
|
||||
fuzz->o2++;
|
||||
}
|
||||
break;
|
||||
case FUZZ_1_BYTE_FLIP:
|
||||
assert(fuzz->flen == fuzz->slen);
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
assert(fuzz->o1 < fuzz->flen);
|
||||
memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen);
|
||||
fuzz->fuzzed[fuzz->o1] ^= 0xff;
|
||||
fuzz->o1++;
|
||||
break;
|
||||
case FUZZ_2_BYTE_FLIP:
|
||||
assert(fuzz->flen == fuzz->slen);
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
assert(fuzz->o1 < fuzz->flen);
|
||||
assert(fuzz->o2 < fuzz->flen);
|
||||
memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen);
|
||||
fuzz->fuzzed[fuzz->o1] ^= 0xff;
|
||||
fuzz->fuzzed[fuzz->o2] ^= 0xff;
|
||||
if (fuzz->o1 >= fuzz->flen) {
|
||||
fuzz->o1 = 0;
|
||||
fuzz->o2++;
|
||||
}
|
||||
break;
|
||||
case FUZZ_TRUNCATE_START:
|
||||
case FUZZ_TRUNCATE_END:
|
||||
assert(fuzz->flen == fuzz->slen);
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
assert(fuzz->o1 < fuzz->flen);
|
||||
memcpy(fuzz->fuzzed, fuzz->seed, fuzz->flen);
|
||||
fuzz->o1++;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fuzz_done(struct fuzz *fuzz)
|
||||
{
|
||||
switch (fuzz->strategy) {
|
||||
case FUZZ_1_BIT_FLIP:
|
||||
return fuzz->o1 >= fuzz->flen * 8;
|
||||
case FUZZ_2_BIT_FLIP:
|
||||
return fuzz->o2 >= fuzz->flen * 8;
|
||||
case FUZZ_1_BYTE_FLIP:
|
||||
return fuzz->o1 >= fuzz->flen;
|
||||
case FUZZ_2_BYTE_FLIP:
|
||||
return fuzz->o2 >= fuzz->flen;
|
||||
case FUZZ_TRUNCATE_START:
|
||||
case FUZZ_TRUNCATE_END:
|
||||
return fuzz->o1 >= fuzz->flen;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
fuzz_len(struct fuzz *fuzz)
|
||||
{
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
switch (fuzz->strategy) {
|
||||
case FUZZ_1_BIT_FLIP:
|
||||
case FUZZ_2_BIT_FLIP:
|
||||
case FUZZ_1_BYTE_FLIP:
|
||||
case FUZZ_2_BYTE_FLIP:
|
||||
return fuzz->flen;
|
||||
case FUZZ_TRUNCATE_START:
|
||||
case FUZZ_TRUNCATE_END:
|
||||
assert(fuzz->o1 < fuzz->flen);
|
||||
return fuzz->flen - fuzz->o1;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
u_char *
|
||||
fuzz_buf(struct fuzz *fuzz)
|
||||
{
|
||||
assert(fuzz->fuzzed != NULL);
|
||||
switch (fuzz->strategy) {
|
||||
case FUZZ_1_BIT_FLIP:
|
||||
case FUZZ_2_BIT_FLIP:
|
||||
case FUZZ_1_BYTE_FLIP:
|
||||
case FUZZ_2_BYTE_FLIP:
|
||||
return fuzz->fuzzed;
|
||||
case FUZZ_TRUNCATE_START:
|
||||
assert(fuzz->o1 < fuzz->flen);
|
||||
return fuzz->fuzzed + fuzz->o1;
|
||||
case FUZZ_TRUNCATE_END:
|
||||
assert(fuzz->o1 < fuzz->flen);
|
||||
return fuzz->fuzzed;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,4 +255,35 @@ void assert_u64(const char *file, int line,
|
||||
#define ASSERT_U64_GE(a1, a2) \
|
||||
assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
|
||||
|
||||
/* Fuzzing support */
|
||||
|
||||
struct fuzz;
|
||||
enum fuzz_strategy {
|
||||
FUZZ_1_BIT_FLIP, /* Flip one bit at a time */
|
||||
FUZZ_2_BIT_FLIP, /* Flip two bits at a time */
|
||||
FUZZ_1_BYTE_FLIP, /* Flip one byte at a time */
|
||||
FUZZ_2_BYTE_FLIP, /* Flip two bytes at a time */
|
||||
FUZZ_TRUNCATE_START, /* Truncate from beginning */
|
||||
FUZZ_TRUNCATE_END, /* Truncate from end */
|
||||
FUZZ_MAX
|
||||
};
|
||||
|
||||
/* Start fuzzing a blob of data */
|
||||
struct fuzz *fuzz_begin(int strategy, void *p, size_t l);
|
||||
|
||||
/* Free a fuzz context */
|
||||
void fuzz_cleanup(struct fuzz *fuzz);
|
||||
|
||||
/* Prepare the next fuzz case in the series */
|
||||
void fuzz_next(struct fuzz *fuzz);
|
||||
|
||||
/* Determine whether the current fuzz sequence is exhausted (nonzero = yes) */
|
||||
int fuzz_done(struct fuzz *fuzz);
|
||||
|
||||
/* Return the length and a pointer to the current fuzzed case */
|
||||
size_t fuzz_len(struct fuzz *fuzz);
|
||||
u_char *fuzz_buf(struct fuzz *fuzz);
|
||||
|
||||
/* Dump the current fuzz case to stderr */
|
||||
void fuzz_dump(struct fuzz *fuzz);
|
||||
#endif /* _TEST_HELPER_H */
|
||||
|
||||
Reference in New Issue
Block a user