diff --git a/test/Makefile b/test/Makefile index c896627e..f68557cc 100644 --- a/test/Makefile +++ b/test/Makefile @@ -11,7 +11,7 @@ else BENCH_LDFLAGS=-lrt endif -.PHONY: clean test valgrind +.PHONY: clean test valgrind fuzz_roundtrip fuzz_decode test: clean test_base64 benchmark ./test_base64 @@ -26,6 +26,12 @@ test_base64: test_base64.c codec_supported.o ../lib/libbase64.o benchmark: benchmark.c codec_supported.o ../lib/libbase64.o $(CC) $(CFLAGS) -o $@ $^ $(BENCH_LDFLAGS) +fuzz_roundtrip: fuzz_roundtrip.c codec_supported.o ../lib/libbase64.o + $(CC) $(CFLAGS) -o $@ $^ $(BENCH_LDFLAGS) -fsanitize=fuzzer + +fuzz_decode: fuzz_decode.c codec_supported.o ../lib/libbase64.o + $(CC) $(CFLAGS) -o $@ $^ $(BENCH_LDFLAGS) -fsanitize=fuzzer + ../%: make -C .. $* @@ -33,4 +39,4 @@ benchmark: benchmark.c codec_supported.o ../lib/libbase64.o $(CC) $(CFLAGS) -o $@ -c $< clean: - rm -f benchmark test_base64 *.o + rm -f benchmark test_base64 *.o fuzz_roundtrip fuzz_decode diff --git a/test/fuzz_decode.c b/test/fuzz_decode.c new file mode 100644 index 00000000..a5a552a5 --- /dev/null +++ b/test/fuzz_decode.c @@ -0,0 +1,35 @@ +#include "../include/libbase64.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "fuzz_helpers.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + const uint8_t *data_cursor = data; + int codec_flag = consume_codec_flags(&data_cursor, &size); + + char *decoded = malloc((size + 3) / 4 * 3); + size_t decoded_length = 0; + switch (base64_decode((char*) data_cursor, size, decoded, &decoded_length, + codec_flag)) { + case 0: + // We will very likely be passing in invalid inputs so this is ok. + break; + case 1: + // Everything worked! + break; + case -1: + // We may sometimes choose an unsupported codec so this is ok. + break; + default: + // This should never happen. + abort(); + } + free(decoded); + return 0; +} diff --git a/test/fuzz_helpers.h b/test/fuzz_helpers.h new file mode 100644 index 00000000..ef93165a --- /dev/null +++ b/test/fuzz_helpers.h @@ -0,0 +1,34 @@ +#include "../include/libbase64.h" +#include "string.h" +#include +#include + +static inline uint64_t consume_uint64(const uint8_t **data_ptr, size_t *size) { + uint64_t result = 0; + if (*size < sizeof(result)) { + return 0; + } + memcpy(&result, *data_ptr, sizeof(result)); + *data_ptr += sizeof(result); + *size -= sizeof(result); + return result; +} + +static inline int consume_codec_flags(const uint8_t **data_ptr, size_t *size) { + int valid_codec_flags[] = { + 0, + BASE64_FORCE_AVX2, + BASE64_FORCE_NEON32, + BASE64_FORCE_NEON64, + BASE64_FORCE_PLAIN, + BASE64_FORCE_SSSE3, + BASE64_FORCE_SSE41, + BASE64_FORCE_SSE42, + BASE64_FORCE_AVX, + BASE64_FORCE_AVX512, + }; + // Pick a flag from the valid codecs using the fuzzed data. + return valid_codec_flags[consume_uint64(data_ptr, size) % + (sizeof(valid_codec_flags) / + sizeof(valid_codec_flags[0]))]; +} diff --git a/test/fuzz_roundtrip.c b/test/fuzz_roundtrip.c new file mode 100644 index 00000000..21621111 --- /dev/null +++ b/test/fuzz_roundtrip.c @@ -0,0 +1,58 @@ +#include "../include/libbase64.h" +#include "fuzz_helpers.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + const uint8_t *data_cursor = data; + int codec_flag = consume_codec_flags(&data_cursor, &size); + + // Create a buffer at least 4/3 the size of the data to encode; + char *encoded = malloc((size + 2) / 3 * 4); + size_t encoded_length = 0; + base64_encode((char *)data_cursor, size, encoded, &encoded_length, + codec_flag); + + char *decoded = malloc((encoded_length + 3) / 4 * 3); + size_t decoded_length = 0; + switch (base64_decode(encoded, encoded_length, decoded, &decoded_length, + codec_flag)) { + case 0: + // When using automatic codec detection we should never get a decode failure + // due to invalid inputs as we encoded the data in the first place. + assert(codec_flag != 0); + break; + case 1: + // Everything worked! + if (decoded_length != size) { + free(encoded); + free(decoded); + printf( + "Decoded length: %zu is not the same as the origional length %zu.\n", + decoded_length, size); + abort(); + } + if (memcmp(decoded, data_cursor, decoded_length) != 0) { + free(encoded); + free(decoded); + printf("Roundtrip encode->decode data does not equal original.\n"); + abort(); + } + break; + case -1: + // We may sometimes choose an unsupported codec so this is ok. + break; + default: + // This should never happen. + abort(); + } + free(encoded); + free(decoded); + return 0; +}