diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/libs/protobuf-mutator/src/libfuzzer | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'contrib/libs/protobuf-mutator/src/libfuzzer')
4 files changed, 517 insertions, 0 deletions
diff --git a/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_macro.cc b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_macro.cc new file mode 100644 index 0000000000..4e506cbe94 --- /dev/null +++ b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_macro.cc @@ -0,0 +1,242 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// 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 +// +// http://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. + +#include "src/libfuzzer/libfuzzer_macro.h" + +#include <algorithm> +#include <memory> +#include <vector> + +#include "src/binary_format.h" +#include "src/libfuzzer/libfuzzer_mutator.h" +#include "src/text_format.h" + +namespace protobuf_mutator { +namespace libfuzzer { + +namespace { + +class InputReader { + public: + InputReader(const uint8_t* data, size_t size) : data_(data), size_(size) {} + virtual ~InputReader() = default; + + virtual bool Read(protobuf::Message* message) const = 0; + + const uint8_t* data() const { return data_; } + size_t size() const { return size_; } + + private: + const uint8_t* data_; + size_t size_; +}; + +class OutputWriter { + public: + OutputWriter(uint8_t* data, size_t size) : data_(data), size_(size) {} + virtual ~OutputWriter() = default; + + virtual size_t Write(const protobuf::Message& message) = 0; + + uint8_t* data() const { return data_; } + size_t size() const { return size_; } + + private: + uint8_t* data_; + size_t size_; +}; + +class TextInputReader : public InputReader { + public: + using InputReader::InputReader; + + bool Read(protobuf::Message* message) const override { + return ParseTextMessage(data(), size(), message); + } +}; + +class TextOutputWriter : public OutputWriter { + public: + using OutputWriter::OutputWriter; + + size_t Write(const protobuf::Message& message) override { + return SaveMessageAsText(message, data(), size()); + } +}; + +class BinaryInputReader : public InputReader { + public: + using InputReader::InputReader; + + bool Read(protobuf::Message* message) const override { + return ParseBinaryMessage(data(), size(), message); + } +}; + +class BinaryOutputWriter : public OutputWriter { + public: + using OutputWriter::OutputWriter; + + size_t Write(const protobuf::Message& message) override { + return SaveMessageAsBinary(message, data(), size()); + } +}; + +class LastMutationCache { + public: + void Store(const uint8_t* data, size_t size, protobuf::Message* message) { + if (!message_) message_.reset(message->New()); + message->GetReflection()->Swap(message, message_.get()); + data_.assign(data, data + size); + } + + bool LoadIfSame(const uint8_t* data, size_t size, + protobuf::Message* message) { + if (!message_ || size != data_.size() || + !std::equal(data_.begin(), data_.end(), data)) + return false; + + message->GetReflection()->Swap(message, message_.get()); + message_.reset(); + return true; + } + + private: + std::vector<uint8_t> data_; + std::unique_ptr<protobuf::Message> message_; +}; + +LastMutationCache* GetCache() { + static LastMutationCache cache; + return &cache; +} + +Mutator* GetMutator() { + static Mutator mutator; + return &mutator; +} + +size_t GetMaxSize(const InputReader& input, const OutputWriter& output, + const protobuf::Message& message) { + size_t max_size = message.ByteSizeLong() + output.size(); + max_size -= std::min(max_size, input.size()); + return max_size; +} + +size_t MutateMessage(unsigned int seed, const InputReader& input, + OutputWriter* output, protobuf::Message* message) { + GetMutator()->Seed(seed); + input.Read(message); + size_t max_size = GetMaxSize(input, *output, *message); + GetMutator()->Mutate(message, max_size); + if (size_t new_size = output->Write(*message)) { + assert(new_size <= output->size()); + GetCache()->Store(output->data(), new_size, message); + return new_size; + } + return 0; +} + +size_t CrossOverMessages(unsigned int seed, const InputReader& input1, + const InputReader& input2, OutputWriter* output, + protobuf::Message* message1, + protobuf::Message* message2) { + GetMutator()->Seed(seed); + input1.Read(message1); + input2.Read(message2); + size_t max_size = GetMaxSize(input1, *output, *message1); + GetMutator()->CrossOver(*message2, message1, max_size); + if (size_t new_size = output->Write(*message1)) { + assert(new_size <= output->size()); + GetCache()->Store(output->data(), new_size, message1); + return new_size; + } + return 0; +} + +size_t MutateTextMessage(uint8_t* data, size_t size, size_t max_size, + unsigned int seed, protobuf::Message* message) { + TextInputReader input(data, size); + TextOutputWriter output(data, max_size); + return MutateMessage(seed, input, &output, message); +} + +size_t CrossOverTextMessages(const uint8_t* data1, size_t size1, + const uint8_t* data2, size_t size2, uint8_t* out, + size_t max_out_size, unsigned int seed, + protobuf::Message* message1, + protobuf::Message* message2) { + TextInputReader input1(data1, size1); + TextInputReader input2(data2, size2); + TextOutputWriter output(out, max_out_size); + return CrossOverMessages(seed, input1, input2, &output, message1, message2); +} + +size_t MutateBinaryMessage(uint8_t* data, size_t size, size_t max_size, + unsigned int seed, protobuf::Message* message) { + BinaryInputReader input(data, size); + BinaryOutputWriter output(data, max_size); + return MutateMessage(seed, input, &output, message); +} + +size_t CrossOverBinaryMessages(const uint8_t* data1, size_t size1, + const uint8_t* data2, size_t size2, uint8_t* out, + size_t max_out_size, unsigned int seed, + protobuf::Message* message1, + protobuf::Message* message2) { + BinaryInputReader input1(data1, size1); + BinaryInputReader input2(data2, size2); + BinaryOutputWriter output(out, max_out_size); + return CrossOverMessages(seed, input1, input2, &output, message1, message2); +} + +} // namespace + +size_t CustomProtoMutator(bool binary, uint8_t* data, size_t size, + size_t max_size, unsigned int seed, + protobuf::Message* input) { + auto mutate = binary ? &MutateBinaryMessage : &MutateTextMessage; + return mutate(data, size, max_size, seed, input); +} + +size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1, + const uint8_t* data2, size_t size2, uint8_t* out, + size_t max_out_size, unsigned int seed, + protobuf::Message* input1, + protobuf::Message* input2) { + auto cross = binary ? &CrossOverBinaryMessages : &CrossOverTextMessages; + return cross(data1, size1, data2, size2, out, max_out_size, seed, input1, + input2); +} + +bool LoadProtoInput(bool binary, const uint8_t* data, size_t size, + protobuf::Message* input) { + if (GetCache()->LoadIfSame(data, size, input)) return true; + auto result = binary ? ParseBinaryMessage(data, size, input) + : ParseTextMessage(data, size, input); + if (!result) return false; + GetMutator()->Seed(size); + GetMutator()->Fix(input); + return true; +} + +void RegisterPostProcessor( + const protobuf::Descriptor* desc, + std::function<void(protobuf::Message* message, unsigned int seed)> + callback) { + GetMutator()->RegisterPostProcessor(desc, callback); +} + +} // namespace libfuzzer +} // namespace protobuf_mutator diff --git a/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_macro.h b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_macro.h new file mode 100644 index 0000000000..1a1fe0a297 --- /dev/null +++ b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_macro.h @@ -0,0 +1,129 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// 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 +// +// http://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. + +#ifndef SRC_LIBFUZZER_LIBFUZZER_MACRO_H_ +#define SRC_LIBFUZZER_LIBFUZZER_MACRO_H_ + +#include <stddef.h> + +#include <cstdint> +#include <functional> +#include <type_traits> + +#include "port/protobuf.h" + +// Defines custom mutator, crossover and test functions using default +// serialization format. Default is text. +#define DEFINE_PROTO_FUZZER(arg) DEFINE_TEXT_PROTO_FUZZER(arg) +// Defines custom mutator, crossover and test functions using text +// serialization. This format is more convenient to read. +#define DEFINE_TEXT_PROTO_FUZZER(arg) DEFINE_PROTO_FUZZER_IMPL(false, arg) +// Defines custom mutator, crossover and test functions using binary +// serialization. This makes mutations faster. However often test function is +// significantly slower than mutator, so fuzzing rate may stay unchanged. +#define DEFINE_BINARY_PROTO_FUZZER(arg) DEFINE_PROTO_FUZZER_IMPL(true, arg) + +// Registers the callback as a potential mutation performed on the parent +// message of a field. This must be called inside an initialization code block. +// libFuzzer suggests putting one-time-initialization in a function used to +// initialize a static variable inside the fuzzer target. For example: +// +// static bool Modify( +// SomeMessage* message /* Fix or additionally modify the message */, +// unsigned int seed /* If random generator is needed use this seed */) { +// ... +// } +// +// DEFINE_PROTO_FUZZER(const SomeMessage& msg) { +// static PostProcessorRegistration reg(&Modify); +// } + +// Implementation of macros above. +#define DEFINE_CUSTOM_PROTO_MUTATOR_IMPL(use_binary, Proto) \ + extern "C" size_t LLVMFuzzerCustomMutator( \ + uint8_t* data, size_t size, size_t max_size, unsigned int seed) { \ + using protobuf_mutator::libfuzzer::CustomProtoMutator; \ + Proto input; \ + return CustomProtoMutator(use_binary, data, size, max_size, seed, &input); \ + } + +#define DEFINE_CUSTOM_PROTO_CROSSOVER_IMPL(use_binary, Proto) \ + extern "C" size_t LLVMFuzzerCustomCrossOver( \ + const uint8_t* data1, size_t size1, const uint8_t* data2, size_t size2, \ + uint8_t* out, size_t max_out_size, unsigned int seed) { \ + using protobuf_mutator::libfuzzer::CustomProtoCrossOver; \ + Proto input1; \ + Proto input2; \ + return CustomProtoCrossOver(use_binary, data1, size1, data2, size2, out, \ + max_out_size, seed, &input1, &input2); \ + } + +#define DEFINE_TEST_ONE_PROTO_INPUT_IMPL(use_binary, Proto) \ + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { \ + using protobuf_mutator::libfuzzer::LoadProtoInput; \ + Proto input; \ + if (LoadProtoInput(use_binary, data, size, &input)) \ + TestOneProtoInput(input); \ + return 0; \ + } + +#define DEFINE_POST_PROCESS_PROTO_MUTATION_IMPL(Proto) \ + using PostProcessorRegistration = \ + protobuf_mutator::libfuzzer::PostProcessorRegistration<Proto>; + +#define DEFINE_PROTO_FUZZER_IMPL(use_binary, arg) \ + static void TestOneProtoInput(arg); \ + using FuzzerProtoType = std::remove_const<std::remove_reference< \ + std::function<decltype(TestOneProtoInput)>::argument_type>::type>::type; \ + DEFINE_CUSTOM_PROTO_MUTATOR_IMPL(use_binary, FuzzerProtoType) \ + DEFINE_CUSTOM_PROTO_CROSSOVER_IMPL(use_binary, FuzzerProtoType) \ + DEFINE_TEST_ONE_PROTO_INPUT_IMPL(use_binary, FuzzerProtoType) \ + DEFINE_POST_PROCESS_PROTO_MUTATION_IMPL(FuzzerProtoType) \ + static void TestOneProtoInput(arg) + +namespace protobuf_mutator { +namespace libfuzzer { + +size_t CustomProtoMutator(bool binary, uint8_t* data, size_t size, + size_t max_size, unsigned int seed, + protobuf::Message* input); +size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1, + const uint8_t* data2, size_t size2, uint8_t* out, + size_t max_out_size, unsigned int seed, + protobuf::Message* input1, + protobuf::Message* input2); +bool LoadProtoInput(bool binary, const uint8_t* data, size_t size, + protobuf::Message* input); + +void RegisterPostProcessor( + const protobuf::Descriptor* desc, + std::function<void(protobuf::Message* message, unsigned int seed)> + callback); + +template <class Proto> +struct PostProcessorRegistration { + PostProcessorRegistration( + const std::function<void(Proto* message, unsigned int seed)>& callback) { + RegisterPostProcessor( + Proto::descriptor(), + [callback](protobuf::Message* message, unsigned int seed) { + callback(static_cast<Proto*>(message), seed); + }); + } +}; + +} // namespace libfuzzer +} // namespace protobuf_mutator + +#endif // SRC_LIBFUZZER_LIBFUZZER_MACRO_H_ diff --git a/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_mutator.cc b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_mutator.cc new file mode 100644 index 0000000000..9c3be921e6 --- /dev/null +++ b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_mutator.cc @@ -0,0 +1,100 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// 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 +// +// http://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. + +#include "src/libfuzzer/libfuzzer_mutator.h" + +#include <string.h> + +#include <algorithm> +#include <cassert> +#include <memory> +#include <string> + +#include "port/protobuf.h" +#include "src/mutator.h" + +// see compiler-rt/lib/sanitizer-common/sanitizer_internal_defs.h; usage same as +// SANITIZER_INTERFACE_WEAK_DEF with some functionality removed +#ifdef _MSC_VER +#if defined(_M_IX86) || defined(__i386__) +#define WIN_SYM_PREFIX "_" +#else +#define WIN_SYM_PREFIX +#endif + +#define STRINGIFY_(A) #A +#define STRINGIFY(A) STRINGIFY_(A) + +#define WEAK_DEFAULT_NAME(Name) Name##__def + +// clang-format off +#define LIB_PROTO_MUTATOR_WEAK_DEF(ReturnType, Name, ...) \ + __pragma(comment(linker, "/alternatename:" \ + WIN_SYM_PREFIX STRINGIFY(Name) "=" \ + WIN_SYM_PREFIX STRINGIFY(WEAK_DEFAULT_NAME(Name))))\ + extern "C" ReturnType Name(__VA_ARGS__); \ + extern "C" ReturnType WEAK_DEFAULT_NAME(Name)(__VA_ARGS__) +// clang-format on +#else +#define LIB_PROTO_MUTATOR_WEAK_DEF(ReturnType, Name, ...) \ + extern "C" __attribute__((weak)) ReturnType Name(__VA_ARGS__) +#endif + +LIB_PROTO_MUTATOR_WEAK_DEF(size_t, LLVMFuzzerMutate, uint8_t*, size_t, size_t) { + return 0; +} + +namespace protobuf_mutator { +namespace libfuzzer { + +namespace { + +template <class T> +T MutateValue(T v) { + size_t size = + LLVMFuzzerMutate(reinterpret_cast<uint8_t*>(&v), sizeof(v), sizeof(v)); + memset(reinterpret_cast<uint8_t*>(&v) + size, 0, sizeof(v) - size); + return v; +} + +} // namespace + +int32_t Mutator::MutateInt32(int32_t value) { return MutateValue(value); } + +int64_t Mutator::MutateInt64(int64_t value) { return MutateValue(value); } + +uint32_t Mutator::MutateUInt32(uint32_t value) { return MutateValue(value); } + +uint64_t Mutator::MutateUInt64(uint64_t value) { return MutateValue(value); } + +float Mutator::MutateFloat(float value) { return MutateValue(value); } + +double Mutator::MutateDouble(double value) { return MutateValue(value); } + +TProtoStringType Mutator::MutateString(const TProtoStringType& value, + int size_increase_hint) { + // Randomly return empty strings as LLVMFuzzerMutate does not produce them. + // Use uint16_t because on Windows, uniform_int_distribution does not support + // any 8 bit types. + if (!std::uniform_int_distribution<uint16_t>(0, 20)(*random())) return {}; + TProtoStringType result = value; + int new_size = value.size() + size_increase_hint; + result.resize(std::max(1, new_size)); + result.resize(LLVMFuzzerMutate(reinterpret_cast<uint8_t*>(&result[0]), + value.size(), result.size())); + return result; +} + +} // namespace libfuzzer +} // namespace protobuf_mutator diff --git a/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_mutator.h b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_mutator.h new file mode 100644 index 0000000000..722993efe8 --- /dev/null +++ b/contrib/libs/protobuf-mutator/src/libfuzzer/libfuzzer_mutator.h @@ -0,0 +1,46 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// 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 +// +// http://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. + +#ifndef SRC_LIBFUZZER_LIBFUZZER_MUTATOR_H_ +#define SRC_LIBFUZZER_LIBFUZZER_MUTATOR_H_ + +#include <string> + +#include "src/mutator.h" + +namespace protobuf_mutator { +namespace libfuzzer { + +// Overrides protobuf_mutator::Mutator::Mutate* methods with implementation +// which uses libFuzzer library. protobuf_mutator::Mutator has very basic +// implementation of this methods. +class Mutator : public protobuf_mutator::Mutator { + public: + using protobuf_mutator::Mutator::Mutator; + + protected: + int32_t MutateInt32(int32_t value) override; + int64_t MutateInt64(int64_t value) override; + uint32_t MutateUInt32(uint32_t value) override; + uint64_t MutateUInt64(uint64_t value) override; + float MutateFloat(float value) override; + double MutateDouble(double value) override; + TProtoStringType MutateString(const TProtoStringType& value, + int size_increase_hint) override; +}; + +} // namespace libfuzzer +} // namespace protobuf_mutator + +#endif // SRC_LIBFUZZER_LIBFUZZER_MUTATOR_H_ |