From 9d5845bb5e19011fd0e8e9842f1aa16add138454 Mon Sep 17 00:00:00 2001 From: Dylan Katz Date: Mon, 11 May 2020 15:44:01 -0700 Subject: [PATCH] Add fuzzers for libutils classes Adds fuzzers for BitSet, FileMap, String8, String16, and Vector. Test: Ran fuzzers on Android Pixel 3a. Aggregate coverage was 1.2% (this is far lower than true coverage due to shared libraries being counted) Change-Id: I739216fe88afa51dc2f73b857da91116853382f0 Removed unneeded cflags, moved libbase to defaults Test: Built Android.bp successfully Signed-off-by: Dylan Katz Change-Id: I739216fe88afa51dc2f73b857da91116853382f0 --- libutils/Android.bp | 43 +++++++++++- libutils/BitSet_fuzz.cpp | 70 +++++++++++++++++++ libutils/FileMap_fuzz.cpp | 50 ++++++++++++++ libutils/String16_fuzz.cpp | 122 +++++++++++++++++++++++++++++++++ libutils/String8_fuzz.cpp | 136 +++++++++++++++++++++++++++++++++++++ libutils/Vector_fuzz.cpp | 83 ++++++++++++++++++++++ 6 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 libutils/BitSet_fuzz.cpp create mode 100644 libutils/FileMap_fuzz.cpp create mode 100644 libutils/String16_fuzz.cpp create mode 100644 libutils/String8_fuzz.cpp create mode 100644 libutils/Vector_fuzz.cpp diff --git a/libutils/Android.bp b/libutils/Android.bp index 2dbfb703e..0f7044a7e 100644 --- a/libutils/Android.bp +++ b/libutils/Android.bp @@ -175,8 +175,8 @@ cc_library { ], shared_libs: [ - "libutils", - "libbacktrace", + "libutils", + "libbacktrace", ], target: { @@ -194,6 +194,45 @@ cc_library { }, } +cc_defaults { + name: "libutils_fuzz_defaults", + host_supported: true, + shared_libs: [ + "libutils", + "libbase", + ], +} + +cc_fuzz { + name: "libutils_fuzz_bitset", + defaults: ["libutils_fuzz_defaults"], + srcs: ["BitSet_fuzz.cpp"], +} + +cc_fuzz { + name: "libutils_fuzz_filemap", + defaults: ["libutils_fuzz_defaults"], + srcs: ["FileMap_fuzz.cpp"], +} + +cc_fuzz { + name: "libutils_fuzz_string8", + defaults: ["libutils_fuzz_defaults"], + srcs: ["String8_fuzz.cpp"], +} + +cc_fuzz { + name: "libutils_fuzz_string16", + defaults: ["libutils_fuzz_defaults"], + srcs: ["String16_fuzz.cpp"], +} + +cc_fuzz { + name: "libutils_fuzz_vector", + defaults: ["libutils_fuzz_defaults"], + srcs: ["Vector_fuzz.cpp"], +} + cc_test { name: "libutils_test", host_supported: true, diff --git a/libutils/BitSet_fuzz.cpp b/libutils/BitSet_fuzz.cpp new file mode 100644 index 000000000..2e6043cf0 --- /dev/null +++ b/libutils/BitSet_fuzz.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 + +#include "fuzzer/FuzzedDataProvider.h" +#include "utils/BitSet.h" +static constexpr uint8_t MAX_OPERATIONS = 50; + +// We need to handle both 32 and 64 bit bitsets, so we use a function template +// here. Sadly, std::function can't be generic, so we generate a vector of +// std::functions using this function. +template +std::vector> getOperationsForType() { + return { + [](T bs, uint32_t val) -> void { bs.markBit(val); }, + [](T bs, uint32_t val) -> void { bs.valueForBit(val); }, + [](T bs, uint32_t val) -> void { bs.hasBit(val); }, + [](T bs, uint32_t val) -> void { bs.clearBit(val); }, + [](T bs, uint32_t val) -> void { bs.getIndexOfBit(val); }, + [](T bs, uint32_t) -> void { bs.clearFirstMarkedBit(); }, + [](T bs, uint32_t) -> void { bs.markFirstUnmarkedBit(); }, + [](T bs, uint32_t) -> void { bs.clearLastMarkedBit(); }, + [](T bs, uint32_t) -> void { bs.clear(); }, + [](T bs, uint32_t) -> void { bs.count(); }, + [](T bs, uint32_t) -> void { bs.isEmpty(); }, + [](T bs, uint32_t) -> void { bs.isFull(); }, + [](T bs, uint32_t) -> void { bs.firstMarkedBit(); }, + [](T bs, uint32_t) -> void { bs.lastMarkedBit(); }, + }; +} + +// Our operations for 32 and 64 bit bitsets +static const std::vector> thirtyTwoBitOps = + getOperationsForType(); +static const std::vector> sixtyFourBitOps = + getOperationsForType(); + +void runOperationFor32Bit(android::BitSet32 bs, uint32_t bit, uint8_t operation) { + thirtyTwoBitOps[operation](bs, bit); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider(data, size); + uint32_t thirty_two_base = dataProvider.ConsumeIntegral(); + uint64_t sixty_four_base = dataProvider.ConsumeIntegral(); + android::BitSet32 b1 = android::BitSet32(thirty_two_base); + android::BitSet64 b2 = android::BitSet64(sixty_four_base); + + size_t opsRun = 0; + while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { + uint32_t bit = dataProvider.ConsumeIntegral(); + uint8_t op = dataProvider.ConsumeIntegral(); + thirtyTwoBitOps[op % thirtyTwoBitOps.size()](b1, bit); + sixtyFourBitOps[op % sixtyFourBitOps.size()](b2, bit); + } + return 0; +} diff --git a/libutils/FileMap_fuzz.cpp b/libutils/FileMap_fuzz.cpp new file mode 100644 index 000000000..d800564f1 --- /dev/null +++ b/libutils/FileMap_fuzz.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 + +#include "android-base/file.h" +#include "fuzzer/FuzzedDataProvider.h" +#include "utils/FileMap.h" + +static constexpr uint16_t MAX_STR_SIZE = 256; +static constexpr uint8_t MAX_FILENAME_SIZE = 32; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider(data, size); + TemporaryFile tf; + // Generate file contents + std::string contents = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE); + // If we have string contents, dump them into the file. + // Otherwise, just leave it as an empty file. + if (contents.length() > 0) { + const char* bytes = contents.c_str(); + android::base::WriteStringToFd(bytes, tf.fd); + } + android::FileMap m; + // Generate create() params + std::string orig_name = dataProvider.ConsumeRandomLengthString(MAX_FILENAME_SIZE); + size_t length = dataProvider.ConsumeIntegralInRange(1, SIZE_MAX); + off64_t offset = dataProvider.ConsumeIntegralInRange(1, INT64_MAX); + bool read_only = dataProvider.ConsumeBool(); + m.create(orig_name.c_str(), tf.fd, offset, length, read_only); + m.getDataOffset(); + m.getFileName(); + m.getDataLength(); + m.getDataPtr(); + int enum_index = dataProvider.ConsumeIntegral(); + m.advise(static_cast(enum_index)); + return 0; +} diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp new file mode 100644 index 000000000..63c280071 --- /dev/null +++ b/libutils/String16_fuzz.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 + +#include "fuzzer/FuzzedDataProvider.h" +#include "utils/String16.h" +static constexpr int MAX_STRING_BYTES = 256; +static constexpr uint8_t MAX_OPERATIONS = 50; + +std::vector> + operations = { + + // Bytes and size + ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void { + str1.string(); + }), + ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void { + str1.isStaticString(); + }), + ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void { + str1.size(); + }), + + // Casing + ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void { + str1.makeLower(); + }), + + // Comparison + ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void { + str1.startsWith(str2); + }), + ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void { + str1.contains(str2.string()); + }), + ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void { + str1.compare(str2); + }), + + // Append and format + ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void { + str1.append(str2); + }), + ([](FuzzedDataProvider& dataProvider, android::String16 str1, + android::String16 str2) -> void { + int pos = dataProvider.ConsumeIntegralInRange(0, str1.size()); + str1.insert(pos, str2.string()); + }), + + // Find and replace operations + ([](FuzzedDataProvider& dataProvider, android::String16 str1, + android::String16) -> void { + char16_t findChar = dataProvider.ConsumeIntegral(); + str1.findFirst(findChar); + }), + ([](FuzzedDataProvider& dataProvider, android::String16 str1, + android::String16) -> void { + char16_t findChar = dataProvider.ConsumeIntegral(); + str1.findLast(findChar); + }), + ([](FuzzedDataProvider& dataProvider, android::String16 str1, + android::String16) -> void { + char16_t findChar = dataProvider.ConsumeIntegral(); + char16_t replaceChar = dataProvider.ConsumeIntegral(); + str1.replaceAll(findChar, replaceChar); + }), + ([](FuzzedDataProvider& dataProvider, android::String16 str1, + android::String16) -> void { + size_t len = dataProvider.ConsumeIntegral(); + size_t begin = dataProvider.ConsumeIntegral(); + str1.remove(len, begin); + }), +}; + +void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1, + android::String16 str2) { + operations[index](dataProvider, str1, str2); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider(data, size); + // We're generating two char vectors. + // First, generate lengths. + const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange(1, MAX_STRING_BYTES); + const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange(1, MAX_STRING_BYTES); + + // Next, populate the vectors + std::vector vec = dataProvider.ConsumeBytesWithTerminator(kVecOneLen); + std::vector vec_two = dataProvider.ConsumeBytesWithTerminator(kVecTwoLen); + + // Get pointers to their data + char* char_one = vec.data(); + char* char_two = vec_two.data(); + + // Create UTF16 representations + android::String16 str_one_utf16 = android::String16(char_one); + android::String16 str_two_utf16 = android::String16(char_two); + + // Run operations against strings + int opsRun = 0; + while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { + uint8_t op = dataProvider.ConsumeIntegralInRange(0, operations.size() - 1); + callFunc(op, dataProvider, str_one_utf16, str_two_utf16); + } + + str_one_utf16.remove(0, str_one_utf16.size()); + str_two_utf16.remove(0, str_two_utf16.size()); + return 0; +} diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp new file mode 100644 index 000000000..2adfe98b0 --- /dev/null +++ b/libutils/String8_fuzz.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 +#include + +#include "fuzzer/FuzzedDataProvider.h" +#include "utils/String8.h" + +static constexpr int MAX_STRING_BYTES = 256; +static constexpr uint8_t MAX_OPERATIONS = 50; + +std::vector> + operations = { + + // Bytes and size + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.bytes(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.isEmpty(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.length(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.size(); + }, + + // Casing + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.toUpper(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.toLower(); + }, + + [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { + str1.removeAll(str2.c_str()); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { + str1.compare(str2); + }, + + // Append and format + [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { + str1.append(str2); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { + str1.appendFormat(str1.c_str(), str2.c_str()); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { + str1.format(str1.c_str(), str2.c_str()); + }, + + // Find operation + [](FuzzedDataProvider& dataProvider, android::String8 str1, + android::String8) -> void { + // We need to get a value from our fuzzer here. + int start_index = dataProvider.ConsumeIntegralInRange(0, str1.size()); + str1.find(str1.c_str(), start_index); + }, + + // Path handling + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.getBasePath(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.getPathExtension(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.getPathLeaf(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.getPathDir(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + str1.convertToResPath(); + }, + [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { + android::String8 path_out_str = android::String8(); + str1.walkPath(&path_out_str); + path_out_str.clear(); + }, + [](FuzzedDataProvider& dataProvider, android::String8 str1, + android::String8) -> void { + str1.setPathName(dataProvider.ConsumeBytesWithTerminator(5).data()); + }, + [](FuzzedDataProvider& dataProvider, android::String8 str1, + android::String8) -> void { + str1.appendPath(dataProvider.ConsumeBytesWithTerminator(5).data()); + }, +}; + +void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1, + android::String8 str2) { + operations[index](dataProvider, str1, str2); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider(data, size); + // Generate vector lengths + const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange(1, MAX_STRING_BYTES); + const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange(1, MAX_STRING_BYTES); + // Populate vectors + std::vector vec = dataProvider.ConsumeBytesWithTerminator(kVecOneLen); + std::vector vec_two = dataProvider.ConsumeBytesWithTerminator(kVecTwoLen); + // Create UTF-8 pointers + android::String8 str_one_utf8 = android::String8(vec.data()); + android::String8 str_two_utf8 = android::String8(vec_two.data()); + + // Run operations against strings + int opsRun = 0; + while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { + uint8_t op = dataProvider.ConsumeIntegralInRange(0, operations.size() - 1); + callFunc(op, dataProvider, str_one_utf8, str_two_utf8); + } + + // Just to be extra sure these can be freed, we're going to explicitly clear + // them + str_one_utf8.clear(); + str_two_utf8.clear(); + return 0; +} diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp new file mode 100644 index 000000000..f6df05135 --- /dev/null +++ b/libutils/Vector_fuzz.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 "fuzzer/FuzzedDataProvider.h" +#include "utils/Vector.h" +static constexpr uint16_t MAX_VEC_SIZE = 5000; + +void runVectorFuzz(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider(data, size); + android::Vector vec = android::Vector(); + // We want to test handling of sizeof as well. + android::Vector vec32 = android::Vector(); + + // We're going to generate two vectors of this size + size_t vectorSize = dataProvider.ConsumeIntegralInRange(0, MAX_VEC_SIZE); + vec.setCapacity(vectorSize); + vec32.setCapacity(vectorSize); + for (size_t i = 0; i < vectorSize; i++) { + uint8_t count = dataProvider.ConsumeIntegralInRange(1, 5); + vec.insertAt((uint8_t)i, i, count); + vec32.insertAt((uint32_t)i, i, count); + vec.push_front(i); + vec32.push(i); + } + + // Now we'll perform some test operations with any remaining data + // Index to perform operations at + size_t index = dataProvider.ConsumeIntegralInRange(0, vec.size()); + std::vector remainingVec = dataProvider.ConsumeRemainingBytes(); + // Insert an array and vector + vec.insertArrayAt(remainingVec.data(), index, remainingVec.size()); + android::Vector vecCopy = android::Vector(vec); + vec.insertVectorAt(vecCopy, index); + // Same thing for 32 bit vector + android::Vector vec32Copy = android::Vector(vec32); + vec32.insertArrayAt(vec32Copy.array(), index, vec32.size()); + vec32.insertVectorAt(vec32Copy, index); + // Replace single character + if (remainingVec.size() > 0) { + vec.replaceAt(remainingVec[0], index); + vec32.replaceAt(static_cast(remainingVec[0]), index); + } else { + vec.replaceAt(0, index); + vec32.replaceAt(0, index); + } + // Add any remaining bytes + for (uint8_t i : remainingVec) { + vec.add(i); + vec32.add(static_cast(i)); + } + // Shrink capactiy + vec.setCapacity(remainingVec.size()); + vec32.setCapacity(remainingVec.size()); + // Iterate through each pointer + size_t sum = 0; + for (auto& it : vec) { + sum += it; + } + for (auto& it : vec32) { + sum += it; + } + // Cleanup + vec.clear(); + vecCopy.clear(); + vec32.clear(); + vec32Copy.clear(); +} +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + runVectorFuzz(data, size); + return 0; +}