From 6d951f9e13cf9d6acab217164a06267582826ad4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 4 Apr 2024 21:14:01 -0400 Subject: [PATCH 1/2] scripts/libFuzzer: do not rely on system libdeflate.h --- scripts/libFuzzer/fuzz.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/libFuzzer/fuzz.sh b/scripts/libFuzzer/fuzz.sh index c5b00a67..b29bebc8 100755 --- a/scripts/libFuzzer/fuzz.sh +++ b/scripts/libFuzzer/fuzz.sh @@ -103,6 +103,6 @@ if [ ! -e "$TARGET/fuzz.c" ]; then exit 1 fi run_cmd clang -g -O1 -fsanitize=fuzzer$EXTRA_SANITIZERS \ - -Wall -Werror -DLIBDEFLATE_ENABLE_ASSERTIONS=1 \ + -Wall -Werror -DLIBDEFLATE_ENABLE_ASSERTIONS=1 -I ../../ \ ../../lib/*{,/*}.c "$TARGET/fuzz.c" -o "$TARGET/fuzz" run_cmd "$TARGET/fuzz" "${EXTRA_FUZZER_ARGS[@]}" "$TARGET/corpus" From 5ccf250ea5ca51b5f8440baf048685efd90f04f1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 4 Apr 2024 21:14:01 -0400 Subject: [PATCH 2/2] scripts/libFuzzer: use guard pages to always catch overflows --- scripts/libFuzzer/deflate_compress/fuzz.c | 63 +++++++++++++++++---- scripts/libFuzzer/deflate_decompress/fuzz.c | 51 +++++++++++++++-- 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/scripts/libFuzzer/deflate_compress/fuzz.c b/scripts/libFuzzer/deflate_compress/fuzz.c index 92cfc5b1..f1455df4 100644 --- a/scripts/libFuzzer/deflate_compress/fuzz.c +++ b/scripts/libFuzzer/deflate_compress/fuzz.c @@ -4,6 +4,36 @@ #include #include #include +#include +#include + +static void +alloc_guarded_buffer(size_t size, uint8_t **start_ret, uint8_t **end_ret) +{ + const size_t pagesize = sysconf(_SC_PAGESIZE); + const size_t nr_pages = (size + pagesize - 1) / pagesize; + uint8_t *base_addr, *start, *end; + + /* Allocate buffer and guard pages. */ + base_addr = mmap(NULL, (nr_pages + 2) * pagesize, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + assert(base_addr != (uint8_t *)MAP_FAILED); + start = base_addr + pagesize; + end = start + (nr_pages * pagesize); + + /* Unmap the guard pages. */ + munmap(base_addr, pagesize); + munmap(end, pagesize); + + *start_ret = start; + *end_ret = end; +} + +static void +free_guarded_buffer(uint8_t *start, uint8_t *end) +{ + munmap(start, end - start); +} /* Fuzz the DEFLATE compression and decompression round trip. */ int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize) @@ -13,8 +43,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize) struct libdeflate_compressor *c; struct libdeflate_decompressor *d; size_t csize_avail; - uint8_t *cbuf; - uint8_t *decompressed; + uint8_t *ubuf_start, *ubuf_end, *ubuf; + uint8_t *cbuf_start, *cbuf_end, *cbuf; + uint8_t *dbuf_start, *dbuf_end, *dbuf; size_t csize; enum libdeflate_result res; @@ -29,24 +60,36 @@ int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize) c = libdeflate_alloc_compressor(level); d = libdeflate_alloc_decompressor(); + /* Use guard pages to make all input/output buffer overflows segfault */ + + alloc_guarded_buffer(insize, &ubuf_start, &ubuf_end); + ubuf = ubuf_end - insize; + memcpy(ubuf, in, insize); + csize_avail = use_bound ? libdeflate_deflate_compress_bound(c, insize) : insize; - cbuf = malloc(csize_avail); - decompressed = malloc(insize); + alloc_guarded_buffer(csize_avail, &cbuf_start, &cbuf_end); + cbuf = cbuf_end - csize_avail; + + alloc_guarded_buffer(insize, &dbuf_start, &dbuf_end); + dbuf = dbuf_end - insize; - csize = libdeflate_deflate_compress(c, in, insize, cbuf, csize_avail); + csize = libdeflate_deflate_compress(c, ubuf, insize, cbuf, csize_avail); if (csize != 0) { - res = libdeflate_deflate_decompress(d, cbuf, csize, decompressed, - insize, NULL); + assert(csize <= csize_avail); + memmove(cbuf_end - csize, cbuf, csize); + res = libdeflate_deflate_decompress(d, cbuf_end - csize, csize, + dbuf, insize, NULL); assert(res == LIBDEFLATE_SUCCESS); - assert(memcmp(in, decompressed, insize) == 0); + assert(memcmp(in, dbuf, insize) == 0); } else { assert(!use_bound); } libdeflate_free_compressor(c); libdeflate_free_decompressor(d); - free(cbuf); - free(decompressed); + free_guarded_buffer(ubuf_start, ubuf_end); + free_guarded_buffer(cbuf_start, cbuf_end); + free_guarded_buffer(dbuf_start, dbuf_end); return 0; } diff --git a/scripts/libFuzzer/deflate_decompress/fuzz.c b/scripts/libFuzzer/deflate_decompress/fuzz.c index 1eed01e3..842eb5cb 100644 --- a/scripts/libFuzzer/deflate_decompress/fuzz.c +++ b/scripts/libFuzzer/deflate_decompress/fuzz.c @@ -1,19 +1,62 @@ +#include #include +#include #include #include +#include +#include +#include + +static void +alloc_guarded_buffer(size_t size, uint8_t **start_ret, uint8_t **end_ret) +{ + const size_t pagesize = sysconf(_SC_PAGESIZE); + const size_t nr_pages = (size + pagesize - 1) / pagesize; + uint8_t *base_addr, *start, *end; + + /* Allocate buffer and guard pages. */ + base_addr = mmap(NULL, (nr_pages + 2) * pagesize, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + assert(base_addr != (uint8_t *)MAP_FAILED); + start = base_addr + pagesize; + end = start + (nr_pages * pagesize); + + /* Unmap the guard pages. */ + munmap(base_addr, pagesize); + munmap(end, pagesize); + + *start_ret = start; + *end_ret = end; +} + +static void +free_guarded_buffer(uint8_t *start, uint8_t *end) +{ + munmap(start, end - start); +} /* Fuzz DEFLATE decompression. */ int LLVMFuzzerTestOneInput(const uint8_t *in, size_t insize) { size_t outsize_avail = 3 * insize; - uint8_t *out; + uint8_t *cbuf_start, *cbuf_end, *cbuf; + uint8_t *dbuf_start, *dbuf_end, *dbuf; struct libdeflate_decompressor *d; - out = malloc(outsize_avail); + /* Use guard pages to make all input/output buffer overflows segfault */ + + alloc_guarded_buffer(insize, &cbuf_start, &cbuf_end); + cbuf = cbuf_end - insize; + memcpy(cbuf, in, insize); + + alloc_guarded_buffer(outsize_avail, &dbuf_start, &dbuf_end); + dbuf = dbuf_end - outsize_avail; d = libdeflate_alloc_decompressor(); - libdeflate_deflate_decompress(d, in, insize, out, outsize_avail, NULL); + libdeflate_deflate_decompress(d, cbuf, insize, dbuf, outsize_avail, + NULL); libdeflate_free_decompressor(d); - free(out); + free_guarded_buffer(cbuf_start, cbuf_end); + free_guarded_buffer(dbuf_start, dbuf_end); return 0; }