From 985b19f678cbccc57853796d2ee0e6885b9e7244 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 7 Sep 2022 22:21:16 +0000 Subject: [PATCH] [repacker] begin implementing a fuzzer for the repacker api. --- src/graph/graph.hh | 12 +++ src/hb-repacker.hh | 6 ++ test/fuzzing/hb-repacker-fuzzer.cc | 134 +++++++++++++++++++++++++++++ test/fuzzing/meson.build | 3 + 4 files changed, 155 insertions(+) create mode 100644 test/fuzzing/hb-repacker-fuzzer.cc diff --git a/src/graph/graph.hh b/src/graph/graph.hh index c6f088499..20d646300 100644 --- a/src/graph/graph.hh +++ b/src/graph/graph.hh @@ -941,6 +941,18 @@ struct graph_t return made_change; } + bool is_fully_connected () + { + update_parents(); + + for (unsigned i = 0; i < root_idx (); i++) + { + if (!vertices_[i].parents) + return false; + } + return true; + } + void print_orphaned_nodes () { if (!DEBUG_ENABLED(SUBSET_REPACK)) return; diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh index 70f1d0e08..ba305577b 100644 --- a/src/hb-repacker.hh +++ b/src/hb-repacker.hh @@ -376,6 +376,12 @@ hb_resolve_overflows (const T& packed, unsigned max_rounds = 20, bool recalculate_extensions = false) { graph_t sorted_graph (packed); + if (!sorted_graph.is_fully_connected ()) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Input graph is not fully connected."); + return nullptr; + } + if (!hb_resolve_graph_overflows (table_tag, max_rounds, recalculate_extensions, sorted_graph)) return nullptr; diff --git a/test/fuzzing/hb-repacker-fuzzer.cc b/test/fuzzing/hb-repacker-fuzzer.cc new file mode 100644 index 000000000..67ba0658d --- /dev/null +++ b/test/fuzzing/hb-repacker-fuzzer.cc @@ -0,0 +1,134 @@ +#include "hb-fuzzer.hh" + +#include +#include +#include +#include + +#include "hb-subset-repacker.h" + +typedef struct +{ + uint16_t parent; + uint16_t delta; + uint16_t position; + uint8_t width; +} link_t; + +template +bool read(const uint8_t** data, size_t* size, T* out) +{ + if (*size < sizeof (T)) return false; + + *out = * ((T*) *data); + + *data += sizeof (T); + *size -= sizeof (T); + + return true; +} + +void cleanup (hb_object_t* objects, uint16_t num_objects) +{ + for (uint32_t i = 0; i < num_objects; i++) + free (objects[i].real_links); +} + +void add_links_to_objects (hb_object_t* objects, uint16_t num_objects, + link_t* links, uint16_t num_links) +{ + unsigned* link_count = (unsigned*) calloc (num_objects, sizeof (unsigned)); + + for (uint32_t i = 0; i < num_links; i++) + { + uint16_t parent_idx = links[i].parent; + link_count[parent_idx]++; + } + + for (uint32_t i = 0; i < num_objects; i++) + { + objects[i].num_real_links = link_count[i]; + objects[i].real_links = (hb_link_t*) calloc (link_count[i], sizeof (hb_link_t)); + objects[i].num_virtual_links = 0; + objects[i].virtual_links = nullptr; + } + + for (uint32_t i = 0; i < num_links; i++) + { + uint16_t parent_idx = links[i].parent; + uint16_t child_idx = links[i].parent + links[i].delta + 1; + hb_link_t* link = &(objects[parent_idx].real_links[link_count[parent_idx] - 1]); + + link->width = links[i].width; + link->position = links[i].position; + link->objidx = child_idx; + link_count[parent_idx]--; + } + + bool* reachable = (bool*) calloc (num_objects, sizeof (bool)); + + + + free (reachable); + free (link_count); +} + +extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + alloc_state = _fuzzing_alloc_state (data, size); + + uint16_t num_objects = 0; + hb_object_t* objects = nullptr; + + uint16_t num_real_links = 0; + link_t* links = nullptr; + + hb_tag_t table_tag; + if (!read (&data, &size, &table_tag)) goto end; + if (!read (&data, &size, &num_objects)) goto end; + + objects = (hb_object_t*) calloc (num_objects, sizeof (hb_object_t)); + for (uint32_t i = 0; i < num_objects; i++) + { + uint16_t blob_size; + if (!read (&data, &size, &blob_size)) goto end; + if (size < blob_size) goto end; + + objects[i].head = (char*) data; + objects[i].tail = (char*) (data + blob_size); + + size -= blob_size; + data += blob_size; + } + + if (!read (&data, &size, &num_real_links)) goto end; + links = (link_t*) calloc (num_real_links, sizeof (link_t)); + for (uint32_t i = 0; i < num_real_links; i++) + { + if (!read (&data, &size, &links[i])) goto end; + + uint32_t child_idx = ((uint32_t) links[i].parent) + ((uint32_t) links[i].delta) + 1; + if (links[i].parent >= num_objects + || child_idx >= num_objects) + goto end; + + if (links[i].width < 2 || links[i].width > 4) goto end; + } + + add_links_to_objects (objects, num_objects, + links, num_real_links); + + hb_blob_destroy (hb_subset_repack_or_fail (table_tag, + objects, + num_objects)); + +end: + if (objects) + { + cleanup (objects, num_objects); + free (objects); + } + free (links); + + return 0; +} diff --git a/test/fuzzing/meson.build b/test/fuzzing/meson.build index 3aba9eb67..a39702b53 100644 --- a/test/fuzzing/meson.build +++ b/test/fuzzing/meson.build @@ -1,6 +1,7 @@ tests = [ 'hb-shape-fuzzer.cc', 'hb-subset-fuzzer.cc', + 'hb-repacker-fuzzer.cc', 'hb-set-fuzzer.cc', 'hb-draw-fuzzer.cc', ] @@ -19,6 +20,8 @@ foreach file_name : tests extra_cpp_args += '-DHB_IS_IN_FUZZER' endif + # TODO tie this to the experimental api setting. + extra_cpp_args += '-DHB_EXPERIMENTAL_API' exe = executable(test_name, sources, cpp_args: cpp_args + extra_cpp_args, include_directories: [incconfig, incsrc],