[repacker] add cycle detection to the graph sort.
This allows us to bail early if the graph is not acyclic.
This commit is contained in:
parent
9e99d08470
commit
554ed06fac
|
@ -50,7 +50,7 @@ struct graph_t
|
|||
unsigned priority = 0;
|
||||
|
||||
|
||||
bool link_positions_valid (unsigned parent_index)
|
||||
bool link_positions_valid ()
|
||||
{
|
||||
hb_set_t assigned_bytes;
|
||||
for (const auto& l : obj.real_links)
|
||||
|
@ -321,8 +321,6 @@ struct graph_t
|
|||
vertices_scratch_.alloc (objects.length);
|
||||
for (unsigned i = 0; i < objects.length; i++)
|
||||
{
|
||||
// TODO(grieger): check all links point to valid objects.
|
||||
|
||||
// If this graph came from a serialization buffer object 0 is the
|
||||
// nil object. We don't need it for our purposes here so drop it.
|
||||
if (i == 0 && !objects[i])
|
||||
|
@ -335,7 +333,8 @@ struct graph_t
|
|||
if (check_success (!vertices_.in_error ()))
|
||||
v->obj = *objects[i];
|
||||
|
||||
check_success (v->link_positions_valid (i));
|
||||
check_success (v->link_positions_valid ());
|
||||
|
||||
if (!removed_nil) continue;
|
||||
// Fix indices to account for removed nil object.
|
||||
for (auto& l : v->obj.all_links_writer ()) {
|
||||
|
@ -455,6 +454,13 @@ struct graph_t
|
|||
hb_swap (sorted_graph[new_id], vertices_[next_id]);
|
||||
const vertex_t& next = sorted_graph[new_id];
|
||||
|
||||
if (unlikely (!check_success(new_id >= 0))) {
|
||||
// We are out of ids. Which means we've visited a node more than once.
|
||||
// This graph contains a cycle which is not allowed.
|
||||
DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle.");
|
||||
return;
|
||||
}
|
||||
|
||||
id_map[next_id] = new_id--;
|
||||
|
||||
for (const auto& link : next.obj.all_links ()) {
|
||||
|
|
|
@ -283,6 +283,11 @@ hb_resolve_graph_overflows (hb_tag_t table_tag,
|
|||
graph_t& sorted_graph /* IN/OUT */)
|
||||
{
|
||||
sorted_graph.sort_shortest_distance ();
|
||||
if (sorted_graph.in_error ())
|
||||
{
|
||||
DEBUG_MSG (SUBSET_REPACK, nullptr, "Sorted graph in error state after initial sort.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool will_overflow = graph::will_overflow (sorted_graph);
|
||||
if (!will_overflow)
|
||||
|
|
|
@ -1476,6 +1476,7 @@ static void test_sort_shortest ()
|
|||
|
||||
graph_t graph (c.object_graph ());
|
||||
graph.sort_shortest_distance ();
|
||||
assert (!graph.in_error ());
|
||||
|
||||
assert(strncmp (graph.object (4).head, "abc", 3) == 0);
|
||||
assert(graph.object (4).real_links.length == 3);
|
||||
|
|
|
@ -15,6 +15,20 @@ typedef struct
|
|||
uint8_t width;
|
||||
} link_t;
|
||||
|
||||
/* The fuzzer seed contains a serialized representation of a object graph which forms
|
||||
* the input graph to the repacker call. The binary format is:
|
||||
*
|
||||
* table tag: 4 bytes
|
||||
* number of objects: 2 bytes
|
||||
* objects[number of objects]:
|
||||
* blob size: 2 bytes
|
||||
* blob: blob size bytes
|
||||
* num of real links: 2 bytes
|
||||
* links[number of real links]: link_t struct
|
||||
*
|
||||
* TODO(garretrieger): add optional virtual links
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
bool read(const uint8_t** data, size_t* size, T* out)
|
||||
{
|
||||
|
@ -108,8 +122,7 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
|
|||
{
|
||||
if (!read<link_t> (&data, &size, &links[i])) goto end;
|
||||
|
||||
if (links[i].parent >= num_objects
|
||||
|| links[i].child > links[i].parent) // Enforces DAG graph
|
||||
if (links[i].parent >= num_objects)
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue