Skip to content

Commit d0c2c77

Browse files
committed
Avoid parsing of last mutated message
It safe some CPU cycles. Still main reason is to avoid coverage noise from parser and custom post processors.
1 parent 3c50b67 commit d0c2c77

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

src/libfuzzer/libfuzzer_macro.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "src/libfuzzer/libfuzzer_macro.h"
1616

1717
#include <algorithm>
18+
#include <memory>
19+
#include <vector>
1820

1921
#include "src/binary_format.h"
2022
#include "src/libfuzzer/libfuzzer_mutator.h"
@@ -91,6 +93,35 @@ class BinaryOutputWriter : public OutputWriter {
9193
}
9294
};
9395

96+
class LastMutationCache {
97+
public:
98+
void Store(const uint8_t* data, size_t size, protobuf::Message* message) {
99+
if (!message_) message_.reset(message->New());
100+
message->GetReflection()->Swap(message, message_.get());
101+
data_.assign(data, data + size);
102+
}
103+
104+
bool LoadIfSame(const uint8_t* data, size_t size,
105+
protobuf::Message* message) {
106+
if (!message_ || size != data_.size() ||
107+
!std::equal(data_.begin(), data_.end(), data))
108+
return false;
109+
110+
message->GetReflection()->Swap(message, message_.get());
111+
message_.reset();
112+
return true;
113+
}
114+
115+
private:
116+
std::vector<uint8_t> data_;
117+
std::unique_ptr<protobuf::Message> message_;
118+
};
119+
120+
LastMutationCache* GetCache() {
121+
static LastMutationCache cache;
122+
return &cache;
123+
}
124+
94125
Mutator* GetMutator() {
95126
static Mutator mutator;
96127
return &mutator;
@@ -111,6 +142,7 @@ size_t MutateMessage(unsigned int seed, const InputReader& input,
111142
GetMutator()->Mutate(message, max_size);
112143
if (size_t new_size = output->Write(*message)) {
113144
assert(new_size <= output->size());
145+
GetCache()->Store(output->data(), new_size, message);
114146
return new_size;
115147
}
116148
return 0;
@@ -127,6 +159,7 @@ size_t CrossOverMessages(unsigned int seed, const InputReader& input1,
127159
GetMutator()->CrossOver(*message2, message1, max_size);
128160
if (size_t new_size = output->Write(*message1)) {
129161
assert(new_size <= output->size());
162+
GetCache()->Store(output->data(), new_size, message1);
130163
return new_size;
131164
}
132165
return 0;
@@ -189,6 +222,7 @@ size_t CustomProtoCrossOver(bool binary, const uint8_t* data1, size_t size1,
189222

190223
bool LoadProtoInput(bool binary, const uint8_t* data, size_t size,
191224
protobuf::Message* input) {
225+
if (GetCache()->LoadIfSame(data, size, input)) return true;
192226
auto result = binary ? ParseBinaryMessage(data, size, input)
193227
: ParseTextMessage(data, size, input);
194228
if (!result) return false;

src/libfuzzer/libfuzzer_test.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,32 @@ TEST(LibFuzzerTest, LLVMFuzzerTestOneInput) {
6666
mock, TestOneInput(AllOf(IsMessageEq(std::cref(msg)), IsInitialized())));
6767
LLVMFuzzerTestOneInput((const uint8_t*)"", 0);
6868
}
69+
70+
TEST(LibFuzzerTest, LLVMFuzzerCustomMutator) {
71+
testing::StrictMock<MockFuzzer> mock;
72+
protobuf_mutator::Msg msg;
73+
EXPECT_CALL(mock, PostProcess(_, _)).WillOnce(SaveArgPointee<0>(&msg));
74+
EXPECT_CALL(
75+
mock, TestOneInput(AllOf(IsMessageEq(std::cref(msg)), IsInitialized())));
76+
77+
uint8_t buff[1024] = {};
78+
size_t size = LLVMFuzzerCustomMutator(buff, 0, sizeof(buff), 5);
79+
ASSERT_GT(size, 0U);
80+
LLVMFuzzerTestOneInput(buff, size);
81+
}
82+
83+
TEST(LibFuzzerTest, LLVMFuzzerCustomCrossOver) {
84+
testing::StrictMock<MockFuzzer> mock;
85+
protobuf_mutator::Msg msg;
86+
EXPECT_CALL(mock, PostProcess(_, _)).WillOnce(SaveArgPointee<0>(&msg));
87+
EXPECT_CALL(
88+
mock, TestOneInput(AllOf(IsMessageEq(std::cref(msg)), IsInitialized())));
89+
90+
uint8_t buff1[1024] = {};
91+
uint8_t buff2[1024] = {};
92+
uint8_t buff3[1024] = {};
93+
size_t size =
94+
LLVMFuzzerCustomCrossOver(buff1, 0, buff2, 0, buff3, sizeof(buff3), 6);
95+
ASSERT_GT(size, 0U);
96+
LLVMFuzzerTestOneInput(buff3, size);
6997
}

0 commit comments

Comments
 (0)