Skip to content

Commit

Permalink
gltfpack: Implement end-to-end fuzzing build mode
Browse files Browse the repository at this point in the history
While individual components of gltfpack like cgltf or mesh encoders have
their own dedicated fuzzers, gltfpack has a lot of code that can be
difficult to validate. We would expect that gltfpack is resistant to
invalid inputs (with the exception, perhaps, of .obj inputs where
fast_obj, the library we use, is currently not fuzz safe).

This change implements a in-memory GLB loading flow (parseGlb) and an
in-memory fuzzer; we disable all file I/O by using NULL paths, and
otherwise share all the same processing code with the exception of final
buffer writing.
  • Loading branch information
zeux committed Oct 26, 2023
1 parent f23b67d commit 93975d7
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 14 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,21 @@ ifeq ($(config),coverage)
endif

ifeq ($(config),sanitize)
CXXFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all
CXXFLAGS+=-fsanitize=address,undefined -fsanitize-undefined-trap-on-error
LDFLAGS+=-fsanitize=address,undefined
endif

ifeq ($(config),analyze)
CXXFLAGS+=--analyze
endif

ifeq ($(config),fuzz)
CXXFLAGS+=-O1 -fsanitize=address,fuzzer
LDFLAGS+=-fsanitize=address,fuzzer

$(GLTFPACK_OBJECTS): CXXFLAGS+=-DGLTFFUZZ
endif

all: $(DEMO)

test: $(DEMO)
Expand Down
28 changes: 28 additions & 0 deletions gltf/gltfpack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,7 @@ unsigned int textureMask(const char* arg)
return result;
}

#ifndef GLTFFUZZ
int main(int argc, char** argv)
{
#ifndef __wasi__
Expand Down Expand Up @@ -1593,6 +1594,7 @@ int main(int argc, char** argv)

return gltfpack(input, output, report, settings);
}
#endif

#ifdef __wasi__
extern "C" int pack(int argc, char** argv)
Expand All @@ -1604,3 +1606,29 @@ extern "C" int pack(int argc, char** argv)
return result;
}
#endif

#ifdef GLTFFUZZ
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* buffer, size_t size)
{
Settings settings = defaults();

settings.texture_embed = true;

std::vector<Mesh> meshes;
std::vector<Animation> animations;

const char* error = NULL;
cgltf_data* data = parseGlb(buffer, size, meshes, animations, &error);

if (error)
return 0;

std::string json, bin, fallback;
size_t fallback_size = 0;
process(data, NULL, NULL, NULL, meshes, animations, settings, json, bin, fallback, fallback_size);

cgltf_free(data);

return 0;
}
#endif
2 changes: 2 additions & 0 deletions gltf/gltfpack.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ void removeFile(const char* path);
cgltf_data* parseObj(const char* path, std::vector<Mesh>& meshes, const char** error);
cgltf_data* parseGltf(const char* path, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const char** error);

cgltf_data* parseGlb(const void* buffer, size_t size, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const char** error);

void processAnimation(Animation& animation, const Settings& settings);
void processMesh(Mesh& mesh, const Settings& settings);

Expand Down
2 changes: 1 addition & 1 deletion gltf/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ bool readImage(const cgltf_image& image, const char* input_path, std::string& da
mime_type = image.mime_type;
return true;
}
else if (image.uri && *image.uri)
else if (image.uri && *image.uri && input_path)
{
std::string path = image.uri;

Expand Down
44 changes: 32 additions & 12 deletions gltf/parsegltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,19 +445,8 @@ static bool freeUnusedBuffers(cgltf_data* data)
return free_bin;
}

cgltf_data* parseGltf(const char* path, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const char** error)
static cgltf_data* parseGltf(cgltf_data* data, cgltf_result result, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const char** error)
{
cgltf_data* data = NULL;

cgltf_options options = {};
cgltf_result result = cgltf_parse_file(&options, path, &data);

if (data && !data->bin)
freeFile(data);

result = (result == cgltf_result_success) ? cgltf_load_buffers(&options, data, path) : result;
result = (result == cgltf_result_success) ? cgltf_validate(data) : result;

*error = NULL;

if (result != cgltf_result_success)
Expand Down Expand Up @@ -495,3 +484,34 @@ cgltf_data* parseGltf(const char* path, std::vector<Mesh>& meshes, std::vector<A

return data;
}

cgltf_data* parseGltf(const char* path, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const char** error)
{
cgltf_data* data = NULL;

cgltf_options options = {};
cgltf_result result = cgltf_parse_file(&options, path, &data);

if (data && !data->bin)
freeFile(data);

result = (result == cgltf_result_success) ? cgltf_load_buffers(&options, data, path) : result;
result = (result == cgltf_result_success) ? cgltf_validate(data) : result;

return parseGltf(data, result, meshes, animations, error);
}

cgltf_data* parseGlb(const void* buffer, size_t size, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const char** error)
{
cgltf_data* data = NULL;

cgltf_options options = {};
options.type = cgltf_file_type_glb;

cgltf_result result = cgltf_parse(&options, buffer, size, &data);

result = (result == cgltf_result_success) ? cgltf_load_buffers(&options, data, NULL) : result;
result = (result == cgltf_result_success) ? cgltf_validate(data) : result;

return parseGltf(data, result, meshes, animations, error);
}

0 comments on commit 93975d7

Please sign in to comment.