diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp new file mode 100644 index 000000000..acbb7468a --- /dev/null +++ b/init/fuzzer/Android.bp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 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. + */ +cc_defaults { + name: "libinit_defaults", + static_libs: [ + "libc++fs", + "liblmkd_utils", + "libmodprobe", + "libprotobuf-cpp-lite", + "libpropertyinfoparser", + "libsnapshot_init", + "libinit", + ], + shared_libs: [ + "libbase", + "libfs_mgr", + "libhidl-gen-utils", + "libkeyutils", + "liblog", + "libprocessgroup", + "libselinux", + ], + header_libs: ["libinit_headers"], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 155276, + }, +} + +cc_fuzz { + name: "init_parser_fuzzer", + srcs: [ + "init_parser_fuzzer.cpp", + ], + shared_libs: ["libhidlmetadata",], + defaults: [ + "libinit_defaults", + ], +} + +cc_fuzz { + name: "init_property_fuzzer", + srcs: [ + "init_property_fuzzer.cpp", + ], + defaults: ["libinit_defaults"], +} + +cc_fuzz { + name: "init_ueventHandler_fuzzer", + srcs: [ + "init_ueventHandler_fuzzer.cpp", + ], + defaults: [ + "libinit_defaults", + ], +} diff --git a/init/fuzzer/README.md b/init/fuzzer/README.md new file mode 100644 index 000000000..fc9a6a675 --- /dev/null +++ b/init/fuzzer/README.md @@ -0,0 +1,98 @@ +# Fuzzers for libinit + +## Table of contents ++ [init_parser_fuzzer](#InitParser) ++ [init_property_fuzzer](#InitProperty) ++ [init_ueventHandler_fuzzer](#InitUeventHandler) + +# Fuzzer for InitParser + +InitParser supports the following parameters: +1. ValidPathNames (parameter name: "kValidPaths") +2. ValidParseInputs (parameter name: "kValidInputs") + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`kValidPaths`| 0.`/system/etc/init/hw/init.rc`,
1.`/system/etc/init` |Value obtained from FuzzedDataProvider| +|`kValidInputs`| 0.`{"","cpu", "10", "10"}`,
1.`{"","RLIM_CPU", "10", "10"}`,
2.`{"","12", "unlimited", "10"}`,
3.`{"","13", "-1", "10"}`,
4.`{"","14", "10", "unlimited"}`,
5.`{"","15", "10", "-1"}` |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) init_parser_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/init_parser_fuzzer/init_parser_fuzzer +``` + +# Fuzzer for InitProperty + +InitProperty supports the following parameters: + PropertyType (parameter name: "PropertyType") + +| Parameter| Valid Values |Configured Value| +|-------------|----------|----- | +|`PropertyType`| 0.`STRING`,
1.`BOOL`,
2.`INT`,
3.`UINT`,
4.`DOUBLE`,
5.`SIZE`,
6.`ENUM`,
7.`RANDOM`|Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) init_property_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/init_property_fuzzer/init_property_fuzzer +``` + +# Fuzzer for InitUeventHandler + +##### Maximize code coverage +The configuration parameters are not hardcoded, but instead selected based on +incoming data. This ensures more code paths are reached by the fuzzer. + +InitUeventHandler supports the following parameters: +1. Major (parameter name: `major`) +2. Minor (parameter name: `minor`) +3. PartitionNum (parameter name: `partition_num`) +4. Uid (parameter name: `uid`) +5. Gid (parameter name: `gid`) +6. Action (parameter name: `action`) +7. Path (parameter name: `path`) +8. Subsystem (parameter name: `subsystem`) +9. PartitionName (parameter name: `partition_name`) +10. DeviceName (parameter name: `device_name`) +11. Modalias (parameter name: `modalias`) +12. DevPath (parameter name: `devPath`) +13. HandlerPath (parameter name: `handlerPath`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +| `major` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider| +| `minor` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider| +| `partition_num ` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider| +| `uid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider| +| `gid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider| +| `action` | `String` | Value obtained from FuzzedDataProvider| +| `path` | `String` | Value obtained from FuzzedDataProvider| +| `subsystem` | `String` | Value obtained from FuzzedDataProvider| +| `partition_name` | `String` | Value obtained from FuzzedDataProvider| +| `device_name` | `String` | Value obtained from FuzzedDataProvider| +| `modalias` | `String` | Value obtained from FuzzedDataProvider| +| `devPath` | `String` | Value obtained from FuzzedDataProvider| +| `handlerPath` | `String` | Value obtained from FuzzedDataProvider| + +This also ensures that the plugin is always deterministic for any given input. + +#### Steps to run +1. Build the fuzzer +``` +$ mm -j$(nproc) init_ueventHandler_fuzzer +``` +2. Run on device +``` +$ adb sync data +$ adb shell /data/fuzz/arm64/init_ueventHandler_fuzzer/init_ueventHandler_fuzzer +``` diff --git a/init/fuzzer/init_parser_fuzzer.cpp b/init/fuzzer/init_parser_fuzzer.cpp new file mode 100644 index 000000000..e6a78a2c4 --- /dev/null +++ b/init/fuzzer/init_parser_fuzzer.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2022 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 +#include +#include + +using namespace android; +using namespace android::init; + +const std::vector kValidInputs[] = { + {"", "cpu", "10", "10"}, {"", "RLIM_CPU", "10", "10"}, {"", "12", "unlimited", "10"}, + {"", "13", "-1", "10"}, {"", "14", "10", "unlimited"}, {"", "15", "10", "-1"}, +}; + +const std::string kValidPaths[] = { + "/system/etc/init/hw/init.rc", + "/system/etc/init", +}; + +const int32_t kMaxBytes = 256; +const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient"; + +class InitParserFuzzer { + public: + InitParserFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){}; + void Process(); + + private: + void InvokeParser(); + void InvokeLimitParser(); + void InvokeInterfaceUtils(); + InterfaceInheritanceHierarchyMap GenerateHierarchyMap(); + std::vector GenerateInterfaceMetadata(); + + FuzzedDataProvider fdp_; +}; + +void InitParserFuzzer::InvokeLimitParser() { + if (fdp_.ConsumeBool()) { + std::vector input; + input.push_back(""); + input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes)); + input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes)); + input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes)); + ParseRlimit(input); + } else { + ParseRlimit(fdp_.PickValueInArray(kValidInputs)); + } +} + +std::vector InitParserFuzzer::GenerateInterfaceMetadata() { + std::vector random_interface; + for (size_t idx = 0; idx < fdp_.ConsumeIntegral(); ++idx) { + HidlInterfaceMetadata metadata; + metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes); + for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral(); ++idx1) { + metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes)); + } + random_interface.push_back(metadata); + } + return random_interface; +} + +InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() { + InterfaceInheritanceHierarchyMap result; + std::vector random_interface; + if (fdp_.ConsumeBool()) { + random_interface = GenerateInterfaceMetadata(); + } else { + random_interface = HidlInterfaceMetadata::all(); + } + + for (const HidlInterfaceMetadata& iface : random_interface) { + std::set inherited_interfaces; + for (const std::string& intf : iface.inherited) { + FQName fqname; + (void)fqname.setTo(intf); + inherited_interfaces.insert(fqname); + } + FQName fqname; + (void)fqname.setTo(iface.name); + result[fqname] = inherited_interfaces; + } + return result; +} + +void InitParserFuzzer::InvokeInterfaceUtils() { + InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap(); + SetKnownInterfaces(hierarchy_map); + IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes)); + std::set interface_set; + for (size_t idx = 0; idx < fdp_.ConsumeIntegral(); ++idx) { + auto set_interface_values = fdp_.PickValueInArray>({ + [&]() { + interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes))); + }, + [&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); }, + [&]() { interface_set.insert(kValidInterfaces); }, + }); + set_interface_values(); + } + CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map); +} + +void InitParserFuzzer::InvokeParser() { + Parser parser; + std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import"; + parser.AddSectionParser(name, std::make_unique(&parser)); + std::string path = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kValidPaths) + : fdp_.ConsumeRandomLengthString(kMaxBytes); + parser.ParseConfig(path); + parser.ParseConfigFileInsecure(path); +} + +void InitParserFuzzer::Process() { + while (fdp_.remaining_bytes()) { + auto invoke_parser_fuzzer = fdp_.PickValueInArray>({ + [&]() { InvokeParser(); }, + [&]() { InvokeInterfaceUtils(); }, + [&]() { InvokeLimitParser(); }, + }); + invoke_parser_fuzzer(); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + InitParserFuzzer init_parser_fuzzer(data, size); + init_parser_fuzzer.Process(); + return 0; +} diff --git a/init/fuzzer/init_property_fuzzer.cpp b/init/fuzzer/init_property_fuzzer.cpp new file mode 100644 index 000000000..22df37559 --- /dev/null +++ b/init/fuzzer/init_property_fuzzer.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2022 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 +#include +#include "fuzzer/FuzzedDataProvider.h" + +using namespace android; +using namespace android::init; +using android::init::persistent_property_filename; + +const std::string kTempDir = "/data/local/tmp/"; +const std::string kFuzzerPropertyFile = kTempDir + "persistent_properties"; +constexpr int32_t kMaxPropertyLength = 10; +const std::string kPrefix = "persist."; +const std::string kPropertyName = kPrefix + "sys.timezone"; +const std::string kPropertyValue = "America/Los_Angeles"; +const std::string kLegacyPropertyFile = "/data/property/persist.properties"; +const std::string kSizeSuffix[3] = {"g", "k", "m"}; +constexpr int32_t kMinNumStrings = 1; +constexpr int32_t kMaxNumStrings = 10; + +enum PropertyType { STRING, BOOL, INT, UINT, DOUBLE, SIZE, ENUM, RANDOM, kMaxValue = RANDOM }; + +class InitPropertyFuzzer { + public: + InitPropertyFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){}; + void process(); + + private: + void InvokeCheckType(); + void InvokeWritePersistentProperty(); + void RemoveFiles(); + void CreateFuzzerPropertyFile(const std::string property_file); + FuzzedDataProvider fdp_; +}; + +void InitPropertyFuzzer::InvokeCheckType() { + std::string property_type; + std::string value; + int type = fdp_.ConsumeEnum(); + switch (type) { + case STRING: + value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength); + property_type = "string"; + break; + case BOOL: + value = fdp_.ConsumeBool(); + property_type = "bool"; + break; + case INT: + value = fdp_.ConsumeIntegral(); + property_type = "int"; + break; + case UINT: + value = fdp_.ConsumeIntegral(); + property_type = "uint"; + break; + case DOUBLE: + value = fdp_.ConsumeFloatingPoint(); + property_type = "double"; + break; + case SIZE: + value = fdp_.ConsumeIntegral(); + value = value.append(fdp_.PickValueInArray(kSizeSuffix)); + property_type = "size"; + break; + case ENUM: + value = fdp_.ConsumeIntegral(); + property_type = "enum"; + break; + case RANDOM: + value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength); + property_type = fdp_.ConsumeRandomLengthString(kMaxPropertyLength); + break; + } + + CheckType(property_type, value); +} + +void InitPropertyFuzzer::InvokeWritePersistentProperty() { + if (fdp_.ConsumeBool()) { + WritePersistentProperty(kPropertyName, kPropertyValue); + } else { + WritePersistentProperty((kPrefix + fdp_.ConsumeRandomLengthString(kMaxPropertyLength)), + fdp_.ConsumeRandomLengthString(kMaxPropertyLength)); + } +} + +void InitPropertyFuzzer::RemoveFiles() { + remove(kFuzzerPropertyFile.c_str()); + remove(kLegacyPropertyFile.c_str()); +} + +void InitPropertyFuzzer::CreateFuzzerPropertyFile(const std::string property_file) { + std::ofstream out; + out.open(property_file, std::ios::binary | std::ofstream::trunc); + chmod(property_file.c_str(), S_IRWXU); + const int32_t numStrings = fdp_.ConsumeIntegralInRange(kMinNumStrings, kMaxNumStrings); + for (int32_t i = 0; i < numStrings; ++i) { + out << fdp_.ConsumeRandomLengthString(kMaxPropertyLength) << "\n"; + } + out.close(); +} + +void InitPropertyFuzzer::process() { + persistent_property_filename = kFuzzerPropertyFile; + /* Property and legacy files are created using createFuzzerPropertyFile() and */ + /* are used in the below APIs. Hence createFuzzerPropertyFile() is not a part */ + /* of the lambda construct. */ + CreateFuzzerPropertyFile(kFuzzerPropertyFile); + CreateFuzzerPropertyFile(kLegacyPropertyFile); + auto property_type = fdp_.PickValueInArray>({ + [&]() { InvokeCheckType(); }, + [&]() { InvokeWritePersistentProperty(); }, + [&]() { LoadPersistentProperties(); }, + }); + property_type(); + RemoveFiles(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + InitPropertyFuzzer initPropertyFuzzer(data, size); + initPropertyFuzzer.process(); + return 0; +} diff --git a/init/fuzzer/init_ueventHandler_fuzzer.cpp b/init/fuzzer/init_ueventHandler_fuzzer.cpp new file mode 100644 index 000000000..b6d5f8a42 --- /dev/null +++ b/init/fuzzer/init_ueventHandler_fuzzer.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 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 +#include +#include +#include +#include + +using namespace android; +using namespace android::init; +constexpr int32_t kMaxBytes = 100; +constexpr int32_t kMaxSize = 1000; +constexpr int32_t kMinSize = 1; + +/*'HandleUevent' prefixes the path with '/sys' and hence this is required to point + * to'/data/local/tmp' dir.*/ +const std::string kPath = "/../data/local/tmp/"; +const std::string kPathPrefix = "/.."; + +void MakeFile(FuzzedDataProvider* fdp, std::string s) { + std::ofstream out; + out.open(s, std::ios::binary | std::ofstream::trunc); + for (int32_t idx = 0; idx < fdp->ConsumeIntegralInRange(kMinSize, kMaxSize); ++idx) { + out << fdp->ConsumeRandomLengthString(kMaxBytes) << "\n"; + } + out.close(); +} + +void CreateDir(std::string Directory, FuzzedDataProvider* fdp) { + std::string tmp = Directory.substr(kPathPrefix.length()); + mkdir_recursive(android::base::Dirname(tmp.c_str()), + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + MakeFile(fdp, tmp + "/data"); + MakeFile(fdp, tmp + "/loading"); +} + +std::string SelectRandomString(FuzzedDataProvider* fdp, std::string s) { + if (fdp->ConsumeBool()) { + if (fdp->ConsumeBool()) { + return fdp->ConsumeRandomLengthString(kMaxBytes); + } else { + return s; + } + } + return ""; +} + +Uevent CreateUevent(FuzzedDataProvider* fdp) { + Uevent uevent; + uevent.action = SelectRandomString(fdp, "add"); + uevent.subsystem = SelectRandomString(fdp, "firmware"); + uevent.path = SelectRandomString(fdp, kPath + fdp->ConsumeRandomLengthString(kMaxBytes)); + uevent.firmware = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : ""; + uevent.partition_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : ""; + uevent.device_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : ""; + uevent.modalias = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : ""; + uevent.partition_num = fdp->ConsumeIntegral(); + uevent.major = fdp->ConsumeIntegral(); + uevent.minor = fdp->ConsumeIntegral(); + return uevent; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + while (fdp.remaining_bytes()) { + auto invoke_uevent_handler_fuzzer = fdp.PickValueInArray>({ + [&]() { + std::vector modalias_vector; + for (size_t idx = 0; + idx < fdp.ConsumeIntegralInRange(kMinSize, kMaxSize); ++idx) { + modalias_vector.push_back(fdp.ConsumeRandomLengthString(kMaxBytes)); + } + ModaliasHandler modalias_handler = ModaliasHandler(modalias_vector); + modalias_handler.HandleUevent(CreateUevent(&fdp)); + }, + [&]() { + std::vector external_handlers; + std::vector firmware_directories; + for (size_t idx = 0; + idx < fdp.ConsumeIntegralInRange(kMinSize, kMaxSize); ++idx) { + std::string devPath = fdp.ConsumeRandomLengthString(kMaxBytes); + uid_t uid = fdp.ConsumeIntegral(); + gid_t gid = fdp.ConsumeIntegral(); + std::string handlerPath = fdp.ConsumeRandomLengthString(kMaxBytes); + ExternalFirmwareHandler externalFirmwareHandler = + ExternalFirmwareHandler(devPath, uid, gid, handlerPath); + external_handlers.push_back(externalFirmwareHandler); + firmware_directories.push_back(fdp.ConsumeRandomLengthString(kMaxBytes)); + } + FirmwareHandler firmware_handler = + FirmwareHandler(firmware_directories, external_handlers); + Uevent uevent = CreateUevent(&fdp); + if (fdp.ConsumeBool() && uevent.path.size() != 0 && + uevent.path.find(kPath) == 0) { + CreateDir(uevent.path, &fdp); + firmware_handler.HandleUevent(uevent); + std::string s = uevent.path.substr(kPathPrefix.length()); + remove(s.c_str()); + } else { + firmware_handler.HandleUevent(uevent); + } + }, + }); + invoke_uevent_handler_fuzzer(); + } + return 0; +}