From b398a0bc73a7724b821673baffbb35baf9d302d7 Mon Sep 17 00:00:00 2001 From: Yi-Yo Chiang Date: Mon, 25 Oct 2021 21:34:05 +0800 Subject: [PATCH] Add libfstab_fuzzer that fuzzes ReadFstabFile() Just a modest initial implementation. Uses the fuzz data as the fstab file content directly. Bug: 204056804 Test: lunch aosp_cf_x86_64_phone-userdebug SANITIZE_TARGET=address m libfstab_fuzzer adb sync data adb shell /data/fuzz/x86_64/libfstab_fuzzer/libfstab_fuzzer Change-Id: I7976a6ee124e9b5da59cfa7f4bae9699be3f1474 --- fs_mgr/fs_mgr_fstab.cpp | 154 ++++++++++++++-------------- fs_mgr/fuzz/Android.bp | 28 +++++ fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp | 30 ++++++ fs_mgr/include_fstab/fstab/fstab.h | 3 + 4 files changed, 138 insertions(+), 77 deletions(-) create mode 100644 fs_mgr/fuzz/Android.bp create mode 100644 fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index 9b6c3dd41..ad321f05e 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp @@ -442,7 +442,81 @@ std::string GetFstabPath() { return ""; } -bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) { +/* Extracts s from the by-name symlinks specified in a fstab: + * /dev/block///by-name/ + * + * can be: platform, pci or vbd. + * + * For example, given the following entries in the input fstab: + * /dev/block/platform/soc/1da4000.ufshc/by-name/system + * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor + * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }. + */ +std::set ExtraBootDevices(const Fstab& fstab) { + std::set boot_devices; + + for (const auto& entry : fstab) { + std::string blk_device = entry.blk_device; + // Skips blk_device that doesn't conform to the format. + if (!android::base::StartsWith(blk_device, "/dev/block") || + android::base::StartsWith(blk_device, "/dev/block/by-name") || + android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) { + continue; + } + // Skips non-by_name blk_device. + // /dev/block///by-name/ + // ^ slash_by_name + auto slash_by_name = blk_device.find("/by-name"); + if (slash_by_name == std::string::npos) continue; + blk_device.erase(slash_by_name); // erases /by-name/ + + // Erases /dev/block/, now we have / + blk_device.erase(0, std::string("/dev/block/").size()); + + // / + // ^ first_slash + auto first_slash = blk_device.find('/'); + if (first_slash == std::string::npos) continue; + + auto boot_device = blk_device.substr(first_slash + 1); + if (!boot_device.empty()) boot_devices.insert(std::move(boot_device)); + } + + return boot_devices; +} + +FstabEntry BuildDsuUserdataFstabEntry() { + constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV; + + FstabEntry userdata = { + .blk_device = "userdata_gsi", + .mount_point = "/data", + .fs_type = "ext4", + .flags = kFlags, + .reserved_size = 128 * 1024 * 1024, + }; + userdata.fs_mgr_flags.wait = true; + userdata.fs_mgr_flags.check = true; + userdata.fs_mgr_flags.logical = true; + userdata.fs_mgr_flags.quota = true; + userdata.fs_mgr_flags.late_mount = true; + userdata.fs_mgr_flags.formattable = true; + return userdata; +} + +bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) { + auto iter = std::remove_if(fstab->begin(), fstab->end(), + [&](const auto& entry) { return entry.mount_point == mount_point; }); + if (iter != fstab->end()) { + fstab->erase(iter, fstab->end()); + return true; + } + return false; +} + +} // namespace + +bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) { ssize_t len; size_t alloc_len = 0; char *line = NULL; @@ -528,80 +602,6 @@ err: return false; } -/* Extracts s from the by-name symlinks specified in a fstab: - * /dev/block///by-name/ - * - * can be: platform, pci or vbd. - * - * For example, given the following entries in the input fstab: - * /dev/block/platform/soc/1da4000.ufshc/by-name/system - * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor - * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }. - */ -std::set ExtraBootDevices(const Fstab& fstab) { - std::set boot_devices; - - for (const auto& entry : fstab) { - std::string blk_device = entry.blk_device; - // Skips blk_device that doesn't conform to the format. - if (!android::base::StartsWith(blk_device, "/dev/block") || - android::base::StartsWith(blk_device, "/dev/block/by-name") || - android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) { - continue; - } - // Skips non-by_name blk_device. - // /dev/block///by-name/ - // ^ slash_by_name - auto slash_by_name = blk_device.find("/by-name"); - if (slash_by_name == std::string::npos) continue; - blk_device.erase(slash_by_name); // erases /by-name/ - - // Erases /dev/block/, now we have / - blk_device.erase(0, std::string("/dev/block/").size()); - - // / - // ^ first_slash - auto first_slash = blk_device.find('/'); - if (first_slash == std::string::npos) continue; - - auto boot_device = blk_device.substr(first_slash + 1); - if (!boot_device.empty()) boot_devices.insert(std::move(boot_device)); - } - - return boot_devices; -} - -FstabEntry BuildDsuUserdataFstabEntry() { - constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV; - - FstabEntry userdata = { - .blk_device = "userdata_gsi", - .mount_point = "/data", - .fs_type = "ext4", - .flags = kFlags, - .reserved_size = 128 * 1024 * 1024, - }; - userdata.fs_mgr_flags.wait = true; - userdata.fs_mgr_flags.check = true; - userdata.fs_mgr_flags.logical = true; - userdata.fs_mgr_flags.quota = true; - userdata.fs_mgr_flags.late_mount = true; - userdata.fs_mgr_flags.formattable = true; - return userdata; -} - -bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) { - auto iter = std::remove_if(fstab->begin(), fstab->end(), - [&](const auto& entry) { return entry.mount_point == mount_point; }); - if (iter != fstab->end()) { - fstab->erase(iter, fstab->end()); - return true; - } - return false; -} - -} // namespace - void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot, const std::vector& dsu_partitions) { static constexpr char kDsuKeysDir[] = "/avb"; @@ -707,7 +707,7 @@ bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) { bool is_proc_mounts = path == "/proc/mounts"; Fstab fstab; - if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) { + if (!ReadFstabFromFp(fstab_file.get(), is_proc_mounts, &fstab)) { LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'"; return false; } @@ -762,7 +762,7 @@ bool ReadFstabFromDt(Fstab* fstab, bool verbose) { return false; } - if (!ReadFstabFile(fstab_file.get(), false, fstab)) { + if (!ReadFstabFromFp(fstab_file.get(), false, fstab)) { if (verbose) { LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl << fstab_buf; diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp new file mode 100644 index 000000000..f86d277d2 --- /dev/null +++ b/fs_mgr/fuzz/Android.bp @@ -0,0 +1,28 @@ +// +// Copyright (C) 2021 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_fuzz { + name: "libfstab_fuzzer", + srcs: [ + "fs_mgr_fstab_fuzzer.cpp", + ], + static_libs: [ + "libfstab", + ], + shared_libs: [ + "libbase", + ], +} diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp new file mode 100644 index 000000000..1fddbf82d --- /dev/null +++ b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp @@ -0,0 +1,30 @@ +// +// Copyright (C) 2021 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 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::unique_ptr fstab_file( + fmemopen(static_cast(const_cast(data)), size, "r"), fclose); + if (fstab_file == nullptr) { + return 0; + } + android::fs_mgr::Fstab fstab; + android::fs_mgr::ReadFstabFromFp(fstab_file.get(), /* proc_mounts= */ false, &fstab); + return 0; +} diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h index 9a4ed465a..d0f32a364 100644 --- a/fs_mgr/include_fstab/fstab/fstab.h +++ b/fs_mgr/include_fstab/fstab/fstab.h @@ -99,6 +99,9 @@ struct FstabEntry { // Unless explicitly requested, a lookup on mount point should always return the 1st one. using Fstab = std::vector; +// Exported for testability. Regular users should use ReadFstabFromfile(). +bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out); + bool ReadFstabFromFile(const std::string& path, Fstab* fstab); bool ReadFstabFromDt(Fstab* fstab, bool verbose = true); bool ReadDefaultFstab(Fstab* fstab);