diff options
73 files changed, 945 insertions, 454 deletions
diff --git a/Android.bp b/Android.bp index 8e29dc8fd..52de77038 100644 --- a/Android.bp +++ b/Android.bp @@ -12,6 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. +// *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE +// CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE +// DEPENDING ON IT IN YOUR PROJECT. *** +package { + default_applicable_licenses: ["bootable_recovery_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "bootable_recovery_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-MIT", + "SPDX-license-identifier-OFL", // by exception only + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "recovery_defaults", @@ -79,6 +113,7 @@ cc_defaults { "librecovery_utils", "libotautil", "libsnapshot_nobinder", + "update_metadata-protos", ], } diff --git a/Android.mk b/Android.mk index 9f691531c..96af417bf 100644 --- a/Android.mk +++ b/Android.mk @@ -28,6 +28,9 @@ TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default include $(CLEAR_VARS) LOCAL_MODULE := librecovery_ui_ext +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-OFL +LOCAL_LICENSE_CONDITIONS := by_exception_only notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE # LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds. LOCAL_MULTILIB := first @@ -54,14 +57,15 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := recovery_deps +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-OFL +LOCAL_LICENSE_CONDITIONS := by_exception_only notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) -ifeq ($(HOST_OS),linux) LOCAL_REQUIRED_MODULES += \ make_f2fs.recovery \ sload_f2fs.recovery endif -endif # On A/B devices recovery-persist reads the recovery related file from the persist storage and # copies them into /data/misc/recovery. Then, for both A/B and non-A/B devices, recovery-persist diff --git a/METADATA b/METADATA new file mode 100644 index 000000000..a1ce3c66a --- /dev/null +++ b/METADATA @@ -0,0 +1,9 @@ +# *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE +# CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE +# DEPENDING ON IT IN YOUR PROJECT. *** +third_party { + # would be NOTICE save for OFL in: + # fonts/README + # fonts/OFL.txt + license_type: BY_EXCEPTION_ONLY +} @@ -143,3 +143,17 @@ Note that this mechanism applies to both of normal boot and recovery modes. Both of the two conditions need to be satisfied. Although `ro.adb.secure` is a runtime property, its value is set at build time (written into `/prop.default`). It defaults to `1` on `-user` builds, and `0` for other build variants. The value is overridable via `PRODUCT_DEFAULT_PROPERTY_OVERRIDES`. + +Localization of the background texts +------------------------------------ + +The recovery image supports localization of several background texts, e.g. installing, error, +factory reset warnings, etc. For devices using `xxhdpi` and `xxxhdpi`, the build system generates +these localization images dynamically since android-10 when building the recovery image. While +the static images under res-*dpi/images/ is used for other display resolutions and as a +backup. + +Check the invocation of the image_generator tool in the [makefile]. And the detailed usage of the +image_generator is documented [here](./tools/image_generator/README.md). + +[makefile]: https://android.googlesource.com/platform/build/+/refs/heads/master/core/Makefile#1800 diff --git a/TEST_MAPPING b/TEST_MAPPING index a3045828e..12372856b 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -5,10 +5,6 @@ }, { "name": "recovery_unit_test" - }, - { - "name": "recovery_host_test", - "host": true } ] } diff --git a/applypatch/Android.bp b/applypatch/Android.bp index 13a962584..0d6d23b90 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["bootable_recovery_applypatch_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "bootable_recovery_applypatch_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "applypatch_defaults", @@ -54,7 +71,7 @@ cc_library_static { "libbz", "libedify", "libotautil", - "libz", + "libz_stable", ], shared_libs: [ @@ -120,7 +137,7 @@ cc_binary { "libbase", "libcrypto", "liblog", - "libz", + "libz_stable", "libziparchive", ], @@ -129,9 +146,9 @@ cc_binary { ], } -cc_library_host_static { +cc_library_static { name: "libimgdiff", - + host_supported: true, defaults: [ "applypatch_defaults", ], @@ -152,14 +169,13 @@ cc_library_host_static { "liblog", "libotautil", "libutils", - "libz", + "libz_stable", "libziparchive", ], } cc_binary_host { name: "imgdiff", - srcs: [ "imgdiff_main.cpp", ], @@ -180,6 +196,6 @@ cc_binary_host { "liblog", "libbrotli", "libbz", - "libz", + "libz_stable", ], } diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 6ad4a6105..376c511c2 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -682,9 +682,9 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl } // Create a list of deflated zip entries, sorted by offset. - std::vector<std::pair<std::string, ZipEntry>> temp_entries; + std::vector<std::pair<std::string, ZipEntry64>> temp_entries; std::string name; - ZipEntry entry; + ZipEntry64 entry; while ((ret = Next(cookie, &entry, &name)) == 0) { if (entry.method == kCompressDeflated || limit_ > 0) { temp_entries.emplace_back(name, entry); @@ -712,8 +712,14 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl // Add the end of zip file (mainly central directory) as a normal chunk. size_t entries_end = 0; if (!temp_entries.empty()) { - entries_end = static_cast<size_t>(temp_entries.back().second.offset + - temp_entries.back().second.compressed_length); + CHECK_GE(temp_entries.back().second.offset, 0); + if (__builtin_add_overflow(temp_entries.back().second.offset, + temp_entries.back().second.compressed_length, &entries_end)) { + LOG(ERROR) << "`entries_end` overflows on entry with offset " + << temp_entries.back().second.offset << " and compressed_length " + << temp_entries.back().second.compressed_length; + return false; + } } CHECK_LT(entries_end, file_content_.size()); chunks_.emplace_back(CHUNK_NORMAL, entries_end, &file_content_, @@ -735,8 +741,16 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl LOG(ERROR) << "Failed to add " << entry_name << " to target chunks"; return false; } - - pos += temp_entries[nextentry].second.compressed_length; + if (temp_entries[nextentry].second.compressed_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Entry " << name << " compressed size exceeds size of address space. " + << entry.compressed_length; + return false; + } + if (__builtin_add_overflow(pos, temp_entries[nextentry].second.compressed_length, &pos)) { + LOG(ERROR) << "`pos` overflows after adding " + << temp_entries[nextentry].second.compressed_length; + return false; + } ++nextentry; continue; } @@ -757,7 +771,13 @@ bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandl } bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, - ZipEntry* entry) { + ZipEntry64* entry) { + if (entry->compressed_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to add " << entry_name + << " because's compressed size exceeds size of address space. " + << entry->compressed_length; + return false; + } size_t compressed_len = entry->compressed_length; if (compressed_len == 0) return true; @@ -775,6 +795,12 @@ bool ZipModeImage::AddZipEntryToChunks(ZipArchiveHandle handle, const std::strin } } else if (entry->method == kCompressDeflated) { size_t uncompressed_len = entry->uncompressed_length; + if (uncompressed_len > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to add " << entry_name + << " because's compressed size exceeds size of address space. " + << uncompressed_len; + return false; + } std::vector<uint8_t> uncompressed_data(uncompressed_len); int ret = ExtractToMemory(handle, entry, uncompressed_data.data(), uncompressed_len); if (ret != 0) { @@ -965,7 +991,7 @@ bool ZipModeImage::SplitZipModeImageWithLimit(const ZipModeImage& tgt_image, used_src_ranges.Insert(src_ranges); split_src_ranges->push_back(std::move(src_ranges)); } - src_ranges.Clear(); + src_ranges = {}; // We don't have enough space for the current chunk; start a new split image and handle // this chunk there. @@ -1035,23 +1061,24 @@ bool ZipModeImage::AddSplitImageFromChunkList(const ZipModeImage& tgt_image, } ZipModeImage split_tgt_image(false); - split_tgt_image.Initialize(std::move(aligned_tgt_chunks), {}); + split_tgt_image.Initialize(aligned_tgt_chunks, {}); split_tgt_image.MergeAdjacentNormalChunks(); - // Construct the dummy source file based on the src_ranges. - std::vector<uint8_t> src_content; + // Construct the split source file based on the split src ranges. + std::vector<uint8_t> split_src_content; for (const auto& r : split_src_ranges) { size_t end = std::min(src_image.file_content_.size(), r.second * BLOCK_SIZE); - src_content.insert(src_content.end(), src_image.file_content_.begin() + r.first * BLOCK_SIZE, - src_image.file_content_.begin() + end); + split_src_content.insert(split_src_content.end(), + src_image.file_content_.begin() + r.first * BLOCK_SIZE, + src_image.file_content_.begin() + end); } // We should not have an empty src in our design; otherwise we will encounter an error in - // bsdiff since src_content.data() == nullptr. - CHECK(!src_content.empty()); + // bsdiff since split_src_content.data() == nullptr. + CHECK(!split_src_content.empty()); ZipModeImage split_src_image(true); - split_src_image.Initialize(split_src_chunks, std::move(src_content)); + split_src_image.Initialize(split_src_chunks, split_src_content); split_tgt_images->push_back(std::move(split_tgt_image)); split_src_images->push_back(std::move(split_src_image)); diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 671605160..b579e56ae 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -211,7 +211,7 @@ class ZipModeImage : public Image { bool Initialize(const std::string& filename) override; - // Initialize a dummy ZipModeImage from an existing ImageChunk vector. For src img pieces, we + // Initialize a fake ZipModeImage from an existing ImageChunk vector. For src img pieces, we // reconstruct a new file_content based on the source ranges; but it's not needed for the tgt img // pieces; because for each chunk both the data and their offset within the file are unchanged. void Initialize(const std::vector<ImageChunk>& chunks, const std::vector<uint8_t>& file_content) { @@ -257,7 +257,8 @@ class ZipModeImage : public Image { // Initialize image chunks based on the zip entries. bool InitializeChunks(const std::string& filename, ZipArchiveHandle handle); // Add the a zip entry to the list. - bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, ZipEntry* entry); + bool AddZipEntryToChunks(ZipArchiveHandle handle, const std::string& entry_name, + ZipEntry64* entry); // Return the real size of the zip file. (omit the trailing zeros that used for alignment) bool GetZipFileSize(size_t* input_file_size); @@ -265,7 +266,7 @@ class ZipModeImage : public Image { const std::vector<ZipModeImage>& split_src_images, std::vector<SortedRangeSet>& split_src_ranges, size_t total_tgt_size); - // Construct the dummy split images based on the chunks info and source ranges; and move them into + // Construct the fake split images based on the chunks info and source ranges; and move them into // the given vectors. Return true if we add a new split image into |split_tgt_images|, and // false otherwise. static bool AddSplitImageFromChunkList(const ZipModeImage& tgt_image, diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index 6443a077c..778fdb93e 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "libbootloader_message_defaults", srcs: ["bootloader_message.cpp"], diff --git a/edify/Android.bp b/edify/Android.bp index 0ab53d6dd..62ff91133 100644 --- a/edify/Android.bp +++ b/edify/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_library_static { name: "libedify", diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h index cd9c70120..3ddf7f5fe 100644 --- a/edify/include/edify/expr.h +++ b/edify/include/edify/expr.h @@ -60,7 +60,7 @@ struct Value { BLOB = 2, }; - Value(Type type, const std::string& str) : type(type), data(str) {} + Value(Type type, std::string str) : type(type), data(std::move(str)) {} Type type; std::string data; diff --git a/etc/init.rc b/etc/init.rc index 3ec45db2f..5cacb8bdb 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -24,9 +24,6 @@ on init symlink /system/bin /bin symlink /system/etc /etc - mount cgroup none /acct cpuacct - mkdir /acct/uid - mkdir /sdcard mkdir /system mkdir /data diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp index 9bf19eb85..4eb21dce8 100644 --- a/fuse_sideload/Android.bp +++ b/fuse_sideload/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_library { name: "libfusesideload", recovery_available: true, diff --git a/install/Android.bp b/install/Android.bp index bed3bc504..e239ddc4e 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -12,11 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "libinstall_defaults", defaults: [ "recovery_defaults", + "libspl_check_defaults", ], shared_libs: [ @@ -40,12 +50,51 @@ cc_defaults { "librecovery_utils", "libotautil", "libsnapshot_nobinder", + "ota_metadata_proto_cc", // external dependencies "libvintf", ], } +cc_test_host { + name: "libinstall_host_unittests", + defaults: [ + "libspl_check_defaults" + ], + srcs: [ + "spl_check_unittests.cpp", + ], + static_libs: [ + "libspl_check", + ], +} + +cc_defaults { + name: "libspl_check_defaults", + static_libs: [ + "libbase", + "ota_metadata_proto_cc", + "liblog", + "libziparchive", + "libz", + "libprotobuf-cpp-lite", + ], +} + +cc_library_static { + name: "libspl_check", + recovery_available: true, + host_supported: true, + defaults: [ + "libspl_check_defaults", + ], + srcs: ["spl_check.cpp"], + export_include_dirs: [ + "include", + ], +} + cc_library_static { name: "libinstall", recovery_available: true, @@ -64,6 +113,7 @@ cc_library_static { "verifier.cpp", "wipe_data.cpp", "wipe_device.cpp", + "spl_check.cpp", ], header_libs: [ diff --git a/install/include/install/spl_check.h b/install/include/install/spl_check.h new file mode 100644 index 000000000..e0bfc62c7 --- /dev/null +++ b/install/include/install/spl_check.h @@ -0,0 +1,26 @@ +/* + * 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 <string_view> + +#include <android-base/logging.h> +#include <ota_metadata.pb.h> +#include <ziparchive/zip_archive.h> + +bool ViolatesSPLDowngrade(const build::tools::releasetools::OtaMetadata& metadata, + std::string_view current_spl); + +bool ViolatesSPLDowngrade(ZipArchiveHandle zip, std::string_view current_spl); diff --git a/install/install.cpp b/install/install.cpp index d404997dc..6e74f80ab 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -47,6 +47,7 @@ #include <android-base/unique_fd.h> #include "install/package.h" +#include "install/spl_check.h" #include "install/verifier.h" #include "install/wipe_data.h" #include "otautil/error_code.h" @@ -67,14 +68,17 @@ static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery // Default allocation of progress bar segments to operations static constexpr int VERIFICATION_PROGRESS_TIME = 60; static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25; - +// The charater used to separate dynamic fingerprints. e.x. sargo|aosp-sargo +static const char* FINGERPRING_SEPARATOR = "|"; static std::condition_variable finish_log_temperature; +static bool isInStringList(const std::string& target_token, const std::string& str_list, + const std::string& deliminator); bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata) { CHECK(metadata != nullptr); static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; - ZipEntry entry; + ZipEntry64 entry; if (FindEntry(zip, METADATA_PATH, &entry) != 0) { LOG(ERROR) << "Failed to find " << METADATA_PATH; return false; @@ -151,7 +155,8 @@ static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& me auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", ""); auto pkg_pre_build_fingerprint = get_value(metadata, "pre-build"); - if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) { + if (!pkg_pre_build_fingerprint.empty() && + !isInStringList(device_fingerprint, pkg_pre_build_fingerprint, FINGERPRING_SEPARATOR)) { LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected " << device_fingerprint; return false; @@ -199,7 +204,8 @@ bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, Ot auto device = android::base::GetProperty("ro.product.device", ""); auto pkg_device = get_value(metadata, "pre-device"); - if (pkg_device != device || pkg_device.empty()) { + // device name can be a | separated list, so need to check + if (pkg_device.empty() || !isInStringList(device, pkg_device, FINGERPRING_SEPARATOR)) { LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device; return false; } @@ -236,12 +242,18 @@ bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset // in the zip file. static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; - ZipEntry properties_entry; + ZipEntry64 properties_entry; if (FindEntry(zip, AB_OTA_PAYLOAD_PROPERTIES, &properties_entry) != 0) { LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES; return false; } - uint32_t properties_entry_length = properties_entry.uncompressed_length; + auto properties_entry_length = properties_entry.uncompressed_length; + if (properties_entry_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES + << " because's uncompressed size exceeds size of address space. " + << properties_entry_length; + return false; + } std::vector<uint8_t> payload_properties(properties_entry_length); int32_t err = ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length); @@ -251,7 +263,7 @@ bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int } static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; - ZipEntry payload_entry; + ZipEntry64 payload_entry; if (FindEntry(zip, AB_OTA_PAYLOAD, &payload_entry) != 0) { LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD; return false; @@ -273,7 +285,7 @@ bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, // In non-A/B updates we extract the update binary from the package. static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; - ZipEntry binary_entry; + ZipEntry64 binary_entry; if (FindEntry(zip, UPDATE_BINARY_NAME, &binary_entry) != 0) { LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME; return false; @@ -337,6 +349,12 @@ static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache, android::base::GetBoolProperty("ro.virtual_ab.allow_non_ab", false); bool device_only_supports_ab = device_supports_ab && !ab_device_supports_nonab; + const auto current_spl = android::base::GetProperty("ro.build.version.security_patch", ""); + if (ViolatesSPLDowngrade(zip, current_spl)) { + LOG(ERROR) << "Denying OTA because it's SPL downgrade"; + return INSTALL_ERROR; + } + if (package_is_ab) { CHECK(package->GetType() == PackageType::kFile); } @@ -699,3 +717,18 @@ bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) { } return true; } + +// Check if `target_token` is in string `str_list`, where `str_list` is expected to be a +// list delimited by `deliminator` +// E.X. isInStringList("a", "a|b|c|d", "|") => true +// E.X. isInStringList("abc", "abc", "|") => true +static bool isInStringList(const std::string& target_token, const std::string& str_list, + const std::string& deliminator) { + if (target_token.length() > str_list.length()) { + return false; + } else if (target_token.length() == str_list.length() || deliminator.length() == 0) { + return target_token == str_list; + } + auto&& list = android::base::Split(str_list, deliminator); + return std::find(list.begin(), list.end(), target_token) != list.end(); +} diff --git a/install/snapshot_utils.cpp b/install/snapshot_utils.cpp index 7235e67c8..336e50f89 100644 --- a/install/snapshot_utils.cpp +++ b/install/snapshot_utils.cpp @@ -32,7 +32,7 @@ bool FinishPendingSnapshotMerges(Device* device) { } RecoveryUI* ui = device->GetUI(); - auto sm = SnapshotManager::NewForFirstStageMount(); + auto sm = SnapshotManager::New(); if (!sm) { ui->Print("Could not create SnapshotManager.\n"); return false; @@ -57,7 +57,7 @@ bool CreateSnapshotPartitions() { return true; } - auto sm = SnapshotManager::NewForFirstStageMount(); + auto sm = SnapshotManager::New(); if (!sm) { // SnapshotManager could not be created. The device is still in a // consistent state and can continue with the mounting of the existing diff --git a/install/spl_check.cpp b/install/spl_check.cpp new file mode 100644 index 000000000..c26ab82f6 --- /dev/null +++ b/install/spl_check.cpp @@ -0,0 +1,78 @@ +/* + * 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 "install/spl_check.h" + +bool ViolatesSPLDowngrade(const build::tools::releasetools::OtaMetadata& metadata, + std::string_view current_spl) { + const auto& post_spl = metadata.postcondition().security_patch_level(); + if (current_spl.empty()) { + LOG(WARNING) << "Failed to get device's current security patch level. Target SPL is " + << post_spl << " permitting OTA install"; + return false; + } + // SPL(security patch level) is expected to be in format yyyy-mm-dd, e.g. 2018-05-29. Given this + // specific format, comparing two SPL can be done by just regular string comparison. If the format + // must lay out year/month/date in the exact order, and must properly prepend dates with 0(for + // example, 05 for May). Otherwise this comparison doesn't work. We don't expect SPL date formats + // to change, leave this as is. + if (post_spl < current_spl) { + LOG(ERROR) << "Current SPL: " << current_spl << " Target SPL: " << post_spl + << " this is considered a downgrade"; + if (metadata.spl_downgrade() || metadata.downgrade()) { + LOG(WARNING) + << "SPL downgrade detected, but OTA package explicitly permitts this(OtaMetadata has " + "spl_downgrade / downgrade bit set).Permitting update anyway.Installing a SPL " + "downgrade OTA can cause /data fail to decrypt and device fails to boot."; + return false; + } + return true; + } else { + LOG(INFO) << "old spl: " << current_spl << " new spl: " << post_spl << " CHECK passes"; + } + return false; +} + +bool ViolatesSPLDowngrade(ZipArchiveHandle zip, std::string_view current_spl) { + static constexpr auto&& OTA_OTA_METADATA = "META-INF/com/android/metadata.pb"; + ZipEntry64 metadata_entry; + if (FindEntry(zip, OTA_OTA_METADATA, &metadata_entry) != 0) { + LOG(WARNING) << "Failed to find " << OTA_OTA_METADATA + << " treating this as non-spl-downgrade, permit OTA install. If device bricks " + "after installing, check kernel log to see if /data failed to decrypt"; + return false; + } + const auto metadata_entry_length = metadata_entry.uncompressed_length; + if (metadata_entry_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to extract " << OTA_OTA_METADATA + << " because's uncompressed size exceeds size of address space. " + << metadata_entry_length; + return false; + } + std::vector<uint8_t> ota_metadata(metadata_entry_length); + int32_t err = ExtractToMemory(zip, &metadata_entry, ota_metadata.data(), metadata_entry_length); + if (err != 0) { + LOG(ERROR) << "Failed to extract " << OTA_OTA_METADATA << ": " << ErrorCodeString(err); + return false; + } + using build::tools::releasetools::OtaMetadata; + OtaMetadata metadata; + if (!metadata.ParseFromArray(ota_metadata.data(), ota_metadata.size())) { + LOG(ERROR) << "Failed to parse ota_medata"; + return false; + } + return ViolatesSPLDowngrade(metadata, current_spl); +} diff --git a/install/spl_check_unittests.cpp b/install/spl_check_unittests.cpp new file mode 100644 index 000000000..709b69c9f --- /dev/null +++ b/install/spl_check_unittests.cpp @@ -0,0 +1,45 @@ +/* + * 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 <gtest/gtest.h> + +#include "install/spl_check.h" +#include "ota_metadata.pb.h" + +using build::tools::releasetools::OtaMetadata; +class SplCheckUnittest : public ::testing::Test { + public: + OtaMetadata metadata; +}; + +TEST_F(SplCheckUnittest, OlderSPL) { + metadata.set_spl_downgrade(false); + metadata.mutable_postcondition()->set_security_patch_level("2021-04-25"); + ASSERT_TRUE(ViolatesSPLDowngrade(metadata, "2021-05-01")); +} + +TEST_F(SplCheckUnittest, NewerSPL) { + metadata.set_spl_downgrade(false); + metadata.mutable_postcondition()->set_security_patch_level("2021-06-01"); + ASSERT_FALSE(ViolatesSPLDowngrade(metadata, "2021-05-05")); +} + +TEST_F(SplCheckUnittest, OlderSPLPermit) { + // If spl_downgrade is set to true, OTA should be permitted + metadata.set_spl_downgrade(true); + metadata.mutable_postcondition()->set_security_patch_level("2021-04-11"); + ASSERT_FALSE(ViolatesSPLDowngrade(metadata, "2021-05-11")); +}
\ No newline at end of file diff --git a/install/verifier.cpp b/install/verifier.cpp index ab750442d..3f0260138 100644 --- a/install/verifier.cpp +++ b/install/verifier.cpp @@ -321,8 +321,14 @@ static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchi std::vector<Certificate> result; std::string_view name; - ZipEntry entry; + ZipEntry64 entry; while ((iter_status = Next(cookie, &entry, &name)) == 0) { + if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to extract " << name + << " because's uncompressed size exceeds size of address space. " + << entry.uncompressed_length; + return {}; + } std::vector<uint8_t> pem_content(entry.uncompressed_length); if (int32_t extract_status = ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size()); diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp index 287208583..4eecf72c6 100644 --- a/install/wipe_data.cpp +++ b/install/wipe_data.cpp @@ -41,9 +41,6 @@ static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); bool is_data = (strcmp(volume, DATA_ROOT) == 0); - ui->SetBackground(RecoveryUI::ERASING); - ui->SetProgressType(RecoveryUI::INDETERMINATE); - std::vector<saved_log_file> log_files; if (is_cache) { // If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the @@ -97,6 +94,9 @@ bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) { } ui->Print("\n-- Wiping cache...\n"); + ui->SetBackground(RecoveryUI::ERASING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); + bool success = EraseVolume("/cache", ui, false); ui->Print("Cache wipe %s.\n", success ? "complete" : "failed"); return success; @@ -105,6 +105,8 @@ bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) { bool WipeData(Device* device, bool convert_fbe) { RecoveryUI* ui = device->GetUI(); ui->Print("\n-- Wiping data...\n"); + ui->SetBackground(RecoveryUI::ERASING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); if (!FinishPendingSnapshotMerges(device)) { ui->Print("Unable to check update status or complete merge, cannot wipe partitions.\n"); diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp index 89d5d31a3..915c87b45 100644 --- a/install/wipe_device.cpp +++ b/install/wipe_device.cpp @@ -49,9 +49,14 @@ std::vector<std::string> GetWipePartitionList(Package* wipe_package) { constexpr char RECOVERY_WIPE_ENTRY_NAME[] = "recovery.wipe"; std::string partition_list_content; - ZipEntry entry; + ZipEntry64 entry; if (FindEntry(zip, RECOVERY_WIPE_ENTRY_NAME, &entry) == 0) { - uint32_t length = entry.uncompressed_length; + auto length = entry.uncompressed_length; + if (length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME + << " because's uncompressed size exceeds size of address space. " << length; + return {}; + } partition_list_content = std::string(length, '\0'); if (auto err = ExtractToMemory( zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length); diff --git a/minadbd/Android.bp b/minadbd/Android.bp index b6ca59efa..2bcfece40 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "minadbd_defaults", @@ -24,7 +33,7 @@ cc_defaults { cpp_std: "experimental", include_dirs: [ - "system/core/adb", + "packages/modules/adb", ], header_libs: [ diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index ff91ba931..0abe8675b 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -23,6 +23,7 @@ #include <string.h> #include <unistd.h> +#include <chrono> #include <functional> #include <memory> #include <set> @@ -142,10 +143,48 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, return kMinadbdSuccess; } +static bool WaitForSocketClose(int fd, std::chrono::milliseconds timeout) { + const auto begin = std::chrono::steady_clock::now(); + const auto end = begin + timeout; + while (std::chrono::steady_clock::now() < end) { + // We don't care about reading the socket, we just want to wait until + // socket closes. In this case .events = 0 will tell the kernel to wait + // for close events. + struct pollfd pfd = { .fd = fd, .events = 0 }; + auto timeout_ms = std::chrono::duration_cast<std::chrono::milliseconds>( + end - std::chrono::steady_clock::now()) + .count(); + int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, timeout_ms)); + if (rc == 1) { + LOG(INFO) << "revents: " << pfd.revents; + if (pfd.revents & (POLLHUP | POLLRDHUP)) { + return true; + } + } else { + PLOG(ERROR) << "poll() failed"; + // poll failed, almost definitely due to timeout + // If not, you're screwed anyway, because it probably means the kernel ran + // out of memory. + return false; + } + } + return false; +} + // Sideload service always exits after serving an install command. static void SideloadHostService(unique_fd sfd, const std::string& args) { + using namespace std::chrono_literals; MinadbdCommandStatus status; - exit(RunAdbFuseSideload(sfd.get(), args, &status)); + auto error = RunAdbFuseSideload(sfd.get(), args, &status); + // No need to wait if the socket is already closed, meaning the other end + // already exited for some reason. + if (error != kMinadbdHostSocketIOError) { + // We sleep for a little bit just to wait for the host to receive last + // "DONEDONE" message. However minadbd process is likely to get terminated + // early due to exit_on_close + WaitForSocketClose(sfd, 3000ms); + } + exit(error); } // Rescue service waits for the next command after an install command. diff --git a/minui/Android.bp b/minui/Android.bp index fff3a8ec9..f68f6c81d 100644 --- a/minui/Android.bp +++ b/minui/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_library { name: "libminui", recovery_available: true, @@ -27,14 +36,12 @@ cc_library { srcs: [ "events.cpp", "graphics.cpp", - "graphics_adf.cpp", "graphics_drm.cpp", "graphics_fbdev.cpp", "resources.cpp", ], whole_static_libs: [ - "libadf", "libdrm", "libsync", ], diff --git a/minui/graphics.cpp b/minui/graphics.cpp index d34da5674..dce1e619a 100644 --- a/minui/graphics.cpp +++ b/minui/graphics.cpp @@ -25,7 +25,6 @@ #include <android-base/properties.h> -#include "graphics_adf.h" #include "graphics_drm.h" #include "graphics_fbdev.h" #include "minui/minui.h" @@ -362,15 +361,10 @@ int gr_init() { ret); } - auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendAdf>() }; + auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendDrm>() }; gr_draw = backend->Init(); if (!gr_draw) { - backend = std::make_unique<MinuiBackendDrm>(); - gr_draw = backend->Init(); - } - - if (!gr_draw) { backend = std::make_unique<MinuiBackendFbdev>(); gr_draw = backend->Init(); } diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp deleted file mode 100644 index 10cd60709..000000000 --- a/minui/graphics_adf.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2014 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 "graphics_adf.h" - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <adf/adf.h> -#include <sync/sync.h> - -#include "minui/minui.h" - -GRSurfaceAdf::~GRSurfaceAdf() { - if (mmapped_buffer_) { - munmap(mmapped_buffer_, pitch * height); - } - if (fence_fd != -1) { - close(fence_fd); - } - if (fd != -1) { - close(fd); - } -} - -std::unique_ptr<GRSurfaceAdf> GRSurfaceAdf::Create(int intf_fd, const drm_mode_modeinfo* mode, - __u32 format, int* err) { - __u32 offset; - __u32 pitch; - auto fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format, - &offset, &pitch); - - if (fd < 0) { - *err = fd; - return nullptr; - } - - std::unique_ptr<GRSurfaceAdf> surf = std::unique_ptr<GRSurfaceAdf>( - new GRSurfaceAdf(mode->hdisplay, mode->vdisplay, pitch, (format == DRM_FORMAT_RGB565 ? 2 : 4), - offset, pitch, fd)); - - auto mmapped = - mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset); - if (mmapped == MAP_FAILED) { - *err = -errno; - return nullptr; - } - surf->mmapped_buffer_ = static_cast<uint8_t*>(mmapped); - return surf; -} - -MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), current_surface(0), n_surfaces(0) {} - -int MinuiBackendAdf::InterfaceInit() { - adf_interface_data intf_data; - if (int err = adf_get_interface_data(intf_fd, &intf_data); err < 0) return err; - - int result = 0; - surfaces[0] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result); - if (!surfaces[0]) { - fprintf(stderr, "Failed to allocate surface 0: %s\n", strerror(-result)); - goto done; - } - - surfaces[1] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result); - if (!surfaces[1]) { - fprintf(stderr, "Failed to allocate surface 1: %s\n", strerror(-result)); - n_surfaces = 1; - } else { - n_surfaces = 2; - } - -done: - adf_free_interface_data(&intf_data); - return result; -} - -int MinuiBackendAdf::DeviceInit(adf_device* dev) { - adf_id_t intf_id; - int err = adf_find_simple_post_configuration(dev, &format, 1, &intf_id, &eng_id); - if (err < 0) return err; - - err = adf_device_attach(dev, eng_id, intf_id); - if (err < 0 && err != -EALREADY) return err; - - intf_fd = adf_interface_open(dev, intf_id, O_RDWR | O_CLOEXEC); - if (intf_fd < 0) return intf_fd; - - err = InterfaceInit(); - if (err < 0) { - close(intf_fd); - intf_fd = -1; - } - - return err; -} - -GRSurface* MinuiBackendAdf::Init() { - PixelFormat pixel_format = gr_pixel_format(); - if (pixel_format == PixelFormat::ABGR) { - format = DRM_FORMAT_ABGR8888; - } else if (pixel_format == PixelFormat::BGRA) { - format = DRM_FORMAT_BGRA8888; - } else if (pixel_format == PixelFormat::RGBX) { - format = DRM_FORMAT_RGBX8888; - } else { - format = DRM_FORMAT_RGB565; - } - - adf_id_t* dev_ids = nullptr; - ssize_t n_dev_ids = adf_devices(&dev_ids); - if (n_dev_ids == 0) { - return nullptr; - } else if (n_dev_ids < 0) { - fprintf(stderr, "enumerating adf devices failed: %s\n", strerror(-n_dev_ids)); - return nullptr; - } - - intf_fd = -1; - - for (ssize_t i = 0; i < n_dev_ids && intf_fd < 0; i++) { - int err = adf_device_open(dev_ids[i], O_RDWR, &dev); - if (err < 0) { - fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], strerror(-err)); - continue; - } - - err = DeviceInit(&dev); - if (err < 0) { - fprintf(stderr, "initializing adf device %u failed: %s\n", dev_ids[i], strerror(-err)); - adf_device_close(&dev); - } - } - - free(dev_ids); - - if (intf_fd < 0) return nullptr; - - GRSurface* ret = Flip(); - - Blank(true); - Blank(false); - - return ret; -} - -void MinuiBackendAdf::Sync(GRSurfaceAdf* surf) { - static constexpr unsigned int kWarningTimeout = 3000; - - if (surf == nullptr) return; - - if (surf->fence_fd >= 0) { - int err = sync_wait(surf->fence_fd, kWarningTimeout); - if (err < 0) { - perror("adf sync fence wait error\n"); - } - - close(surf->fence_fd); - surf->fence_fd = -1; - } -} - -GRSurface* MinuiBackendAdf::Flip() { - const auto& surf = surfaces[current_surface]; - - int fence_fd = adf_interface_simple_post(intf_fd, eng_id, surf->width, surf->height, format, - surf->fd, surf->offset, surf->pitch, -1); - if (fence_fd >= 0) surf->fence_fd = fence_fd; - - current_surface = (current_surface + 1) % n_surfaces; - Sync(surfaces[current_surface].get()); - return surfaces[current_surface].get(); -} - -void MinuiBackendAdf::Blank(bool blank) { - adf_interface_blank(intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); -} - -MinuiBackendAdf::~MinuiBackendAdf() { - adf_device_close(&dev); - if (intf_fd >= 0) close(intf_fd); -} diff --git a/minui/graphics_adf.h b/minui/graphics_adf.h deleted file mode 100644 index 79d8d2acb..000000000 --- a/minui/graphics_adf.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#include <memory> - -#include <adf/adf.h> - -#include "graphics.h" -#include "minui/minui.h" - -class GRSurfaceAdf : public GRSurface { - public: - ~GRSurfaceAdf() override; - - static std::unique_ptr<GRSurfaceAdf> Create(int intf_fd, const drm_mode_modeinfo* mode, - __u32 format, int* err); - - uint8_t* data() override { - return mmapped_buffer_; - } - - private: - friend class MinuiBackendAdf; - - GRSurfaceAdf(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes, __u32 offset, - __u32 pitch, int fd) - : GRSurface(width, height, row_bytes, pixel_bytes), offset(offset), pitch(pitch), fd(fd) {} - - const __u32 offset; - const __u32 pitch; - - int fd; - int fence_fd{ -1 }; - uint8_t* mmapped_buffer_{ nullptr }; -}; - -class MinuiBackendAdf : public MinuiBackend { - public: - MinuiBackendAdf(); - ~MinuiBackendAdf() override; - GRSurface* Init() override; - GRSurface* Flip() override; - void Blank(bool) override; - - private: - int InterfaceInit(); - int DeviceInit(adf_device* dev); - void Sync(GRSurfaceAdf* surf); - - int intf_fd; - adf_id_t eng_id; - __u32 format; - adf_device dev; - size_t current_surface; - size_t n_surfaces; - std::unique_ptr<GRSurfaceAdf> surfaces[2]; -}; diff --git a/minui/resources.cpp b/minui/resources.cpp index f635acd1a..d7b927700 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -450,5 +450,5 @@ int res_create_localized_alpha_surface(const char* name, } void res_free_surface(GRSurface* surface) { - free(surface); + delete(surface); } diff --git a/otautil/Android.bp b/otautil/Android.bp index 3b3f9cbc4..557b8a313 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_library_static { name: "libotautil", diff --git a/otautil/include/otautil/error_code.h b/otautil/include/otautil/error_code.h index 2b73c1353..7b52ce599 100644 --- a/otautil/include/otautil/error_code.h +++ b/otautil/include/otautil/error_code.h @@ -22,7 +22,7 @@ enum ErrorCode : int { kLowBattery = 20, kZipVerificationFailure, kZipOpenFailure, - kBootreasonInBlacklist, + kBootreasonInBlocklist, kPackageCompatibilityFailure, kScriptExecutionFailure, kMapFileFailure, diff --git a/recovery.cpp b/recovery.cpp index 7675121d4..36924fbdf 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -421,15 +421,15 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) case Device::REBOOT: case Device::SHUTDOWN: if (!ui->IsTextVisible()) { - return Device::REBOOT; + return chosen_action; } // okay to reboot; no need to ask. if (!update_in_progress) { - return Device::REBOOT; + return chosen_action; } // An update might have been failed. Ask if user really wants to reboot. if (AskToReboot(device, chosen_action)) { - return Device::REBOOT; + return chosen_action; } break; @@ -559,15 +559,15 @@ static void set_retry_bootloader_message(int retry_count, const std::vector<std: } } -static bool bootreason_in_blacklist() { +static bool bootreason_in_blocklist() { std::string bootreason = android::base::GetProperty("ro.boot.bootreason", ""); if (!bootreason.empty()) { // More bootreasons can be found in "system/core/bootstat/bootstat.cpp". - static const std::vector<std::string> kBootreasonBlacklist{ + static const std::vector<std::string> kBootreasonBlocklist{ "kernel_panic", "Panic", }; - for (const auto& str : kBootreasonBlacklist) { + for (const auto& str : kBootreasonBlocklist) { if (android::base::EqualsIgnoreCase(str, bootreason)) return true; } } @@ -702,7 +702,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri } std::vector<std::string> title_lines = - android::base::Split(android::base::GetProperty("ro.bootimage.build.fingerprint", ""), ":"); + android::base::Split(android::base::GetProperty("ro.build.fingerprint", ""), ":"); title_lines.insert(std::begin(title_lines), "Android Recovery"); ui->SetTitle(title_lines); @@ -734,10 +734,10 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri // Log the error code to last_install when installation skips due to low battery. log_failure_code(kLowBattery, update_package); status = INSTALL_SKIPPED; - } else if (retry_count == 0 && bootreason_in_blacklist()) { + } else if (retry_count == 0 && bootreason_in_blocklist()) { // Skip update-on-reboot when bootreason is kernel_panic or similar - ui->Print("bootreason is in the blacklist; skip OTA installation\n"); - log_failure_code(kBootreasonInBlacklist, update_package); + ui->Print("bootreason is in the blocklist; skip OTA installation\n"); + log_failure_code(kBootreasonInBlocklist, update_package); status = INSTALL_SKIPPED; } else { // It's a fresh update. Initialize the retry_count in the BCB to 1; therefore we can later diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp index 9dfee5fd5..f64b0d1c9 100644 --- a/recovery_ui/Android.bp +++ b/recovery_ui/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_library { name: "librecovery_ui", recovery_available: true, @@ -22,6 +31,7 @@ cc_library { srcs: [ "device.cpp", + "ethernet_device.cpp", "ethernet_ui.cpp", "screen_ui.cpp", "stub_ui.cpp", @@ -102,7 +112,7 @@ cc_library_static { ], srcs: [ - "ethernet_device.cpp", + "default_ethernet_device.cpp", ], shared_libs: [ diff --git a/recovery_ui/default_ethernet_device.cpp b/recovery_ui/default_ethernet_device.cpp new file mode 100644 index 000000000..1fdff0d86 --- /dev/null +++ b/recovery_ui/default_ethernet_device.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 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 "recovery_ui/device.h" +#include "recovery_ui/ethernet_device.h" +#include "recovery_ui/ethernet_ui.h" + +Device* make_device() { + return new EthernetDevice(new EthernetRecoveryUI); +} diff --git a/recovery_ui/ethernet_device.cpp b/recovery_ui/ethernet_device.cpp index 39ec65dc4..d79f41dec 100644 --- a/recovery_ui/ethernet_device.cpp +++ b/recovery_ui/ethernet_device.cpp @@ -27,23 +27,9 @@ #include <sys/types.h> #include "recovery_ui/device.h" +#include "recovery_ui/ethernet_device.h" #include "recovery_ui/ethernet_ui.h" -class EthernetDevice : public Device { - public: - explicit EthernetDevice(EthernetRecoveryUI* ui); - - void PreRecovery() override; - void PreFastboot() override; - - private: - int SetInterfaceFlags(const unsigned set, const unsigned clr); - void SetTitleIPv6LinkLocalAddress(const bool interface_up); - - android::base::unique_fd ctl_sock_; - static const std::string interface; -}; - const std::string EthernetDevice::interface = "eth0"; EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui) @@ -129,8 +115,3 @@ void EthernetDevice::SetTitleIPv6LinkLocalAddress(const bool interface_up) { recovery_ui->SetIPv6LinkLocalAddress(); } - -// ----------------------------------------------------------------------------------------- -Device* make_device() { - return new EthernetDevice(new EthernetRecoveryUI); -} diff --git a/recovery_ui/include/recovery_ui/ethernet_device.h b/recovery_ui/include/recovery_ui/ethernet_device.h new file mode 100644 index 000000000..ea710ab93 --- /dev/null +++ b/recovery_ui/include/recovery_ui/ethernet_device.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 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. + */ + +#ifndef _ETHERNET_RECOVERY_DEVICE_H +#define _ETHERNET_RECOVERY_DEVICE_H + +#include "device.h" + +#include <android-base/unique_fd.h> + +// Forward declaration to avoid including "ethernet_ui.h". +class EthernetRecoveryUI; + +class EthernetDevice : public Device { + public: + explicit EthernetDevice(EthernetRecoveryUI* ui); + + void PreRecovery() override; + void PreFastboot() override; + + private: + int SetInterfaceFlags(const unsigned set, const unsigned clr); + void SetTitleIPv6LinkLocalAddress(const bool interface_up); + + android::base::unique_fd ctl_sock_; + static const std::string interface; +}; + +#endif // _ETHERNET_RECOVERY_DEVICE_H diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index 08ec1d76a..512732f90 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -192,6 +192,8 @@ class RecoveryUI { return key_interrupted_; } + virtual bool IsUsbConnected(); + protected: void EnqueueKey(int key_code); @@ -226,8 +228,6 @@ class RecoveryUI { void ProcessKey(int key_code, int updown); void TimeKey(int key_code, int count); - bool IsUsbConnected(); - bool InitScreensaver(); void SetScreensaverState(ScreensaverState state); diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 087fc0e84..b2c828f34 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -37,6 +37,7 @@ #include <unordered_map> #include <vector> +#include <android-base/chrono_utils.h> #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -448,7 +449,9 @@ void ScreenRecoveryUI::draw_foreground_locked() { int frame_height = gr_get_height(frame); int frame_x = (ScreenWidth() - frame_width) / 2; int frame_y = GetAnimationBaseline(); - DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); + if (frame_x >= 0 && frame_y >= 0 && (frame_x + frame_width) < ScreenWidth() && + (frame_y + frame_height) < ScreenHeight()) + DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); } if (progressBarType != EMPTY) { @@ -879,10 +882,28 @@ bool ScreenRecoveryUI::LoadWipeDataMenuText() { return true; } +static bool InitGraphics() { + // Timeout is same as init wait for file default of 5 seconds and is arbitrary + const unsigned timeout = 500; // 10ms increments + for (auto retry = timeout; retry > 0; --retry) { + if (gr_init() == 0) { + if (retry < timeout) { + // Log message like init wait for file completion log for consistency. + LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms"; + } + return true; + } + std::this_thread::sleep_for(10ms); + } + // Log message like init wait for file timeout log for consistency. + LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms"; + return false; +} + bool ScreenRecoveryUI::Init(const std::string& locale) { RecoveryUI::Init(locale); - if (gr_init() == -1) { + if (!InitGraphics()) { return false; } diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index 330721773..6e67b1d4e 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -48,6 +48,10 @@ constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_b constexpr const char* BRIGHTNESS_FILE_SDM = "/sys/class/backlight/panel0-backlight/brightness"; constexpr const char* MAX_BRIGHTNESS_FILE_SDM = "/sys/class/backlight/panel0-backlight/max_brightness"; +constexpr const char* BRIGHTNESS_FILE_PWM = + "/sys/class/backlight/pwm-backlight.0/brightness"; +constexpr const char* MAX_BRIGHTNESS_FILE_PWM = + "/sys/class/backlight/pwm-backlight.0/max_brightness"; constexpr int kDefaultTouchLowThreshold = 50; constexpr int kDefaultTouchHighThreshold = 90; @@ -106,10 +110,19 @@ bool RecoveryUI::InitScreensaver() { return false; } if (access(brightness_file_.c_str(), R_OK | W_OK)) { - brightness_file_ = BRIGHTNESS_FILE_SDM; + if (!access(BRIGHTNESS_FILE_SDM, R_OK | W_OK)) { + brightness_file_ = BRIGHTNESS_FILE_SDM; + } else { + brightness_file_ = BRIGHTNESS_FILE_PWM; + } } + if (access(max_brightness_file_.c_str(), R_OK)) { - max_brightness_file_ = MAX_BRIGHTNESS_FILE_SDM; + if (!access(MAX_BRIGHTNESS_FILE_SDM, R_OK)) { + max_brightness_file_ = MAX_BRIGHTNESS_FILE_SDM; + } else { + max_brightness_file_ = MAX_BRIGHTNESS_FILE_PWM; + } } // Set the initial brightness level based on the max brightness. Note that reading the initial // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp index bf79a2e87..e0e9ec058 100644 --- a/recovery_utils/Android.bp +++ b/recovery_utils/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "librecovery_utils_defaults", diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp index 99f3c5dc9..19484478c 100644 --- a/recovery_utils/roots.cpp +++ b/recovery_utils/roots.cpp @@ -259,6 +259,12 @@ int format_volume(const std::string& volume, const std::string& directory) { make_f2fs_cmd.push_back("-C"); make_f2fs_cmd.push_back("utf8"); } + if (v->fs_mgr_flags.fs_compress) { + make_f2fs_cmd.push_back("-O"); + make_f2fs_cmd.push_back("compression"); + make_f2fs_cmd.push_back("-O"); + make_f2fs_cmd.push_back("extra_attr"); + } make_f2fs_cmd.push_back(v->blk_device); if (length >= kSectorSize) { make_f2fs_cmd.push_back(std::to_string(length / kSectorSize)); diff --git a/tests/Android.bp b/tests/Android.bp index 4c23255ad..5ef4d58c0 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "recovery_test_defaults", @@ -31,7 +40,6 @@ cc_defaults { "libpng", "libprocessgroup", "libselinux", - "libz", "libziparchive", ], @@ -65,7 +73,7 @@ libapplypatch_static_libs = [ "libbase", "libbrotli", "libbz", - "libz", + "libz_stable", "libziparchive", ] @@ -95,6 +103,24 @@ librecovery_static_libs = [ "libc++fs", ] +// recovery image for unittests. +// ======================================================== +genrule { + name: "recovery_image", + cmd: "cat $(location testdata/recovery_head) <(cat $(location testdata/recovery_body) | $(location minigzip)) $(location testdata/recovery_tail) > $(out)", + srcs: [ + "testdata/recovery_head", + "testdata/recovery_body", + "testdata/recovery_tail", + ], + tools: [ + "minigzip", + ], + out: [ + "testdata/recovery.img", + ], +} + cc_test { name: "recovery_unit_test", isolated: true, @@ -122,12 +148,15 @@ cc_test { "libupdater_core", "libupdate_verifier", - "libgtest_prod", "libprotobuf-cpp-lite", ], + header_libs: [ + "libgtest_prod_headers", + ], data: [ "testdata/*", + ":recovery_image", ":res-testdata", ], } @@ -182,3 +211,23 @@ cc_test_host { }, }, } + +cc_fuzz { + name: "libinstall_verify_package_fuzzer", + defaults: [ + "recovery_test_defaults", + ], + + srcs: ["fuzz/verify_package_fuzzer.cpp"], + + corpus: [ + "testdata/otasigned*.zip", + ], + + static_libs: [ + "libotautil", + "libinstall", + "librecovery_ui", + "libminui", + ], +} diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml new file mode 100644 index 000000000..0ac75e4ea --- /dev/null +++ b/tests/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<configuration description="Runs recovery_host_test."> + <option name="null-device" value="true" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + <option name="force-root" value="false" /> + </target_preparer> + <option name="not-shardable" value="true" /> + + <test class="com.android.tradefed.testtype.HostGTest" > + <option name="module-name" value="recovery_host_test" /> + <option name="native-test-timeout" value="5m"/> + </test> +</configuration> diff --git a/tests/fuzz/verify_package_fuzzer.cpp b/tests/fuzz/verify_package_fuzzer.cpp new file mode 100644 index 000000000..baa44e070 --- /dev/null +++ b/tests/fuzz/verify_package_fuzzer.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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 "install/install.h" +#include "install/package.h" +#include "recovery_ui/stub_ui.h" + +std::unique_ptr<Package> CreatePackage(std::vector<uint8_t>& content) { + return Package::CreateMemoryPackage(content, [](float) -> void {}); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider data_provider(data, size); + auto package_contents = data_provider.ConsumeRemainingBytes<uint8_t>(); + if (package_contents.size() == 0) { + return 0; + } + auto package = CreatePackage(package_contents); + StubRecoveryUI ui; + verify_package(package.get(), &ui); + return 0; +} diff --git a/tests/testdata/recovery-from-boot.p b/tests/testdata/recovery-from-boot.p Binary files differindex 06f6c299f..81738ed29 100644 --- a/tests/testdata/recovery-from-boot.p +++ b/tests/testdata/recovery-from-boot.p diff --git a/tests/testdata/recovery.img b/tests/testdata/recovery.img Binary files differdeleted file mode 100644 index b862e6f0c..000000000 --- a/tests/testdata/recovery.img +++ /dev/null diff --git a/tests/testdata/recovery_body b/tests/testdata/recovery_body Binary files differnew file mode 100644 index 000000000..48d7c10a5 --- /dev/null +++ b/tests/testdata/recovery_body diff --git a/tests/testdata/recovery_head b/tests/testdata/recovery_head Binary files differnew file mode 100644 index 000000000..7f494d098 --- /dev/null +++ b/tests/testdata/recovery_head diff --git a/tests/testdata/recovery_tail b/tests/testdata/recovery_tail Binary files differnew file mode 100644 index 000000000..7fe2c6ce8 --- /dev/null +++ b/tests/testdata/recovery_tail diff --git a/tests/testdata/ziptest_dummy-update.zip b/tests/testdata/ziptest_fake-update.zip Binary files differindex 6976bf155..6976bf155 100644 --- a/tests/testdata/ziptest_dummy-update.zip +++ b/tests/testdata/ziptest_fake-update.zip diff --git a/tests/unit/host/imgdiff_test.cpp b/tests/unit/host/imgdiff_test.cpp index e76ccbdfb..978ac7c2b 100644 --- a/tests/unit/host/imgdiff_test.cpp +++ b/tests/unit/host/imgdiff_test.cpp @@ -35,7 +35,6 @@ using android::base::get_unaligned; -// Sanity check for the given imgdiff patch header. static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw, size_t* num_deflate) { const size_t size = patch.size(); diff --git a/tests/unit/install_test.cpp b/tests/unit/install_test.cpp index ee753494c..c3415479d 100644 --- a/tests/unit/install_test.cpp +++ b/tests/unit/install_test.cpp @@ -76,7 +76,7 @@ TEST(InstallTest, read_metadata_from_package_smoke) { TEST(InstallTest, read_metadata_from_package_no_entry) { TemporaryFile temp_file; - BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); + BuildZipArchive({ { "fake_entry", "" } }, temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -153,7 +153,7 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) { TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { TemporaryFile temp_file; // The archive must have something to be opened correctly. - BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); + BuildZipArchive({ { "fake_entry", "" } }, temp_file.release(), kCompressStored); // Missing update binary. ZipArchiveHandle zip; @@ -190,7 +190,7 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ZipEntry payload_entry; + ZipEntry64 payload_entry; ASSERT_EQ(0, FindEntry(zip, "payload.bin", &payload_entry)); std::map<std::string, std::string> metadata; @@ -334,7 +334,7 @@ TEST(InstallTest, CheckPackageMetadata_device_type) { metadata = android::base::Join( std::vector<std::string>{ "ota-type=BRICK", - "pre-device=dummy_device_type", + "pre-device=fake_device_type", }, "\n"); TestCheckPackageMetadata(metadata, OtaType::BRICK, false); @@ -358,7 +358,7 @@ TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { std::vector<std::string>{ "ota-type=BRICK", "pre-device=" + device, - "serialno=dummy_serial", + "serialno=fake_serial", }, "\n"); TestCheckPackageMetadata(metadata, OtaType::BRICK, false); @@ -383,7 +383,7 @@ TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { ASSERT_NE("", serialno); std::vector<std::string> serial_numbers; - // Creates a dummy serial number string. + // Creates a fake serial number string. for (char c = 'a'; c <= 'z'; c++) { serial_numbers.emplace_back(serialno.size(), c); } @@ -431,7 +431,7 @@ TEST(InstallTest, CheckPackageMetadata_ab_build_version) { std::vector<std::string>{ "ota-type=AB", "pre-device=" + device, - "pre-build-incremental=dummy_build", + "pre-build-incremental=fake_build", "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), }, "\n"); @@ -459,7 +459,35 @@ TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { std::vector<std::string>{ "ota-type=AB", "pre-device=" + device, - "pre-build=dummy_build_fingerprint", + "pre-build=fake_build_fingerprint", + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + TestCheckPackageMetadata(metadata, OtaType::AB, false); +} + +TEST(InstallTest, CheckPackageMetadata_dynamic_fingerprint) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_FALSE(device.empty()); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_FALSE(finger_print.empty()); + + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=please|work|" + device + "|please|work", + "pre-build=" + finger_print = "pass|this|test", + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + TestCheckPackageMetadata(metadata, OtaType::AB, true); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=fake_build_fingerprint", "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), }, "\n"); diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp index 5e31f7fa5..164a93d57 100644 --- a/tests/unit/package_test.cpp +++ b/tests/unit/package_test.cpp @@ -106,7 +106,7 @@ TEST_F(PackageTest, GetZipArchiveHandle_extract_entry) { // Check that we can extract one zip entry. std::string_view entry_name = "dir1/file3.txt"; - ZipEntry entry; + ZipEntry64 entry; ASSERT_EQ(0, FindEntry(zip, entry_name, &entry)); std::vector<uint8_t> extracted(entry_name.size()); diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index 0753d64e1..e065bb859 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -28,7 +28,7 @@ #include "otautil/sysutil.h" TEST(ZipTest, OpenFromMemory) { - std::string zip_path = from_testdata_base("ziptest_dummy-update.zip"); + std::string zip_path = from_testdata_base("ziptest_fake-update.zip"); MemMapping map; ASSERT_TRUE(map.MapFile(zip_path)); @@ -37,7 +37,7 @@ TEST(ZipTest, OpenFromMemory) { ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle)); static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary"; - ZipEntry binary_entry; + ZipEntry64 binary_entry; // Make sure the package opens correctly and its entry can be read. ASSERT_EQ(0, FindEntry(handle, BINARY_PATH, &binary_entry)); diff --git a/tools/image_generator/Android.bp b/tools/image_generator/Android.bp index 83000407c..c9748fade 100644 --- a/tools/image_generator/Android.bp +++ b/tools/image_generator/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + java_library_host { name: "RecoveryImageGenerator", diff --git a/tools/image_generator/ImageGenerator.java b/tools/image_generator/ImageGenerator.java index 1da43e5c6..6c5ea4b5f 100644 --- a/tools/image_generator/ImageGenerator.java +++ b/tools/image_generator/ImageGenerator.java @@ -113,30 +113,30 @@ public class ImageGenerator { private static final Map<String, String> LANGUAGE_TO_FONT_MAP = new TreeMap<String, String>() { { - put("am", "NotoSansEthiopic-Regular"); + put("am", "NotoSansEthiopic-VF"); put("ar", "NotoNaskhArabicUI-Regular"); - put("as", "NotoSansBengaliUI-Regular"); - put("bn", "NotoSansBengaliUI-Regular"); + put("as", "NotoSansBengaliUI-VF"); + put("bn", "NotoSansBengaliUI-VF"); put("fa", "NotoNaskhArabicUI-Regular"); put("gu", "NotoSansGujaratiUI-Regular"); - put("hi", "NotoSansDevanagariUI-Regular"); - put("hy", "NotoSansArmenian-Regular"); + put("hi", "NotoSansDevanagariUI-VF"); + put("hy", "NotoSansArmenian-VF"); put("iw", "NotoSansHebrew-Regular"); put("ja", "NotoSansCJK-Regular"); put("ka", "NotoSansGeorgian-VF"); put("ko", "NotoSansCJK-Regular"); put("km", "NotoSansKhmerUI-Regular"); - put("kn", "NotoSansKannadaUI-Regular"); + put("kn", "NotoSansKannadaUI-VF"); put("lo", "NotoSansLaoUI-Regular"); - put("ml", "NotoSansMalayalamUI-Regular"); - put("mr", "NotoSansDevanagariUI-Regular"); + put("ml", "NotoSansMalayalamUI-VF"); + put("mr", "NotoSansDevanagariUI-VF"); put("my", "NotoSansMyanmarUI-Regular"); - put("ne", "NotoSansDevanagariUI-Regular"); + put("ne", "NotoSansDevanagariUI-VF"); put("or", "NotoSansOriya-Regular"); - put("pa", "NotoSansGurmukhiUI-Regular"); - put("si", "NotoSansSinhala-Regular"); - put("ta", "NotoSansTamilUI-Regular"); - put("te", "NotoSansTeluguUI-Regular"); + put("pa", "NotoSansGurmukhiUI-VF"); + put("si", "NotoSansSinhalaUI-VF"); + put("ta", "NotoSansTamilUI-VF"); + put("te", "NotoSansTeluguUI-VF"); put("th", "NotoSansThaiUI-Regular"); put("ur", "NotoNaskhArabicUI-Regular"); put("zh", "NotoSansCJK-Regular"); diff --git a/tools/image_generator/README.md b/tools/image_generator/README.md index 5d70354e4..1230ad510 100644 --- a/tools/image_generator/README.md +++ b/tools/image_generator/README.md @@ -19,3 +19,13 @@ emulators with different dpi. 4. `resourceDirectory`: The resource directory that contains all the translated strings in xml format, e.g. bootable/recovery/tools/recovery_l10n/res/ 5. `outputFilename`: Path to the generated image. + +# Locales +Supported locales and background texts are located in +[tools/recovery_l10n/res/](../recovery_l10n/res/values). For each background text, the tool renders +a localized image for every supported locale. + +Each individual localized image contains an encoded locale header string, and the rendered +background text. The locale header string is generated by `Locale.forLanguageTag`. And sample +result include `en-US`, `zh-CN`, etc. These individual images are then concatenated together to +form the final resource image that locates in res/images, e.g. `install_text.png` diff --git a/tools/recovery_l10n/Android.bp b/tools/recovery_l10n/Android.bp index d0a6d4b47..ac08e1a5b 100644 --- a/tools/recovery_l10n/Android.bp +++ b/tools/recovery_l10n/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + android_app { name: "RecoveryLocalizer", diff --git a/uncrypt/Android.bp b/uncrypt/Android.bp index 107a7f0fc..fbb4c1f03 100644 --- a/uncrypt/Android.bp +++ b/uncrypt/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_binary { name: "uncrypt", diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index f1f4f69f0..c798e31d6 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -477,9 +477,9 @@ static int Uncrypt(const std::string& input_path, const std::string& map_file, i return kUncryptRealpathFindError; } - bool encryptable; - bool encrypted; - bool f2fs_fs; + bool encryptable = false; + bool encrypted = false; + bool f2fs_fs = false; const std::string blk_dev = FindBlockDevice(path, &encryptable, &encrypted, &f2fs_fs); if (blk_dev.empty()) { LOG(ERROR) << "Failed to find block device for " << path; diff --git a/update_verifier/Android.bp b/update_verifier/Android.bp index f6567137e..ff2eff903 100644 --- a/update_verifier/Android.bp +++ b/update_verifier/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "update_verifier_defaults", @@ -112,12 +121,12 @@ python_binary_host { version: { py2: { - enabled: true, - embedded_launcher: true, - }, - py3: { enabled: false, embedded_launcher: false, }, + py3: { + enabled: true, + embedded_launcher: true, + }, }, } diff --git a/update_verifier/care_map_generator.py b/update_verifier/care_map_generator.py index 051d98deb..c6f2dad24 100644 --- a/update_verifier/care_map_generator.py +++ b/update_verifier/care_map_generator.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright (C) 2018 The Android Open Source Project # @@ -115,13 +115,13 @@ def main(argv): content = input_care_map.read() if args.parse_proto: - result = ParseProtoMessage(content, args.fingerprint_enabled) + result = ParseProtoMessage(content, args.fingerprint_enabled).encode() else: care_map_proto = GenerateCareMapProtoFromLegacyFormat( content.rstrip().splitlines(), args.fingerprint_enabled) result = care_map_proto.SerializeToString() - with open(args.output_file, 'w') as output: + with open(args.output_file, 'wb') as output: output.write(result) diff --git a/updater/Android.bp b/updater/Android.bp index f00a192b9..35debaaf1 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + cc_defaults { name: "libupdater_static_libs", @@ -27,7 +36,6 @@ cc_defaults { "libfec_rs", "libavb", "libverity_tree", - "libgtest_prod", "liblog", "liblp", "libselinux", @@ -42,6 +50,9 @@ cc_defaults { "libcutils", "libutils", ], + header_libs: [ + "libgtest_prod_headers", + ], } cc_defaults { diff --git a/updater/Android.mk b/updater/Android.mk index 46300d974..bb1c07d40 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -34,7 +34,6 @@ updater_common_static_libraries := \ libfec_rs \ libavb \ libverity_tree \ - libgtest_prod \ liblog \ liblp \ libselinux \ @@ -72,6 +71,9 @@ endef include $(CLEAR_VARS) LOCAL_MODULE := updater +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE LOCAL_SRC_FILES := \ updater_main.cpp @@ -93,6 +95,8 @@ LOCAL_STATIC_LIBRARIES := \ libtune2fs \ $(tune2fs_static_libraries) +LOCAL_HEADER_LIBRARIES := libgtest_prod_headers + LOCAL_MODULE_CLASS := EXECUTABLES inc := $(call local-generated-sources-dir)/register.inc diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 2d41f610b..b29aa8ce3 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -348,7 +348,7 @@ class RangeSinkWriter { */ struct NewThreadInfo { ZipArchiveHandle za; - ZipEntry entry; + ZipEntry64 entry{}; bool brotli_compressed; std::unique_ptr<RangeSinkWriter> writer; @@ -1626,7 +1626,7 @@ static bool Sha1DevicePath(const std::string& path, uint8_t digest[SHA_DIGEST_LE static Value* PerformBlockImageUpdate(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv, const CommandMap& command_map, bool dryrun) { - CommandParameters params = {}; + CommandParameters params{}; stash_map.clear(); params.canwrite = !dryrun; @@ -1687,7 +1687,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, } std::string_view path_data(patch_data_fn->data); - ZipEntry patch_entry; + ZipEntry64 patch_entry; if (FindEntry(za, path_data, &patch_entry) != 0) { LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package"; return StringValue(""); @@ -1695,7 +1695,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, params.patch_start = updater->GetMappedPackageAddress() + patch_entry.offset; std::string_view new_data(new_data_fn->data); - ZipEntry new_entry; + ZipEntry64 new_entry; if (FindEntry(za, new_data, &new_entry) != 0) { LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package"; return StringValue(""); diff --git a/updater/commands.cpp b/updater/commands.cpp index aed63369c..1a7c272b5 100644 --- a/updater/commands.cpp +++ b/updater/commands.cpp @@ -128,7 +128,6 @@ bool Command::ParseTargetInfoAndSourceInfo(const std::vector<std::string>& token // No stashes, only source ranges. SourceInfo result(src_hash, src_ranges, {}, {}); - // Sanity check the block count. if (result.blocks() != src_blocks) { *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(), @@ -262,7 +261,7 @@ Command Command::Parse(const std::string& line, size_t index, std::string* err) return {}; } } else if (op == Type::ABORT) { - // No-op, other than sanity checking the input args. + // Abort takes no arguments, so there's nothing else to check. if (pos != tokens.size()) { *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)", tokens.size() - pos); diff --git a/updater/include/private/commands.h b/updater/include/private/commands.h index 79f915434..7a23bb78b 100644 --- a/updater/include/private/commands.h +++ b/updater/include/private/commands.h @@ -307,7 +307,7 @@ class Command { : type_(type), index_(index), cmdline_(std::move(cmdline)), - patch_(std::move(patch)), + patch_(patch), target_(std::move(target)), source_(std::move(source)), stash_(std::move(stash)) {} diff --git a/updater/install.cpp b/updater/install.cpp index afa5195d0..295965047 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -35,6 +35,7 @@ #include <unistd.h> #include <utime.h> +#include <limits> #include <memory> #include <string> #include <vector> @@ -115,7 +116,7 @@ Value* PackageExtractFileFn(const char* name, State* state, std::string dest_path = args[1]; ZipArchiveHandle za = state->updater->GetPackageHandle(); - ZipEntry entry; + ZipEntry64 entry; if (FindEntry(za, zip_path, &entry) != 0) { LOG(ERROR) << name << ": no " << zip_path << " in package"; return StringValue(""); @@ -165,13 +166,18 @@ Value* PackageExtractFileFn(const char* name, State* state, const std::string& zip_path = args[0]; ZipArchiveHandle za = state->updater->GetPackageHandle(); - ZipEntry entry; + ZipEntry64 entry; if (FindEntry(za, zip_path, &entry) != 0) { return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, zip_path.c_str()); } std::string buffer; + if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) { + return ErrorAbort(state, kPackageExtractFileFailure, + "%s(): Entry `%s` Uncompressed size exceeds size of address space.", name, + zip_path.c_str()); + } buffer.resize(entry.uncompressed_length); int32_t ret = diff --git a/updater/target_files.cpp b/updater/target_files.cpp index 919ec4e04..207146f52 100644 --- a/updater/target_files.cpp +++ b/updater/target_files.cpp @@ -115,7 +115,7 @@ bool TargetFile::EntryExists(const std::string_view name) const { } CHECK(handle_); - ZipEntry img_entry; + ZipEntry64 img_entry; return FindEntry(handle_, name, &img_entry) == 0; } @@ -126,7 +126,7 @@ bool TargetFile::ReadEntryToString(const std::string_view name, std::string* con } CHECK(handle_); - ZipEntry entry; + ZipEntry64 entry; if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) { LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err); return false; @@ -137,6 +137,13 @@ bool TargetFile::ReadEntryToString(const std::string_view name, std::string* con return true; } + if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to extract " << name + << " because's uncompressed size exceeds size of address space. " + << entry.uncompressed_length; + return false; + } + content->resize(entry.uncompressed_length); if (auto extract_err = ExtractToMemory( handle_, &entry, reinterpret_cast<uint8_t*>(&content->at(0)), entry.uncompressed_length); @@ -157,7 +164,7 @@ bool TargetFile::ExtractEntryToTempFile(const std::string_view name, } CHECK(handle_); - ZipEntry entry; + ZipEntry64 entry; if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) { LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err); return false; diff --git a/updater/updater.cpp b/updater/updater.cpp index 8f4a6ede5..c52673462 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -163,14 +163,19 @@ void Updater::ParseAndReportErrorCode(State* state) { bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, std::string* content) { - ZipEntry entry; + ZipEntry64 entry; int find_err = FindEntry(za, entry_name, &entry); if (find_err != 0) { LOG(ERROR) << "failed to find " << entry_name << " in the package: " << ErrorCodeString(find_err); return false; } - + if (entry.uncompressed_length > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Failed to extract " << entry_name + << " because's uncompressed size exceeds size of address space. " + << entry.uncompressed_length; + return false; + } content->resize(entry.uncompressed_length); int extract_err = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&content->at(0)), entry.uncompressed_length); diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp index e93830505..bac078cf9 100644 --- a/updater/updater_runtime.cpp +++ b/updater/updater_runtime.cpp @@ -44,29 +44,25 @@ std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) con return std::string(name); } -static struct { - const char* name; - unsigned flag; -} mount_flags_list[] = { - { "noatime", MS_NOATIME }, - { "noexec", MS_NOEXEC }, - { "nosuid", MS_NOSUID }, - { "nodev", MS_NODEV }, - { "nodiratime", MS_NODIRATIME }, - { "ro", MS_RDONLY }, - { "rw", 0 }, - { "remount", MS_REMOUNT }, - { "bind", MS_BIND }, - { "rec", MS_REC }, - { "unbindable", MS_UNBINDABLE }, - { "private", MS_PRIVATE }, - { "slave", MS_SLAVE }, - { "shared", MS_SHARED }, - { "defaults", 0 }, - { 0, 0 }, -}; - static bool setMountFlag(const std::string& flag, unsigned* mount_flags) { + static constexpr std::pair<const char*, unsigned> mount_flags_list[] = { + { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, + { "defaults", 0 }, + }; + for (const auto& [name, value] : mount_flags_list) { if (flag == name) { *mount_flags |= value; diff --git a/updater_sample/Android.bp b/updater_sample/Android.bp index a014248b0..9222d0631 100644 --- a/updater_sample/Android.bp +++ b/updater_sample/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + android_app { name: "SystemUpdaterSample", sdk_version: "system_current", diff --git a/updater_sample/tests/Android.bp b/updater_sample/tests/Android.bp index 806babd9e..4bdfe2cdf 100644 --- a/updater_sample/tests/Android.bp +++ b/updater_sample/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "bootable_recovery_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["bootable_recovery_license"], +} + android_test { name: "SystemUpdaterSampleTests", sdk_version: "system_current", |