1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 #include <stdint.h>
7 #include <string.h>
8 
9 // clang-format off
10 #include <hb.h>
11 #include <hb-subset.h>
12 // clang-format on
13 
14 #include "base/check.h"
15 #include "base/stl_util.h"
16 #include "third_party/harfbuzz-ng/utils/hb_scoped.h"
17 
18 namespace {
19 
TrySubset(hb_face_t * face,const hb_codepoint_t text[],const int text_length,const uint8_t flags)20 void TrySubset(hb_face_t* face,
21                const hb_codepoint_t text[],
22                const int text_length,
23                const uint8_t flags) {
24   bool drop_hints = flags & (1 << 0);
25   bool drop_layout = flags & (1 << 1);
26   bool retain_gids = flags & (1 << 2);
27 
28   HbScoped<hb_subset_input_t> input(hb_subset_input_create_or_fail());
29   hb_subset_input_set_drop_hints(input.get(), drop_hints);
30   hb_subset_input_set_retain_gids(input.get(), retain_gids);
31   hb_set_t* codepoints = hb_subset_input_unicode_set(input.get());
32 
33   if (!drop_layout) {
34     hb_set_del(hb_subset_input_drop_tables_set(input.get()),
35                HB_TAG('G', 'S', 'U', 'B'));
36     hb_set_del(hb_subset_input_drop_tables_set(input.get()),
37                HB_TAG('G', 'P', 'O', 'S'));
38     hb_set_del(hb_subset_input_drop_tables_set(input.get()),
39                HB_TAG('G', 'D', 'E', 'F'));
40   }
41 
42   for (int i = 0; i < text_length; i++) {
43     hb_set_add(codepoints, text[i]);
44   }
45 
46   HbScoped<hb_face_t> result(hb_subset(face, input.get()));
47   HbScoped<hb_blob_t> blob(hb_face_reference_blob(result.get()));
48   uint32_t length;
49   const char* data = hb_blob_get_data(blob.get(), &length);
50 
51   // Access all the blob data
52   uint32_t bytes_count = 0;
53   if (data) {
54     for (uint32_t i = 0; i < length; ++i) {
55       if (data[i])
56         ++bytes_count;
57     }
58   }
59   CHECK(bytes_count || !length);
60 }
61 
62 }  // namespace
63 
64 constexpr size_t kMaxInputLength = 16800;
65 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)66 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
67   if (size > kMaxInputLength)
68     return 0;
69 
70   const char* data_ptr = reinterpret_cast<const char*>(data);
71   HbScoped<hb_blob_t> blob(hb_blob_create(
72       data_ptr, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
73   HbScoped<hb_face_t> face(hb_face_create(blob.get(), 0));
74 
75   // Test hb_set API
76   {
77     HbScoped<hb_set_t> output(hb_set_create());
78     hb_face_collect_unicodes(face.get(), output.get());
79   }
80 
81   uint8_t subset_flags = 0;
82   const hb_codepoint_t text[] = {'A', 'B', 'C', 'D', 'E', 'X', 'Y',
83                                  'Z', '1', '2', '3', '@', '_', '%',
84                                  '&', ')', '*', '$', '!'};
85 
86   TrySubset(face.get(), text, base::size(text), subset_flags);
87 
88   hb_codepoint_t text_from_data[16];
89   if (size > sizeof(text_from_data) + 1) {
90     memcpy(text_from_data, data + size - sizeof(text_from_data),
91            sizeof(text_from_data));
92     subset_flags = data[size - sizeof(text_from_data) - 1];
93     size_t text_size = base::size(text_from_data);
94     TrySubset(face.get(), text_from_data, text_size, subset_flags);
95   }
96 
97   return 0;
98 }
99