Skip to content

Commit dad575b

Browse files
authored
Add support for Ubuntu 20.04 LTS (where OSS-Fuzz is at) (#274)
* Support Protobuf <3.7.0 lacking headers port_def.inc and port_undef.inc There are two Protobuf version macros: GOOGLE_PROTOBUF_VERSION and PROTOBUF_VERSION without the "GOOGLE_" prefix. Both ancient and recent only expose macro GOOGLE_PROTOBUF_VERSION with the "GOOGLE_" prefix but not the other, so we depart from PROTOBUF_VERSION. Motivation for this change was that headers port_def.inc and port_undef.inc were only introduced to Protobuf in commit 6bbe197e9c1b6fc38cbdc45e3bf83fa7ced792a3 of release >=3.7.0 while we want to support Protobuf 3.6.1.3 of Ubuntu 20.04, too. Signed-off-by: Sebastian Pipping <[email protected]> * Support Protobuf <3.12.0 lacking FieldDescriptor::has_presence() Signed-off-by: Sebastian Pipping <[email protected]> * Support Protobuf <3.8.0 lacking Any::GetDescriptor() Signed-off-by: Sebastian Pipping <[email protected]> * Support Protobuf <3.8.0 lacking .SetRecursionLimit and .AllowUnknownField Signed-off-by: Sebastian Pipping <[email protected]> * fuzzer_test.h: Make tests/CI more deterministic/stable Seed 9 was selected after quick experiments with values 1 to 20 for its speed, i.e. low number of runs needed to find the respective bug in both libfuzzer_example (text) and libfuzzer_bin_example (binary), combined (i.e. number of runs added). It was not a scientifc experiment by any means, and determinism (rather than speed) is my primary goal here. Signed-off-by: Sebastian Pipping <[email protected]> * fuzzer_test.h: Add -print_final_stats=1 to libFuzzer call .. to ease future debugging Signed-off-by: Sebastian Pipping <[email protected]> * Make LibFuzzerExampleTest fail earlier .. now that the fixed seed makes that safe to do Signed-off-by: Sebastian Pipping <[email protected]> * Reject Clang <12 because it breaks libfuzzer_example_test Signed-off-by: Sebastian Pipping <[email protected]> * Make CI cover Ubuntu 20.04 (with -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=OFF) .. with Protobuf 3.6.1.3 (see https://packages.ubuntu.com/focal/libprotobuf-dev) Signed-off-by: Sebastian Pipping <[email protected]> * Sync CMake requirement for -DLIB_PROTO_MUTATOR_EXAMPLES=ON with reality .. while allowing to build the core project with CMake 3.10. Signed-off-by: Sebastian Pipping <[email protected]> * README.md: Document dependencies of core libprotobuf-mutator Signed-off-by: Sebastian Pipping <[email protected]> --------- Signed-off-by: Sebastian Pipping <[email protected]>
1 parent 2dcf442 commit dad575b

File tree

10 files changed

+74
-14
lines changed

10 files changed

+74
-14
lines changed

.github/workflows/cmake-multi-platform.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ jobs:
4040
c_compiler: clang
4141
cpp_compiler: clang++
4242
bundled_protobuf: 'OFF'
43+
- os: ubuntu-20.04
44+
build_type: Release
45+
c_compiler: clang-12
46+
cpp_compiler: clang++-12
47+
bundled_protobuf: 'OFF'
48+
- os: ubuntu-20.04
49+
build_type: Debug
50+
c_compiler: clang-12
51+
cpp_compiler: clang++-12
52+
bundled_protobuf: 'OFF'
4353

4454
steps:
4555
- uses: actions/checkout@v3

CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ if (MSVC)
6161
endif (LIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF OR LIB_PROTO_MUTATOR_MSVC_STATIC_RUNTIME)
6262
endif (MSVC)
6363

64+
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12)
65+
message(SEND_ERROR "Clang/libFuzzer >=12.0.0 is needed but you are using ${CMAKE_CXX_COMPILER_VERSION}, please fix.")
66+
endif()
67+
6468
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
6569
check_cxx_compiler_flag(-fsanitize=address LIB_PROTO_MUTATOR_HAS_SANITIZE_ADDRESS)
6670
check_cxx_compiler_flag("-fsanitize=address -fsanitize-address-use-after-scope"
@@ -148,6 +152,11 @@ add_subdirectory(src)
148152

149153
if (LIB_PROTO_MUTATOR_EXAMPLES AND (NOT "${LIB_PROTO_MUTATOR_FUZZER_LIBRARIES}" STREQUAL "" OR
150154
NOT "${FUZZING_FLAGS}" STREQUAL ""))
155+
# NOTE: We need the maximum from:
156+
# - CMake >=3.10 for libexpat 2.6.4
157+
# - CMake >=3.13 for GoogleTest 1.15.0
158+
# - CMake >=3.18 for libxml2 2.13.6
159+
cmake_minimum_required(VERSION 3.18)
151160
add_subdirectory(examples EXCLUDE_FROM_ALL)
152161
endif()
153162

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ libprotobuf-mutator is a library to randomly mutate
88
[protobuffers](https://github.com/google/protobuf). <BR>
99
It could be used together with guided fuzzing engines, such as [libFuzzer](http://libfuzzer.info).
1010

11+
The core of libprotobuf-mutator has the following dependencies:
12+
13+
- Protobuf >=3.6.1.3 (library and compiler)
14+
- Clang >=12.0.0 including libFuzzer
15+
1116
## Quick start on Debian/Ubuntu
1217

1318
Install prerequisites:

examples/fuzzer_test.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class FuzzerTest : public testing::Test {
3939

4040
int RunFuzzer(const std::string& name, int max_len, int runs) {
4141
std::string cmd = "ASAN_OPTIONS=detect_leaks=0 ./" + name;
42+
cmd += " -seed=9";
43+
cmd += " -print_final_stats=1";
4244
cmd += " -detect_leaks=0";
4345
cmd += " -len_control=0";
4446
cmd += " -max_len=" + std::to_string(max_len);

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", 1000, 10000000)));
28+
GetError(RunFuzzer("libfuzzer_example", 1000, 100000)));
2929
}
3030

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

3636
} // namespace

port/protobuf.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,14 @@
2020
#include "google/protobuf/any.pb.h"
2121
#include "google/protobuf/descriptor.pb.h"
2222
#include "google/protobuf/message.h"
23+
#include "google/protobuf/stubs/common.h" // for GOOGLE_PROTOBUF_VERSION
2324
#include "google/protobuf/text_format.h"
2425
#include "google/protobuf/util/message_differencer.h"
2526
#include "google/protobuf/wire_format.h"
2627

27-
// clang-format off
28-
#include "google/protobuf/port_def.inc" // MUST be last header included
29-
// clang-format on
3028
namespace google {
3129
namespace protobuf {
32-
#if PROTOBUF_VERSION < 4025000
30+
#if GOOGLE_PROTOBUF_VERSION < 4025000
3331

3432
template <typename T>
3533
const T* DownCastMessage(const Message* message) {
@@ -42,7 +40,7 @@ T* DownCastMessage(Message* message) {
4240
return const_cast<T*>(DownCastMessage<T>(message_const));
4341
}
4442

45-
#elif PROTOBUF_VERSION < 5029000
43+
#elif GOOGLE_PROTOBUF_VERSION < 5029000
4644

4745
template <typename T>
4846
const T* DownCastMessage(const Message* message) {
@@ -54,11 +52,9 @@ T* DownCastMessage(Message* message) {
5452
return DownCastToGenerated<T>(message);
5553
}
5654

57-
#endif // PROTOBUF_VERSION
55+
#endif // GOOGLE_PROTOBUF_VERSION
5856
} // namespace protobuf
5957
} // namespace google
60-
#include "google/protobuf/port_undef.inc"
61-
6258

6359
namespace protobuf_mutator {
6460

src/field_instance.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class ConstFieldInstance {
191191
}
192192

193193
bool EnforceUtf8() const {
194-
#if PROTOBUF_VERSION >= 4022000 // v3(!).22.0 (commit d85c9944c55fb38f4eae149979a0f680ea125ecb) for requires_utf8_validation
194+
#if GOOGLE_PROTOBUF_VERSION >= 4022000 // v3(!).22.0 (commit d85c9944c55fb38f4eae149979a0f680ea125ecb) for requires_utf8_validation
195195
return descriptor_->requires_utf8_validation();
196196
#else
197197
return descriptor_->type() == protobuf::FieldDescriptor::TYPE_STRING &&

src/mutator.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,17 @@ bool GetRandomBool(RandomEngine* random, size_t n = 2) {
8787
}
8888

8989
bool IsProto3SimpleField(const FieldDescriptor& field) {
90-
return !field.is_repeated() && !field.has_presence();
90+
#if GOOGLE_PROTOBUF_VERSION >= 3012000 // commit bb30225f06c36399757dc698b409d5f79738e8d1 of >=3.12.0
91+
const bool has_presence = field.has_presence();
92+
#else
93+
// NOTE: This mimics Protobuf 3.21.12 ("3021012")
94+
const bool has_presence = ! field.is_repeated() && (
95+
field.cpp_type() == FieldDescriptor::CppType::CPPTYPE_MESSAGE
96+
|| field.containing_oneof()
97+
|| field.file()->syntax() == FileDescriptor::SYNTAX_PROTO2
98+
);
99+
#endif
100+
return !field.is_repeated() && !has_presence;
91101
}
92102

93103
struct CreateDefaultField : public FieldFunction<CreateDefaultField> {
@@ -380,13 +390,23 @@ std::unique_ptr<Message> UnpackAny(const Any& any) {
380390
}
381391

382392
const Any* CastToAny(const Message* message) {
383-
return Any::GetDescriptor() == message->GetDescriptor()
393+
#if GOOGLE_PROTOBUF_VERSION >= 3008000 // commit 1467e08d7c26a7087e5e5b14a4ab2755926e7249 of >=3.8.0
394+
const Descriptor* any_descriptor = Any::GetDescriptor();
395+
#else
396+
const Descriptor* any_descriptor = Any::descriptor();
397+
#endif
398+
return any_descriptor == message->GetDescriptor()
384399
? protobuf::DownCastMessage<Any>(message)
385400
: nullptr;
386401
}
387402

388403
Any* CastToAny(Message* message) {
389-
return Any::GetDescriptor() == message->GetDescriptor()
404+
#if GOOGLE_PROTOBUF_VERSION >= 3008000 // commit 1467e08d7c26a7087e5e5b14a4ab2755926e7249 of >=3.8.0
405+
const Descriptor* any_descriptor = Any::GetDescriptor();
406+
#else
407+
const Descriptor* any_descriptor = Any::descriptor();
408+
#endif
409+
return any_descriptor == message->GetDescriptor()
390410
? protobuf::DownCastMessage<Any>(message)
391411
: nullptr;
392412
}

src/mutator_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,12 +678,23 @@ TYPED_TEST(MutatorTypedTest, Serialization) {
678678
}
679679

680680
TYPED_TEST(MutatorTypedTest, UnknownFieldTextFormat) {
681+
// NOTE: This test has little chance of passing when method
682+
// TextFormat::Parser::AllowUnknownField is not available
683+
#if GOOGLE_PROTOBUF_VERSION >= 3008000 // commit 176f7db11d8242b36a3ea6abb1cc436fca5bf75d of >=3.8.0
681684
typename TestFixture::Message parsed;
682685
EXPECT_TRUE(ParseTextMessage(kUnknownFieldInput, &parsed));
683686
EXPECT_EQ(SaveMessageAsText(parsed), kUnknownFieldExpected);
687+
#else
688+
(void)kUnknownFieldExpected;
689+
(void)kUnknownFieldInput;
690+
GTEST_SKIP();
691+
#endif // GOOGLE_PROTOBUF_VERSION
684692
}
685693

686694
TYPED_TEST(MutatorTypedTest, DeepRecursion) {
695+
// NOTE: This test has little chance of passing when method
696+
// TextFormat::Parser::SetRecursionLimit is not available
697+
#if GOOGLE_PROTOBUF_VERSION >= 3008000 // commit d8c2501b43c1b56e3efa74048a18f8ce06ba07fe of >=3.8.0
687698
typename TestFixture::Message message;
688699
typename TestFixture::Message* last = &message;
689700
for (int i = 0; i < 150; ++i) {
@@ -695,6 +706,9 @@ TYPED_TEST(MutatorTypedTest, DeepRecursion) {
695706
EXPECT_EQ(i < 100,
696707
ParseBinaryMessage(SaveMessageAsBinary(message), &parsed));
697708
}
709+
#else
710+
GTEST_SKIP();
711+
#endif // GOOGLE_PROTOBUF_VERSION
698712
}
699713

700714
TYPED_TEST(MutatorTypedTest, EmptyMessage) {

src/text_format.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ bool ParseTextMessage(const uint8_t* data, size_t size, Message* output) {
2828
bool ParseTextMessage(const std::string& data, protobuf::Message* output) {
2929
output->Clear();
3030
TextFormat::Parser parser;
31+
#if GOOGLE_PROTOBUF_VERSION >= 3008000 // commit d8c2501b43c1b56e3efa74048a18f8ce06ba07fe of >=3.8.0
3132
parser.SetRecursionLimit(100);
33+
#endif
3234
parser.AllowPartialMessage(true);
35+
#if GOOGLE_PROTOBUF_VERSION >= 3008000 // commit 176f7db11d8242b36a3ea6abb1cc436fca5bf75d of >=3.8.0
3336
parser.AllowUnknownField(true);
37+
#endif
3438
if (!parser.ParseFromString(data, output)) {
3539
output->Clear();
3640
return false;

0 commit comments

Comments
 (0)