Skip to content

Commit dbc4c0f

Browse files
committed
Implement Any support
Fixes #151
1 parent 1989ccb commit dbc4c0f

File tree

6 files changed

+151
-13
lines changed

6 files changed

+151
-13
lines changed

examples/libfuzzer/libfuzzer_bin_example.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <cmath>
1616

1717
#include "examples/libfuzzer/libfuzzer_example.pb.h"
18+
#include "port/protobuf.h"
1819
#include "src/libfuzzer/libfuzzer_macro.h"
1920

2021
protobuf_mutator::protobuf::LogSilencer log_silincer;
@@ -26,15 +27,35 @@ DEFINE_BINARY_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
2627
message->set_optional_uint64(
2728
std::hash<std::string>{}(message->optional_string()));
2829
}
30+
31+
if (message->has_any()) {
32+
auto* any = message->mutable_any();
33+
34+
// Guide mutator to usefull 'Any' types.
35+
static const char* const expected_types[] = {
36+
"type.googleapis.com/google.protobuf.DescriptorProto",
37+
"type.googleapis.com/google.protobuf.FileDescriptorProto",
38+
};
39+
40+
if (!std::count(std::begin(expected_types), std::end(expected_types),
41+
any->type_url())) {
42+
const size_t num =
43+
(std::end(expected_types) - std::begin(expected_types));
44+
any->set_type_url(expected_types[seed % num]);
45+
}
46+
}
2947
}};
3048

49+
protobuf_mutator::protobuf::FileDescriptorProto file;
50+
3151
// Emulate a bug.
3252
if (message.optional_uint64() ==
3353
std::hash<std::string>{}(message.optional_string()) &&
3454
message.optional_string() == "abcdefghijklmnopqrstuvwxyz" &&
3555
!std::isnan(message.optional_float()) &&
3656
std::fabs(message.optional_float()) > 1000 &&
37-
std::fabs(message.optional_float()) < 1E10) {
57+
message.any().UnpackTo(&file) && !file.name().empty()) {
58+
std::cerr << message.DebugString() << "\n";
3859
abort();
3960
}
4061
}

examples/libfuzzer/libfuzzer_example.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <cmath>
1616

1717
#include "examples/libfuzzer/libfuzzer_example.pb.h"
18+
#include "port/protobuf.h"
1819
#include "src/libfuzzer/libfuzzer_macro.h"
1920

2021
protobuf_mutator::protobuf::LogSilencer log_silincer;
@@ -26,15 +27,35 @@ DEFINE_PROTO_FUZZER(const libfuzzer_example::Msg& message) {
2627
message->set_optional_uint64(
2728
std::hash<std::string>{}(message->optional_string()));
2829
}
30+
31+
if (message->has_any()) {
32+
auto* any = message->mutable_any();
33+
34+
// Guide mutator to usefull 'Any' types.
35+
static const char* const expected_types[] = {
36+
"type.googleapis.com/google.protobuf.DescriptorProto",
37+
"type.googleapis.com/google.protobuf.FileDescriptorProto",
38+
};
39+
40+
if (!std::count(std::begin(expected_types), std::end(expected_types),
41+
any->type_url())) {
42+
const size_t num =
43+
(std::end(expected_types) - std::begin(expected_types));
44+
any->set_type_url(expected_types[seed % num]);
45+
}
46+
}
2947
}};
3048

49+
protobuf_mutator::protobuf::FileDescriptorProto file;
50+
3151
// Emulate a bug.
3252
if (message.optional_uint64() ==
3353
std::hash<std::string>{}(message.optional_string()) &&
3454
message.optional_string() == "abcdefghijklmnopqrstuvwxyz" &&
3555
!std::isnan(message.optional_float()) &&
3656
std::fabs(message.optional_float()) > 1000 &&
37-
std::fabs(message.optional_float()) < 1E10) {
57+
message.any().UnpackTo(&file) && !file.name().empty()) {
58+
std::cerr << message.DebugString() << "\n";
3859
abort();
3960
}
4061
}

examples/libfuzzer/libfuzzer_example_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ int GetError(int exit_code) { return WSTOPSIG(exit_code); }
2525

2626
TEST_F(LibFuzzerExampleTest, Text) {
2727
EXPECT_EQ(kDefaultLibFuzzerError,
28-
GetError(RunFuzzer("libfuzzer_example", 150, 10000000)));
28+
GetError(RunFuzzer("libfuzzer_example", 1000, 10000000)));
2929
}
3030

3131
TEST_F(LibFuzzerExampleTest, Binary) {
3232
EXPECT_EQ(kDefaultLibFuzzerError,
33-
GetError(RunFuzzer("libfuzzer_bin_example", 150, 10000000)));
33+
GetError(RunFuzzer("libfuzzer_bin_example", 1000, 10000000)));
3434
}
3535

3636
} // namespace

port/protobuf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <string>
1919

20+
#include "google/protobuf/any.pb.h"
2021
#include "google/protobuf/descriptor.pb.h"
2122
#include "google/protobuf/message.h"
2223
#include "google/protobuf/text_format.h"

src/mutator.cc

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <algorithm>
1818
#include <bitset>
1919
#include <map>
20+
#include <memory>
2021
#include <random>
2122
#include <string>
2223
#include <vector>
@@ -27,6 +28,7 @@
2728

2829
namespace protobuf_mutator {
2930

31+
using protobuf::Any;
3032
using protobuf::Descriptor;
3133
using protobuf::FieldDescriptor;
3234
using protobuf::FileDescriptor;
@@ -360,15 +362,76 @@ class DataSourceSampler {
360362
WeightedReservoirSampler<ConstFieldInstance, RandomEngine> sampler_;
361363
};
362364

365+
using UnpackedAny =
366+
std::unordered_map<const Message*, std::unique_ptr<Message>>;
367+
368+
const Descriptor* GetAnyTypeDescriptor(const Any& any) {
369+
std::string type_name;
370+
if (!Any::ParseAnyTypeUrl(any.type_url(), &type_name)) return nullptr;
371+
return any.descriptor()->file()->pool()->FindMessageTypeByName(type_name);
372+
}
373+
374+
std::unique_ptr<Message> UnpackAny(const Any& any) {
375+
const Descriptor* desc = GetAnyTypeDescriptor(any);
376+
if (!desc) return {};
377+
std::unique_ptr<Message> message(
378+
any.GetReflection()->GetMessageFactory()->GetPrototype(desc)->New());
379+
message->ParsePartialFromString(any.value());
380+
return message;
381+
}
382+
383+
const Any* CastToAny(const Message* message) {
384+
return Any::GetDescriptor() == message->GetDescriptor()
385+
? static_cast<const Any*>(message)
386+
: nullptr;
387+
}
388+
389+
Any* CastToAny(Message* message) {
390+
return Any::GetDescriptor() == message->GetDescriptor()
391+
? static_cast<Any*>(message)
392+
: nullptr;
393+
}
394+
395+
std::unique_ptr<Message> UnpackIfAny(const Message& message) {
396+
if (const Any* any = CastToAny(&message)) return UnpackAny(*any);
397+
return {};
398+
}
399+
400+
void UnpackAny(const Message& message, UnpackedAny* result) {
401+
if (std::unique_ptr<Message> any = UnpackIfAny(message)) {
402+
UnpackAny(*any, result);
403+
result->emplace(&message, std::move(any));
404+
return;
405+
}
406+
407+
const Descriptor* descriptor = message.GetDescriptor();
408+
const Reflection* reflection = message.GetReflection();
409+
410+
for (int i = 0; i < descriptor->field_count(); ++i) {
411+
const FieldDescriptor* field = descriptor->field(i);
412+
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
413+
if (field->is_repeated()) {
414+
const int field_size = reflection->FieldSize(message, field);
415+
for (int j = 0; j < field_size; ++j) {
416+
UnpackAny(reflection->GetRepeatedMessage(message, field, j), result);
417+
}
418+
} else if (reflection->HasField(message, field)) {
419+
UnpackAny(reflection->GetMessage(message, field), result);
420+
}
421+
}
422+
}
423+
}
424+
363425
class PostProcessing {
364426
public:
365427
using PostProcessors =
366428
std::unordered_multimap<const Descriptor*, Mutator::PostProcess>;
367429

368430
PostProcessing(bool keep_initialized, const PostProcessors& post_processors,
369-
RandomEngine* random)
431+
UnpackedAny& any, RandomEngine* random)
370432
: keep_initialized_(keep_initialized),
371433
post_processors_(post_processors),
434+
any_(any),
372435
random_(random) {}
373436

374437
void Run(Message* message, int max_depth) {
@@ -410,6 +473,22 @@ class PostProcessing {
410473
}
411474
}
412475

476+
if (Any* any = CastToAny(message)) {
477+
if (max_depth < 0) {
478+
// Clear deep Any fields to avoid stack overflow.
479+
any->Clear();
480+
} else {
481+
auto It = any_.find(message);
482+
if (It != any_.end()) {
483+
Run(It->second.get(), max_depth);
484+
// assert(GetAnyTypeDescriptor(*any) == It->second->GetDescriptor());
485+
// if (GetAnyTypeDescriptor(*any) != It->second->GetDescriptor()) {}
486+
It->second->SerializePartialToString(any->mutable_value());
487+
}
488+
}
489+
}
490+
491+
// Call user callback after message trimmed, initialized and packed.
413492
auto range = post_processors_.equal_range(descriptor);
414493
for (auto it = range.first; it != range.second; ++it)
415494
it->second(message, (*random_)());
@@ -418,6 +497,7 @@ class PostProcessing {
418497
private:
419498
bool keep_initialized_;
420499
const PostProcessors& post_processors_;
500+
UnpackedAny& any_;
421501
RandomEngine* random_;
422502
};
423503

@@ -543,30 +623,47 @@ struct CreateField : public FieldFunction<CreateField> {
543623
void Mutator::Seed(uint32_t value) { random_.seed(value); }
544624

545625
void Mutator::Mutate(Message* message, size_t max_size_hint) {
626+
UnpackedAny any;
627+
UnpackAny(*message, &any);
628+
546629
Messages messages;
630+
messages.reserve(any.size() + 1);
547631
messages.push_back(message);
632+
for (const auto& kv : any) messages.push_back(kv.second.get());
633+
548634
ConstMessages sources(messages.begin(), messages.end());
549635
MutateImpl(sources, messages, false,
550636
static_cast<int>(max_size_hint) -
551637
static_cast<int>(message->ByteSizeLong()));
552638

553-
PostProcessing(keep_initialized_, post_processors_, &random_)
639+
PostProcessing(keep_initialized_, post_processors_, any, &random_)
554640
.Run(message, kMaxInitializeDepth);
555641
assert(IsInitialized(*message));
556642
}
557643

558644
void Mutator::CrossOver(const Message& message1, Message* message2,
559645
size_t max_size_hint) {
646+
UnpackedAny any;
647+
UnpackAny(*message2, &any);
648+
560649
Messages messages;
650+
messages.reserve(any.size() + 1);
561651
messages.push_back(message2);
652+
for (auto& kv : any) messages.push_back(kv.second.get());
653+
654+
UnpackAny(message1, &any);
655+
562656
ConstMessages sources;
657+
sources.reserve(any.size() + 2);
563658
sources.push_back(&message1);
564659
sources.push_back(message2);
565-
int size_increase_hint = static_cast<int>(max_size_hint) -
566-
static_cast<int>(message2->ByteSizeLong());
567-
MutateImpl(sources, messages, true, size_increase_hint);
660+
for (const auto& kv : any) sources.push_back(kv.second.get());
661+
662+
MutateImpl(sources, messages, true,
663+
static_cast<int>(max_size_hint) -
664+
static_cast<int>(message2->ByteSizeLong()));
568665

569-
PostProcessing(keep_initialized_, post_processors_, &random_)
666+
PostProcessing(keep_initialized_, post_processors_, any, &random_)
570667
.Run(message2, kMaxInitializeDepth);
571668
assert(IsInitialized(*message2));
572669
}

src/mutator_test.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,6 @@ std::vector<TestParams> GetFieldTestParams(
365365
for (auto t : tests) {
366366
auto lines = Split(t);
367367
for (size_t i = 0; i != lines.size(); ++i) {
368-
if (lines[i].find("any {") != std::string::npos) break;
369368
if (lines[i].find(':') != std::string::npos)
370369
results.push_back(
371370
std::make_tuple(&T::default_instance(), t, i, lines[i]));
@@ -381,7 +380,6 @@ std::vector<TestParams> GetMessageTestParams(
381380
for (auto t : tests) {
382381
auto lines = Split(t);
383382
for (size_t i = 0; i != lines.size(); ++i) {
384-
if (lines[i].find("any {") != std::string::npos) break;
385383
if (lines[i].find("{}") != std::string::npos)
386384
results.push_back(
387385
std::make_tuple(&T::default_instance(), t, i, lines[i]));
@@ -398,7 +396,7 @@ bool Mutate(const protobuf::Message& from, const protobuf::Message& to,
398396
EXPECT_FALSE(MessageDifferencer::Equals(from, to));
399397
for (int j = 0; j < iterations; ++j) {
400398
message->CopyFrom(from);
401-
mutator.Mutate(message.get(), 1000);
399+
mutator.Mutate(message.get(), 1500);
402400
if (MessageDifferencer::Equals(*message, to)) return true;
403401
}
404402

0 commit comments

Comments
 (0)