From d85a9b2baf277b73beb9a60fa39cac9308e4a88c Mon Sep 17 00:00:00 2001 From: Isaac Brodsky Date: Tue, 6 Sep 2022 09:53:52 -0700 Subject: [PATCH] Add fuzzer for internal algos.c functions --- CMakeLists.txt | 18 +++++++- src/apps/applib/include/aflHarness.h | 15 +++++-- src/apps/fuzzers/README.md | 19 +++++++-- src/apps/fuzzers/fuzzerInternalAlgos.c | 57 ++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 src/apps/fuzzers/fuzzerInternalAlgos.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c97e36ebe..b9d8d7587 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,15 @@ option(BUILD_GENERATORS "Build code generation applications." ON) # for afl++ is provided instead. option(ENABLE_LIBFUZZER "Build fuzzers with libFuzzer support." OFF) +# These options exist for integration with OSS-Fuzz, so that the fuzzer options +# can be passed through only to the fuzzer executables but not the H3 library, +# since passing those options to the library too may result in too many implementations +# of main in the fuzzer executables. +option(H3_FUZZER_NO_MAIN "Build fuzzers with no main." OFF) +mark_as_advanced(H3_FUZZER_NO_MAIN) +set(H3_FUZZER_EXTRA_OPTIONS "" CACHE STRING "Extra compilation options for fuzzers particularly.") +mark_as_advanced(H3_FUZZER_EXTRA_OPTIONS) + if(WIN32) # Use bash (usually from Git for Windows) for piping results set(SHELL bash -c) @@ -242,6 +251,7 @@ set(OTHER_SOURCE_FILES src/apps/fuzzers/fuzzerLocalIj.c src/apps/fuzzers/fuzzerPolygonToCells.c src/apps/fuzzers/fuzzerPolygonToCellsNoHoles.c + src/apps/fuzzers/fuzzerInternalAlgos.c src/apps/benchmarks/benchmarkPolygonToCells.c src/apps/benchmarks/benchmarkPolygon.c src/apps/benchmarks/benchmarkCellsToLinkedMultiPolygon.c @@ -464,9 +474,12 @@ if(BUILD_FUZZERS) macro(add_h3_fuzzer name srcfile) add_h3_executable(${name} ${srcfile} ${APP_SOURCE_FILES}) - if(ENABLE_LIBFUZZER) + if(ENABLE_LIBFUZZER OR H3_FUZZER_NO_MAIN) target_compile_definitions(${name} PRIVATE H3_USE_LIBFUZZER) endif() + if(H3_FUZZER_EXTRA_OPTIONS) + target_compile_options(${name} PRIVATE ${H3_FUZZER_EXTRA_OPTIONS}) + endif() add_dependencies(fuzzers ${name}) endmacro() @@ -487,6 +500,9 @@ if(BUILD_FUZZERS) add_h3_fuzzer(fuzzerLocalIj src/apps/fuzzers/fuzzerLocalIj.c) add_h3_fuzzer(fuzzerPolygonToCells src/apps/fuzzers/fuzzerPolygonToCells.c) add_h3_fuzzer(fuzzerPolygonToCellsNoHoles src/apps/fuzzers/fuzzerPolygonToCellsNoHoles.c) + if(ENABLE_REQUIRES_ALL_SYMBOLS) + add_h3_fuzzer(fuzzerInternalAlgos src/apps/fuzzers/fuzzerInternalAlgos.c) + endif() endif() if(BUILD_BENCHMARKS) diff --git a/src/apps/applib/include/aflHarness.h b/src/apps/applib/include/aflHarness.h index a5ba47e63..68d16a421 100644 --- a/src/apps/applib/include/aflHarness.h +++ b/src/apps/applib/include/aflHarness.h @@ -37,8 +37,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); int generateTestCase(const char *filename, size_t expectedSize) { FILE *fp = fopen(filename, "wb"); uint8_t zero = 0; - if (fwrite(&zero, sizeof(zero), expectedSize, fp) != expectedSize) { - error("Error writing\n"); + for (size_t i = 0; i < expectedSize; i += sizeof(zero)) { + if (fwrite(&zero, sizeof(zero), 1, fp) != 1) { + error("Error writing\n"); + } } fclose(fp); return 0; @@ -55,13 +57,18 @@ int generateTestCase(const char *filename, size_t expectedSize) { return generateTestCase(argv[2], expectedSize); \ } \ if (argc != 2) { \ - error("Should have one argument (test case file)\n"); \ + error( \ + "Should have one argument, test case file, or --generate " \ + "test_case_file\n"); \ } \ const char *filename = argv[1]; \ FILE *fp = fopen(filename, "rb"); \ + if (!fp) { \ + error("Error opening test case file\n"); \ + } \ uint8_t data[expectedSize]; \ if (fread(&data, expectedSize, 1, fp) != 1) { \ - error("Error reading\n"); \ + error("Error reading test case file\n"); \ } \ fclose(fp); \ return LLVMFuzzerTestOneInput(data, expectedSize); \ diff --git a/src/apps/fuzzers/README.md b/src/apps/fuzzers/README.md index f84fb27bf..33749ea3e 100644 --- a/src/apps/fuzzers/README.md +++ b/src/apps/fuzzers/README.md @@ -1,14 +1,16 @@ # Fuzzer harnesses for H3 This directory contains helper programs for testing the H3 library using the -''[American fuzzy lop](https://lcamtuf.coredump.cx/afl/)''/ -''[AFL++](https://github.com/AFLplusplus/AFLplusplus)'' or -''[libFuzzer](https://www.llvm.org/docs/LibFuzzer.html)'' fuzzers. +[American fuzzy lop](https://lcamtuf.coredump.cx/afl/)/ +[AFL++](https://github.com/AFLplusplus/AFLplusplus) or +[libFuzzer](https://www.llvm.org/docs/LibFuzzer.html) fuzzers. Fuzzing is a technique for discovering crashes and other edge cases in code such as the H3 core library. # Function coverage +The public API of H3 is covered in the following fuzzers: + | Function | File or status | -------- | -------------- | latLngToCell | [fuzzerLatLngToCell](./fuzzerLatLngToCell.c) @@ -59,6 +61,17 @@ such as the H3 core library. | cellToLocalIj | [fuzzerLocalIj](./fuzzerLocalIj.c) | localIjToCell | [fuzzerLocalIj](./fuzzerLocalIj.c) +## Internal function coverage + +In addition to the public API, the following internal functions of H3 are covered in fuzzers: + +| Function | File +| -------- | ---- +| h3NeighborRotations | [fuzzerInternalAlgos](./fuzzerInternalAlgos.c) +| directionForNeighbor | [fuzzerInternalAlgos](./fuzzerInternalAlgos.c) +| h3SetToVertexGraph | [fuzzerInternalAlgos](./fuzzerInternalAlgos.c) +| _vertexGraphToLinkedGeo | [fuzzerInternalAlgos](./fuzzerInternalAlgos.c) + # libFuzzer Usage libFuzzer is one of the supported fuzzing drivers. diff --git a/src/apps/fuzzers/fuzzerInternalAlgos.c b/src/apps/fuzzers/fuzzerInternalAlgos.c new file mode 100644 index 000000000..f70a43c35 --- /dev/null +++ b/src/apps/fuzzers/fuzzerInternalAlgos.c @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** @file + * @brief Fuzzer program for internal functions in algos.c + */ + +#include "aflHarness.h" +#include "algos.h" +#include "h3api.h" +#include "utility.h" + +typedef struct { + H3Index index; + Direction dir; + int rotations; + H3Index index2; +} inputArgs; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < sizeof(inputArgs)) { + return 0; + } + const inputArgs *args = (const inputArgs *)data; + + H3Index out; + int rotations = args->rotations; + h3NeighborRotations(args->index, args->dir, &rotations, &out); + + directionForNeighbor(args->index, args->index2); + + VertexGraph graph; + H3Index *h3Set = (H3Index *)data; + size_t inputSize = size / sizeof(H3Index); + H3Error err = h3SetToVertexGraph(h3Set, inputSize, &graph); + if (!err) { + LinkedGeoPolygon linkedGeoPolygon; + _vertexGraphToLinkedGeo(&graph, &linkedGeoPolygon); + H3_EXPORT(destroyLinkedMultiPolygon)(&linkedGeoPolygon); + destroyVertexGraph(&graph); + } + return 0; +} + +AFL_HARNESS_MAIN(sizeof(inputArgs));