Coverage Report

Created: 2026-10-10 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/test/fuzzing/hb-repacker-fuzzer.cc
Line
Count
Source
1
#include "hb-fuzzer.hh"
2
3
#include <stdlib.h>
4
#include <stdio.h>
5
#include <string.h>
6
#include <assert.h>
7
8
#include "hb-subset.h"
9
10
typedef struct
11
{
12
  uint16_t parent;
13
  uint16_t child;
14
  uint16_t position;
15
  uint8_t width;
16
} link_t;
17
18
/* The fuzzer seed contains a serialized representation of a object graph which forms
19
 * the input graph to the repacker call. The binary format is:
20
 *
21
 * table tag: 4 bytes
22
 * number of objects: 2 bytes
23
 * objects[number of objects]:
24
 *   blob size: 2 bytes
25
 *   blob: blob size bytes
26
 * num of real links: 2 bytes
27
 * links[number of real links]: link_t struct
28
 *
29
 * TODO(garretrieger): add optional virtual links
30
 */
31
32
template <typename T>
33
bool read(const uint8_t** data, size_t* size, T* out)
34
29.7M
{
35
29.7M
  if (*size < sizeof (T)) return false;
36
37
29.7M
  memcpy(out, *data, sizeof (T));
38
39
29.7M
  *data += sizeof (T);
40
29.7M
  *size -= sizeof (T);
41
42
29.7M
  return true;
43
29.7M
}
bool read<unsigned int>(unsigned char const**, unsigned long*, unsigned int*)
Line
Count
Source
34
3.35k
{
35
3.35k
  if (*size < sizeof (T)) return false;
36
37
3.34k
  memcpy(out, *data, sizeof (T));
38
39
3.34k
  *data += sizeof (T);
40
3.34k
  *size -= sizeof (T);
41
42
3.34k
  return true;
43
3.35k
}
bool read<unsigned short>(unsigned char const**, unsigned long*, unsigned short*)
Line
Count
Source
34
11.7M
{
35
11.7M
  if (*size < sizeof (T)) return false;
36
37
11.7M
  memcpy(out, *data, sizeof (T));
38
39
11.7M
  *data += sizeof (T);
40
11.7M
  *size -= sizeof (T);
41
42
11.7M
  return true;
43
11.7M
}
bool read<link_t>(unsigned char const**, unsigned long*, link_t*)
Line
Count
Source
34
17.9M
{
35
17.9M
  if (*size < sizeof (T)) return false;
36
37
17.9M
  memcpy(out, *data, sizeof (T));
38
39
17.9M
  *data += sizeof (T);
40
17.9M
  *size -= sizeof (T);
41
42
17.9M
  return true;
43
17.9M
}
44
45
void cleanup (hb_subset_serialize_object_t* objects, uint16_t num_objects)
46
3.33k
{
47
12.9M
  for (uint32_t i = 0; i < num_objects; i++)
48
12.9M
  {
49
12.9M
    free (objects[i].head);
50
12.9M
    free (objects[i].real_links);
51
12.9M
  }
52
3.33k
}
53
54
void add_links_to_objects (hb_subset_serialize_object_t* objects, uint16_t num_objects,
55
                           link_t* links, uint16_t num_links)
56
3.19k
{
57
3.19k
  unsigned* link_count = (unsigned*) calloc (num_objects, sizeof (unsigned));
58
59
17.9M
  for (uint32_t i = 0; i < num_links; i++)
60
17.9M
  {
61
17.9M
    uint16_t parent_idx = links[i].parent;
62
17.9M
    link_count[parent_idx]++;
63
17.9M
  }
64
65
11.7M
  for (uint32_t i = 0; i < num_objects; i++)
66
11.7M
  {
67
11.7M
    objects[i].num_real_links = link_count[i];
68
11.7M
    objects[i].real_links = (hb_subset_serialize_link_t*) calloc (link_count[i], sizeof (hb_subset_serialize_link_t));
69
11.7M
    objects[i].num_virtual_links = 0;
70
11.7M
    objects[i].virtual_links = nullptr;
71
11.7M
  }
72
73
17.9M
  for (uint32_t i = 0; i < num_links; i++)
74
17.9M
  {
75
17.9M
    uint16_t parent_idx = links[i].parent;
76
17.9M
    uint16_t child_idx = links[i].child + 1; // All indices are shifted by 1 by the null object.
77
17.9M
    hb_subset_serialize_link_t* link = &(objects[parent_idx].real_links[link_count[parent_idx] - 1]);
78
79
17.9M
    link->width = links[i].width;
80
17.9M
    link->position = links[i].position;
81
17.9M
    link->objidx = child_idx;
82
17.9M
    link_count[parent_idx]--;
83
17.9M
  }
84
85
3.19k
  free (link_count);
86
3.19k
}
87
88
extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
89
3.35k
{
90
  // TODO(garretrieger): move graph validity checks into repacker graph creation.
91
3.35k
  alloc_state = _fuzzing_alloc_state (data, size);
92
93
3.35k
  uint16_t num_objects = 0;
94
3.35k
  hb_subset_serialize_object_t* objects = nullptr;
95
96
3.35k
  uint16_t num_real_links = 0;
97
3.35k
  link_t* links = nullptr;
98
99
3.35k
  hb_tag_t table_tag;
100
3.35k
  if (!read<hb_tag_t> (&data, &size, &table_tag)) goto end;
101
3.34k
  if (!read<uint16_t> (&data, &size, &num_objects)) goto end;
102
103
3.33k
  objects = (hb_subset_serialize_object_t*) calloc (num_objects, sizeof (hb_subset_serialize_object_t));
104
11.7M
  for (uint32_t i = 0; i < num_objects; i++)
105
11.7M
  {
106
11.7M
    uint16_t blob_size;
107
11.7M
    if (!read<uint16_t> (&data, &size, &blob_size)) goto end;
108
11.7M
    if (size < blob_size) goto end;
109
110
11.7M
    char* copy = (char*) calloc (1, blob_size);
111
11.7M
    memcpy (copy, data, blob_size);
112
11.7M
    objects[i].head = (char*) copy;
113
11.7M
    objects[i].tail = (char*) (copy + blob_size);
114
115
11.7M
    size -= blob_size;
116
11.7M
    data += blob_size;
117
11.7M
  }
118
119
3.27k
  if (!read<uint16_t> (&data, &size, &num_real_links)) goto end;
120
3.27k
  links = (link_t*) calloc (num_real_links, sizeof (link_t));
121
17.9M
  for (uint32_t i = 0; i < num_real_links; i++)
122
17.9M
  {
123
17.9M
    if (!read<link_t> (&data, &size, &links[i])) goto end;
124
125
17.9M
    if (links[i].parent >= num_objects)
126
51
      goto end;
127
17.9M
  }
128
129
3.19k
  add_links_to_objects (objects, num_objects,
130
3.19k
                        links, num_real_links);
131
132
3.19k
  hb_blob_destroy (hb_subset_serialize_or_fail (table_tag,
133
3.19k
                                                objects,
134
3.19k
                                                num_objects));
135
136
3.35k
end:
137
3.35k
  if (objects)
138
3.33k
  {
139
3.33k
    cleanup (objects, num_objects);
140
3.33k
    free (objects);
141
3.33k
  }
142
3.35k
  free (links);
143
144
3.35k
  return 0;
145
3.19k
}