diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 0000000..cb89ae7 --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,6 @@ +FROM gcr.io/oss-fuzz-base/base-builder +RUN apt-get update && apt-get install -y make autoconf automake libtool + +COPY . $SRC/fast_obj +COPY .clusterfuzzlite/build.sh $SRC/build.sh +WORKDIR $SRC/fast_obj \ No newline at end of file diff --git a/.clusterfuzzlite/README.md b/.clusterfuzzlite/README.md new file mode 100644 index 0000000..6bb7ef1 --- /dev/null +++ b/.clusterfuzzlite/README.md @@ -0,0 +1,3 @@ +# ClusterFuzzLite set up + +This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite). \ No newline at end of file diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100644 index 0000000..a11ead3 --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash -eu +$CXX $CFLAGS $LIB_FUZZING_ENGINE \ + $SRC/fast_obj/.clusterfuzzlite/fast_obj_fuzzer.cpp \ + -o $OUT/fast_obj_fuzzer \ + -I$SRC/fast_obj \ No newline at end of file diff --git a/.clusterfuzzlite/fast_obj_fuzzer.cpp b/.clusterfuzzlite/fast_obj_fuzzer.cpp new file mode 100644 index 0000000..a625db1 --- /dev/null +++ b/.clusterfuzzlite/fast_obj_fuzzer.cpp @@ -0,0 +1,27 @@ +#define FAST_OBJ_IMPLEMENTATION +#include "fast_obj.h" + +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char filename[256]; + sprintf(filename, "/tmp/libfuzzer.%d", getpid()); + + FILE *fp = fopen(filename, "wb"); + if (!fp) + return 0; + fwrite(data, size, 1, fp); + fclose(fp); + + fastObjMesh *m = fast_obj_read(filename); + if (!m) { + unlink(filename); + return 0; + } + + fast_obj_destroy(m); + unlink(filename); + return 0; +} \ No newline at end of file diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 0000000..7f563eb --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: c++ \ No newline at end of file diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml new file mode 100644 index 0000000..d0bce9d --- /dev/null +++ b/.github/workflows/cflite_pr.yml @@ -0,0 +1,30 @@ +name: ClusterFuzzLite PR fuzzing +on: + workflow_dispatch: + pull_request: + branches: [ main ] +permissions: read-all +jobs: + PR: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sanitizer: [address] + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + sanitizer: ${{ matrix.sanitizer }} + language: c++ + bad-build-check: false + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 100 + mode: 'code-change' + report-unreproducible-crashes: false + sanitizer: ${{ matrix.sanitizer }} \ No newline at end of file diff --git a/fast_obj.h b/fast_obj.h index 9fd9723..c283c61 100644 --- a/fast_obj.h +++ b/fast_obj.h @@ -65,6 +65,9 @@ typedef struct float d; /* Disolve (alpha) */ int illum; /* Illumination model */ + /* Set for materials that don't come from the associated mtllib */ + int fallback; + /* Texture map indices in fastObjMesh textures array */ unsigned int map_Ka; unsigned int map_Kd; @@ -127,6 +130,9 @@ typedef struct unsigned int normal_count; float* normals; + unsigned int color_count; + float* colors; + /* Face data: one element for each face */ unsigned int face_count; unsigned int* face_vertices; @@ -273,15 +279,14 @@ static void* array_realloc(void* ptr, fastObjUInt n, fastObjUInt b) fastObjUInt sz = array_size(ptr); fastObjUInt nsz = sz + n; fastObjUInt cap = array_capacity(ptr); - fastObjUInt ncap = 3 * cap / 2; + fastObjUInt ncap = cap + cap / 2; fastObjUInt* r; - if (ncap < nsz) ncap = nsz; ncap = (ncap + 15) & ~15u; - r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, b * ncap + 2 * sizeof(fastObjUInt))); + r = (fastObjUInt*)(memory_realloc(ptr ? _array_header(ptr) : 0, (size_t)b * ncap + 2 * sizeof(fastObjUInt))); if (!r) return 0; @@ -303,7 +308,7 @@ void* file_open(const char* path, void* user_data) static void file_close(void* file, void* user_data) { - FILE* f; + FILE* f; (void)(user_data); f = (FILE*)(file); @@ -314,7 +319,7 @@ void file_close(void* file, void* user_data) static size_t file_read(void* file, void* dst, size_t bytes, void* user_data) { - FILE* f; + FILE* f; (void)(user_data); f = (FILE*)(file); @@ -329,7 +334,7 @@ unsigned long file_size(void* file, void* user_data) long p; long n; (void)(user_data); - + f = (FILE*)(file); p = ftell(f); @@ -419,12 +424,6 @@ int is_whitespace(char c) return (c == ' ' || c == '\t' || c == '\r'); } -static -int is_end_of_name(char c) -{ - return (c == '\t' || c == '\r' || c == '\n'); -} - static int is_newline(char c) { @@ -446,6 +445,21 @@ int is_exponent(char c) } +static +const char* skip_name(const char* ptr) +{ + const char* s = ptr; + + while (!is_newline(*ptr)) + ptr++; + + while (ptr > s && is_whitespace(*(ptr - 1))) + ptr--; + + return ptr; +} + + static const char* skip_whitespace(const char* ptr) { @@ -664,6 +678,23 @@ const char* parse_vertex(fastObjData* data, const char* ptr) array_push(data->mesh->positions, v); } + + ptr = skip_whitespace(ptr); + if (!is_newline(*ptr)) + { + /* Fill the colors array until it matches the size of the positions array */ + for (ii = array_size(data->mesh->colors); ii < array_size(data->mesh->positions) - 3; ++ii) + { + array_push(data->mesh->colors, 1.0f); + } + + for (ii = 0; ii < 3; ++ii) + { + ptr = parse_float(ptr, &v); + array_push(data->mesh->colors, v); + } + } + return ptr; } @@ -737,8 +768,10 @@ const char* parse_face(fastObjData* data, const char* ptr) if (v < 0) vn.p = (array_size(data->mesh->positions) / 3) - (fastObjUInt)(-v); - else + else if (v > 0) vn.p = (fastObjUInt)(v); + else + return ptr; /* Skip lines with no valid vertex index */ if (t < 0) vn.t = (array_size(data->mesh->texcoords) / 2) - (fastObjUInt)(-t); @@ -780,9 +813,7 @@ const char* parse_object(fastObjData* data, const char* ptr) ptr = skip_whitespace(ptr); s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - + ptr = skip_name(ptr); e = ptr; flush_object(data); @@ -802,9 +833,7 @@ const char* parse_group(fastObjData* data, const char* ptr) ptr = skip_whitespace(ptr); s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - + ptr = skip_name(ptr); e = ptr; flush_group(data); @@ -856,6 +885,8 @@ fastObjMaterial mtl_default(void) mtl.d = 1.0; mtl.illum = 1; + mtl.fallback = 0; + mtl.map_Ka = 0; mtl.map_Kd = 0; mtl.map_Ks = 0; @@ -883,9 +914,7 @@ const char* parse_usemtl(fastObjData* data, const char* ptr) /* Parse the material name */ s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - + ptr = skip_name(ptr); e = ptr; /* Find an existing material with the same name */ @@ -905,6 +934,7 @@ const char* parse_usemtl(fastObjData* data, const char* ptr) { fastObjMaterial new_mtl = mtl_default(); new_mtl.name = string_copy(s, e); + new_mtl.fallback = 1; array_push(data->mesh->materials, new_mtl); } @@ -971,9 +1001,7 @@ const char* read_map(fastObjData* data, const char* ptr, unsigned int* idx) /* Read name */ s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - + ptr = skip_name(ptr); e = ptr; /* Try to find an existing texture map with the same name */ @@ -1060,8 +1088,7 @@ int read_mtllib(fastObjData* data, void* file, const fastObjCallbacks* callbacks p++; s = p; - while (!is_end_of_name(*p)) - p++; + p = skip_name(p); mtl.name = string_copy(s, p); } @@ -1203,9 +1230,7 @@ const char* parse_mtllib(fastObjData* data, const char* ptr, const fastObjCallba ptr = skip_whitespace(ptr); s = ptr; - while (!is_end_of_name(*ptr)) - ptr++; - + ptr = skip_name(ptr); e = ptr; lib = string_concat(data->base, s, e); @@ -1338,6 +1363,15 @@ void parse_buffer(fastObjData* data, const char* ptr, const char* end, const fas data->line++; } + if (array_size(data->mesh->colors) > 0) + { + /* Fill the remaining slots in the colors array */ + unsigned int ii; + for (ii = array_size(data->mesh->colors); ii < array_size(data->mesh->positions); ++ii) + { + array_push(data->mesh->colors, 1.0f); + } + } } @@ -1361,6 +1395,7 @@ void fast_obj_destroy(fastObjMesh* m) array_clean(m->positions); array_clean(m->texcoords); array_clean(m->normals); + array_clean(m->colors); array_clean(m->face_vertices); array_clean(m->face_materials); array_clean(m->indices); @@ -1416,6 +1451,7 @@ fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbac m->positions = 0; m->texcoords = 0; m->normals = 0; + m->colors = 0; m->face_vertices = 0; m->face_materials = 0; m->indices = 0; @@ -1526,6 +1562,7 @@ fastObjMesh* fast_obj_read_with_callbacks(const char* path, const fastObjCallbac m->position_count = array_size(m->positions) / 3; m->texcoord_count = array_size(m->texcoords) / 2; m->normal_count = array_size(m->normals) / 3; + m->color_count = array_size(m->colors) / 3; m->face_count = array_size(m->face_vertices); m->index_count = array_size(m->indices); m->material_count = array_size(m->materials);