...

Text file src/github.com/google/flatbuffers/tests/fuzzer/flatbuffers_monster_fuzzer.cc

Documentation: github.com/google/flatbuffers/tests/fuzzer

     1/*
     2 * Copyright 2014 Google Inc. All rights reserved.
     3 *
     4 * Licensed under the Apache License, Version 2.0 (the "License");
     5 * you may not use this file except in compliance with the License.
     6 * You may obtain a copy of the License at
     7 *
     8 *     http://www.apache.org/licenses/LICENSE-2.0
     9 *
    10 * Unless required by applicable law or agreed to in writing, software
    11 * distributed under the License is distributed on an "AS IS" BASIS,
    12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13 * See the License for the specific language governing permissions and
    14 * limitations under the License.
    15 */
    16
    17#include <stddef.h>
    18#include <stdint.h>
    19
    20#include <clocale>
    21#include <filesystem>
    22#include <string>
    23
    24#include "cpp17/generated_cpp17/monster_test_generated.h"
    25#include "flatbuffers/idl.h"
    26#include "test_assert.h"
    27#include "test_init.h"
    28
    29namespace fs = std::filesystem;
    30
    31// Utility for test run.
    32OneTimeTestInit OneTimeTestInit::one_time_init_;
    33// The current executable path (see LLVMFuzzerInitialize).
    34static fs::path exe_path_;
    35
    36static flatbuffers::Parser parser_;
    37
    38namespace {
    39
    40static constexpr size_t kMinInputLength = 1;
    41static constexpr size_t kMaxInputLength = 16384;
    42
    43static constexpr uint8_t flags_strict_json = 0x80;
    44static constexpr uint8_t flags_skip_unexpected_fields_in_json = 0x40;
    45static constexpr uint8_t flags_allow_non_utf8 = 0x20;
    46
    47bool TestFileExists(fs::path file_path) {
    48  if (file_path.has_filename() && fs::exists(file_path)) return true;
    49
    50  TEST_OUTPUT_LINE("@DEBUG: file '%s' not found", file_path.string().c_str());
    51  for (const auto &entry : fs::directory_iterator(file_path.parent_path())) {
    52    TEST_OUTPUT_LINE("@DEBUG: parent path entry: '%s'",
    53                     entry.path().string().c_str());
    54  }
    55  return false;
    56}
    57
    58std::string LoadBinarySchema(const char *file_name) {
    59  const auto file_path = exe_path_.parent_path() / file_name;
    60  TEST_EQ(true, TestFileExists(file_path));
    61  std::string schemafile;
    62  TEST_EQ(true,
    63          flatbuffers::LoadFile(file_path.string().c_str(), true, &schemafile));
    64
    65  flatbuffers::Verifier verifier(
    66      reinterpret_cast<const uint8_t *>(schemafile.c_str()), schemafile.size());
    67  TEST_EQ(true, reflection::VerifySchemaBuffer(verifier));
    68  return schemafile;
    69}
    70
    71std::string do_test(const flatbuffers::IDLOptions &opts,
    72                    const std::string input_json, const bool check_parser) {
    73  // (re)define parser options
    74  parser_.opts = opts;
    75
    76  std::string jsongen;
    77  if (parser_.ParseJson(input_json.c_str())) {
    78    flatbuffers::Verifier verifier(parser_.builder_.GetBufferPointer(),
    79                                   parser_.builder_.GetSize());
    80    TEST_EQ(true, MyGame::Example::VerifyMonsterBuffer(verifier));
    81    TEST_NULL(
    82        GenText(parser_, parser_.builder_.GetBufferPointer(), &jsongen));
    83  } else if (check_parser) {
    84    TEST_OUTPUT_LINE("parser failed with JSON:\n%s", input_json.c_str());
    85    TEST_EQ_STR("", parser_.error_.c_str());
    86    TEST_ASSERT(false);
    87  }
    88  return jsongen;
    89};
    90}  // namespace
    91
    92// https://google.github.io/oss-fuzz/further-reading/fuzzer-environment/
    93// Current working directory
    94// You should not make any assumptions about the current working directory of
    95// your fuzz target. If you need to load data files, please use argv[0] to get
    96// the directory where your fuzz target executable is located.
    97// You must not modify argv[0].
    98extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
    99  (void)argc;
   100  exe_path_ = (*argv)[0];
   101
   102  static const std::string schemafile = LoadBinarySchema("monster_test.bfbs");
   103  // parse schema first, so we can use it to parse the data after
   104  parser_.Deserialize(reinterpret_cast<const uint8_t *>(schemafile.c_str()),
   105                      schemafile.size());
   106  return 0;
   107}
   108
   109extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   110  // Reserve one byte for Parser flags and one byte for repetition counter.
   111  if (size < 3) return 0;
   112  const uint8_t flags = data[0];
   113  (void)data[1];  //  reserved
   114  data += 2;
   115  size -= 2;  // bypass
   116
   117  const std::string original(reinterpret_cast<const char *>(data), size);
   118  auto input = std::string(original.c_str());  // until '\0'
   119  if (input.size() < kMinInputLength || input.size() > kMaxInputLength)
   120    return 0;
   121
   122  flatbuffers::IDLOptions opts;
   123  opts.strict_json = (flags & flags_strict_json);
   124  opts.skip_unexpected_fields_in_json =
   125      (flags & flags_skip_unexpected_fields_in_json);
   126  opts.allow_non_utf8 = (flags & flags_allow_non_utf8);
   127
   128  do {
   129    const std::string jsongen_1 = do_test(opts, input, false);
   130    if (!jsongen_1.empty()) {
   131      const std::string jsongen_2 = do_test(opts, jsongen_1, true);
   132      if (jsongen_1 != jsongen_2 && !opts.output_default_scalars_in_json) {
   133        // This gets tricky when the jsongen_1 includes a default-value, as the
   134        // generated jsongen_2 doesn't emit default-values. So enable default
   135        // scalars and re-run it.
   136        opts.output_default_scalars_in_json = true;
   137        continue;
   138      }
   139      TEST_EQ(jsongen_1, jsongen_2);
   140    }
   141  } while (0);
   142
   143  return 0;
   144}

View as plain text