Merge "Add tests for zero snapshot size."
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b8eee4a..2553353 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -625,7 +625,7 @@
if (!sm) {
return device->WriteFail("Unable to create SnapshotManager");
}
- if (!sm->HandleImminentDataWipe()) {
+ if (!sm->FinishMergeInRecovery()) {
return device->WriteFail("Unable to finish snapshot merge");
}
} else {
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index bde9d0a..cae43e6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -91,6 +91,7 @@
cc_test {
name: "fiemap_image_test",
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -99,7 +100,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
@@ -118,6 +118,7 @@
"-DSKIP_TEST_IN_PRESUBMIT",
],
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -126,7 +127,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 40da1bc..e916693 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -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/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 758d66c..cf0b085 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -43,6 +43,7 @@
(const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
(override));
MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
+ MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
(const std::unique_ptr<AutoDevice>& metadata_device), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8081866..5acd039 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;
@@ -201,6 +202,10 @@
// optional callback fires periodically to query progress via GetUpdateState.
virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
+ // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
+ // but does not expect a data wipe after.
+ virtual bool FinishMergeInRecovery() = 0;
+
// This method is only allowed in recovery and is used as a helper to
// initialize the snapshot devices as a requirement to mount a snapshotted
// /system in recovery.
@@ -289,6 +294,7 @@
const std::string& super_device,
const std::chrono::milliseconds& timeout_ms = {}) override;
bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
CreateResult RecoveryCreateSnapshotDevices() override;
CreateResult RecoveryCreateSnapshotDevices(
const std::unique_ptr<AutoDevice>& metadata_device) override;
@@ -579,6 +585,7 @@
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
bool has_local_image_manager_ = false;
+ bool in_factory_data_reset_ = false;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 9b2590c..9c82906 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -41,6 +41,7 @@
const std::string& super_device,
const std::chrono::milliseconds& timeout_ms = {}) override;
bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
CreateResult RecoveryCreateSnapshotDevices() override;
CreateResult RecoveryCreateSnapshotDevices(
const std::unique_ptr<AutoDevice>& metadata_device) override;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index eafeea2..03efd68 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -577,8 +577,16 @@
return false;
}
+ auto other_suffix = device_->GetOtherSlotSuffix();
+
auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
+ if (android::base::EndsWith(snapshot, other_suffix)) {
+ // Allow the merge to continue, but log this unexpected case.
+ LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
+ continue;
+ }
+
// The device has to be mapped, since everything should be merged at
// the same time. This is a fairly serious error. We could forcefully
// map everything here, but it should have been mapped during first-
@@ -1008,6 +1016,15 @@
}
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+ // It's not possible to remove update state in recovery, so write an
+ // indicator that cleanup is needed on reboot. If a factory data reset
+ // was requested, it doesn't matter, everything will get wiped anyway.
+ // To make testing easier we consider a /data wipe as cleaned up.
+ if (device_->IsRecovery() && !in_factory_data_reset_) {
+ WriteUpdateState(lock, UpdateState::MergeCompleted);
+ return;
+ }
+
RemoveAllUpdateState(lock);
}
@@ -2528,7 +2545,43 @@
}
return true;
};
- if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {
+
+ in_factory_data_reset_ = true;
+ bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+ in_factory_data_reset_ = false;
+
+ if (!ok) {
+ return false;
+ }
+
+ // Nothing should be depending on partitions now, so unmap them all.
+ if (!UnmapAllPartitions()) {
+ LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+ }
+ return true;
+}
+
+bool SnapshotManager::FinishMergeInRecovery() {
+ if (!device_->IsRecovery()) {
+ LOG(ERROR) << "Data wipes are only allowed in recovery.";
+ return false;
+ }
+
+ auto mount = EnsureMetadataMounted();
+ if (!mount || !mount->HasDevice()) {
+ return false;
+ }
+
+ auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_path = device_->GetSuperDevice(slot_number);
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+ LOG(ERROR) << "Unable to map partitions to complete merge.";
+ return false;
+ }
+
+ UpdateState state = ProcessUpdateState();
+ if (state != UpdateState::MergeCompleted) {
+ LOG(ERROR) << "Merge returned unexpected status: " << state;
return false;
}
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/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 2747b03..2aaa78c 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -89,6 +89,11 @@
return false;
}
+bool SnapshotManagerStub::FinishMergeInRecovery() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
LOG(ERROR) << __FUNCTION__ << " should never be called.";
return CreateResult::ERROR;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 53a37be..2bd0135 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1459,6 +1459,52 @@
ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
}
+// Test that a merge does not clear the snapshot state in fastboot.
+TEST_F(SnapshotUpdateTest, MergeInFastboot) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ // Initiate the merge and then immediately stop it to simulate a reboot.
+ auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ test_device->set_recovery(true);
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+ ASSERT_TRUE(new_sm->FinishMergeInRecovery());
+
+ auto mount = new_sm->EnsureMetadataMounted();
+ ASSERT_TRUE(mount && mount->HasDevice());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+
+ // Finish the merge in a normal boot.
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ init = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
+}
+
// Test that after an OTA, before a merge, we can wipe data in recovery.
TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
// Execute the first update.
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/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/libcutils/Android.bp b/libcutils/Android.bp
index 175b2b7..d7c83a2 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -33,6 +33,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -59,6 +60,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
export_include_dirs: ["include"],
@@ -142,6 +144,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
srcs: [
"config_utils.cpp",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..33ae13d 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -26,4 +26,5 @@
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
+ min_sdk_version: "29",
}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 2c1b255..bda11e9 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -17,6 +17,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
}
cc_library {
@@ -60,4 +61,5 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
}
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/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/logd/Android.bp b/logd/Android.bp
index b337b7c..2663271 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -36,7 +36,6 @@
"CommandListener.cpp",
"LogListener.cpp",
"LogReader.cpp",
- "FlushCommand.cpp",
"LogBuffer.cpp",
"LogBufferElement.cpp",
"LogTimes.cpp",
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 694b5fa..4044dc9 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -31,6 +31,7 @@
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
@@ -38,37 +39,20 @@
#include "LogCommand.h"
#include "LogUtils.h"
-CommandListener::CommandListener(LogBuffer* buf, LogReader* /*reader*/,
- LogListener* /*swl*/)
- : FrameworkListener(getLogSocket()) {
- // registerCmd(new ShutdownCmd(buf, writer, swl));
- registerCmd(new ClearCmd(buf));
- registerCmd(new GetBufSizeCmd(buf));
- registerCmd(new SetBufSizeCmd(buf));
- registerCmd(new GetBufSizeUsedCmd(buf));
- registerCmd(new GetStatisticsCmd(buf));
- registerCmd(new SetPruneListCmd(buf));
- registerCmd(new GetPruneListCmd(buf));
- registerCmd(new GetEventTagCmd(buf));
- registerCmd(new ReinitCmd());
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
+ : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+ registerCmd(new ClearCmd(this));
+ registerCmd(new GetBufSizeCmd(this));
+ registerCmd(new SetBufSizeCmd(this));
+ registerCmd(new GetBufSizeUsedCmd(this));
+ registerCmd(new GetStatisticsCmd(this));
+ registerCmd(new SetPruneListCmd(this));
+ registerCmd(new GetPruneListCmd(this));
+ registerCmd(new GetEventTagCmd(this));
+ registerCmd(new ReinitCmd(this));
registerCmd(new ExitCmd(this));
}
-CommandListener::ShutdownCmd::ShutdownCmd(LogReader* reader, LogListener* swl)
- : LogCommand("shutdown"), mReader(*reader), mSwl(*swl) {
-}
-
-int CommandListener::ShutdownCmd::runCommand(SocketClient* /*cli*/,
- int /*argc*/, char** /*argv*/) {
- mSwl.stopListener();
- mReader.stopListener();
- exit(0);
-}
-
-CommandListener::ClearCmd::ClearCmd(LogBuffer* buf)
- : LogCommand("clear"), mBuf(*buf) {
-}
-
static void setname() {
static bool name_set;
if (!name_set) {
@@ -96,14 +80,10 @@
return 0;
}
- cli->sendMsg(mBuf.clear((log_id_t)id, uid) ? "busy" : "success");
+ cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
return 0;
}
-CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer* buf)
- : LogCommand("getLogSize"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -118,17 +98,13 @@
return 0;
}
- unsigned long size = mBuf.getSize((log_id_t)id);
+ unsigned long size = buf()->getSize((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer* buf)
- : LogCommand("setLogSize"), mBuf(*buf) {
-}
-
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -149,7 +125,7 @@
}
unsigned long size = atol(argv[2]);
- if (mBuf.setSize((log_id_t)id, size)) {
+ if (buf()->setSize((log_id_t)id, size)) {
cli->sendMsg("Range Error");
return 0;
}
@@ -158,10 +134,6 @@
return 0;
}
-CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer* buf)
- : LogCommand("getLogSizeUsed"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -176,17 +148,13 @@
return 0;
}
- unsigned long size = mBuf.getSizeUsed((log_id_t)id);
+ unsigned long size = buf()->getSizeUsed((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer* buf)
- : LogCommand("getStatistics"), mBuf(*buf) {
-}
-
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
// prefix includes the length of the prefix itself.
static std::string PackageString(const std::string& str) {
@@ -241,25 +209,17 @@
}
}
- cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+ cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
return 0;
}
-CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer* buf)
- : LogCommand("getPruneList"), mBuf(*buf) {
-}
-
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
int /*argc*/, char** /*argv*/) {
setname();
- cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
+ cli->sendMsg(PackageString(prune()->format()).c_str());
return 0;
}
-CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer* buf)
- : LogCommand("setPruneList"), mBuf(*buf) {
-}
-
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -276,7 +236,7 @@
str += argv[i];
}
- int ret = mBuf.initPrune(str.c_str());
+ int ret = prune()->init(str.c_str());
if (ret) {
cli->sendMsg("Invalid");
@@ -288,10 +248,6 @@
return 0;
}
-CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer* buf)
- : LogCommand("getEventTag"), mBuf(*buf) {
-}
-
int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -328,39 +284,45 @@
cli->sendMsg("can not mix id= with either format= or name=");
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
+ cli->sendMsg(PackageString(tags()->formatEntry(atoi(id), uid)).c_str());
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
+ cli->sendMsg(PackageString(tags()->formatGetEventTag(uid, name, format)).c_str());
return 0;
}
-CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
-}
-
int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
- reinit_signal_handler(SIGHUP);
+ android::prdebug("logd reinit");
+ buf()->init();
+ prune()->init(nullptr);
+
+ // This only works on userdebug and eng devices to re-read the
+ // /data/misc/logd/event-log-tags file right after /data is mounted.
+ // The operation is near to boot and should only happen once. There
+ // are races associated with its use since it can trigger a Rebuild
+ // of the file, but that is a can-not-happen since the file was not
+ // read yet. More dangerous if called later, but if all is well it
+ // should just skip over everything and not write any new entries.
+ if (__android_log_is_debuggable()) {
+ tags()->ReadFileEventLogTags(tags()->debug_event_log_tags);
+ }
cli->sendMsg("success");
return 0;
}
-CommandListener::ExitCmd::ExitCmd(CommandListener* parent)
- : LogCommand("EXIT"), mParent(*parent) {
-}
-
int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
cli->sendMsg("success");
- release(cli);
+ parent_->release(cli);
return 0;
}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index ed99419..c90c247 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -14,85 +14,53 @@
* limitations under the License.
*/
-#ifndef _COMMANDLISTENER_H__
-#define _COMMANDLISTENER_H__
+#pragma once
#include <sysutils/FrameworkListener.h>
+
#include "LogBuffer.h"
#include "LogCommand.h"
#include "LogListener.h"
#include "LogReader.h"
-
-// See main.cpp for implementation
-void reinit_signal_handler(int /*signal*/);
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
class CommandListener : public FrameworkListener {
- public:
- CommandListener(LogBuffer* buf, LogReader* reader, LogListener* swl);
- virtual ~CommandListener() {
- }
+ public:
+ CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+ virtual ~CommandListener() {}
- private:
+ private:
static int getLogSocket();
- class ShutdownCmd : public LogCommand {
- LogReader& mReader;
- LogListener& mSwl;
+ LogBuffer* buf_;
+ LogTags* tags_;
+ PruneList* prune_;
- public:
- ShutdownCmd(LogReader* reader, LogListener* swl);
- virtual ~ShutdownCmd() {
- }
- int runCommand(SocketClient* c, int argc, char** argv);
- };
-
-#define LogBufferCmd(name) \
+#define LogCmd(name, command_string) \
class name##Cmd : public LogCommand { \
- LogBuffer& mBuf; \
+ public: \
+ explicit name##Cmd(CommandListener* parent) \
+ : LogCommand(#command_string), parent_(parent) {} \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient* c, int argc, char** argv); \
\
- public: \
- explicit name##Cmd(LogBuffer* buf); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
+ private: \
+ LogBuffer* buf() const { return parent_->buf_; } \
+ LogTags* tags() const { return parent_->tags_; } \
+ PruneList* prune() const { return parent_->prune_; } \
+ CommandListener* parent_; \
}
- LogBufferCmd(Clear);
- LogBufferCmd(GetBufSize);
- LogBufferCmd(SetBufSize);
- LogBufferCmd(GetBufSizeUsed);
- LogBufferCmd(GetStatistics);
- LogBufferCmd(GetPruneList);
- LogBufferCmd(SetPruneList);
- LogBufferCmd(GetEventTag);
-
-#define LogCmd(name) \
- class name##Cmd : public LogCommand { \
- public: \
- name##Cmd(); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- }
-
- LogCmd(Reinit);
-
-#define LogParentCmd(name) \
- class name##Cmd : public LogCommand { \
- CommandListener& mParent; \
- \
- public: \
- name##Cmd(); \
- explicit name##Cmd(CommandListener* parent); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- void release(SocketClient* c) { \
- mParent.release(c); \
- } \
- }
-
- LogParentCmd(Exit);
+ LogCmd(Clear, clear);
+ LogCmd(GetBufSize, getLogSize);
+ LogCmd(SetBufSize, setLogSize);
+ LogCmd(GetBufSizeUsed, getLogSizeUsed);
+ LogCmd(GetStatistics, getStatistics);
+ LogCmd(GetPruneList, getPruneList);
+ LogCmd(SetPruneList, setPruneList);
+ LogCmd(GetEventTag, getEventTag);
+ LogCmd(Reinit, reinit);
+ LogCmd(Exit, EXIT);
+#undef LogCmd
};
-
-#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
deleted file mode 100644
index 0845504..0000000
--- a/logd/FlushCommand.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-#include "LogUtils.h"
-
-// runSocketCommand is called once for every open client on the
-// log reader socket. Here we manage and associated the reader
-// client tracking and log region locks LastLogTimes list of
-// LogTimeEntrys, and spawn a transitory per-client thread to
-// work at filing data to the socket.
-//
-// global LogTimeEntry::wrlock() is used to protect access,
-// reference counts are used to ensure that individual
-// LogTimeEntry lifetime is managed when not protected.
-void FlushCommand::runSocketCommand(SocketClient* client) {
- LogTimeEntry* entry = nullptr;
- LastLogTimes& times = mReader.logbuf().mTimes;
-
- LogTimeEntry::wrlock();
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- entry = it->get();
- if (entry->mClient == client) {
- if (!entry->isWatchingMultiple(mLogMask)) {
- LogTimeEntry::unlock();
- return;
- }
- if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
- LogTimeEntry::unlock();
- return;
- }
- entry->triggerReader_Locked();
- LogTimeEntry::unlock();
- return;
- }
- it++;
- }
-
- LogTimeEntry::unlock();
-}
-
-bool FlushCommand::hasReadLogs(SocketClient* client) {
- return clientHasLogCredentials(client);
-}
-
-static bool clientHasSecurityCredentials(SocketClient* client) {
- return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
-}
-
-bool FlushCommand::hasSecurityLogs(SocketClient* client) {
- return clientHasSecurityCredentials(client);
-}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
deleted file mode 100644
index a69d439..0000000
--- a/logd/FlushCommand.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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 _FLUSH_COMMAND_H
-#define _FLUSH_COMMAND_H
-
-#include <android/log.h>
-#include <sysutils/SocketClientCommand.h>
-
-class LogBufferElement;
-
-#include "LogTimes.h"
-
-class LogReader;
-
-class FlushCommand : public SocketClientCommand {
- LogReader& mReader;
- log_mask_t mLogMask;
-
- public:
- explicit FlushCommand(LogReader& reader, log_mask_t logMask)
- : mReader(reader), mLogMask(logMask) {
- }
-
- virtual void runSocketCommand(SocketClient* client);
-
- static bool hasReadLogs(SocketClient* client);
- static bool hasSecurityLogs(SocketClient* client);
-};
-
-#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 36273de..a3e4e09 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -92,11 +92,7 @@
unlock();
}
- // We may have been triggered by a SIGHUP. Release any sleeping reader
- // threads to dump their current content.
- //
- // NB: this is _not_ performed in the context of a SIGHUP, it is
- // performed during startup, and in context of reinit administrative thread
+ // Release any sleeping reader threads to dump their current content.
LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
@@ -109,8 +105,11 @@
LogTimeEntry::unlock();
}
-LogBuffer::LogBuffer(LastLogTimes* times)
- : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
+ : monotonic(android_log_clockid() == CLOCK_MONOTONIC),
+ mTimes(*times),
+ tags_(tags),
+ prune_(prune) {
pthread_rwlock_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
@@ -130,14 +129,14 @@
LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
auto it = mLogElements.begin();
- if (mOldest[log_id]) {
- it = *mOldest[log_id];
+ if (oldest_[log_id]) {
+ it = *oldest_[log_id];
}
while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
it++;
}
if (it != mLogElements.end()) {
- mOldest[log_id] = it;
+ oldest_[log_id] = it;
}
return it;
}
@@ -232,7 +231,7 @@
const char* tag = nullptr;
size_t tag_len = 0;
if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
- tag = tagToName(elem->getTag());
+ tag = tags_->tagToName(elem->getTag());
if (tag) {
tag_len = strlen(tag);
}
@@ -461,7 +460,7 @@
bool setLast[LOG_ID_MAX];
bool doSetLast = false;
- log_id_for_each(i) { doSetLast |= setLast[i] = mOldest[i] && it == *mOldest[i]; }
+ log_id_for_each(i) { doSetLast |= setLast[i] = oldest_[i] && it == *oldest_[i]; }
#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
LogBufferElementCollection::iterator bad = it;
int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
@@ -473,9 +472,9 @@
log_id_for_each(i) {
if (setLast[i]) {
if (__predict_false(it == mLogElements.end())) {
- mOldest[i] = std::nullopt;
+ oldest_[i] = std::nullopt;
} else {
- mOldest[i] = it; // Store the next iterator even if it does not correspond to
+ oldest_[i] = it; // Store the next iterator even if it does not correspond to
// the same log_id, as a starting point for GetOldest().
}
}
@@ -698,7 +697,7 @@
}
// prune by worst offenders; by blacklist, UID, and by PID of system UID
- bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
int worst = -1; // not valid for getUid() or getKey()
@@ -706,7 +705,7 @@
size_t second_worst_sizes = 0;
pid_t worstPid = 0; // POSIX guarantees PID != 0
- if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+ if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
// Calculate threshold as 12.5% of available storage
size_t threshold = log_buffer_size(id) / 8;
@@ -720,7 +719,7 @@
.findWorst(worst, worst_sizes, second_worst_sizes,
threshold);
- if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+ if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
stats.sortPids(worst, (pid_t)0, 2, id)
.findWorst(worstPid, worst_sizes, second_worst_sizes);
}
@@ -802,7 +801,7 @@
? element->getTag()
: element->getUid();
- if (hasBlacklist && mPrune.naughty(element)) {
+ if (hasBlacklist && prune_->naughty(element)) {
last.clear(element);
it = erase(it);
if (dropped) {
@@ -899,13 +898,13 @@
}
last.clear();
- if (!kick || !mPrune.worstUidEnabled()) {
+ if (!kick || !prune_->worstUidEnabled()) {
break; // the following loop will ask bad clients to skip/drop
}
}
bool whitelist = false;
- bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
it = GetOldest(id);
while ((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement* element = *it;
@@ -921,7 +920,7 @@
break;
}
- if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+ if (hasWhitelist && !element->getDropped() && prune_->nice(element)) {
// WhiteListed
whitelist = true;
it++;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 458fbbb..9a36712 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_BUFFER_H__
-#define _LOGD_LOG_BUFFER_H__
+#pragma once
#include <sys/types.h>
@@ -81,10 +80,6 @@
LogStatistics stats;
- PruneList mPrune;
- // 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;
@@ -98,8 +93,6 @@
bool monotonic;
- LogTags tags;
-
LogBufferElement* lastLoggedElements[LOG_ID_MAX];
LogBufferElement* droppedElements[LOG_ID_MAX];
void log(LogBufferElement* elem);
@@ -107,7 +100,7 @@
public:
LastLogTimes& mTimes;
- explicit LogBuffer(LastLogTimes* times);
+ LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
~LogBuffer();
void init();
bool isMonotonic() {
@@ -136,24 +129,6 @@
stats.enableStatistics();
}
- int initPrune(const char* cp) {
- return mPrune.init(cp);
- }
- std::string formatPrune() {
- return mPrune.format();
- }
-
- std::string formatGetEventTag(uid_t uid, const char* name,
- const char* format) {
- return tags.formatGetEventTag(uid, name, format);
- }
- std::string formatEntry(uint32_t tag, uid_t uid) {
- return tags.formatEntry(tag, uid);
- }
- const char* tagToName(uint32_t tag) {
- return tags.tagToName(tag);
- }
-
// helper must be protected directly or implicitly by wrlock()/unlock()
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
@@ -186,6 +161,11 @@
// 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__
+ LogTags* tags_;
+ PruneList* prune_;
+
+ // 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> oldest_[LOG_ID_MAX];
+};
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index f79d39c..c6dea69 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -24,21 +24,35 @@
#include <cutils/sockets.h>
#include <private/android_logger.h>
-#include "FlushCommand.h"
#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogReader.h"
#include "LogUtils.h"
+static bool CanReadSecurityLogs(SocketClient* client) {
+ return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
+}
+
LogReader::LogReader(LogBuffer* logbuf)
: SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
}
// When we are notified a new log entry is available, inform
// listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(log_mask_t logMask) {
- FlushCommand command(*this, logMask);
- runOnEachSocket(&command);
+void LogReader::notifyNewLog(log_mask_t log_mask) {
+ LastLogTimes& times = mLogbuf.mTimes;
+
+ LogTimeEntry::wrlock();
+ for (const auto& entry : times) {
+ if (!entry->isWatchingMultiple(log_mask)) {
+ continue;
+ }
+ if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
+ continue;
+ }
+ entry->triggerReader_Locked();
+ }
+ LogTimeEntry::unlock();
}
// Note returning false will release the SocketClient instance.
@@ -129,6 +143,9 @@
nonBlock = true;
}
+ bool privileged = clientHasLogCredentials(cli);
+ bool can_read_security = CanReadSecurityLogs(cli);
+
uint64_t sequence = 1;
// Convert realtime to sequence number
if (start != log_time::EPOCH) {
@@ -178,8 +195,7 @@
} logFindStart(logMask, pid, start, sequence,
logbuf().isMonotonic() && android::isMonotonic(start));
- logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
- FlushCommand::hasSecurityLogs(cli),
+ logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security,
logFindStart.callback, &logFindStart);
if (!logFindStart.found()) {
@@ -203,7 +219,7 @@
LogTimeEntry::wrlock();
auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
- sequence, timeout);
+ sequence, timeout, privileged, can_read_security);
if (!entry->startReader_Locked()) {
LogTimeEntry::unlock();
return false;
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index e45cc8a..3e52b38 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -391,23 +391,6 @@
return me->tagToName(tag);
}
-// Prototype in LogUtils.h allowing external access to our database.
-//
-// This only works on userdebug and eng devices to re-read the
-// /data/misc/logd/event-log-tags file right after /data is mounted.
-// The operation is near to boot and should only happen once. There
-// are races associated with its use since it can trigger a Rebuild
-// of the file, but that is a can-not-happen since the file was not
-// read yet. More dangerous if called later, but if all is well it
-// should just skip over everything and not write any new entries.
-void android::ReReadEventLogTags() {
- LogTags* me = logtags;
-
- if (me && __android_log_is_debuggable()) {
- me->ReadFileEventLogTags(me->debug_event_log_tags);
- }
-}
-
// converts an event tag into a format
const char* LogTags::tagToFormat(uint32_t tag) const {
tag2format_const_iterator iform;
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ed8d2f5..ad150bd 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -18,7 +18,6 @@
#include <string.h>
#include <sys/prctl.h>
-#include "FlushCommand.h"
#include "LogBuffer.h"
#include "LogReader.h"
#include "LogTimes.h"
@@ -27,7 +26,8 @@
LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
unsigned long tail, log_mask_t logMask, pid_t pid, log_time start_time,
- uint64_t start, uint64_t timeout)
+ uint64_t start, uint64_t timeout, bool privileged,
+ bool can_read_security_logs)
: leadingDropped(false),
mReader(reader),
mLogMask(logMask),
@@ -38,7 +38,9 @@
mClient(client),
mStartTime(start_time),
mStart(start),
- mNonBlock(nonBlock) {
+ mNonBlock(nonBlock),
+ privileged_(privileged),
+ can_read_security_logs_(can_read_security_logs) {
mTimeout.tv_sec = timeout / NS_PER_SEC;
mTimeout.tv_nsec = timeout % NS_PER_SEC;
memset(mLastTid, 0, sizeof(mLastTid));
@@ -72,9 +74,6 @@
LogBuffer& logbuf = me->mReader.logbuf();
- bool privileged = FlushCommand::hasReadLogs(client);
- bool security = FlushCommand::hasSecurityLogs(client);
-
me->leadingDropped = true;
wrlock();
@@ -96,12 +95,12 @@
unlock();
if (me->mTail) {
- logbuf.flushTo(client, start, nullptr, privileged, security,
+ logbuf.flushTo(client, start, nullptr, me->privileged_, me->can_read_security_logs_,
FilterFirstPass, me);
me->leadingDropped = true;
}
- start = logbuf.flushTo(client, start, me->mLastTid, privileged,
- security, FilterSecondPass, me);
+ start = logbuf.flushTo(client, start, me->mLastTid, me->privileged_,
+ me->can_read_security_logs_, FilterSecondPass, me);
// We only ignore entries before the original start time for the first flushTo(), if we
// get entries after this first flush before the original start time, then the client
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index a99c73b..56c930a 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -52,7 +52,7 @@
public:
LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock, unsigned long tail,
log_mask_t logMask, pid_t pid, log_time start_time, uint64_t sequence,
- uint64_t timeout);
+ uint64_t timeout, bool privileged, bool can_read_security_logs);
SocketClient* mClient;
log_time mStartTime;
@@ -98,6 +98,10 @@
// flushTo filter callbacks
static int FilterFirstPass(const LogBufferElement* element, void* me);
static int FilterSecondPass(const LogBufferElement* element, void* me);
+
+ private:
+ bool privileged_;
+ bool can_read_security_logs_;
};
typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..f9cd42d 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_UTILS_H__
-#define _LOGD_LOG_UTILS_H__
+#pragma once
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -41,7 +40,6 @@
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
-void ReReadEventLogTags();
// Furnished by LogKlog.cpp
char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
@@ -72,5 +70,3 @@
return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
(id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
}
-
-#endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 4d1589b..14c5163 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -94,12 +94,14 @@
}
LastLogTimes times;
- LogBuffer log_buffer(×);
+ LogTags tags;
+ PruneList prune_list;
+ LogBuffer log_buffer(×, &tags, &prune_list);
size_t data_left = size;
const uint8_t** pdata = &data;
log_buffer.enableStatistics();
- log_buffer.initPrune(nullptr);
+ prune_list.init(nullptr);
// We want to get pruning code to get called.
log_id_for_each(i) { log_buffer.setSize(i, 10000); }
diff --git a/logd/main.cpp b/logd/main.cpp
index 23bbf86..cc45eb3 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -52,6 +52,7 @@
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogListener.h"
+#include "LogTags.h"
#include "LogUtils.h"
#define KMSG_PRIORITY(PRI) \
@@ -150,50 +151,6 @@
}
}
-static sem_t reinit;
-static bool reinit_running = false;
-static LogBuffer* logBuf = nullptr;
-
-static void* reinit_thread_start(void* /*obj*/) {
- prctl(PR_SET_NAME, "logd.daemon");
-
- while (reinit_running && !sem_wait(&reinit) && reinit_running) {
- if (fdDmesg >= 0) {
- static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
- 'l',
- 'o',
- 'g',
- 'd',
- '.',
- 'd',
- 'a',
- 'e',
- 'm',
- 'o',
- 'n',
- ':',
- ' ',
- 'r',
- 'e',
- 'i',
- 'n',
- 'i',
- 't',
- '\n' };
- write(fdDmesg, reinit_message, sizeof(reinit_message));
- }
-
- // Anything that reads persist.<property>
- if (logBuf) {
- logBuf->init();
- logBuf->initPrune(nullptr);
- }
- android::ReReadEventLogTags();
- }
-
- return nullptr;
-}
-
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -220,12 +177,6 @@
return userdata.name;
}
-// Serves as a global method to trigger reinitialization
-// and as a function that can be provided to signal().
-void reinit_signal_handler(int /*signal*/) {
- sem_post(&reinit);
-}
-
static void readDmesg(LogAudit* al, LogKlog* kl) {
if (!al && !kl) {
return;
@@ -336,24 +287,10 @@
return EXIT_FAILURE;
}
- // Reinit Thread
- sem_init(&reinit, 0, 0);
- pthread_attr_t attr;
- if (!pthread_attr_init(&attr)) {
- struct sched_param param;
-
- memset(¶m, 0, sizeof(param));
- pthread_attr_setschedparam(&attr, ¶m);
- pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- pthread_t thread;
- reinit_running = true;
- if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
- reinit_running = false;
- }
- }
- pthread_attr_destroy(&attr);
- }
+ // A cache of event log tags
+ LogTags log_tags;
+ // Pruning configuration.
+ PruneList prune_list;
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
@@ -364,9 +301,7 @@
// LogBuffer is the object which is responsible for holding all
// log entries.
- logBuf = new LogBuffer(times);
-
- signal(SIGHUP, reinit_signal_handler);
+ LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
if (__android_logger_property_get_bool(
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
@@ -396,7 +331,7 @@
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
- CommandListener* cl = new CommandListener(logBuf, reader, swl);
+ CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
if (cl->startListener()) {
return EXIT_FAILURE;
}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 405f5a9..5de422f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so nopreload
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index b565340..77f8bb8 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -17,6 +17,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 7cbda08..82196e4 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so