Merge "Add libnativehelper.so to public libraries"
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/base/file.cpp b/base/file.cpp
index 6321fc6..97cc2b2 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -225,7 +225,7 @@
content->reserve(sb.st_size);
}
- char buf[BUFSIZ];
+ char buf[BUFSIZ] __attribute__((__uninitialized__));
ssize_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
content->append(buf, n);
diff --git a/base/logging.cpp b/base/logging.cpp
index 6e9c67f..5bd21da 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -260,7 +260,7 @@
// The kernel's printk buffer is only 1024 bytes.
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
- char buf[1024];
+ char buf[1024] __attribute__((__uninitialized__));
size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
if (size > sizeof(buf)) {
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index 78e1e8d..e83ab13 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -25,7 +25,7 @@
void StringAppendV(std::string* dst, const char* format, va_list ap) {
// First try with a small fixed size buffer
- char space[1024];
+ char space[1024] __attribute__((__uninitialized__));
// It's possible for methods that use a va_list to invalidate
// the data in it upon use. The fix is to make a copy
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 24cbad7..052efa7 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -105,20 +105,23 @@
} // namespace
+enum RemountStatus {
+ REMOUNT_SUCCESS = 0,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ MUST_REBOOT
+};
+
static int do_remount(int argc, char* argv[]) {
- enum {
- SUCCESS = 0,
- NOT_USERDEBUG,
- BADARG,
- NOT_ROOT,
- NO_FSTAB,
- UNKNOWN_PARTITION,
- INVALID_PARTITION,
- VERITY_PARTITION,
- BAD_OVERLAY,
- NO_MOUNTS,
- REMOUNT_FAILED,
- } retval = SUCCESS;
+ RemountStatus retval = REMOUNT_SUCCESS;
// If somehow this executable is delivered on a "user" build, it can
// not function, so providing a clear message to the caller rather than
@@ -304,8 +307,7 @@
if (partitions.empty() || just_disabled_verity) {
if (reboot_later) reboot(setup_overlayfs);
if (user_please_reboot_later) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- return 0;
+ return MUST_REBOOT;
}
LOG(WARNING) << "No partitions to remount";
return retval;
@@ -394,6 +396,12 @@
int main(int argc, char* argv[]) {
android::base::InitLogging(argv, MyLogger);
int result = do_remount(argc, argv);
- printf("remount %s\n", result ? "failed" : "succeeded");
+ if (result == MUST_REBOOT) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ } else if (result == REMOUNT_SUCCESS) {
+ printf("remount succeeded\n");
+ } else {
+ printf("remount failed\n");
+ }
return result;
}
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 9d18a44..bde9d0a 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -69,6 +69,7 @@
"libdm",
"libfs_mgr",
"liblog",
+ "libgsi",
],
data: [
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 22a3722..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -35,6 +35,7 @@
#include <libdm/loop_control.h>
#include <libfiemap/fiemap_writer.h>
#include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
#include "utility.h"
@@ -148,7 +149,10 @@
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
EXPECT_EQ(fptr->size(), gBlockSize);
EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+
+ if (!android::gsi::IsGsiRunning()) {
+ EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+ }
}
TEST_F(FiemapWriterTest, CheckFileCreated) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 705d296..e916693 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -161,23 +161,23 @@
"snapshot_test.cpp",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbinder",
"libcrypto",
"libhidlbase",
"libprotobuf-cpp-lite",
- "libsparse",
"libutils",
"libz",
],
static_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libfs_mgr",
"libgsi",
"libgmock",
"liblp",
"libsnapshot",
"libsnapshot_test_helpers",
+ "libsparse",
],
header_libs: [
"libstorage_literals_headers",
@@ -254,34 +254,47 @@
native_coverage : true,
srcs: [
+ // Compile the protobuf definition again with type full.
+ "android/snapshot/snapshot_fuzz.proto",
+ "update_engine/update_metadata.proto",
+ "fuzz_utils.cpp",
"snapshot_fuzz.cpp",
"snapshot_fuzz_utils.cpp",
- "fuzz_utils.cpp",
+
+ // Compile libsnapshot sources directly to avoid dependency
+ // to update_metadata-protos
+ ":libsnapshot_sources",
],
static_libs: [
"libbase",
"libcrypto_static",
"libcutils",
+ "libext2_uuid",
+ "libext4_utils",
+ "libfstab",
"libfs_mgr",
"libgtest", // from libsnapshot_test_helpers
"libgmock", // from libsnapshot_test_helpers
"liblog",
"liblp",
- "libsnapshot_init", // don't use binder or hwbinder
"libsnapshot_test_helpers",
- "libprotobuf-cpp-lite",
- "update_metadata-protos",
+ "libprotobuf-mutator",
],
header_libs: [
+ "libfiemap_headers",
"libstorage_literals_headers",
],
+ proto: {
+ type: "full",
+ canonical_path_from_root: false,
+ local_include_dirs: ["."],
+ },
fuzz_config: {
cc: ["android-virtual-ab+bugs@google.com"],
componentid: 30545,
hotlists: ["1646452"],
fuzz_on_haiku_host: false,
- // TODO(b/154633114): set to true to run this automatically.
- fuzz_on_haiku_device: false,
+ fuzz_on_haiku_device: true,
},
}
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
new file mode 100644
index 0000000..91fbb60
--- /dev/null
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
@@ -0,0 +1,103 @@
+// 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.
+
+syntax = "proto3";
+package android.snapshot;
+
+import "update_engine/update_metadata.proto";
+
+// Controls the behavior of IDeviceInfo.
+// Next: 6
+message FuzzDeviceInfoData {
+ bool slot_suffix_is_a = 1;
+ bool is_overlayfs_setup = 2;
+ bool allow_set_boot_control_merge_status = 3;
+ bool allow_set_slot_as_unbootable = 4;
+ bool is_recovery = 5;
+}
+
+// Controls the behavior of the test SnapshotManager.
+// Next: 2
+message FuzzSnapshotManagerData {
+ bool is_local_image_manager = 1;
+}
+
+// A simplified version of CreateLogicalPartitionParams for fuzzing.
+// Next: 9
+message CreateLogicalPartitionParamsProto {
+ bool use_correct_super = 1;
+ string block_device = 2;
+ bool has_metadata_slot = 3;
+ uint32 metadata_slot = 4;
+ string partition_name = 5;
+ bool force_writable = 6;
+ int64 timeout_millis = 7;
+ string device_name = 8;
+}
+
+// Mimics the API of ISnapshotManager. Defines one action on the snapshot
+// manager.
+// Next: 18
+message SnapshotManagerActionProto {
+ message NoArgs {}
+ message ProcessUpdateStateArgs {
+ bool has_before_cancel = 1;
+ bool fail_before_cancel = 2;
+ }
+ message CreateLogicalAndSnapshotPartitionsArgs {
+ bool use_correct_super = 1;
+ string super = 2;
+ int64 timeout_millis = 3;
+ }
+ message RecoveryCreateSnapshotDevicesArgs {
+ bool has_metadata_device_object = 1;
+ bool metadata_mounted = 2;
+ }
+ oneof value {
+ NoArgs begin_update = 1;
+ NoArgs cancel_update = 2;
+ bool finished_snapshot_writes = 3;
+ NoArgs initiate_merge = 4;
+ ProcessUpdateStateArgs process_update_state = 5;
+ bool get_update_state = 6;
+ chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7;
+ CreateLogicalPartitionParamsProto map_update_snapshot = 8;
+ string unmap_update_snapshot = 9;
+ NoArgs need_snapshots_in_first_stage_mount = 10;
+ CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11;
+ bool handle_imminent_data_wipe = 12;
+ NoArgs recovery_create_snapshot_devices = 13;
+ RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14;
+ NoArgs dump = 15;
+ NoArgs ensure_metadata_mounted = 16;
+ NoArgs get_snapshot_merge_stats_instance = 17;
+ }
+}
+
+// Includes all data that needs to be fuzzed.
+message SnapshotFuzzData {
+ FuzzDeviceInfoData device_info_data = 1;
+ FuzzSnapshotManagerData manager_data = 2;
+
+ // If true:
+ // - if super_data is empty, create empty super partition metadata.
+ // - otherwise, create super partition metadata accordingly.
+ // If false, no valid super partition metadata (it is zeroed)
+ bool is_super_metadata_valid = 3;
+ chromeos_update_engine.DeltaArchiveManifest super_data = 4;
+
+ // More data used to prep the test before running actions.
+ reserved 5 to 9999;
+ repeated SnapshotManagerActionProto actions = 10000;
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
index 64d8224..2910129 100755
--- a/fs_mgr/libsnapshot/fuzz.sh
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -18,8 +18,8 @@
build_cov() {
pushd $(gettop)
- ret=$?
NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+ ret=$?
popd
return ${ret}
}
@@ -46,7 +46,7 @@
}
# run_snapshot_fuzz -runs=10000
-generate_corpse() {
+generate_corpus() {
[[ "$@" ]] || { echo "run with -runs=X"; return 1; }
prepare_device &&
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
index 509eb1b..0263f7e 100644
--- a/fs_mgr/libsnapshot/fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/fuzz_utils.cpp
@@ -22,4 +22,17 @@
CHECK(value) << msg;
}
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+ const google::protobuf::Descriptor* action_desc) {
+ CHECK(action_desc);
+ CHECK(action_desc->oneof_decl_count() == 1)
+ << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name()
+ << "; only one is expected.";
+ auto* oneof_value_desc = action_desc->oneof_decl(0);
+ CHECK(oneof_value_desc);
+ CHECK(oneof_value_desc->name() == "value")
+ << "oneof field has name " << oneof_value_desc->name();
+ return oneof_value_desc;
+}
+
} // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
index 4e14b9c..4dc6cdc 100644
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -12,256 +12,254 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <optional>
+#pragma once
+
+#include <map>
#include <string>
#include <string_view>
-#include <vector>
-// Generic classes for fuzzing a collection of APIs.
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>
+
+// Utilities for using a protobuf definition to fuzz APIs in a class.
+// Terms:
+// The "fuzzed class" is the C++ class definition whose functions are fuzzed.
+// The "fuzzed object" is an instantiated object of the fuzzed class. It is
+// typically created and destroyed for each test run.
+// An "action" is an operation on the fuzzed object that may mutate its state.
+// This typically involves one function call into the fuzzed object.
namespace android::fuzz {
-// My custom boolean type -- to avoid conflict with (u)int8_t and char.
-struct Bool {
- bool value;
- operator bool() const { return value; }
-};
-
-// Helper for FuzzData.
-// A wrapper over an optional const object T. The buffer is maintained elsewhere.
-template <typename T>
-class Optional {
- public:
- Optional(const T* ptr) : ptr_(ptr) {}
- const T& operator*() const { return *ptr_; }
- const T& value() const { return *ptr_; }
- bool has_value() const { return ptr_; }
-
- private:
- const T* ptr_;
-};
-
-// Helper for FuzzData.
-// A wrapper over an optional boolean. The boolean is owned by this object.
-template <>
-class Optional<Bool> {
- public:
- Optional(std::optional<Bool>&& val) : val_(std::move(val)) {}
- const Bool& operator*() const { return *val_; }
- const Bool& value() const { return val_.value(); }
- bool has_value() const { return val_.has_value(); }
-
- private:
- std::optional<Bool> val_;
-};
-
-// Helper for FuzzData.
-// A view on a raw data buffer. Client is responsible for maintaining the lifetime of the data
-// buffer.
-class DataView {
- public:
- DataView(const uint8_t* data, uint64_t size) : data_(data), size_(size) {}
- DataView(const void* data, uint64_t size) : DataView(static_cast<const uint8_t*>(data), size) {}
- inline uint64_t size() const { return size_; }
- inline const uint8_t* data() const { return data_; }
- inline bool CanConsume(uint64_t size) { return size_ >= size; }
- // Consume the first |size| bytes from |this| and return a DataView object that represents
- // the consumed data. Data pointer in |this| is incremented by |size| bytes.
- // If not enough bytes, return nullopt.
- std::optional<DataView> Consume(uint64_t size) {
- if (!CanConsume(size)) return std::nullopt;
- DataView ret(data_, size);
- size_ -= size;
- data_ += size;
- return ret;
- }
-
- private:
- const uint8_t* data_;
- uint64_t size_;
-};
-
-// A view on the fuzz data. Provides APIs to consume typed objects.
-class FuzzData : public DataView {
- public:
- // Inherit constructors.
- using DataView::DataView;
- // Consume a data object T and return the pointer (into the buffer). No copy is done.
- // If not enough bytes, return nullptr.
- template <typename T>
- inline Optional<T> Consume() {
- auto data_view = DataView::Consume(sizeof(T));
- if (!data_view.has_value()) return nullptr;
- return reinterpret_cast<const T*>(data_view->data());
- }
- // To provide enough entropy for booleans, they are consumed bit by bit.
- // Hence, the returned value is not indexed into the buffer. See Optional<Bool>.
- template <>
- Optional<Bool> Consume<Bool>() {
- if (!boolean_buffer_.has_value() || boolean_bit_offset_ >= sizeof(*boolean_buffer_)) {
- boolean_buffer_ = Consume<uint8_t>();
- boolean_bit_offset_ = 0;
- }
- if (!boolean_buffer_.has_value()) {
- return Optional<Bool>(std::nullopt);
- }
- const auto& byte = *boolean_buffer_;
- bool ret = (byte >> boolean_bit_offset_) & 0x1;
- boolean_bit_offset_++;
- return Optional<Bool>(Bool{ret});
- }
-
- private:
- // Separate buffer for booleans.
- Optional<uint8_t> boolean_buffer_ = nullptr;
- uint8_t boolean_bit_offset_ = 0;
-};
-
-enum class CallResult {
- SUCCESS,
- NOT_ENOUGH_DATA,
-};
-
-inline bool AllArgsHasValue() {
- return true;
-}
-template <typename T>
-inline bool AllArgsHasValue(const Optional<T>& t) {
- return t.has_value();
-}
-template <typename First, typename... Remaining>
-inline bool AllArgsHasValue(const Optional<First>& first, const Optional<Remaining>&... remaining) {
- return first.has_value() && AllArgsHasValue(remaining...);
-}
-
-// Base class of FuzzFunction.
-class FuzzFunctionBase {
- public:
- virtual ~FuzzFunctionBase() = default;
- virtual CallResult Call(FuzzData* fuzz_data) const = 0;
-};
-
-template <typename T>
-class FuzzFunction; // undefined
-
-// A wrapper over a fuzzed function.
-template <typename R, typename... Args>
-class FuzzFunction<R(Args...)> : public FuzzFunctionBase {
- public:
- using Function = std::function<R(Args...)>;
- FuzzFunction(Function&& function) : function_(std::move(function)) {}
- // Eat necessary data in |fuzz_data| and invoke the function.
- CallResult Call(FuzzData* fuzz_data) const override {
- return CallWithOptionalArgs(fuzz_data->Consume<std::remove_reference_t<Args>>()...);
- }
-
- private:
- Function function_;
-
- CallResult CallWithOptionalArgs(const Optional<std::remove_reference_t<Args>>&... args) const {
- if (!AllArgsHasValue(args...)) {
- return CallResult::NOT_ENOUGH_DATA;
- }
- (void)function_(args.value()...); // ignore returned value
- return CallResult::SUCCESS;
- }
-};
-
// CHECK(value) << msg
void CheckInternal(bool value, std::string_view msg);
-// A collection of FuzzFunction's.
-// FunctionsSizeType must be an integral type where
-// functions_.size() <= std::numeric_limits<FunctionSizeType>::max().
-template <typename FunctionsSizeType>
-class FuzzFunctions {
+// Get the oneof descriptor inside Action
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+ const google::protobuf::Descriptor* action_desc);
+
+template <typename Class>
+using FunctionMapImpl =
+ std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc)>>;
+
+template <typename Class>
+class FunctionMap : public FunctionMapImpl<Class> {
public:
- // Subclass should override this to register functions via AddFunction.
- FuzzFunctions() = default;
- virtual ~FuzzFunctions() = default;
- // Eat some amount of data in |fuzz_data| and call one of the |functions_|.
- CallResult CallOne(FuzzData* fuzz_data) const {
- auto opt_number = fuzz_data->Consume<FunctionsSizeType>();
- if (!opt_number.has_value()) {
- return CallResult::NOT_ENOUGH_DATA;
- }
- auto function_index = opt_number.value() % functions_.size();
- return functions_[function_index]->Call(fuzz_data);
+ void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
+ typename FunctionMapImpl<Class>::mapped_type&& value) {
+ auto [it, inserted] = this->emplace(key, std::move(value));
+ CheckInternal(inserted,
+ "Multiple implementation registered for tag number " + std::to_string(key));
}
-
- private:
- template <typename R, typename... Args>
- struct FunctionTraits {
- using Function = std::function<R(Args...)>;
- };
-
- public:
- // There are no deduction guide from lambda to std::function, so the
- // signature of the lambda must be specified in the template argument.
- // FuzzFunctions provide the following 3 ways to specify the signature of
- // the lambda:
-
- // AddFunction<R(Args...)>, e.g. AddFunction<ReturnType(ArgType, ArgType)>
- template <typename T>
- void AddFunction(std::function<T>&& func) {
- functions_.push_back(std::make_unique<FuzzFunction<T>>(std::move(func)));
- }
-
- // AddFunction<R, Args...>, e.g. AddFunction<ReturnType, ArgType, ArgType>
- template <typename R, typename... Args>
- void AddFunction(typename FunctionTraits<R, Args...>::Function&& func) {
- functions_.push_back(std::make_unique<FuzzFunction<R(Args...)>>(std::move(func)));
- }
-
- // AddFunction<ArgType...>. Equivalent to AddFunction<void, Args...>
- template <typename... Args>
- void AddFunction(typename FunctionTraits<void, Args...>::Function&& func) {
- functions_.push_back(std::make_unique<FuzzFunction<void(Args...)>>(std::move(func)));
- }
-
- // Use |fuzz_data| as a guide to call |functions_| until |fuzz_data| is
- // depleted. Return
- void DepleteData(FuzzData* fuzz_data) const {
- CallResult result;
- while ((result = CallOne(fuzz_data)) == CallResult::SUCCESS)
- ;
- CheckInternal(result == CallResult::NOT_ENOUGH_DATA,
- "result is " + std::to_string(static_cast<int>(result)));
- }
-
- protected:
- // Helper for subclass to check that size of |functions_| is actually within
- // SizeType. Should be called after all functions are registered.
- void CheckFunctionsSize() const {
- CheckInternal(functions_.size() <= std::numeric_limits<FunctionsSizeType>::max(),
- "Need to extend number of bits for function count; there are " +
- std::to_string(functions_.size()) + " functions now.");
- }
-
- private:
- std::vector<std::unique_ptr<FuzzFunctionBase>> functions_;
};
-// An object whose APIs are being fuzzed.
-template <typename T, typename FunctionsSizeType>
-class FuzzObject : public FuzzFunctions<FunctionsSizeType> {
- public:
- // Not thread-safe; client is responsible for ensuring only one thread calls DepleteData.
- void DepleteData(T* obj, FuzzData* fuzz_data) {
- obj_ = obj;
- FuzzFunctions<FunctionsSizeType>::DepleteData(fuzz_data);
- obj_ = nullptr;
+template <typename Action>
+int CheckConsistency() {
+ const auto* function_map = Action::GetFunctionMap();
+ const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+ for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
+ const auto* field_desc = action_value_desc->field(field_index);
+ CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
+ "Missing impl for function " + field_desc->camelcase_name());
+ }
+ return 0;
+}
+
+template <typename Action>
+void ExecuteActionProto(typename Action::Class* module,
+ const typename Action::Proto& action_proto) {
+ static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+ auto* action_refl = Action::Proto::GetReflection();
+ if (!action_refl->HasOneof(action_proto, action_value_desc)) {
+ return;
}
- protected:
- // Helper for subclass to get the module under test in the added functions.
- T* get() const {
- CheckInternal(obj_ != nullptr, "No module under test is found.");
- return obj_;
+ const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+ auto number = field_desc->number();
+ const auto& map = *Action::GetFunctionMap();
+ auto it = map.find(number);
+ CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
+ const auto& func = it->second;
+ func(module, action_proto, field_desc);
+}
+
+template <typename Action>
+void ExecuteAllActionProtos(
+ typename Action::Class* module,
+ const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
+ for (const auto& proto : action_protos) {
+ ExecuteActionProto<Action>(module, proto);
+ }
+}
+
+// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
+template <typename T>
+const T* SafeCast(const google::protobuf::Message& message) {
+ if (message.GetDescriptor() != T::GetDescriptor()) {
+ return nullptr;
+ }
+ return static_cast<const T*>(&message);
+}
+
+// Cast message to const T&. Abort if type mismatch.
+template <typename T>
+const T& CheckedCast(const google::protobuf::Message& message) {
+ const auto* ptr = SafeCast<T>(message);
+ CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
+ T::GetDescriptor()->name());
+ return *ptr;
+}
+
+// A templated way to a primitive field from a message using reflection.
+template <typename T>
+struct PrimitiveGetter;
+#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name) \
+ template <> \
+ struct PrimitiveGetter<type> { \
+ static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
}
- private:
- T* obj_ = nullptr;
+FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
+FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
+
+// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
+// with these arguments.
+template <typename FuzzFunction, typename Signature, typename Enabled = void>
+struct ActionPerfomer; // undefined
+
+template <typename FuzzFunction, typename MessageProto>
+struct ActionPerfomer<
+ FuzzFunction, void(const MessageProto&),
+ typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
+ static void Invoke(typename FuzzFunction::Class* module,
+ const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
+ action_proto.GetReflection()->GetMessage(action_proto, field_desc));
+ FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction, typename Primitive>
+struct ActionPerfomer<FuzzFunction, void(Primitive),
+ typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
+ static void Invoke(typename FuzzFunction::Class* module,
+ const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
+ action_proto, field_desc);
+ FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerfomer<FuzzFunction, void()> {
+ static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
+ const google::protobuf::FieldDescriptor*) {
+ FuzzFunction::ImplBody(module);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
+ static void Invoke(typename FuzzFunction::Class* module,
+ const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ std::string scratch;
+ const std::string& arg = action_proto.GetReflection()->GetStringReference(
+ action_proto, field_desc, &scratch);
+ FuzzFunction::ImplBody(module, arg);
+ }
};
} // namespace android::fuzz
+
+// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
+//
+// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
+// message FooActionProto {
+// message NoArgs {}
+// oneof value {
+// bool do_foo = 1;
+// NoArgs do_bar = 1;
+// }
+// }
+// Use it to fuzz a C++ class Foo by doing the following:
+// FUZZ_CLASS(Foo, FooAction)
+// After linking functions of Foo to FooAction, execute all actions by:
+// FooAction::ExecuteAll(foo_object, action_protos)
+#define FUZZ_CLASS(ClassType, Action) \
+ class Action { \
+ public: \
+ using Proto = Action##Proto; \
+ using Class = ClassType; \
+ using FunctionMap = android::fuzz::FunctionMap<Class>; \
+ static FunctionMap* GetFunctionMap() { \
+ static Action::FunctionMap map; \
+ return ↦ \
+ } \
+ static void ExecuteAll(Class* module, \
+ const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
+ [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>(); \
+ android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos); \
+ } \
+ }
+
+#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
+#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
+
+// Implement an action defined in protobuf. Example:
+// message FooActionProto {
+// oneof value {
+// bool do_foo = 1;
+// }
+// }
+// class Foo { public: void DoAwesomeFoo(bool arg); };
+// FUZZ_OBJECT(FooAction, Foo);
+// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) {
+// module->DoAwesomeFoo(arg);
+// }
+// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
+#define FUZZ_FUNCTION(Action, FunctionName, module, ...) \
+ class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \
+ public: \
+ using Class = Action::Class; \
+ static void ImplBody(Action::Class*, ##__VA_ARGS__); \
+ \
+ private: \
+ static bool registered_; \
+ }; \
+ auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \
+ auto tag = Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
+ auto func = \
+ &::android::fuzz::ActionPerfomer<FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName), \
+ void(__VA_ARGS__)>::Invoke; \
+ Action::GetFunctionMap()->CheckEmplace(tag, func); \
+ return true; \
+ })(); \
+ void FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(Action::Class* module, \
+ ##__VA_ARGS__)
+
+// Implement a simple action by linking it to the function with the same name. Example:
+// message FooActionProto {
+// message NoArgs {}
+// oneof value {
+// NoArgs do_bar = 1;
+// }
+// }
+// class Foo { public void DoBar(); };
+// FUZZ_OBJECT(FooAction, Foo);
+// FUZZ_FUNCTION(FooAction, DoBar);
+// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
+// also the name of the function of Foo.
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
+ FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8081866..4658fb4 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -173,6 +173,7 @@
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
+ // |snapshot_path| must not be nullptr.
virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
std::string* snapshot_path) = 0;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index efdb59f..0df5664 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -181,6 +181,13 @@
ret.snapshot_status.set_device_size(target_partition->size());
ret.snapshot_status.set_snapshot_size(target_partition->size());
+ if (ret.snapshot_status.snapshot_size() == 0) {
+ LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
+ ret.snapshot_status.set_cow_partition_size(0);
+ ret.snapshot_status.set_cow_file_size(0);
+ return ret;
+ }
+
// Being the COW partition virtual, its size doesn't affect the storage
// memory that will be occupied by the target.
// The actual storage space is affected by the COW file, whose size depends
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 12a0531..421154d 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -21,6 +21,7 @@
#include <tuple>
#include <android-base/logging.h>
+#include <src/libfuzzer/libfuzzer_macro.h>
#include <storage_literals/storage_literals.h>
#include "fuzz_utils.h"
@@ -31,11 +32,12 @@
using android::base::SetLogger;
using android::base::StderrLogger;
using android::base::StdioLogger;
-using android::fuzz::Bool;
-using android::fuzz::FuzzData;
-using android::fuzz::FuzzObject;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fuzz::CheckedCast;
+using android::snapshot::SnapshotFuzzData;
using android::snapshot::SnapshotFuzzEnv;
-using android::snapshot::SnapshotManagerFuzzData;
+using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::RepeatedPtrField;
// Avoid linking to libgsi since it needs disk I/O.
namespace android::gsi {
@@ -51,49 +53,106 @@
namespace android::snapshot {
-class FuzzSnapshotManager : public FuzzObject<ISnapshotManager, uint8_t> {
- public:
- FuzzSnapshotManager();
-};
+const SnapshotFuzzData* current_data = nullptr;
-FuzzSnapshotManager::FuzzSnapshotManager() {
- AddFunction([this]() { (void)get()->BeginUpdate(); });
- AddFunction([this]() { (void)get()->CancelUpdate(); });
- AddFunction<Bool>([this](Bool wipe) { (void)get()->FinishedSnapshotWrites(wipe); });
- AddFunction([this]() { (void)get()->InitiateMerge(); });
- AddFunction<Bool, Bool>([this](auto has_before_cancel, auto fail_before_cancel) {
- std::function<bool()> before_cancel;
- if (has_before_cancel) {
- before_cancel = [=]() { return fail_before_cancel; };
- }
- (void)get()->ProcessUpdateState({}, before_cancel);
- });
- AddFunction<Bool>([this](auto has_progress_arg) {
- double progress;
- (void)get()->GetUpdateState(has_progress_arg ? &progress : nullptr);
- });
- // TODO add CreateUpdateSnapshots according to proto
- // TODO add MapUpdateSnapshot
- // TODO add UnmapUpdateSnapshot using names from the dictionary
- AddFunction([this]() { (void)get()->NeedSnapshotsInFirstStageMount(); });
- // TODO add CreateLogicalAndSnapshotPartitions
- AddFunction<Bool>([this](const Bool& has_callback) {
- std::function<void()> callback;
- if (has_callback) {
- callback = []() {};
- }
- (void)get()->HandleImminentDataWipe(callback);
- });
- AddFunction([this]() { (void)get()->RecoveryCreateSnapshotDevices(); });
- // TODO add RecoveryCreateSnapshotDevices with metadata_device arg
- AddFunction([this]() {
- std::stringstream ss;
- (void)get()->Dump(ss);
- });
- AddFunction([this]() { (void)get()->EnsureMetadataMounted(); });
- AddFunction([this]() { (void)get()->GetSnapshotMergeStatsInstance(); });
+SnapshotFuzzEnv* GetSnapshotFuzzEnv();
- CheckFunctionsSize();
+FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
+
+using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
+using CreateLogicalAndSnapshotPartitionsArgs =
+ SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
+using RecoveryCreateSnapshotDevicesArgs =
+ SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
+
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
+
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
+ FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
+
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
+ (void)snapshot->FinishedSnapshotWrites(wipe);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) {
+ std::function<bool()> before_cancel;
+ if (args.has_before_cancel()) {
+ before_cancel = [&]() { return args.fail_before_cancel(); };
+ }
+ (void)snapshot->ProcessUpdateState({}, before_cancel);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
+ double progress;
+ (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
+ std::function<void()> callback;
+ if (has_callback) {
+ callback = []() {};
+ }
+ (void)snapshot->HandleImminentDataWipe(callback);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(Dump) {
+ std::stringstream ss;
+ (void)snapshot->Dump(ss);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
+ (void)snapshot->CreateUpdateSnapshots(manifest);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
+ (void)snapshot->UnmapUpdateSnapshot(name);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
+ const CreateLogicalAndSnapshotPartitionsArgs& args) {
+ const std::string* super;
+ if (args.use_correct_super()) {
+ super = &GetSnapshotFuzzEnv()->super();
+ } else {
+ super = &args.super();
+ }
+ (void)snapshot->CreateLogicalAndSnapshotPartitions(
+ *super, std::chrono::milliseconds(args.timeout_millis()));
+}
+
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,
+ const RecoveryCreateSnapshotDevicesArgs& args) {
+ std::unique_ptr<AutoDevice> device;
+ if (args.has_metadata_device_object()) {
+ device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
+ }
+ (void)snapshot->RecoveryCreateSnapshotDevices(device);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) {
+ auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
+ CreateLogicalPartitionParams params;
+ if (params_proto.use_correct_super()) {
+ params.block_device = GetSnapshotFuzzEnv()->super();
+ } else {
+ params.block_device = params_proto.block_device();
+ }
+ if (params_proto.has_metadata_slot()) {
+ params.metadata_slot = params_proto.metadata_slot();
+ }
+ params.partition_name = params_proto.partition_name();
+ params.force_writable = params_proto.force_writable();
+ params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
+ params.device_name = params_proto.device_name();
+ params.partition_opener = partition_opener.get();
+ std::string path;
+ (void)snapshot->MapUpdateSnapshot(params, &path);
}
// During global init, log all messages to stdio. This is only done once.
@@ -107,35 +166,50 @@
unsigned int line, const char* message) {
if (severity == LogSeverity::FATAL) {
StderrLogger(logid, severity, tag, file, line, message);
+
+ // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
+ // nothing else we can do.
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+ "Attempting to dump current corpus:");
+ if (current_data == nullptr) {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
+ } else {
+ std::string content;
+ if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+ "Failed to print corpus to string.");
+ } else {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
+ }
+ }
}
}
// Stop logging (except fatal messages) after global initialization. This is only done once.
int StopLoggingAfterGlobalInit() {
+ [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
SetLogger(&FatalOnlyLogger);
return 0;
}
-} // namespace android::snapshot
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- using namespace android::snapshot;
-
+SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
[[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
static SnapshotFuzzEnv env;
- static FuzzSnapshotManager fuzz_snapshot_manager;
[[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+ return &env;
+}
- CHECK(env.InitOk());
- FuzzData fuzz_data(data, size);
+} // namespace android::snapshot
- auto snapshot_manager_data = fuzz_data.Consume<SnapshotManagerFuzzData>();
- if (!snapshot_manager_data.has_value()) {
- return 0;
- }
- auto snapshot_manager = env.CreateSnapshotManager(snapshot_manager_data.value());
+DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
+ using namespace android::snapshot;
+
+ current_data = &snapshot_fuzz_data;
+
+ auto env = GetSnapshotFuzzEnv();
+ env->CheckSoftReset();
+
+ auto snapshot_manager = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
CHECK(snapshot_manager);
- fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data);
-
- return 0;
+ SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions());
}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index d5e3e96..8101d03 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -19,6 +19,7 @@
#include <sys/stat.h>
#include <sysexits.h>
+#include <chrono>
#include <string>
#include <android-base/file.h>
@@ -29,17 +30,30 @@
#include <storage_literals/storage_literals.h>
#include "snapshot_fuzz_utils.h"
+#include "utility.h"
+
+// Prepends the errno string, but it is good enough.
+#ifndef PCHECK
+#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
+#endif
using namespace android::storage_literals;
+using namespace std::chrono_literals;
using namespace std::string_literals;
using android::base::StringPrintf;
using android::base::unique_fd;
using android::base::WriteStringToFile;
+using android::dm::LoopControl;
using android::fiemap::IImageManager;
using android::fiemap::ImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::IPartitionOpener;
+using chromeos_update_engine::DynamicPartitionMetadata;
-static const char MNT_DIR[] = "/mnt";
+// This directory is exempted from pinning in ImageManager.
+static const char MNT_DIR[] = "/data/gsi/ota/test/";
+
static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
static const auto SUPER_IMAGE_SIZE = 16_MiB;
static const auto FAKE_ROOT_SIZE = 64_MiB;
@@ -128,6 +142,9 @@
class AutoMemBasedDir : public AutoDevice {
public:
static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
+ if (!Mkdir(MNT_DIR)) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
if (!ret->auto_delete_mount_dir_->HasDevice()) {
@@ -137,20 +154,31 @@
if (!ret->auto_umount_mount_point_->HasDevice()) {
return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
}
- // path() does not need to be deleted upon destruction, hence it is not wrapped with
- // AutoDeleteDir.
- if (!Mkdir(ret->path())) {
+ // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
+ // not wrapped with AutoDeleteDir.
+ if (!Mkdir(ret->tmp_path())) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ if (!Mkdir(ret->persist_path())) {
return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
}
return ret;
}
- // Return the scratch directory.
- std::string path() const {
+ // Return the temporary scratch directory.
+ std::string tmp_path() const {
CHECK(HasDevice());
- return mount_path() + "/root";
+ return mount_path() + "/tmp";
}
- // Delete all contents in path() and start over. path() itself is re-created.
- bool SoftReset() { return RmdirRecursive(path()) && Mkdir(path()); }
+ // Return the temporary scratch directory.
+ std::string persist_path() const {
+ CHECK(HasDevice());
+ return mount_path() + "/persist";
+ }
+ // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
+ void CheckSoftReset() {
+ PCHECK(RmdirRecursive(tmp_path()));
+ PCHECK(Mkdir(tmp_path()));
+ }
private:
AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
@@ -164,64 +192,123 @@
SnapshotFuzzEnv::SnapshotFuzzEnv() {
fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
+ CHECK(fake_root_ != nullptr);
+ CHECK(fake_root_->HasDevice());
+ loop_control_ = std::make_unique<LoopControl>();
+ mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_);
}
SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
-bool SnapshotFuzzEnv::InitOk() const {
- if (fake_root_ == nullptr || !fake_root_->HasDevice()) return false;
- return true;
+void CheckZeroFill(const std::string& file, size_t size) {
+ std::string zeros(size, '\0');
+ PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
}
-bool SnapshotFuzzEnv::SoftReset() {
- return fake_root_->SoftReset();
+void SnapshotFuzzEnv::CheckSoftReset() {
+ fake_root_->CheckSoftReset();
+ CheckZeroFill(super(), SUPER_IMAGE_SIZE);
}
-std::unique_ptr<IImageManager> SnapshotFuzzEnv::CreateFakeImageManager(
- const std::string& fake_root) {
- auto images_dir = fake_root + "/images";
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
+ const std::string& path) {
+ auto images_dir = path + "/images";
auto metadata_dir = images_dir + "/metadata";
auto data_dir = images_dir + "/data";
- if (!Mkdir(images_dir) || !Mkdir(metadata_dir) || !Mkdir(data_dir)) {
- return nullptr;
- }
+ PCHECK(Mkdir(images_dir));
+ PCHECK(Mkdir(metadata_dir));
+ PCHECK(Mkdir(data_dir));
return ImageManager::Open(metadata_dir, data_dir);
}
-std::unique_ptr<TestPartitionOpener> SnapshotFuzzEnv::CreatePartitionOpener(
- const std::string& fake_root) {
- auto fake_super = fake_root + "/super.img";
- std::string zeros(SUPER_IMAGE_SIZE, '\0');
-
- if (!WriteStringToFile(zeros, fake_super)) {
- PLOG(ERROR) << "Cannot write zeros to " << fake_super;
- return nullptr;
- }
-
- return std::make_unique<TestPartitionOpener>(fake_super);
+// Helper to create a loop device for a file.
+static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
+ static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
+ android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
+ PCHECK(file_fd >= 0) << "Could not open file: " << file;
+ CHECK(control->Attach(file_fd, timeout_ms, path))
+ << "Could not create loop device for: " << file;
}
-std::string SnapshotFuzzEnv::root() const {
- CHECK(InitOk());
- return fake_root_->path();
+class AutoDetachLoopDevice : public AutoDevice {
+ public:
+ AutoDetachLoopDevice(LoopControl* control, const std::string& device)
+ : AutoDevice(device), control_(control) {}
+ ~AutoDetachLoopDevice() { control_->Detach(name_); }
+
+ private:
+ LoopControl* control_;
+};
+
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapSuper(const std::string& fake_persist_path,
+ LoopControl* control,
+ std::string* fake_super) {
+ auto super_img = fake_persist_path + "/super.img";
+ CheckZeroFill(super_img, SUPER_IMAGE_SIZE);
+ CheckCreateLoopDevice(control, super_img, 1s, fake_super);
+
+ return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
}
-std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CreateSnapshotManager(
- const SnapshotManagerFuzzData& data) {
- // TODO(b/154633114): create valid super partition according to fuzz data
- auto partition_opener = CreatePartitionOpener(root());
- if (partition_opener == nullptr) return nullptr;
- auto metadata_dir = root() + "/snapshot_metadata";
- if (!Mkdir(metadata_dir)) return nullptr;
+std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
+ const SnapshotFuzzData& data) {
+ auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+ CheckWriteSuperMetadata(data, *partition_opener);
+ auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
+ PCHECK(Mkdir(metadata_dir));
- auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data,
+ auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
std::move(partition_opener), metadata_dir);
auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
- snapshot->images_ = CreateFakeImageManager(root());
- snapshot->has_local_image_manager_ = data.is_local_image_manager;
+ snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path());
+ snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
return snapshot;
}
+const std::string& SnapshotFuzzEnv::super() const {
+ return fake_super_;
+}
+
+void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
+ const IPartitionOpener& opener) {
+ if (!data.is_super_metadata_valid()) {
+ // Leave it zero.
+ return;
+ }
+
+ BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
+ std::vector<BlockDeviceInfo> devices = {super_device};
+ auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ CHECK(builder != nullptr);
+
+ // Attempt to create a super partition metadata using proto. All errors are ignored.
+ for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
+ (void)builder->AddGroup(group_proto.name(), group_proto.size());
+ for (const auto& partition_name : group_proto.partition_names()) {
+ (void)builder->AddPartition(partition_name, group_proto.name(),
+ LP_PARTITION_ATTR_READONLY);
+ }
+ }
+
+ for (const auto& partition_proto : data.super_data().partitions()) {
+ auto p = builder->FindPartition(partition_proto.partition_name());
+ if (p == nullptr) continue;
+ (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
+ }
+
+ auto metadata = builder->Export();
+ // metadata may be nullptr if it is not valid (e.g. partition name too long).
+ // In this case, just use empty super partition data.
+ if (metadata == nullptr) {
+ builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ CHECK(builder != nullptr);
+ metadata = builder->Export();
+ CHECK(metadata != nullptr);
+ }
+ CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
+}
+
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 32910a9..5533def 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -16,32 +16,27 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android/snapshot/snapshot_fuzz.pb.h>
+#include <libdm/loop_control.h>
#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/test_helpers.h>
// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
// by SnapshotManager.
+#include "android/snapshot/snapshot_fuzz.pb.h"
+
namespace android::snapshot {
-// Controls the behavior of IDeviceInfo.
-typedef struct SnapshotFuzzDeviceInfoData {
- bool slot_suffix_is_a : 1;
- bool is_overlayfs_setup : 1;
- bool allow_set_boot_control_merge_status : 1;
- bool allow_set_slot_as_unbootable : 1;
- bool is_recovery : 1;
-} __attribute__((packed)) SnapshotFuzzDeviceInfoData;
-
-// Controls the behavior of the test SnapshotManager.
-typedef struct SnapshotManagerFuzzData {
- SnapshotFuzzDeviceInfoData device_info_data;
- bool is_local_image_manager : 1;
-} __attribute__((packed)) SnapshotManagerFuzzData;
-
class AutoMemBasedDir;
+class DummyAutoDevice : public AutoDevice {
+ public:
+ DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
+};
+
// Prepare test environment. This has a heavy overhead and should be done once.
class SnapshotFuzzEnv {
public:
@@ -52,36 +47,41 @@
SnapshotFuzzEnv();
~SnapshotFuzzEnv();
- // Check if environment is initialized properly.
- bool InitOk() const;
-
- // A scratch directory for the test to play around with. The scratch directory
- // is backed by tmpfs. SoftReset() clears the directory.
- std::string root() const;
-
// Soft reset part of the environment before running the next test.
- bool SoftReset();
+ // Abort if fails.
+ void CheckSoftReset();
// Create a snapshot manager for this test run.
// Client is responsible for maintaining the lifetime of |data| over the life time of
// ISnapshotManager.
- std::unique_ptr<ISnapshotManager> CreateSnapshotManager(const SnapshotManagerFuzzData& data);
+ std::unique_ptr<ISnapshotManager> CheckCreateSnapshotManager(const SnapshotFuzzData& data);
+
+ // Return path to super partition.
+ const std::string& super() const;
private:
std::unique_ptr<AutoMemBasedDir> fake_root_;
+ std::unique_ptr<android::dm::LoopControl> loop_control_;
+ std::unique_ptr<AutoDevice> mapped_super_;
+ std::string fake_super_;
- static std::unique_ptr<android::fiemap::IImageManager> CreateFakeImageManager(
- const std::string& fake_root);
- static std::unique_ptr<TestPartitionOpener> CreatePartitionOpener(const std::string& fake_root);
+ static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
+ const std::string& fake_tmp_path);
+ static std::unique_ptr<AutoDevice> CheckMapSuper(const std::string& fake_persist_path,
+ android::dm::LoopControl* control,
+ std::string* fake_super);
+
+ void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
+ const android::fs_mgr::IPartitionOpener& opener);
};
class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
public:
// Client is responsible for maintaining the lifetime of |data|.
- SnapshotFuzzDeviceInfo(const SnapshotFuzzDeviceInfoData& data,
+ SnapshotFuzzDeviceInfo(const FuzzDeviceInfoData& data,
std::unique_ptr<TestPartitionOpener>&& partition_opener,
const std::string& metadata_dir)
- : data_(data),
+ : data_(&data),
partition_opener_(std::move(partition_opener)),
metadata_dir_(metadata_dir) {}
@@ -97,17 +97,21 @@
}
// Following APIs are fuzzed.
- std::string GetSlotSuffix() const override { return data_.slot_suffix_is_a ? "_a" : "_b"; }
- std::string GetOtherSlotSuffix() const override { return data_.slot_suffix_is_a ? "_b" : "_a"; }
- bool IsOverlayfsSetup() const override { return data_.is_overlayfs_setup; }
- bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
- return data_.allow_set_boot_control_merge_status;
+ std::string GetSlotSuffix() const override { return data_->slot_suffix_is_a() ? "_a" : "_b"; }
+ std::string GetOtherSlotSuffix() const override {
+ return data_->slot_suffix_is_a() ? "_b" : "_a";
}
- bool SetSlotAsUnbootable(unsigned int) override { return data_.allow_set_slot_as_unbootable; }
- bool IsRecovery() const override { return data_.is_recovery; }
+ bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
+ bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
+ return data_->allow_set_boot_control_merge_status();
+ }
+ bool SetSlotAsUnbootable(unsigned int) override {
+ return data_->allow_set_slot_as_unbootable();
+ }
+ bool IsRecovery() const override { return data_->is_recovery(); }
private:
- SnapshotFuzzDeviceInfoData data_;
+ const FuzzDeviceInfoData* data_;
std::unique_ptr<TestPartitionOpener> partition_opener_;
std::string metadata_dir_;
};
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b036606..f82a602 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -212,8 +212,8 @@
return AssertionFailure() << strerror(errno);
}
bsize_ = buf.f_bsize;
- free_space_ = buf.f_bsize * buf.f_bfree;
- available_space_ = buf.f_bsize * buf.f_bavail;
+ free_space_ = bsize_ * buf.f_bfree;
+ available_space_ = bsize_ * buf.f_bavail;
return AssertionSuccess();
}
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
new file mode 100644
index 0000000..be5e1fe
--- /dev/null
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -0,0 +1,75 @@
+//
+// 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.
+//
+
+// A subset of system/update_engine/update_metadata.proto. A separate file is
+// used here because:
+// - The original file is optimized for LITE_RUNTIME, but fuzzing needs
+// reflection.
+// - The definition here has less fields. libsnapshot only uses fields declared
+// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If
+// libsnapshot uses more fields in system/update_engine/update_metadata.proto
+// in the future, they must be added here too, otherwise it will fail to
+// compile.
+//
+// It is okay that this file is older than
+// system/update_engine/update_metadata.proto as long as the messages defined
+// here can also be parsed by protobuf defined there. However, it is not
+// okay to add fields here without adding them to
+// system/update_engine/update_metadata.proto. Doing so will cause a compiler
+// error when libsnapshot code starts to use these dangling fields.
+
+syntax = "proto2";
+
+package chromeos_update_engine;
+
+message Extent {
+ optional uint64 start_block = 1;
+ optional uint64 num_blocks = 2;
+}
+
+message PartitionInfo {
+ optional uint64 size = 1;
+}
+
+message InstallOperation {
+ enum Type { SOURCE_COPY = 4; }
+ required Type type = 1;
+ repeated Extent src_extents = 4;
+ repeated Extent dst_extents = 6;
+}
+
+message PartitionUpdate {
+ required string partition_name = 1;
+ optional PartitionInfo new_partition_info = 7;
+ repeated InstallOperation operations = 8;
+ optional Extent hash_tree_extent = 11;
+ optional Extent fec_extent = 15;
+}
+
+message DynamicPartitionGroup {
+ required string name = 1;
+ optional uint64 size = 2;
+ repeated string partition_names = 3;
+}
+
+message DynamicPartitionMetadata {
+ repeated DynamicPartitionGroup groups = 1;
+}
+
+message DeltaArchiveManifest {
+ repeated PartitionUpdate partitions = 13;
+ optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
+}
diff --git a/init/README.md b/init/README.md
index 13f1bac..726c0cc 100644
--- a/init/README.md
+++ b/init/README.md
@@ -322,6 +322,10 @@
This is mutually exclusive with the console option, which additionally connects stdin to the
given console.
+`task_profiles <profile> [ <profile>\* ]`
+> Set task profiles for the process when it forks. This is designed to replace the use of
+ writepid option for moving a process into a cgroup.
+
`timeout_period <seconds>`
> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
here, so oneshot services do not automatically restart, however all other services will.
@@ -356,6 +360,8 @@
cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
'/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
+ The use of this option for moving a process into a cgroup is obsolete. Please
+ use task_profiles option instead.
Triggers
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index cae53f4..cfa0d99 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -16,6 +16,7 @@
#include "first_stage_console.h"
+#include <stdio.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
@@ -87,8 +88,18 @@
_exit(127);
}
-bool FirstStageConsole(const std::string& cmdline) {
- return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+int FirstStageConsole(const std::string& cmdline) {
+ auto pos = cmdline.find("androidboot.first_stage_console=");
+ if (pos != std::string::npos) {
+ int val = 0;
+ if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
+ return FirstStageConsoleParam::DISABLED;
+ }
+ if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+ return val;
+ }
+ }
+ return FirstStageConsoleParam::DISABLED;
}
} // namespace init
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index 7485339..8f36a7c 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -21,8 +21,15 @@
namespace android {
namespace init {
+enum FirstStageConsoleParam {
+ DISABLED = 0,
+ CONSOLE_ON_FAILURE = 1,
+ IGNORE_FAILURE = 2,
+ MAX_PARAM_VALUE = IGNORE_FAILURE,
+};
+
void StartConsole();
-bool FirstStageConsole(const std::string& cmdline);
+int FirstStageConsole(const std::string& cmdline);
} // namespace init
} // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 5eca644..1a608f6 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -200,16 +200,16 @@
}
Modprobe m({"/lib/modules"}, module_load_file);
- auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
+ auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
if (!m.LoadListedModules(!want_console)) {
- if (want_console) {
+ if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
- if (want_console) {
+ if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
StartConsole();
}
diff --git a/init/init.cpp b/init/init.cpp
index a9d6301..3f8f628 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -76,6 +76,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
+#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"
@@ -100,8 +101,6 @@
static int signal_fd = -1;
static int property_fd = -1;
-static std::unique_ptr<Subcontext> subcontext;
-
struct PendingControlMessage {
std::string message;
std::string name;
@@ -216,16 +215,6 @@
prop_waiter_state.ResetWaitForProp();
}
-static void UnwindMainThreadStack() {
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
- }
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << backtrace->FormatFrameData(i);
- }
-}
-
static class ShutdownState {
public:
void TriggerShutdown(const std::string& command) {
@@ -243,13 +232,13 @@
std::optional<std::string> CheckShutdown() {
auto lock = std::lock_guard{shutdown_command_lock_};
if (do_shutdown_ && !IsShuttingDown()) {
- do_shutdown_ = false;
return shutdown_command_;
}
return {};
}
bool do_shutdown() const { return do_shutdown_; }
+ void set_do_shutdown(bool value) { do_shutdown_ = value; }
private:
std::mutex shutdown_command_lock_;
@@ -257,16 +246,28 @@
bool do_shutdown_ = false;
} shutdown_state;
+static void UnwindMainThreadStack() {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
+ if (!backtrace->Unwind(0)) {
+ LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+ }
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+ }
+}
+
void DebugRebootLogging() {
- LOG(INFO) << "do_shutdown: " << shutdown_state.do_shutdown()
+ LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
<< " IsShuttingDown: " << IsShuttingDown();
if (shutdown_state.do_shutdown()) {
LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
UnwindMainThreadStack();
+ DumpShutdownDebugInformation();
}
if (IsShuttingDown()) {
LOG(ERROR) << "sys.powerctl set while init is already shutting down";
UnwindMainThreadStack();
+ DumpShutdownDebugInformation();
}
}
@@ -279,9 +280,8 @@
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &service_list, subcontext.get(), std::nullopt));
- parser.AddSectionParser("on",
- std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+ &service_list, GetSubcontext(), std::nullopt));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
@@ -291,9 +291,9 @@
Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, subcontext.get(),
- std::nullopt, from_apex));
+ parser.AddSectionParser(
+ "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
+ from_apex));
return parser;
}
@@ -721,7 +721,7 @@
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
- InitKernelLogging(argv);
+ InitSecondStageLogging(argv);
LOG(INFO) << "init second stage started!";
// Init should not crash because of a dependence on any other process, therefore we ignore
@@ -809,7 +809,7 @@
PLOG(FATAL) << "SetupMountNamespaces failed";
}
- subcontext = InitializeSubcontext();
+ InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
@@ -874,6 +874,7 @@
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
+ shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
diff --git a/init/init_test.cpp b/init/init_test.cpp
index caf3e03..07b4724 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -239,6 +239,28 @@
EXPECT_EQ(6, num_executed);
}
+TEST(init, RejectsCriticalAndOneshotService) {
+ std::string init_script =
+ R"init(
+service A something
+ class first
+ critical
+ oneshot
+)init";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+ ServiceList service_list;
+ Parser parser;
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+ ASSERT_EQ(1u, parser.parse_error_count());
+}
+
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 72f0450..e89f74a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -758,20 +758,24 @@
static Result<void> DoUserspaceReboot() {
LOG(INFO) << "Userspace reboot initiated";
- auto guard = android::base::make_scope_guard([] {
+ // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
+ std::string sub_reason = "";
+ auto guard = android::base::make_scope_guard([&sub_reason] {
// Leave shutdown so that we can handle a full reboot.
LeaveShutdown();
- trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
+ trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
});
// Triggering userspace-reboot-requested will result in a bunch of setprop
// actions. We should make sure, that all of them are propagated before
// proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
// property is not perfect, but it should do the trick.
if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+ sub_reason = "setprop";
return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
}
EnterShutdown();
if (!SetProperty("sys.powerctl", "")) {
+ sub_reason = "resetprop";
return Error() << "Failed to reset sys.powerctl property";
}
std::vector<Service*> stop_first;
@@ -800,18 +804,22 @@
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " post-data services are still running";
}
if (auto result = KillZramBackingDevice(); !result.ok()) {
+ sub_reason = "zram";
return result;
}
if (auto result = CallVdc("volume", "reset"); !result.ok()) {
+ sub_reason = "vold_reset";
return result;
}
if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill_debug";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
@@ -822,9 +830,11 @@
LOG(INFO) << "sync() took " << sync_timer;
}
if (auto result = UnmountAllApexes(); !result.ok()) {
+ sub_reason = "apex";
return result;
}
if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
// Remove services that were defined in an APEX.
diff --git a/init/service.cpp b/init/service.cpp
index 20400a0..165b848 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -513,6 +513,10 @@
LOG(ERROR) << "failed to write pid to files: " << result.error();
}
+ if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+ LOG(ERROR) << "failed to set task profiles";
+ }
+
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
SetProcessAttributesAndCaps();
diff --git a/init/service.h b/init/service.h
index 9f1d697..34ed5ef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -170,6 +170,8 @@
std::vector<std::string> writepid_files_;
+ std::vector<std::string> task_profiles_;
+
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
// keycodes for triggering this service via /dev/input/input*
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 560f693..bdac077 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -360,6 +360,12 @@
return Error() << "Invalid shutdown option";
}
+Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ service_->task_profiles_ = std::move(args);
+ return {};
+}
+
Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 1)) {
@@ -529,6 +535,7 @@
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"stdio_to_kmsg", {0, 0, &ServiceParser::ParseStdioToKmsg}},
+ {"task_profiles", {1, kMax, &ServiceParser::ParseTaskProfiles}},
{"timeout_period", {1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},
@@ -598,6 +605,13 @@
}
}
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+ if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
+ return Error() << "service '" << service_->name()
+ << "' can't be both critical and oneshot";
+ }
+ }
+
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 7bb0cc0..0fd2da5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -78,6 +78,7 @@
Result<void> ParseSigstop(std::vector<std::string>&& args);
Result<void> ParseSocket(std::vector<std::string>&& args);
Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
+ Result<void> ParseTaskProfiles(std::vector<std::string>&& args);
Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
Result<void> ParseFile(std::vector<std::string>&& args);
Result<void> ParseUser(std::vector<std::string>&& args);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 5263c14..f3dd538 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -52,6 +52,8 @@
namespace {
std::string shutdown_command;
+static bool subcontext_terminated_by_shutdown;
+static std::unique_ptr<Subcontext> subcontext;
class SubcontextProcess {
public:
@@ -323,34 +325,30 @@
return expanded_args;
}
-static std::vector<Subcontext> subcontexts;
-static bool shutting_down;
-
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+void InitializeSubcontext() {
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
- return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
- kVendorContext);
+ subcontext.reset(
+ new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
- return nullptr;
+}
+
+Subcontext* GetSubcontext() {
+ return subcontext.get();
}
bool SubcontextChildReap(pid_t pid) {
- for (auto& subcontext : subcontexts) {
- if (subcontext.pid() == pid) {
- if (!shutting_down) {
- subcontext.Restart();
- }
- return true;
+ if (subcontext->pid() == pid) {
+ if (!subcontext_terminated_by_shutdown) {
+ subcontext->Restart();
}
+ return true;
}
return false;
}
void SubcontextTerminate() {
- shutting_down = true;
- for (auto& subcontext : subcontexts) {
- kill(subcontext.pid(), SIGTERM);
- }
+ subcontext_terminated_by_shutdown = true;
+ kill(subcontext->pid(), SIGTERM);
}
} // namespace init
diff --git a/init/subcontext.h b/init/subcontext.h
index 5e1d8a8..788d3be 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,8 @@
};
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+void InitializeSubcontext();
+Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/init/util.cpp b/init/util.cpp
index 24f94ec..f9be055 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,7 +30,9 @@
#include <time.h>
#include <unistd.h>
+#include <mutex>
#include <thread>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -660,5 +662,50 @@
return access("/system/bin/recovery", F_OK) == 0;
}
+// TODO(b/155203339): remove this
+// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
+// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
+// if we identify that the device is stuck.
+constexpr size_t kRecordedLogsSize = 30;
+std::string recorded_logs[kRecordedLogsSize];
+size_t recorded_log_position = 0;
+std::mutex recorded_logs_lock;
+
+void InitSecondStageLogging(char** argv) {
+ SetFatalRebootTarget();
+ auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ // We only store logs for init, not its children, and only if they're not related to
+ // sys.powerctl.
+ if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
+ auto lock = std::lock_guard{recorded_logs_lock};
+ recorded_logs[recorded_log_position++] = message;
+ if (recorded_log_position == kRecordedLogsSize) {
+ recorded_log_position = 0;
+ }
+ }
+ android::base::KernelLogger(log_id, severity, tag, file, line, message);
+ };
+ android::base::InitLogging(argv, second_stage_logger, InitAborter);
+}
+
+void DumpShutdownDebugInformation() {
+ auto lock = std::lock_guard{recorded_logs_lock};
+ android::base::KernelLogger(
+ android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ "===================== Dumping previous init lines =====================");
+ for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ recorded_logs[i].c_str());
+ }
+ for (size_t i = 0; i < recorded_log_position; ++i) {
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ recorded_logs[i].c_str());
+ }
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ "===================== End of dump =====================");
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index ad322d9..8167b02 100644
--- a/init/util.h
+++ b/init/util.h
@@ -78,6 +78,8 @@
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
+void InitSecondStageLogging(char** argv);
+void DumpShutdownDebugInformation();
bool IsRecoveryMode();
} // namespace init
} // namespace android
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..1913c1e 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -1,14 +1,20 @@
-/* cutils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed. It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
+/*
+ * Copyright (C) 2008 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 _CUTILS_ASHMEM_H
-#define _CUTILS_ASHMEM_H
+#pragma once
#include <stddef.h>
@@ -30,5 +36,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /* _CUTILS_ASHMEM_H */
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index f27b8e6..820b7cb 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -144,8 +144,11 @@
/*
* Release any logger resources (a new log write will immediately re-acquire)
*
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
*/
void __android_log_close(void);
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c8fafe7..8e4faeb 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -23,14 +23,6 @@
#endif
/*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char* tag,
- const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
- __attribute__((__format__(printf, 4, 5)));
-
-/*
* log_id_t helpers
*/
log_id_t android_name_to_log_id(const char* logName);
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3a8af6d..3497d63 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -1,11 +1,18 @@
/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * 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
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index 8b8a362..f5525c1 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -17,7 +17,6 @@
#pragma once
#include <android/log.h>
-#include <log/log_id.h>
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index ffd3b52..24b88d2 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -19,7 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <log/log_id.h>
+#include <android/log.h>
#include <log/log_time.h>
#ifdef __cplusplus
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index d3e9b19..b2604b5 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -1,11 +1,18 @@
/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * 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
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index eaec741..6f40515 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -17,7 +17,6 @@
#pragma once
#include <android/log.h>
-#include <log/log_id.h>
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 67376f4..a230749 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -32,58 +32,53 @@
#include <time.h>
#include <unistd.h>
-#include <shared_mutex>
-
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int logd_socket;
-static RwLock logd_socket_lock;
+static atomic_int logd_socket;
-static void OpenSocketLocked() {
- logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
- if (logd_socket <= 0) {
- return;
- }
-
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
sockaddr_un un = {};
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/logdw");
-
- if (TEMP_FAILURE_RETRY(
- connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
- close(logd_socket);
- logd_socket = 0;
- }
+ TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
}
-static void OpenSocket() {
- auto lock = std::unique_lock{logd_socket_lock};
- if (logd_socket > 0) {
- // Someone raced us and opened the socket already.
+// logd_socket should only be opened once. If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket. If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+ if (logd_socket != 0) {
return;
}
- OpenSocketLocked();
-}
-
-static void ResetSocket(int old_socket) {
- auto lock = std::unique_lock{logd_socket_lock};
- if (old_socket != logd_socket) {
- // Someone raced us and reset the socket already.
+ int new_socket =
+ TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (new_socket <= 0) {
return;
}
- close(logd_socket);
- logd_socket = 0;
- OpenSocketLocked();
+
+ int uninitialized_value = 0;
+ if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+ close(new_socket);
+ return;
+ }
+
+ LogdConnect();
}
+// This is the one exception to the above. Zygote uses this to clean up open FD's after fork() and
+// before specialization. It is single threaded at this point and therefore this function is
+// explicitly not thread safe. It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
void LogdClose() {
- auto lock = std::unique_lock{logd_socket_lock};
if (logd_socket > 0) {
close(logd_socket);
}
@@ -99,12 +94,7 @@
static atomic_int dropped;
static atomic_int droppedSecurity;
- auto lock = std::shared_lock{logd_socket_lock};
- if (logd_socket <= 0) {
- lock.unlock();
- OpenSocket();
- lock.lock();
- }
+ GetSocket();
if (logd_socket <= 0) {
return -EBADF;
@@ -183,10 +173,7 @@
// the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
if (ret < 0 && errno != EAGAIN) {
- int old_socket = logd_socket;
- lock.unlock();
- ResetSocket(old_socket);
- lock.lock();
+ LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
}
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..0751e2c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
#include <sys/types.h>
#include <time.h>
-#include <shared_mutex>
-
#include <log/log_properties.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
-static void PmsgOpen() {
- auto lock = std::unique_lock{pmsg_fd_lock};
- if (pmsg_fd > 0) {
- // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+ if (pmsg_fd != 0) {
return;
}
- pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (new_fd <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+ close(new_fd);
+ return;
+ }
}
void PmsgClose() {
- auto lock = std::unique_lock{pmsg_fd_lock};
if (pmsg_fd > 0) {
close(pmsg_fd);
}
@@ -77,13 +83,7 @@
}
}
- auto lock = std::shared_lock{pmsg_fd_lock};
-
- if (pmsg_fd <= 0) {
- lock.unlock();
- PmsgOpen();
- lock.lock();
- }
+ GetPmsgFd();
if (pmsg_fd <= 0) {
return -EBADF;
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
deleted file mode 100644
index 00f1806..0000000
--- a/liblog/rwlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 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 <pthread.h>
-
-// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
-// combination of std::mutex and std::condition variable, which is obviously less efficient. This
-// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
-// std::unique_lock.
-
-class RwLock {
- public:
- RwLock() {}
- ~RwLock() {}
-
- void lock() { pthread_rwlock_wrlock(&rwlock_); }
- void unlock() { pthread_rwlock_unlock(&rwlock_); }
-
- void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
- void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
-
- private:
- pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
-};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 385b079..2a6424b 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -63,8 +63,8 @@
"log_system_test.cpp",
"log_time_test.cpp",
"log_wrap_test.cpp",
+ "logd_writer_test.cpp",
"logprint_test.cpp",
- "rwlock_test.cpp",
],
shared_libs: [
"libcutils",
@@ -72,6 +72,7 @@
],
static_libs: ["liblog"],
isolated: true,
+ require_root: true,
}
// Build tests for the device (with .so). Run with:
@@ -108,7 +109,6 @@
"liblog_host_test.cpp",
"liblog_default_tag.cpp",
"liblog_global_state.cpp",
- "rwlock_test.cpp",
],
isolated: true,
}
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..b8e4726
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket. This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ auto temp_dir = TemporaryDir();
+ auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+ unique_fd server_socket;
+
+ auto open_server_socket = [&] {
+ server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+ ASSERT_TRUE(server_socket.ok());
+
+ sockaddr_un server_sockaddr = {};
+ server_sockaddr.sun_family = AF_UNIX;
+ strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+ sizeof(server_sockaddr))));
+ };
+
+ // Open the server socket.
+ open_server_socket();
+
+ // Open the client socket.
+ auto client_socket =
+ unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+ ASSERT_TRUE(client_socket.ok());
+ sockaddr_un client_sockaddr = {};
+ client_sockaddr.sun_family = AF_UNIX;
+ strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))));
+
+ // Ensure that communication works.
+ constexpr static char kSmoke[] = "smoke test";
+ ssize_t smoke_len = sizeof(kSmoke);
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ char read_buf[512];
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+
+ // Close the server socket.
+ server_socket.reset();
+ ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+ // Ensure that write() from the client returns an error since the server is closed.
+ ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+ // Open the server socket again.
+ open_server_socket();
+
+ // Reconnect the same client socket.
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))))
+ << strerror(errno);
+
+ // Ensure that communication works.
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
deleted file mode 100644
index 617d5c4..0000000
--- a/liblog/tests/rwlock_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2019 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 "../rwlock.h"
-
-#include <chrono>
-#include <shared_mutex>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace std::literals;
-
-TEST(rwlock, reader_then_reader_lock) {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = std::shared_lock{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = std::shared_lock{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(true, thread_ran);
-
- // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
- read_guard.unlock();
- reader_thread.join();
-}
-
-template <template <typename> typename L1, template <typename> typename L2>
-void TestBlockingLocks() {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = L1{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = L2{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(false, thread_ran);
-
- read_guard.unlock();
- reader_thread.join();
-
- EXPECT_EQ(true, thread_ran);
-}
-
-TEST(rwlock, reader_then_writer_lock) {
- TestBlockingLocks<std::shared_lock, std::unique_lock>();
-}
-
-TEST(rwlock, writer_then_reader_lock) {
- TestBlockingLocks<std::unique_lock, std::shared_lock>();
-}
-
-TEST(rwlock, writer_then_writer_lock) {
- TestBlockingLocks<std::unique_lock, std::unique_lock>();
-}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 12474f1..766ea0f 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -100,13 +100,12 @@
"libjsoncpp",
"libprotobuf-cpp-full",
],
- target: {
- android: {
- test_config: "vts_processgroup_validate_test.xml",
- },
- },
+ test_suites: [
+ "vts",
+ ],
}
vts_config {
name: "VtsProcessgroupValidateTest",
+ test_config: "vts_processgroup_validate_test.xml",
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 3f08535..a515e58 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -144,6 +144,19 @@
}
]
},
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index caea048..cbc65ff 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -48,6 +48,7 @@
export_include_dirs: ["include"],
static_libs: ["libgtest_prod"],
apex_available: ["com.android.resolv"],
+ min_sdk_version: "29",
}
cc_test {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 690dc94..6882ab2 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -55,6 +55,7 @@
export_include_dirs: ["include"],
host_supported: true,
apex_available: ["com.android.resolv"],
+ min_sdk_version: "29",
}
cc_benchmark {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index bf7d69e..f3d3f27 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -75,13 +75,29 @@
],
target: {
- // Always disable optimizations for host to make it easier to debug.
host: {
+ // Always disable optimizations for host to make it easier to debug.
cflags: [
"-O0",
"-g",
],
},
+ android: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ linux_bionic: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
},
arch: {
@@ -102,16 +118,6 @@
"liblog",
"liblzma",
],
-
- header_libs: [
- "bionic_libc_platform_headers",
- ],
-
- product_variables: {
- experimental_mte: {
- cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
- },
- },
}
cc_library {
@@ -281,13 +287,22 @@
"tests/files/offline/straddle_arm64/*",
],
- header_libs: [
- "bionic_libc_platform_headers",
- ],
-
- product_variables: {
- experimental_mte: {
- cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ target: {
+ android: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ linux_bionic: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
},
},
}
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 8f49ad9..670d904 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -159,6 +159,8 @@
search_map_idx = old_map_idx + 1;
if (new_map_idx + 1 < maps_.size()) {
maps_[new_map_idx + 1]->prev_map = info.get();
+ maps_[new_map_idx + 1]->prev_real_map =
+ info->IsBlank() ? info->prev_real_map : info.get();
}
maps_[new_map_idx] = nullptr;
total_entries--;
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
index d1d0ebc..46a546e 100644
--- a/libunwindstack/MemoryMte.cpp
+++ b/libunwindstack/MemoryMte.cpp
@@ -17,6 +17,7 @@
#if defined(ANDROID_EXPERIMENTAL_MTE)
#include <sys/ptrace.h>
+#include <sys/uio.h>
#include <bionic/mte.h>
#include <bionic/mte_kernel.h>
@@ -28,7 +29,13 @@
long MemoryRemote::ReadTag(uint64_t addr) {
#if defined(__aarch64__)
- return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+ char tag;
+ iovec iov = {&tag, 1};
+ if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
+ iov.iov_len != 1) {
+ return -1;
+ }
+ return tag;
#else
(void)addr;
return -1;
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
index b816b9a..99afb0b 100644
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -271,4 +271,103 @@
EXPECT_TRUE(map_info->name.empty());
}
+TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+ "8000-9000 r-xp 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(3U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+}
+
+TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(4U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+
+ map_info = maps_.Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_TRUE(map_info->IsBlank());
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_map);
+ EXPECT_TRUE(map_info->name.empty());
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
+ "a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(6U, maps_.Total());
+
+ map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+
+ map_info = maps_.Get(4);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib2.so", map_info->name);
+ EXPECT_EQ(maps_.Get(3), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+
+ map_info = maps_.Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0xA000U, map_info->start);
+ EXPECT_EQ(0xB000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib3.so", map_info->name);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_real_map);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 385078d..621893b 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -28,8 +28,6 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
-#include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
#include <gtest/gtest.h>
#include "MemoryRemote.h"
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1f8ad05..36273de 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -46,9 +46,6 @@
void LogBuffer::init() {
log_id_for_each(i) {
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
-
if (setSize(i, __android_logger_get_buffer_size(i))) {
setSize(i, LOG_BUFFER_MIN_SIZE);
}
@@ -131,6 +128,20 @@
}
}
+LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
+ auto it = mLogElements.begin();
+ if (mOldest[log_id]) {
+ it = *mOldest[log_id];
+ }
+ while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
+ it++;
+ }
+ if (it != mLogElements.end()) {
+ mOldest[log_id] = it;
+ }
+ return it;
+}
+
enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
static enum match_type identical(LogBufferElement* elem,
@@ -450,9 +461,7 @@
bool setLast[LOG_ID_MAX];
bool doSetLast = false;
- log_id_for_each(i) {
- doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
- }
+ log_id_for_each(i) { doSetLast |= setLast[i] = mOldest[i] && it == *mOldest[i]; }
#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
LogBufferElementCollection::iterator bad = it;
int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
@@ -463,11 +472,11 @@
if (doSetLast) {
log_id_for_each(i) {
if (setLast[i]) {
- if (__predict_false(it == mLogElements.end())) { // impossible
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
+ if (__predict_false(it == mLogElements.end())) {
+ mOldest[i] = std::nullopt;
} else {
- mLast[i] = it; // push down the road as next-best-watermark
+ mOldest[i] = it; // Store the next iterator even if it does not correspond to
+ // the same log_id, as a starting point for GetOldest().
}
}
}
@@ -486,11 +495,6 @@
b.first);
}
}
- if (mLastSet[i] && (bad == mLast[i])) {
- android::prdebug("stale mLast[%d]\n", i);
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
- }
}
#endif
if (coalesce) {
@@ -668,7 +672,7 @@
if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
// Only here if clear all request from non system source, so chatty
// filter logistics is not required.
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ it = GetOldest(id);
while (it != mLogElements.end()) {
LogBufferElement* element = *it;
@@ -678,11 +682,6 @@
continue;
}
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
if (oldest && oldest->mStart <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
@@ -734,8 +733,8 @@
}
bool kick = false;
- bool leading = true;
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ bool leading = true; // true if starting from the oldest log entry, false if starting from
+ // a specific chatty entry.
// Perform at least one mandatory garbage collection cycle in following
// - clear leading chatty tags
// - coalesce chatty tags
@@ -763,6 +762,9 @@
}
}
}
+ if (leading) {
+ it = GetOldest(id);
+ }
static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
LogBufferElementCollection::iterator lastt;
lastt = mLogElements.end();
@@ -783,11 +785,6 @@
}
// below this point element->getLogId() == id
- if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
uint16_t dropped = element->getDropped();
// remove any leading drops
@@ -909,7 +906,7 @@
bool whitelist = false;
bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ it = GetOldest(id);
while ((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement* element = *it;
@@ -918,11 +915,6 @@
continue;
}
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
if (oldest && oldest->mStart <= element->getSequence()) {
busy = true;
if (!whitelist) kickMe(oldest, id, pruneRows);
@@ -942,7 +934,7 @@
// Do not save the whitelist if we are reader range limited
if (whitelist && (pruneRows > 0)) {
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ it = GetOldest(id);
while ((it != mLogElements.end()) && (pruneRows > 0)) {
LogBufferElement* element = *it;
@@ -951,11 +943,6 @@
continue;
}
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
if (oldest && oldest->mStart <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 16225a5..458fbbb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <list>
+#include <optional>
#include <string>
#include <android/log.h>
@@ -81,9 +82,9 @@
LogStatistics stats;
PruneList mPrune;
- // watermark for last per log id
- LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
- bool mLastSet[LOG_ID_MAX];
+ // Keeps track of the iterator to the oldest log message of a given log type, as an
+ // optimization when pruning logs. Use GetOldest() to retrieve.
+ std::optional<LogBufferElementCollection::iterator> mOldest[LOG_ID_MAX];
// watermark of any worst/chatty uid processing
typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
LogBufferIteratorMap;
@@ -181,6 +182,10 @@
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
LogBufferElementCollection::iterator it, bool coalesce = false);
+
+ // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
+ // there are no logs for the given log type. Requires mLogElementsLock to be held.
+ LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
};
#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index b092489..1dd5c86 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -583,6 +583,7 @@
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
}
+#ifdef ENABLE_FLAKY_TESTS
// b/26447386 refined behavior
TEST(logd, timeout) {
#ifdef __ANDROID__
@@ -717,6 +718,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6564e8f..a380ebb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,6 +78,9 @@
mkdir /dev/boringssl 0755 root root
mkdir /dev/boringssl/selftest 0755 root root
+ # Mount tracefs
+ mount tracefs tracefs /sys/kernel/tracing
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32