You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

109 lines
3.6 KiB

// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// This file implements a basic fuzz test for the Detokenizer.
// An instance of the Detokenizer is created from a minimal, nearly-empty token
// database. Fuzz data is fed to the detokenizer in various supported input
// argument formats at random, when then decodes this data and tries to match
// it to tokens in the database.
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
#include "pw_fuzzer/fuzzed_data_provider.h"
#include "pw_preprocessor/util.h"
#include "pw_tokenizer/detokenize.h"
namespace pw::tokenizer {
namespace {
constexpr size_t kFuzzRangeMin = 0;
constexpr size_t kFuzzRangeMax = 10000;
enum DetokenizeBufferArgumentType : uint8_t {
kSpan = 0,
kStringView,
kPtrAndLength,
kMaxValue = kPtrAndLength
};
// In order to better fuzz the detokenizer, rather than use an empty token
// database, we construct a minimal database with 4 entries out of a string
// literal array that matches the token database format (see token_database.h
// for detailed info on the database entry format)
alignas(TokenDatabase::RawEntry) constexpr char kBasicData[] =
"TOKENS\0\0"
"\x04\x00\x00\x00"
"\0\0\0\0"
"\x01\x00\x00\x00----"
"\x05\x00\x00\x00----"
"\xFF\x00\x00\x00----"
"\xFF\xEE\xEE\xDD----"
"One\0"
"TWO\0"
"333\0"
"FOUR";
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static Detokenizer detokenizer(TokenDatabase::Create<kBasicData>());
FuzzedDataProvider provider(data, size);
while (provider.remaining_bytes() != 0) {
// Map the first word of the remaining fuzz data to a buffer argument
// type, and feed the Detokenizer with a random length buffer to be
// detokenized in the relevant format. The detokenized string returned
// is itself of little consequence to this test.
switch (provider.ConsumeEnum<DetokenizeBufferArgumentType>()) {
case kSpan: {
size_t consumed_size = provider.ConsumeIntegralInRange<size_t>(
kFuzzRangeMin, kFuzzRangeMax);
std::vector<uint8_t> buffer =
provider.ConsumeBytes<uint8_t>(consumed_size);
auto detokenized_string =
detokenizer.Detokenize(std::span(&buffer[0], buffer.size()));
static_cast<void>(detokenized_string);
break;
}
case kStringView: {
std::string str =
provider.ConsumeRandomLengthString(provider.remaining_bytes());
auto detokenized_string = detokenizer.Detokenize(str);
static_cast<void>(detokenized_string);
break;
}
case kPtrAndLength: {
size_t consumed_size = provider.ConsumeIntegralInRange<size_t>(
kFuzzRangeMin, kFuzzRangeMax);
std::vector<uint8_t> buffer =
provider.ConsumeBytes<uint8_t>(consumed_size);
auto detokenized_string =
detokenizer.Detokenize(&buffer[0], buffer.size());
static_cast<void>(detokenized_string);
break;
}
}
}
return 0;
}
} // namespace pw::tokenizer