chromium/third_party/harfbuzz-ng/fuzz/hb_subset_fuzzer.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>
#include <string.h>

// clang-format off
#include <hb.h>
#include <hb-subset.h>
#include <hb-cplusplus.hh>
// clang-format on

#include <iterator>

#include "base/check.h"

namespace {

void TrySubset(hb_face_t* face,
               const hb_codepoint_t text[],
               const int text_length,
               const uint8_t flags) {
  bool drop_layout = flags & (1 << 1);
  unsigned input_set_flags =
      0                                                          //
      | ((flags & (1 << 0)) ? HB_SUBSET_FLAGS_NO_HINTING : 0)    //
      | ((flags & (1 << 2)) ? HB_SUBSET_FLAGS_RETAIN_GIDS : 0);  //

  hb::unique_ptr<hb_subset_input_t> input(hb_subset_input_create_or_fail());
  hb_subset_input_set_flags(input.get(), input_set_flags);
  hb_set_t* codepoints = hb_subset_input_unicode_set(input.get());

  if (!drop_layout) {
    hb_set_del(hb_subset_input_set(input.get(), HB_SUBSET_SETS_DROP_TABLE_TAG),
               HB_TAG('G', 'S', 'U', 'B'));
    hb_set_del(hb_subset_input_set(input.get(), HB_SUBSET_SETS_DROP_TABLE_TAG),
               HB_TAG('G', 'P', 'O', 'S'));
    hb_set_del(hb_subset_input_set(input.get(), HB_SUBSET_SETS_DROP_TABLE_TAG),
               HB_TAG('G', 'D', 'E', 'F'));
  }

  for (int i = 0; i < text_length; i++) {
    hb_set_add(codepoints, text[i]);
  }

  hb::unique_ptr<hb_face_t> result(hb_subset_or_fail(face, input.get()));
  if (!result) {
    // Subset failed, so nothing to check.
    return;
  }
  hb::unique_ptr<hb_blob_t> blob(hb_face_reference_blob(result.get()));
  uint32_t length;
  const char* data = hb_blob_get_data(blob.get(), &length);

  // Access all the blob data
  uint32_t bytes_count = 0;
  if (data) {
    for (uint32_t i = 0; i < length; ++i) {
      if (data[i])
        ++bytes_count;
    }
  }
  CHECK(bytes_count || !length);
}

}  // namespace

constexpr size_t kMaxInputLength = 16800;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  if (size > kMaxInputLength)
    return 0;

  const char* data_ptr = reinterpret_cast<const char*>(data);
  hb::unique_ptr<hb_blob_t> blob(hb_blob_create(
      data_ptr, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
  hb::unique_ptr<hb_face_t> face(hb_face_create(blob.get(), 0));

  // Test hb_set API
  {
    hb::unique_ptr<hb_set_t> output(hb_set_create());
    hb_face_collect_unicodes(face.get(), output.get());
  }

  uint8_t subset_flags = 0;
  const hb_codepoint_t text[] = {'A', 'B', 'C', 'D', 'E', 'X', 'Y',
                                 'Z', '1', '2', '3', '@', '_', '%',
                                 '&', ')', '*', '$', '!'};

  TrySubset(face.get(), text, std::size(text), subset_flags);

  hb_codepoint_t text_from_data[16];
  if (size > sizeof(text_from_data) + 1) {
    memcpy(text_from_data, data + size - sizeof(text_from_data),
           sizeof(text_from_data));
    subset_flags = data[size - sizeof(text_from_data) - 1];
    size_t text_size = std::size(text_from_data);
    TrySubset(face.get(), text_from_data, text_size, subset_flags);
  }

  return 0;
}