Merge changes I5d17fc87,If1cc6b9a * changes: Cgroup APIs are guarded Remove __ANDROID_API__ guards
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 7938a61..45e555f 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp
@@ -16,6 +16,7 @@ #include <err.h> #include <fcntl.h> +#include <malloc.h> #include <stdlib.h> #include <sys/capability.h> #include <sys/mman.h> @@ -32,7 +33,6 @@ #include <android/fdsan.h> #include <android/set_abort_message.h> -#include <bionic/malloc.h> #include <bionic/mte.h> #include <bionic/reserved_signals.h> @@ -385,8 +385,7 @@ #if defined(__aarch64__) static void SetTagCheckingLevelSync() { - HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC; - if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) { + if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_SYNC) == 0) { abort(); } }
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash index 406e8b8..f5a3384 100644 --- a/fastboot/fastboot.bash +++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@ cur="${COMP_WORDS[COMP_CWORD]}" if [[ $i -eq $COMP_CWORD ]]; then - partitions="boot bootloader dtbo modem odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm" + partitions="boot bootloader dtbo modem odm odm_dlkm oem product pvmfw radio recovery system vbmeta vendor vendor_dlkm" COMPREPLY=( $(compgen -W "$partitions" -- $cur) ) else _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 62f6ac7..f7edf8e 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp
@@ -147,6 +147,7 @@ { "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal }, { "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal }, { "product", "product.img", "product.sig", "product", true, ImageType::Normal }, + { "pvmfw", "pvmfw.img", "pvmfw.sig", "pvmfw", true, ImageType::BootCritical }, { "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical }, { "super", "super.img", "super.sig", "super", true, ImageType::Extra }, { "system", "system.img", "system.sig", "system", false, ImageType::Normal },
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp index defe8d4..a96352a 100644 --- a/fs_mgr/libsnapshot/cow_api_test.cpp +++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -60,6 +60,7 @@ TEST_F(CowTest, ReadWrite) { CowOptions options; + options.cluster_ops = 0; CowWriter writer(options); ASSERT_TRUE(writer.Initialize(cow_->fd)); @@ -137,6 +138,7 @@ TEST_F(CowTest, CompressGz) { CowOptions options; + options.cluster_ops = 0; options.compression = "gz"; CowWriter writer(options); @@ -238,6 +240,7 @@ TEST_F(CowTest, CompressTwoBlocks) { CowOptions options; options.compression = "gz"; + options.cluster_ops = 0; CowWriter writer(options); ASSERT_TRUE(writer.Initialize(cow_->fd)); @@ -280,6 +283,7 @@ TEST_P(CompressionTest, HorribleSink) { CowOptions options; options.compression = GetParam(); + options.cluster_ops = 0; CowWriter writer(options); ASSERT_TRUE(writer.Initialize(cow_->fd)); @@ -309,6 +313,7 @@ TEST_F(CowTest, GetSize) { CowOptions options; + options.cluster_ops = 0; CowWriter writer(options); if (ftruncate(cow_->fd, 0) < 0) { perror("Fails to set temp file size"); @@ -334,6 +339,7 @@ TEST_F(CowTest, AppendLabelSmall) { CowOptions options; + options.cluster_ops = 0; auto writer = std::make_unique<CowWriter>(options); ASSERT_TRUE(writer->Initialize(cow_->fd)); @@ -399,6 +405,7 @@ TEST_F(CowTest, AppendLabelMissing) { CowOptions options; + options.cluster_ops = 0; auto writer = std::make_unique<CowWriter>(options); ASSERT_TRUE(writer->Initialize(cow_->fd)); @@ -452,6 +459,7 @@ TEST_F(CowTest, AppendExtendedCorrupted) { CowOptions options; + options.cluster_ops = 0; auto writer = std::make_unique<CowWriter>(options); ASSERT_TRUE(writer->Initialize(cow_->fd)); @@ -504,6 +512,7 @@ TEST_F(CowTest, AppendbyLabel) { CowOptions options; + options.cluster_ops = 0; auto writer = std::make_unique<CowWriter>(options); ASSERT_TRUE(writer->Initialize(cow_->fd));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h index fd43cce..22ddfa6 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -35,7 +35,7 @@ std::optional<uint64_t> max_blocks; // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1. - uint32_t cluster_ops = 0; + uint32_t cluster_ops = 200; }; // Interface for writing to a snapuserd COW. All operations are ordered; merges
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index f8d3cdc..397ff2e 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -306,13 +306,17 @@ // Helper function for second stage init to restorecon on the rollback indicator. static std::string GetGlobalRollbackIndicatorPath(); - // Initiate the transition from first-stage to second-stage snapuserd. This - // process involves re-creating the dm-user table entries for each device, - // so that they connect to the new daemon. Once all new tables have been - // activated, we ask the first-stage daemon to cleanly exit. - // - // The caller must pass a function which starts snapuserd. - bool PerformSecondStageTransition(); + // Detach dm-user devices from the current snapuserd, and populate + // |snapuserd_argv| with the necessary arguments to restart snapuserd + // and reattach them. + bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv); + + // Perform the transition from the selinux stage of snapuserd into the + // second-stage of snapuserd. This process involves re-creating the dm-user + // table entries for each device, so that they connect to the new daemon. + // Once all new tables have been activated, we ask the first-stage daemon + // to cleanly exit. + bool PerformSecondStageInitTransition(); // ISnapshotManager overrides. bool BeginUpdate() override; @@ -626,6 +630,9 @@ // The reverse of MapPartitionWithSnapshot. bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name); + // Unmap a dm-user device through snapuserd. + bool UnmapDmUserDevice(const std::string& snapshot_name); + // If there isn't a previous update, return true. |needs_merge| is set to false. // If there is a previous update but the device has not boot into it, tries to cancel the // update and delete any snapshots. Return true if successful. |needs_merge| is set to false. @@ -693,6 +700,19 @@ // returns true. bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms); + enum class InitTransition { SELINUX_DETACH, SECOND_STAGE }; + + // Initiate the transition from first-stage to second-stage snapuserd. This + // process involves re-creating the dm-user table entries for each device, + // so that they connect to the new daemon. Once all new tables have been + // activated, we ask the first-stage daemon to cleanly exit. + // + // If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will + // be populated with a list of snapuserd arguments to pass to execve(). It + // is otherwise ignored. + bool PerformInitTransition(InitTransition transition, + std::vector<std::string>* snapuserd_argv = nullptr); + std::string gsid_dir_; std::string metadata_dir_; std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h index aa9ba6e..1dab361 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -32,8 +32,6 @@ static constexpr char kSnapuserdSocket[] = "snapuserd"; -static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID"; - // Ensure that the second-stage daemon for snapuserd is running. bool EnsureSnapuserdStarted();
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 04c3ccc..f0646fc 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1247,6 +1247,10 @@ return false; } + if (status.compression_enabled()) { + UnmapDmUserDevice(name); + } + // Cleanup the base device as well, since it is no longer used. This does // not block cleanup. auto base_name = GetBaseDeviceName(name); @@ -1291,12 +1295,13 @@ return RemoveAllUpdateState(lock, before_cancel); } -bool SnapshotManager::PerformSecondStageTransition() { - LOG(INFO) << "Performing second-stage transition for snapuserd."; +bool SnapshotManager::PerformInitTransition(InitTransition transition, + std::vector<std::string>* snapuserd_argv) { + LOG(INFO) << "Performing transition for snapuserd."; // Don't use EnsuerSnapuserdConnected() because this is called from init, // and attempting to do so will deadlock. - if (!snapuserd_client_) { + if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) { snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s); if (!snapuserd_client_) { LOG(ERROR) << "Unable to connect to snapuserd"; @@ -1343,6 +1348,9 @@ } auto misc_name = user_cow_name; + if (transition == InitTransition::SELINUX_DETACH) { + misc_name += "-selinux"; + } DmTable table; table.Emplace<DmTargetUser>(0, target.spec.length, misc_name); @@ -1378,6 +1386,17 @@ continue; } + if (transition == InitTransition::SELINUX_DETACH) { + auto message = misc_name + "," + cow_image_device + "," + backing_device; + snapuserd_argv->emplace_back(std::move(message)); + + // Do not attempt to connect to the new snapuserd yet, it hasn't + // been started. We do however want to wait for the misc device + // to have been created. + ok_cows++; + continue; + } + uint64_t base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device); if (base_sectors == 0) { @@ -2048,27 +2067,8 @@ auto& dm = DeviceMapper::Instance(); - auto dm_user_name = GetDmUserCowName(name); - if (IsCompressionEnabled() && dm.GetState(dm_user_name) != DmDeviceState::INVALID) { - if (!EnsureSnapuserdConnected()) { - return false; - } - if (!dm.DeleteDeviceIfExists(dm_user_name)) { - LOG(ERROR) << "Cannot unmap " << dm_user_name; - return false; - } - - if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) { - LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete"; - return false; - } - - // Ensure the control device is gone so we don't run into ABA problems. - auto control_device = "/dev/dm-user/" + dm_user_name; - if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) { - LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink"; - return false; - } + if (IsCompressionEnabled() && !UnmapDmUserDevice(name)) { + return false; } auto cow_name = GetCowName(name); @@ -2085,6 +2085,37 @@ return true; } +bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) { + auto& dm = DeviceMapper::Instance(); + + if (!EnsureSnapuserdConnected()) { + return false; + } + + auto dm_user_name = GetDmUserCowName(snapshot_name); + if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) { + return true; + } + + if (!dm.DeleteDeviceIfExists(dm_user_name)) { + LOG(ERROR) << "Cannot unmap " << dm_user_name; + return false; + } + + if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) { + LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete"; + return false; + } + + // Ensure the control device is gone so we don't run into ABA problems. + auto control_device = "/dev/dm-user/" + dm_user_name; + if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) { + LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink"; + return false; + } + return true; +} + bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) { auto lock = LockExclusive(); if (!lock) return false; @@ -3311,5 +3342,13 @@ return status.state() != UpdateState::None && status.compression_enabled(); } +bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) { + return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv); +} + +bool SnapshotManager::PerformSecondStageInitTransition() { + return PerformInitTransition(InitTransition::SECOND_STAGE); +} + } // namespace snapshot } // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index c6a1786..bb44425 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -146,6 +146,7 @@ "base-device", "test_partition_b", "test_partition_b-base", + "test_partition_b-base", }; for (const auto& partition : partitions) { DeleteDevice(partition); @@ -180,12 +181,22 @@ } // If |path| is non-null, the partition will be mapped after creation. - bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) { + bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr, + const std::optional<std::string> group = {}) { TestPartitionOpener opener(fake_super); auto builder = MetadataBuilder::New(opener, "super", 0); if (!builder) return false; - auto partition = builder->AddPartition(name, 0); + std::string partition_group = std::string(android::fs_mgr::kDefaultGroup); + if (group) { + partition_group = *group; + } + return CreatePartition(builder.get(), name, size, path, partition_group); + } + + bool CreatePartition(MetadataBuilder* builder, const std::string& name, uint64_t size, + std::string* path, const std::string& group) { + auto partition = builder->AddPartition(name, group, 0); if (!partition) return false; if (!builder->ResizePartition(partition, size)) { return false; @@ -194,6 +205,8 @@ // Update the source slot. auto metadata = builder->Export(); if (!metadata) return false; + + TestPartitionOpener opener(fake_super); if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) { return false; } @@ -210,39 +223,54 @@ return CreateLogicalPartition(params, path); } - bool MapUpdatePartitions() { + AssertionResult MapUpdateSnapshot(const std::string& name, + std::unique_ptr<ICowWriter>* writer) { TestPartitionOpener opener(fake_super); - auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1); - if (!builder) return false; + CreateLogicalPartitionParams params{ + .block_device = fake_super, + .metadata_slot = 1, + .partition_name = name, + .timeout_ms = 10s, + .partition_opener = &opener, + }; - auto metadata = builder->Export(); - if (!metadata) return false; - - // Update the destination slot, mark it as updated. - if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) { - return false; + auto result = sm->OpenSnapshotWriter(params, {}); + if (!result) { + return AssertionFailure() << "Cannot open snapshot for writing: " << name; + } + if (!result->Initialize()) { + return AssertionFailure() << "Cannot initialize snapshot for writing: " << name; } - for (const auto& partition : metadata->partitions) { - CreateLogicalPartitionParams params = { - .block_device = fake_super, - .metadata = metadata.get(), - .partition = &partition, - .force_writable = true, - .timeout_ms = 10s, - .device_name = GetPartitionName(partition) + "-base", - }; - std::string ignore_path; - if (!CreateLogicalPartition(params, &ignore_path)) { - return false; - } + if (writer) { + *writer = std::move(result); } - return true; + return AssertionSuccess(); + } + + AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) { + TestPartitionOpener opener(fake_super); + CreateLogicalPartitionParams params{ + .block_device = fake_super, + .metadata_slot = 1, + .partition_name = name, + .timeout_ms = 10s, + .partition_opener = &opener, + }; + + auto result = sm->MapUpdateSnapshot(params, path); + if (!result) { + return AssertionFailure() << "Cannot open snapshot for writing: " << name; + } + return AssertionSuccess(); } AssertionResult DeleteSnapshotDevice(const std::string& snapshot) { AssertionResult res = AssertionSuccess(); if (!(res = DeleteDevice(snapshot))) return res; + if (!sm->UnmapDmUserDevice(snapshot)) { + return AssertionFailure() << "Cannot delete dm-user device for " << snapshot; + } if (!(res = DeleteDevice(snapshot + "-inner"))) return res; if (!(res = DeleteDevice(snapshot + "-cow"))) return res; if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) { @@ -289,37 +317,55 @@ // Prepare A/B slot for a partition named "test_partition". AssertionResult PrepareOneSnapshot(uint64_t device_size, - std::string* out_snap_device = nullptr) { - std::string base_device, cow_device, snap_device; - if (!CreatePartition("test_partition_a", device_size)) { - return AssertionFailure(); + std::unique_ptr<ICowWriter>* writer = nullptr) { + lock_ = nullptr; + + DeltaArchiveManifest manifest; + + auto group = manifest.mutable_dynamic_partition_metadata()->add_groups(); + group->set_name("group"); + group->set_size(device_size * 2); + group->add_partition_names("test_partition"); + + auto pu = manifest.add_partitions(); + pu->set_partition_name("test_partition"); + pu->set_estimate_cow_size(device_size); + SetSize(pu, device_size); + + auto extent = pu->add_operations()->add_dst_extents(); + extent->set_start_block(0); + if (device_size) { + extent->set_num_blocks(device_size / manifest.block_size()); } - if (!MapUpdatePartitions()) { - return AssertionFailure(); + + TestPartitionOpener opener(fake_super); + auto builder = MetadataBuilder::New(opener, "super", 0); + if (!builder) { + return AssertionFailure() << "Failed to open MetadataBuilder"; } - if (!dm_.GetDmDevicePathByName("test_partition_b-base", &base_device)) { - return AssertionFailure(); + builder->AddGroup("group_a", 16_GiB); + builder->AddGroup("group_b", 16_GiB); + if (!CreatePartition(builder.get(), "test_partition_a", device_size, nullptr, "group_a")) { + return AssertionFailure() << "Failed create test_partition_a"; } - SnapshotStatus status; - status.set_name("test_partition_b"); - status.set_device_size(device_size); - status.set_snapshot_size(device_size); - status.set_cow_file_size(device_size); - if (!sm->CreateSnapshot(lock_.get(), &status)) { - return AssertionFailure(); + + if (!sm->CreateUpdateSnapshots(manifest)) { + return AssertionFailure() << "Failed to create update snapshots"; } - if (!CreateCowImage("test_partition_b")) { - return AssertionFailure(); + + if (writer) { + auto res = MapUpdateSnapshot("test_partition_b", writer); + if (!res) { + return res; + } + } else if (!IsCompressionEnabled()) { + std::string ignore; + if (!MapUpdateSnapshot("test_partition_b", &ignore)) { + return AssertionFailure() << "Failed to map test_partition_b"; + } } - if (!MapCowImage("test_partition_b", 10s, &cow_device)) { - return AssertionFailure(); - } - if (!sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s, - &snap_device)) { - return AssertionFailure(); - } - if (out_snap_device) { - *out_snap_device = std::move(snap_device); + if (!AcquireLock()) { + return AssertionFailure() << "Failed to acquire lock"; } return AssertionSuccess(); } @@ -328,20 +374,37 @@ AssertionResult SimulateReboot() { lock_ = nullptr; if (!sm->FinishedSnapshotWrites(false)) { - return AssertionFailure(); + return AssertionFailure() << "Failed to finish snapshot writes"; } - if (!dm_.DeleteDevice("test_partition_b")) { - return AssertionFailure(); + if (!sm->UnmapUpdateSnapshot("test_partition_b")) { + return AssertionFailure() << "Failed to unmap COW for test_partition_b"; } - if (!DestroyLogicalPartition("test_partition_b-base")) { - return AssertionFailure(); + if (!dm_.DeleteDeviceIfExists("test_partition_b")) { + return AssertionFailure() << "Failed to delete test_partition_b"; } - if (!sm->UnmapCowImage("test_partition_b")) { - return AssertionFailure(); + if (!dm_.DeleteDeviceIfExists("test_partition_b-base")) { + return AssertionFailure() << "Failed to destroy test_partition_b-base"; } return AssertionSuccess(); } + std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount( + const std::string& slot_suffix = "_a") { + auto info = new TestDeviceInfo(fake_super, slot_suffix); + return NewManagerForFirstStageMount(info); + } + + std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) { + auto init = SnapshotManager::NewForFirstStageMount(info); + if (!init) { + return nullptr; + } + init->SetUeventRegenCallback([](const std::string& device) -> bool { + return android::fs_mgr::WaitForFile(device, snapshot_timeout_); + }); + return init; + } + static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s; DeviceMapper& dm_; std::unique_ptr<SnapshotManager::LockedFile> lock_; @@ -439,8 +502,7 @@ TEST_F(SnapshotTest, CleanFirstStageMount) { // If there's no update in progress, there should be no first-stage mount // needed. - TestDeviceInfo* info = new TestDeviceInfo(fake_super); - auto sm = SnapshotManager::NewForFirstStageMount(info); + auto sm = NewManagerForFirstStageMount(); ASSERT_NE(sm, nullptr); ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount()); } @@ -449,8 +511,7 @@ ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); // We didn't change the slot, so we shouldn't need snapshots. - TestDeviceInfo* info = new TestDeviceInfo(fake_super); - auto sm = SnapshotManager::NewForFirstStageMount(info); + auto sm = NewManagerForFirstStageMount(); ASSERT_NE(sm, nullptr); ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount()); @@ -462,32 +523,30 @@ ASSERT_TRUE(AcquireLock()); static const uint64_t kDeviceSize = 1024 * 1024; - std::string snap_device; - ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &snap_device)); - std::string test_string = "This is a test string."; - { - unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC)); - ASSERT_GE(fd, 0); - ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size())); - } - - // Note: we know there is no inner/outer dm device since we didn't request - // a linear segment. - DeviceMapper::TargetInfo target; - ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target)); - ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot"); + std::unique_ptr<ICowWriter> writer; + ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer)); // Release the lock. lock_ = nullptr; + std::string test_string = "This is a test string."; + test_string.resize(writer->options().block_size); + ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size())); + ASSERT_TRUE(writer->Finalize()); + writer = nullptr; + // Done updating. ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); + ASSERT_TRUE(sm->UnmapUpdateSnapshot("test_partition_b")); + test_device->set_slot_suffix("_b"); + ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); ASSERT_TRUE(sm->InitiateMerge()); // The device should have been switched to a snapshot-merge target. + DeviceMapper::TargetInfo target; ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target)); ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge"); @@ -503,7 +562,7 @@ // Test that we can read back the string we wrote to the snapshot. Note // that the base device is gone now. |snap_device| contains the correct // partition. - unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC)); + unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC)); ASSERT_GE(fd, 0); std::string buffer(test_string.size(), '\0'); @@ -518,7 +577,7 @@ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize)); ASSERT_TRUE(SimulateReboot()); - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -547,7 +606,7 @@ FormatFakeSuper(); ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize)); - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -574,7 +633,7 @@ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize)); ASSERT_TRUE(SimulateReboot()); - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -899,47 +958,7 @@ return AssertionSuccess(); } - AssertionResult MapUpdateSnapshot(const std::string& name, - std::unique_ptr<ICowWriter>* writer) { - CreateLogicalPartitionParams params{ - .block_device = fake_super, - .metadata_slot = 1, - .partition_name = name, - .timeout_ms = 10s, - .partition_opener = opener_.get(), - }; - - auto result = sm->OpenSnapshotWriter(params, {}); - if (!result) { - return AssertionFailure() << "Cannot open snapshot for writing: " << name; - } - if (!result->Initialize()) { - return AssertionFailure() << "Cannot initialize snapshot for writing: " << name; - } - - if (writer) { - *writer = std::move(result); - } - return AssertionSuccess(); - } - - AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) { - CreateLogicalPartitionParams params{ - .block_device = fake_super, - .metadata_slot = 1, - .partition_name = name, - .timeout_ms = 10s, - .partition_opener = opener_.get(), - }; - - auto result = sm->MapUpdateSnapshot(params, path); - if (!result) { - return AssertionFailure() << "Cannot open snapshot for writing: " << name; - } - return AssertionSuccess(); - } - - AssertionResult MapUpdateSnapshot(const std::string& name) { + AssertionResult MapOneUpdateSnapshot(const std::string& name) { if (IsCompressionEnabled()) { std::unique_ptr<ICowWriter> writer; return MapUpdateSnapshot(name, &writer); @@ -983,7 +1002,7 @@ AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b", "prd_b"}) { for (const auto& name : names) { - auto res = MapUpdateSnapshot(name); + auto res = MapOneUpdateSnapshot(name); if (!res) { return res; } @@ -1070,7 +1089,7 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1202,7 +1221,7 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1214,7 +1233,7 @@ // Simulate shutting down the device again. ASSERT_TRUE(UnmapAll()); - init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a")); + init = NewManagerForFirstStageMount("_a"); ASSERT_NE(init, nullptr); ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1251,7 +1270,7 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1387,8 +1406,8 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img" - // won't be set. + // Normally we should use NewManagerForFirstStageMount, but if so, + // "gsid.mapped_image.sys_b-cow-img" won't be set. auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b")); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1495,7 +1514,7 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1509,7 +1528,7 @@ // 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()); + new_sm = NewManagerForFirstStageMount(test_device.release()); ASSERT_TRUE(new_sm->HandleImminentDataWipe()); ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None); @@ -1527,7 +1546,7 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1541,7 +1560,7 @@ // 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()); + new_sm = NewManagerForFirstStageMount(test_device.release()); ASSERT_TRUE(new_sm->FinishMergeInRecovery()); @@ -1551,12 +1570,12 @@ // Finish the merge in a normal boot. test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b"); - init = SnapshotManager::NewForFirstStageMount(test_device.release()); + init = NewManagerForFirstStageMount(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()); + new_sm = NewManagerForFirstStageMount(test_device.release()); ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted); ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None); } @@ -1575,7 +1594,7 @@ // Simulate a reboot into recovery. auto test_device = new TestDeviceInfo(fake_super, "_b"); test_device->set_recovery(true); - auto new_sm = SnapshotManager::NewForFirstStageMount(test_device); + auto new_sm = NewManagerForFirstStageMount(test_device); ASSERT_TRUE(new_sm->HandleImminentDataWipe()); // Manually mount metadata so that we can call GetUpdateState() below. @@ -1600,7 +1619,7 @@ // Simulate a rollback, with reboot into recovery. auto test_device = new TestDeviceInfo(fake_super, "_a"); test_device->set_recovery(true); - auto new_sm = SnapshotManager::NewForFirstStageMount(test_device); + auto new_sm = NewManagerForFirstStageMount(test_device); ASSERT_TRUE(new_sm->HandleImminentDataWipe()); EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None); @@ -1628,7 +1647,7 @@ // Simulate a reboot into recovery. auto test_device = new TestDeviceInfo(fake_super, "_b"); test_device->set_recovery(true); - auto new_sm = SnapshotManager::NewForFirstStageMount(test_device); + auto new_sm = NewManagerForFirstStageMount(test_device); ASSERT_TRUE(new_sm->HandleImminentDataWipe()); // Manually mount metadata so that we can call GetUpdateState() below. @@ -1639,7 +1658,7 @@ // Now reboot into new slot. test_device = new TestDeviceInfo(fake_super, "_b"); - auto init = SnapshotManager::NewForFirstStageMount(test_device); + auto init = NewManagerForFirstStageMount(test_device); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); // Verify that we are on the downgraded build. for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { @@ -1685,7 +1704,7 @@ ASSERT_TRUE(UnmapAll()); // After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); @@ -1773,7 +1792,7 @@ ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); ASSERT_TRUE(UnmapAll()); - auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b")); + auto init = NewManagerForFirstStageMount("_b"); ASSERT_NE(init, nullptr); ASSERT_TRUE(init->EnsureSnapuserdConnected()); @@ -1785,7 +1804,7 @@ ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0); ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1); - ASSERT_TRUE(init->PerformSecondStageTransition()); + ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE)); // The control device should have been renamed. ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s)); @@ -1887,8 +1906,7 @@ ASSERT_TRUE(UnmapAll()); // Simulate reboot. After reboot, init does first stage mount. - auto init = SnapshotManager::NewForFirstStageMount( - new TestDeviceInfo(fake_super, flashed_slot_suffix)); + auto init = NewManagerForFirstStageMount(flashed_slot_suffix); ASSERT_NE(init, nullptr); if (flashed_slot && after_merge) {
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp index 8351155..38abaec 100644 --- a/fs_mgr/libsnapshot/snapuserd_server.cpp +++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -77,9 +77,8 @@ JoinAllThreads(); } -const std::string& DmUserHandler::GetMiscName() const { - return snapuserd_->GetMiscName(); -} +DmUserHandler::DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd) + : snapuserd_(std::move(snapuserd)), misc_name_(snapuserd_->GetMiscName()) {} bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) { ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), 0)); @@ -148,7 +147,7 @@ LOG(ERROR) << "Could not find handler: " << out[1]; return Sendmsg(fd, "fail"); } - if ((*iter)->snapuserd()->IsAttached()) { + if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) { LOG(ERROR) << "Tried to re-attach control device: " << out[1]; return Sendmsg(fd, "fail"); } @@ -185,7 +184,7 @@ LOG(ERROR) << "Malformed delete message, " << out.size() << " parts"; return Sendmsg(fd, "fail"); } - if (!RemoveHandler(out[1], true)) { + if (!RemoveAndJoinHandler(out[1])) { return Sendmsg(fd, "fail"); } return Sendmsg(fd, "success"); @@ -203,20 +202,38 @@ } void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) { - LOG(INFO) << "Entering thread for handler: " << handler->GetMiscName(); + LOG(INFO) << "Entering thread for handler: " << handler->misc_name(); while (!StopRequested()) { if (!handler->snapuserd()->Run()) { - LOG(INFO) << "Snapuserd: Thread terminating"; break; } } - LOG(INFO) << "Exiting thread for handler: " << handler->GetMiscName(); + auto misc_name = handler->misc_name(); + LOG(INFO) << "Handler thread about to exit: " << misc_name; - // If the main thread called RemoveHandler, the handler was already removed - // from within the lock, and calling RemoveHandler again has no effect. - RemoveHandler(handler->GetMiscName(), false); + { + std::lock_guard<std::mutex> lock(lock_); + auto iter = FindHandler(&lock, handler->misc_name()); + if (iter == dm_users_.end()) { + // RemoveAndJoinHandler() already removed us from the list, and is + // now waiting on a join(), so just return. + LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name; + return; + } + + LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name; + + if (handler->snapuserd()->IsAttached()) { + handler->thread().detach(); + } + + // Important: free resources within the lock. This ensures that if + // WaitForDelete() is called, the handler is either in the list, or + // it's not and its resources are guaranteed to be freed. + handler->FreeResources(); + } } bool SnapuserdServer::Start(const std::string& socketname) { @@ -351,7 +368,7 @@ CHECK(!handler->snapuserd()->IsAttached()); if (!handler->snapuserd()->InitBackingAndControlDevice()) { - LOG(ERROR) << "Failed to initialize control device: " << handler->GetMiscName(); + LOG(ERROR) << "Failed to initialize control device: " << handler->misc_name(); return false; } @@ -364,14 +381,14 @@ CHECK(proof_of_lock); for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) { - if ((*iter)->GetMiscName() == misc_name) { + if ((*iter)->misc_name() == misc_name) { return iter; } } return dm_users_.end(); } -bool SnapuserdServer::RemoveHandler(const std::string& misc_name, bool wait) { +bool SnapuserdServer::RemoveAndJoinHandler(const std::string& misc_name) { std::shared_ptr<DmUserHandler> handler; { std::lock_guard<std::mutex> lock(lock_); @@ -386,10 +403,8 @@ } auto& th = handler->thread(); - if (th.joinable() && wait) { + if (th.joinable()) { th.join(); - } else if (handler->snapuserd()->IsAttached()) { - th.detach(); } return true; }
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h index 01e2365..7cbc2de 100644 --- a/fs_mgr/libsnapshot/snapuserd_server.h +++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -46,18 +46,19 @@ }; class DmUserHandler { - private: - std::thread thread_; - std::unique_ptr<Snapuserd> snapuserd_; - public: - explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd) - : snapuserd_(std::move(snapuserd)) {} + explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd); + void FreeResources() { snapuserd_ = nullptr; } const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; } std::thread& thread() { return thread_; } - const std::string& GetMiscName() const; + const std::string& misc_name() const { return misc_name_; } + + private: + std::thread thread_; + std::unique_ptr<Snapuserd> snapuserd_; + std::string misc_name_; }; class Stoppable { @@ -71,8 +72,9 @@ bool StopRequested() { // checks if value in future object is available - if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) + if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) { return false; + } return true; } // Request the thread to stop by setting value in promise object @@ -98,7 +100,7 @@ bool Receivemsg(android::base::borrowed_fd fd, const std::string& str); void ShutdownThreads(); - bool RemoveHandler(const std::string& control_device, bool wait); + bool RemoveAndJoinHandler(const std::string& control_device); DaemonOperations Resolveop(std::string& input); std::string GetDaemonStatus(); void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp index 27a6452..2d9a820 100644 --- a/gatekeeperd/Android.bp +++ b/gatekeeperd/Android.bp
@@ -28,6 +28,7 @@ shared_libs: [ "libbinder", + "libbinder_ndk", "libgatekeeper", "libgsi", "liblog", @@ -40,6 +41,8 @@ "libhidlbase", "android.hardware.gatekeeper@1.0", "libgatekeeper_aidl", + "android.hardware.security.keymint-unstable-ndk_platform", + "android.security.authorization-ndk_platform", ], static_libs: ["libscrypt_static"],
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp index b982dbc..ced7573 100644 --- a/gatekeeperd/gatekeeperd.cpp +++ b/gatekeeperd/gatekeeperd.cpp
@@ -19,42 +19,44 @@ #include <android/service/gatekeeper/BnGateKeeperService.h> #include <gatekeeper/GateKeeperResponse.h> +#include <endian.h> #include <errno.h> #include <fcntl.h> -#include <inttypes.h> -#include <stdint.h> #include <unistd.h> #include <memory> -#include <android/security/keystore/IKeystoreService.h> #include <android-base/logging.h> #include <android-base/properties.h> +#include <android/binder_manager.h> +#include <android/security/keystore/IKeystoreService.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> -#include <gatekeeper/password_handle.h> // for password_handle_t -#include <hardware/gatekeeper.h> +#include <gatekeeper/password_handle.h> // for password_handle_t #include <hardware/hw_auth_token.h> -#include <keystore/keystore.h> // For error code #include <keystore/keystore_return_types.h> #include <libgsi/libgsi.h> #include <log/log.h> -#include <utils/Log.h> #include <utils/String16.h> -#include <hidl/HidlSupport.h> +#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h> +#include <aidl/android/security/authorization/IKeystoreAuthorization.h> #include <android/hardware/gatekeeper/1.0/IGatekeeper.h> +#include <hidl/HidlSupport.h> using android::sp; -using android::hardware::gatekeeper::V1_0::IGatekeeper; -using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode; -using android::hardware::gatekeeper::V1_0::GatekeeperResponse; using android::hardware::Return; +using android::hardware::gatekeeper::V1_0::GatekeeperResponse; +using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode; +using android::hardware::gatekeeper::V1_0::IGatekeeper; using ::android::binder::Status; using ::android::service::gatekeeper::BnGateKeeperService; using GKResponse = ::android::service::gatekeeper::GateKeeperResponse; using GKResponseCode = ::android::service::gatekeeper::ResponseCode; +using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType; +using ::aidl::android::hardware::security::keymint::HardwareAuthToken; +using ::aidl::android::security::authorization::IKeystoreAuthorization; namespace android { @@ -62,7 +64,7 @@ static const String16 DUMP_PERMISSION("android.permission.DUMP"); class GateKeeperProxy : public BnGateKeeperService { -public: + public: GateKeeperProxy() { clear_state_if_needed_done = false; hw_device = IGatekeeper::getService(); @@ -73,8 +75,7 @@ } } - virtual ~GateKeeperProxy() { - } + virtual ~GateKeeperProxy() {} void store_sid(uint32_t userId, uint64_t sid) { char filename[21]; @@ -96,7 +97,7 @@ if (mark_cold_boot() && !is_running_gsi) { ALOGI("cold boot: clearing state"); if (hw_device) { - hw_device->deleteAllUsers([](const GatekeeperResponse &){}); + hw_device->deleteAllUsers([](const GatekeeperResponse&) {}); } } @@ -104,7 +105,7 @@ } bool mark_cold_boot() { - const char *filename = ".coldboot"; + const char* filename = ".coldboot"; if (access(filename, F_OK) == -1) { int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { @@ -299,7 +300,34 @@ if (gkResponse->response_code() == GKResponseCode::OK) { if (gkResponse->payload().size() != 0) { + // try to connect to IKeystoreAuthorization AIDL service first. + ::ndk::SpAIBinder authzBinder( + AServiceManager_getService("android.security.authorization")); + auto authzService = IKeystoreAuthorization::fromBinder(authzBinder); + if (authzService) { + if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) { + LOG(ERROR) << "Incorrect size of AuthToken payload."; + return GK_ERROR; + } + + const hw_auth_token_t* hwAuthToken = + reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data()); + HardwareAuthToken authToken; + + authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp); + authToken.challenge = hwAuthToken->challenge; + authToken.authenticatorId = hwAuthToken->authenticator_id; + authToken.authenticatorType = static_cast<HardwareAuthenticatorType>( + betoh32(hwAuthToken->authenticator_type)); + authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]); + auto result = authzService->addAuthToken(authToken); + if (!result.isOk()) { + LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService."; + return GK_ERROR; + } + } sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("android.security.keystore")); sp<security::keystore::IKeystoreService> service = interface_cast<security::keystore::IKeystoreService>(binder); @@ -310,9 +338,12 @@ if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) { LOG(ERROR) << "Failure sending auth token to KeyStore: " << result; + return GK_ERROR; } } else { - LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with Keystore."; + LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with " + "Keystore."; + return GK_ERROR; } } @@ -366,23 +397,23 @@ } if (hw_device == NULL) { - const char *result = "Device not available"; + const char* result = "Device not available"; write(fd, result, strlen(result) + 1); } else { - const char *result = "OK"; + const char* result = "OK"; write(fd, result, strlen(result) + 1); } return OK; } -private: + private: sp<IGatekeeper> hw_device; bool clear_state_if_needed_done; bool is_running_gsi; }; -}// namespace android +} // namespace android int main(int argc, char* argv[]) { ALOGI("Starting gatekeeperd...");
diff --git a/healthd/Android.bp b/healthd/Android.bp index b3de9c4..251a45b 100644 --- a/healthd/Android.bp +++ b/healthd/Android.bp
@@ -62,29 +62,6 @@ srcs: [ "HealthServiceDefault.cpp", ], - - overrides: [ - "healthd", - ] -} - -cc_binary { - name: "healthd", - defaults: ["android.hardware.health@2.0-service_defaults"], - - init_rc: ["healthd.rc"], - srcs: [ - "HealthServiceHealthd.cpp", - ], - local_include_dirs: ["include"], - - shared_libs: [ - "android.hardware.health@1.0", - ], - - vintf_fragments: [ - "manifest_healthd.xml" - ], } cc_library_static {
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp deleted file mode 100644 index 5fd2597..0000000 --- a/healthd/HealthServiceHealthd.cpp +++ /dev/null
@@ -1,93 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "healthd" -#include <android-base/logging.h> - -#include <android/hardware/health/1.0/IHealth.h> -#include <android/hardware/health/1.0/types.h> -#include <hal_conversion.h> -#include <health2/service.h> -#include <healthd/healthd.h> -#include <hidl/HidlTransportSupport.h> - -using android::OK; -using android::NAME_NOT_FOUND; -using android::hardware::health::V1_0::HealthConfig; -using android::hardware::health::V1_0::HealthInfo; -using android::hardware::health::V1_0::Result; -using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig; -using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig; -using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo; -using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo; - -using IHealthLegacy = android::hardware::health::V1_0::IHealth; - -static android::sp<IHealthLegacy> gHealth_1_0; - -static int healthd_board_get_energy_counter(int64_t* energy) { - if (gHealth_1_0 == nullptr) { - return NAME_NOT_FOUND; - } - - Result result = Result::NOT_SUPPORTED; - gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) { - result = ret; - *energy = energyOut; - }); - - return result == Result::SUCCESS ? OK : NAME_NOT_FOUND; -} - -void healthd_board_init(struct healthd_config* config) { - gHealth_1_0 = IHealthLegacy::getService(); - - if (gHealth_1_0 == nullptr) { - return; - } - - HealthConfig halConfig{}; - convertToHealthConfig(config, halConfig); - gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) { - convertFromHealthConfig(halConfigOut, config); - // always redirect energy counter queries - config->energyCounter = healthd_board_get_energy_counter; - }); - LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL"; -} - -// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update -// with logthis returned. -int healthd_board_battery_update(struct android::BatteryProperties* props) { - int logthis = 0; - - if (gHealth_1_0 == nullptr) { - return logthis; - } - - HealthInfo info; - convertToHealthInfo(props, info); - gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) { - logthis = ret; - convertFromHealthInfo(infoOut, props); - }); - - return logthis; -} - -int main() { - return health_service_main("backup"); -}
diff --git a/healthd/manifest_healthd.xml b/healthd/manifest_healthd.xml deleted file mode 100644 index 097a7d8..0000000 --- a/healthd/manifest_healthd.xml +++ /dev/null
@@ -1,11 +0,0 @@ -<manifest version="1.0" type="framework"> - <hal> - <name>android.hardware.health</name> - <transport>hwbinder</transport> - <version>2.0</version> - <interface> - <name>IHealth</name> - <instance>backup</instance> - </interface> - </hal> -</manifest>
diff --git a/init/Android.bp b/init/Android.bp index 19ba21b..cd295cf 100644 --- a/init/Android.bp +++ b/init/Android.bp
@@ -60,6 +60,7 @@ "selabel.cpp", "selinux.cpp", "sigchld_handler.cpp", + "snapuserd_transition.cpp", "switch_root.cpp", "uevent_listener.cpp", "ueventd.cpp",
diff --git a/init/Android.mk b/init/Android.mk index c881e2f..561d641 100644 --- a/init/Android.mk +++ b/init/Android.mk
@@ -57,6 +57,8 @@ reboot_utils.cpp \ selabel.cpp \ selinux.cpp \ + service_utils.cpp \ + snapuserd_transition.cpp \ switch_root.cpp \ uevent_listener.cpp \ util.cpp \
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h index 79fe4ec..ec39ce0 100644 --- a/init/block_dev_initializer.h +++ b/init/block_dev_initializer.h
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#pragma once + #include <memory> #include <set> #include <string>
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp index 0949fc5..6954c03 100644 --- a/init/first_stage_init.cpp +++ b/init/first_stage_init.cpp
@@ -42,6 +42,7 @@ #include "first_stage_mount.h" #include "reboot_utils.h" #include "second_stage_resources.h" +#include "snapuserd_transition.h" #include "switch_root.h" #include "util.h" @@ -90,6 +91,12 @@ } } } + } else if (de->d_type == DT_REG) { + // Do not free snapuserd if we will need the ramdisk copy during the + // selinux transition. + if (de->d_name == "snapuserd"s && IsFirstStageSnapuserdRunning()) { + continue; + } } unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0); }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index a0511cc..7c46281 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp
@@ -44,6 +44,7 @@ #include "block_dev_initializer.h" #include "devices.h" +#include "snapuserd_transition.h" #include "switch_root.h" #include "uevent.h" #include "uevent_listener.h" @@ -87,6 +88,7 @@ protected: bool InitRequiredDevices(std::set<std::string> devices); bool CreateLogicalPartitions(); + bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm); bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, Fstab::iterator* end = nullptr); @@ -109,6 +111,7 @@ bool need_dm_verity_; bool dsu_not_on_userdata_ = false; + bool use_snapuserd_ = false; Fstab fstab_; // The super path is only set after InitDevices, and is invalid before. @@ -338,21 +341,7 @@ return false; } if (sm->NeedSnapshotsInFirstStageMount()) { - // When COW images are present for snapshots, they are stored on - // the data partition. - if (!InitRequiredDevices({"userdata"})) { - return false; - } - sm->SetUeventRegenCallback([this](const std::string& device) -> bool { - if (android::base::StartsWith(device, "/dev/block/dm-")) { - return block_dev_init_.InitDmDevice(device); - } - if (android::base::StartsWith(device, "/dev/dm-user/")) { - return block_dev_init_.InitDmUser(android::base::Basename(device)); - } - return block_dev_init_.InitDevices({device}); - }); - return sm->CreateLogicalAndSnapshotPartitions(super_path_); + return CreateSnapshotPartitions(sm.get()); } } @@ -367,6 +356,37 @@ return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_); } +bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) { + // When COW images are present for snapshots, they are stored on + // the data partition. + if (!InitRequiredDevices({"userdata"})) { + return false; + } + + use_snapuserd_ = sm->IsSnapuserdRequired(); + if (use_snapuserd_) { + LaunchFirstStageSnapuserd(); + } + + sm->SetUeventRegenCallback([this](const std::string& device) -> bool { + if (android::base::StartsWith(device, "/dev/block/dm-")) { + return block_dev_init_.InitDmDevice(device); + } + if (android::base::StartsWith(device, "/dev/dm-user/")) { + return block_dev_init_.InitDmUser(android::base::Basename(device)); + } + return block_dev_init_.InitDevices({device}); + }); + if (!sm->CreateLogicalAndSnapshotPartitions(super_path_)) { + return false; + } + + if (use_snapuserd_) { + CleanupSnapuserdSocket(); + } + return true; +} + bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, Fstab::iterator* end) { // Sets end to begin + 1, so we can just return on failure below. @@ -466,6 +486,10 @@ if (system_partition == fstab_.end()) return true; + if (use_snapuserd_) { + SaveRamdiskPathToSnapuserd(); + } + if (MountPartition(system_partition, false /* erase_same_mounts */)) { if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) { LOG(ERROR) << "check_most_at_once forbidden on external media";
diff --git a/init/init.cpp b/init/init.cpp index d1998a5..ca2d5da 100644 --- a/init/init.cpp +++ b/init/init.cpp
@@ -79,6 +79,7 @@ #include "service.h" #include "service_parser.h" #include "sigchld_handler.h" +#include "snapuserd_transition.h" #include "subcontext.h" #include "system/core/init/property_service.pb.h" #include "util.h" @@ -741,10 +742,15 @@ return {}; } svc->Start(); + svc->SetShutdownCritical(); - if (!sm->PerformSecondStageTransition()) { + if (!sm->PerformSecondStageInitTransition()) { LOG(FATAL) << "Failed to transition snapuserd to second-stage"; } + + if (auto pid = GetSnapuserdFirstStagePid()) { + KillFirstStageSnapuserd(pid.value()); + } return {}; } @@ -872,6 +878,21 @@ auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0"; SetProperty(gsi::kGsiInstalledProp, is_installed); + /* + * For debug builds of S launching devices, init mounts debugfs for + * enabling vendor debug data collection setup at boot time. Init will unmount it on + * boot-complete after vendor code has performed the required initializations + * during boot. Dumpstate will then mount debugfs in order to read data + * from the same using the dumpstate HAL during bugreport creation. + * Dumpstate will also unmount debugfs after bugreport creation. + * first_api_level comparison is done here instead + * of init.rc since init.rc parser does not support >/< operators. + */ + auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0); + bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false); + auto mount_debugfs = (is_debuggable && (api_level >= 31)) ? "1" : "0"; + SetProperty("init.mount_debugfs", mount_debugfs); + am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups"); am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict"); am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
diff --git a/init/property_service.cpp b/init/property_service.cpp index e71c386..ce67386 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp
@@ -890,6 +890,69 @@ } } +// If the ro.product.cpu.abilist* properties have not been explicitly +// set, derive them from ro.${partition}.product.cpu.abilist* properties. +static void property_initialize_ro_cpu_abilist() { + // From high to low priority. + const char* kAbilistSources[] = { + "product", + "odm", + "vendor", + "system", + }; + const std::string EMPTY = ""; + const char* kAbilistProp = "ro.product.cpu.abilist"; + const char* kAbilist32Prop = "ro.product.cpu.abilist32"; + const char* kAbilist64Prop = "ro.product.cpu.abilist64"; + + // If the properties are defined explicitly, just use them. + if (GetProperty(kAbilistProp, EMPTY) != EMPTY) { + return; + } + + // Find the first source defining these properties by order. + std::string abilist32_prop_val; + std::string abilist64_prop_val; + for (const auto& source : kAbilistSources) { + const auto abilist32_prop = std::string("ro.") + source + ".product.cpu.abilist32"; + const auto abilist64_prop = std::string("ro.") + source + ".product.cpu.abilist64"; + abilist32_prop_val = GetProperty(abilist32_prop, EMPTY); + abilist64_prop_val = GetProperty(abilist64_prop, EMPTY); + // The properties could be empty on 32-bit-only or 64-bit-only devices, + // but we cannot identify a property is empty or undefined by GetProperty(). + // So, we assume both of these 2 properties are empty as undefined. + if (abilist32_prop_val != EMPTY || abilist64_prop_val != EMPTY) { + break; + } + } + + // Merge ABI lists for ro.product.cpu.abilist + auto abilist_prop_val = abilist64_prop_val; + if (abilist32_prop_val != EMPTY) { + if (abilist_prop_val != EMPTY) { + abilist_prop_val += ","; + } + abilist_prop_val += abilist32_prop_val; + } + + // Set these properties + const std::pair<const char*, const std::string&> set_prop_list[] = { + {kAbilistProp, abilist_prop_val}, + {kAbilist32Prop, abilist32_prop_val}, + {kAbilist64Prop, abilist64_prop_val}, + }; + for (const auto& [prop, prop_val] : set_prop_list) { + LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'"; + + std::string error; + uint32_t res = PropertySet(prop, prop_val, &error); + if (res != PROP_SUCCESS) { + LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error + << ")"; + } + } +} + void PropertyLoadBootDefaults() { // We read the properties and their values into a map, in order to always allow properties // loaded in the later property files to override the properties in loaded in the earlier @@ -957,8 +1020,6 @@ load_properties_from_partition("odm", /* support_legacy_path_until */ 28); load_properties_from_partition("product", /* support_legacy_path_until */ 30); - load_properties_from_file("/factory/factory.prop", "ro.*", &properties); - if (access(kDebugRamdiskProp, R_OK) == 0) { LOG(INFO) << "Loading " << kDebugRamdiskProp; load_properties_from_file(kDebugRamdiskProp, nullptr, &properties); @@ -974,6 +1035,7 @@ property_initialize_ro_product_props(); property_derive_build_fingerprint(); + property_initialize_ro_cpu_abilist(); update_sys_usb_config(); }
diff --git a/init/selinux.cpp b/init/selinux.cpp index f03ca6b..0336936 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp
@@ -45,7 +45,7 @@ // 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them) // have been updated out of sync with /vendor (or /odm if it is present) and the init needs to // compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the -// LoadSplitPolicy() function below to compile the SEPolicy to a temp directory and load it. +// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it. // That function contains even more documentation with the specific implementation details of how // the SEPolicy is compiled if needed. @@ -74,6 +74,7 @@ #include "block_dev_initializer.h" #include "debug_ramdisk.h" #include "reboot_utils.h" +#include "snapuserd_transition.h" #include "util.h" using namespace std::string_literals; @@ -298,7 +299,12 @@ return access(plat_policy_cil_file, R_OK) != -1; } -bool LoadSplitPolicy() { +struct PolicyFile { + unique_fd fd; + std::string path; +}; + +bool OpenSplitPolicy(PolicyFile* policy_file) { // IMPLEMENTATION NOTE: Split policy consists of three CIL files: // * platform -- policy needed due to logic contained in the system image, // * non-platform -- policy needed due to logic contained in the vendor image, @@ -325,10 +331,8 @@ if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) { unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)); if (fd != -1) { - if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) { - LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file; - return false; - } + policy_file->fd = std::move(fd); + policy_file->path = std::move(precompiled_sepolicy_file); return true; } } @@ -446,34 +450,39 @@ } unlink(compiled_sepolicy); - LOG(INFO) << "Loading compiled SELinux policy"; - if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) { - LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy; - return false; - } - + policy_file->fd = std::move(compiled_sepolicy_fd); + policy_file->path = compiled_sepolicy; return true; } -bool LoadMonolithicPolicy() { - LOG(VERBOSE) << "Loading SELinux policy from monolithic file"; - if (selinux_android_load_policy() < 0) { - PLOG(ERROR) << "Failed to load monolithic SELinux policy"; +bool OpenMonolithicPolicy(PolicyFile* policy_file) { + static constexpr char kSepolicyFile[] = "/sepolicy"; + + LOG(VERBOSE) << "Opening SELinux policy from monolithic file"; + policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (policy_file->fd < 0) { + PLOG(ERROR) << "Failed to open monolithic SELinux policy"; return false; } + policy_file->path = kSepolicyFile; return true; } -bool LoadPolicy() { - return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy(); -} +void ReadPolicy(std::string* policy) { + PolicyFile policy_file; -void SelinuxInitialize() { - LOG(INFO) << "Loading SELinux policy"; - if (!LoadPolicy()) { - LOG(FATAL) << "Unable to load SELinux policy"; + bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file) + : OpenMonolithicPolicy(&policy_file); + if (!ok) { + LOG(FATAL) << "Unable to open SELinux policy"; } + if (!android::base::ReadFdToString(policy_file.fd, policy)) { + PLOG(FATAL) << "Failed to read policy file: " << policy_file.path; + } +} + +void SelinuxSetEnforcement() { bool kernel_enforcing = (security_getenforce() == 1); bool is_enforcing = IsEnforcing(); if (kernel_enforcing != is_enforcing) { @@ -670,6 +679,30 @@ } } +static void LoadSelinuxPolicy(std::string& policy) { + LOG(INFO) << "Loading SELinux policy"; + + set_selinuxmnt("/sys/fs/selinux"); + if (security_load_policy(policy.data(), policy.size()) < 0) { + PLOG(FATAL) << "SELinux: Could not load policy"; + } +} + +// The SELinux setup process is carefully orchestrated around snapuserd. Policy +// must be loaded off dynamic partitions, and during an OTA, those partitions +// cannot be read without snapuserd. But, with kernel-privileged snapuserd +// running, loading the policy will immediately trigger audits. +// +// We use a five-step process to address this: +// (1) Read the policy into a string, with snapuserd running. +// (2) Rewrite the snapshot device-mapper tables, to generate new dm-user +// devices and to flush I/O. +// (3) Kill snapuserd, which no longer has any dm-user devices to attach to. +// (4) Load the sepolicy and issue critical restorecons in /dev, carefully +// avoiding anything that would read from /system. +// (5) Re-launch snapuserd and attach it to the dm-user devices from step (2). +// +// After this sequence, it is safe to enable enforcing mode and continue booting. int SetupSelinux(char** argv) { SetStdioToDevNull(argv); InitKernelLogging(argv); @@ -682,9 +715,31 @@ MountMissingSystemPartitions(); - // Set up SELinux, loading the SELinux policy. SelinuxSetupKernelLogging(); - SelinuxInitialize(); + + LOG(INFO) << "Opening SELinux policy"; + + // Read the policy before potentially killing snapuserd. + std::string policy; + ReadPolicy(&policy); + + auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded(); + if (snapuserd_helper) { + // Kill the old snapused to avoid audit messages. After this we cannot + // read from /system (or other dynamic partitions) until we call + // FinishTransition(). + snapuserd_helper->StartTransition(); + } + + LoadSelinuxPolicy(policy); + + if (snapuserd_helper) { + // Before enforcing, finish the pending snapuserd transition. + snapuserd_helper->FinishTransition(); + snapuserd_helper = nullptr; + } + + SelinuxSetEnforcement(); // We're in the kernel domain and want to transition to the init domain. File systems that // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp new file mode 100644 index 0000000..19b5c57 --- /dev/null +++ b/init/snapuserd_transition.cpp
@@ -0,0 +1,305 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "snapuserd_transition.h" + +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/syscall.h> +#include <sys/xattr.h> +#include <unistd.h> + +#include <filesystem> +#include <string> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/parseint.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <cutils/sockets.h> +#include <libsnapshot/snapshot.h> +#include <libsnapshot/snapuserd_client.h> +#include <private/android_filesystem_config.h> +#include <selinux/android.h> + +#include "block_dev_initializer.h" +#include "service_utils.h" +#include "util.h" + +namespace android { +namespace init { + +using namespace std::string_literals; + +using android::base::unique_fd; +using android::snapshot::SnapshotManager; +using android::snapshot::SnapuserdClient; + +static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd"; +static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID"; +static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD"; +static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0"; +static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0"; + +void LaunchFirstStageSnapuserd() { + SocketDescriptor socket_desc; + socket_desc.name = android::snapshot::kSnapuserdSocket; + socket_desc.type = SOCK_STREAM; + socket_desc.perm = 0660; + socket_desc.uid = AID_SYSTEM; + socket_desc.gid = AID_SYSTEM; + + // We specify a label here even though it technically is not needed. During + // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded, + // we bypass the socket entirely. + auto socket = socket_desc.Create(kSnapuserdSocketLabel); + if (!socket.ok()) { + LOG(FATAL) << "Could not create snapuserd socket: " << socket.error(); + } + + pid_t pid = fork(); + if (pid < 0) { + PLOG(FATAL) << "Cannot launch snapuserd; fork failed"; + } + if (pid == 0) { + socket->Publish(); + char arg0[] = "/system/bin/snapuserd"; + char* const argv[] = {arg0, nullptr}; + if (execv(arg0, argv) < 0) { + PLOG(FATAL) << "Cannot launch snapuserd; execv failed"; + } + _exit(127); + } + + setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); + + LOG(INFO) << "Relaunched snapuserd with pid: " << pid; +} + +std::optional<pid_t> GetSnapuserdFirstStagePid() { + const char* pid_str = getenv(kSnapuserdFirstStagePidVar); + if (!pid_str) { + return {}; + } + + int pid = 0; + if (!android::base::ParseInt(pid_str, &pid)) { + LOG(FATAL) << "Could not parse pid in environment, " << kSnapuserdFirstStagePidVar << "=" + << pid_str; + } + return {pid}; +} + +static void RelabelLink(const std::string& link) { + selinux_android_restorecon(link.c_str(), 0); + + std::string path; + if (android::base::Readlink(link, &path)) { + selinux_android_restorecon(path.c_str(), 0); + } +} + +static void RelabelDeviceMapper() { + selinux_android_restorecon("/dev/device-mapper", 0); + + std::error_code ec; + for (auto& iter : std::filesystem::directory_iterator("/dev/block", ec)) { + const auto& path = iter.path(); + if (android::base::StartsWith(path.string(), "/dev/block/dm-")) { + selinux_android_restorecon(path.string().c_str(), 0); + } + } +} + +static std::optional<int> GetRamdiskSnapuserdFd() { + const char* fd_str = getenv(kSnapuserdFirstStageFdVar); + if (!fd_str) { + return {}; + } + + int fd; + if (!android::base::ParseInt(fd_str, &fd)) { + LOG(FATAL) << "Could not parse fd in environment, " << kSnapuserdFirstStageFdVar << "=" + << fd_str; + } + return {fd}; +} + +void RestoreconRamdiskSnapuserd(int fd) { + if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) { + PLOG(FATAL) << "fsetxattr snapuserd failed"; + } +} + +SnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid) + : sm_(std::move(sm)), old_pid_(old_pid) { + // Only dm-user device names change during transitions, so the other + // devices are expected to be present. + sm_->SetUeventRegenCallback([this](const std::string& device) -> bool { + if (android::base::StartsWith(device, "/dev/dm-user/")) { + return block_dev_init_.InitDmUser(android::base::Basename(device)); + } + return true; + }); +} + +void SnapuserdSelinuxHelper::StartTransition() { + LOG(INFO) << "Starting SELinux transition of snapuserd"; + + // The restorecon path reads from /system etc, so make sure any reads have + // been cached before proceeding. + auto handle = selinux_android_file_context_handle(); + if (!handle) { + LOG(FATAL) << "Could not create SELinux file context handle"; + } + selinux_android_set_sehandle(handle); + + // We cannot access /system after the transition, so make sure init is + // pinned in memory. + if (mlockall(MCL_CURRENT) < 0) { + LOG(FATAL) << "mlockall failed"; + } + + argv_.emplace_back("snapuserd"); + argv_.emplace_back("-no_socket"); + if (!sm_->DetachSnapuserdForSelinux(&argv_)) { + LOG(FATAL) << "Could not perform selinux transition"; + } + + // Make sure the process is gone so we don't have any selinux audits. + KillFirstStageSnapuserd(old_pid_); +} + +void SnapuserdSelinuxHelper::FinishTransition() { + RelabelLink("/dev/block/by-name/super"); + RelabelDeviceMapper(); + + selinux_android_restorecon("/dev/null", 0); + selinux_android_restorecon("/dev/urandom", 0); + selinux_android_restorecon("/dev/kmsg", 0); + selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE); + + RelaunchFirstStageSnapuserd(); + + if (munlockall() < 0) { + PLOG(ERROR) << "munlockall failed"; + } +} + +void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { + auto fd = GetRamdiskSnapuserdFd(); + if (!fd) { + LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!"; + } + unsetenv(kSnapuserdFirstStageFdVar); + + RestoreconRamdiskSnapuserd(fd.value()); + + pid_t pid = fork(); + if (pid < 0) { + PLOG(FATAL) << "Fork to relaunch snapuserd failed"; + } + if (pid > 0) { + // We don't need the descriptor anymore, and it should be closed to + // avoid leaking into subprocesses. + close(fd.value()); + + setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); + + LOG(INFO) << "Relaunched snapuserd with pid: " << pid; + return; + } + + // Make sure the descriptor is gone after we exec. + if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) { + PLOG(FATAL) << "fcntl FD_CLOEXEC failed for snapuserd fd"; + } + + std::vector<char*> argv; + for (auto& arg : argv_) { + argv.emplace_back(arg.data()); + } + argv.emplace_back(nullptr); + + int rv = syscall(SYS_execveat, fd.value(), "", reinterpret_cast<char* const*>(argv.data()), + nullptr, AT_EMPTY_PATH); + if (rv < 0) { + PLOG(FATAL) << "Failed to execveat() snapuserd"; + } +} + +std::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() { + if (IsRecoveryMode()) { + return nullptr; + } + + auto old_pid = GetSnapuserdFirstStagePid(); + if (!old_pid) { + return nullptr; + } + + auto sm = SnapshotManager::NewForFirstStageMount(); + if (!sm) { + LOG(FATAL) << "Unable to create SnapshotManager"; + } + return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value()); +} + +void KillFirstStageSnapuserd(pid_t pid) { + if (kill(pid, SIGTERM) < 0 && errno != ESRCH) { + LOG(ERROR) << "Kill snapuserd pid failed: " << pid; + } else { + LOG(INFO) << "Sent SIGTERM to snapuserd process " << pid; + } +} + +void CleanupSnapuserdSocket() { + auto socket_path = ANDROID_SOCKET_DIR "/"s + android::snapshot::kSnapuserdSocket; + if (access(socket_path.c_str(), F_OK) != 0) { + return; + } + + // Tell the daemon to stop accepting connections and to gracefully exit + // once all outstanding handlers have terminated. + if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) { + client->DetachSnapuserd(); + } + + // Unlink the socket so we can create it again in second-stage. + if (unlink(socket_path.c_str()) < 0) { + PLOG(FATAL) << "unlink " << socket_path << " failed"; + } +} + +void SaveRamdiskPathToSnapuserd() { + int fd = open(kSnapuserdPath, O_PATH); + if (fd < 0) { + PLOG(FATAL) << "Unable to open snapuserd: " << kSnapuserdPath; + } + + auto value = std::to_string(fd); + if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) { + PLOG(FATAL) << "setenv failed: " << kSnapuserdFirstStageFdVar << "=" << value; + } +} + +bool IsFirstStageSnapuserdRunning() { + return GetSnapuserdFirstStagePid().has_value(); +} + +} // namespace init +} // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h new file mode 100644 index 0000000..a5ab652 --- /dev/null +++ b/init/snapuserd_transition.h
@@ -0,0 +1,87 @@ +/* + * 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. + */ + +#pragma once + +#include <sys/types.h> + +#include <optional> +#include <string> +#include <vector> + +#include <libsnapshot/snapshot.h> + +#include "block_dev_initializer.h" + +namespace android { +namespace init { + +// Fork and exec a new copy of snapuserd. +void LaunchFirstStageSnapuserd(); + +class SnapuserdSelinuxHelper final { + using SnapshotManager = android::snapshot::SnapshotManager; + + public: + SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid); + + void StartTransition(); + void FinishTransition(); + + // Return a helper for facilitating the selinux transition of snapuserd. + // If snapuserd is not in use, null is returned. StartTransition() should + // be called after reading policy. FinishTransition() should be called + // after loading policy. In between, no reads of /system or other dynamic + // partitions are possible. + static std::unique_ptr<SnapuserdSelinuxHelper> CreateIfNeeded(); + + private: + void RelaunchFirstStageSnapuserd(); + void ExecSnapuserd(); + + std::unique_ptr<SnapshotManager> sm_; + BlockDevInitializer block_dev_init_; + pid_t old_pid_; + std::vector<std::string> argv_; +}; + +// Remove /dev/socket/snapuserd. This ensures that (1) the existing snapuserd +// will receive no new requests, and (2) the next copy we transition to can +// own the socket. +void CleanupSnapuserdSocket(); + +// Kill an instance of snapuserd given a pid. +void KillFirstStageSnapuserd(pid_t pid); + +// Save an open fd to /system/bin (in the ramdisk) into an environment. This is +// used to later execveat() snapuserd. +void SaveRamdiskPathToSnapuserd(); + +// Returns true if first-stage snapuserd is running. +bool IsFirstStageSnapuserdRunning(); + +// Return the pid of the first-stage instances of snapuserd, if it was started. +std::optional<pid_t> GetSnapuserdFirstStagePid(); + +// Save an open fd to /system/bin (in the ramdisk) into an environment. This is +// used to later execveat() snapuserd. +void SaveRamdiskPathToSnapuserd(); + +// Returns true if first-stage snapuserd is running. +bool IsFirstStageSnapuserdRunning(); + +} // namespace init +} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp index cf809f1..d46aeab 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp
@@ -210,9 +210,6 @@ "uevent.cpp", ], }, - bionic: { - header_libs: ["bionic_libc_platform_headers"], - }, android_arm: { sanitize: {
diff --git a/libcutils/memory.cpp b/libcutils/memory.cpp index f526520..5a410c2 100644 --- a/libcutils/memory.cpp +++ b/libcutils/memory.cpp
@@ -18,19 +18,18 @@ #include <log/log.h> -#ifdef __BIONIC__ -#include <bionic/malloc.h> +#if !defined(__APPLE__) +#include <malloc.h> #endif void process_disable_memory_mitigations() { bool success = false; #ifdef __BIONIC__ - // TODO(b/158870657) is fixed and scudo is used globally, we can assert when an - // an error is returned. - - success = android_mallopt(M_DISABLE_MEMORY_MITIGATIONS, nullptr, 0); + success = mallopt(M_BIONIC_DISABLE_MEMORY_MITIGATIONS, 0); #endif + // TODO: if b/158870657 is fixed and scudo is used globally, + // we can assert on failure rather than just log. if (success) { ALOGI("Disabled memory mitigations for process."); } else {
diff --git a/rootdir/init.rc b/rootdir/init.rc index 4478bea..049301d 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc
@@ -934,6 +934,9 @@ write /proc/sys/vm/dirty_expire_centisecs 200 write /proc/sys/vm/dirty_background_ratio 5 +on property:sys.boot_completed=1 && property:init.mount_debugfs=1 + umount /sys/kernel/debug + on boot # basic network init ifup lo @@ -1202,6 +1205,10 @@ on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1 setprop sys.init.userspace_reboot.in_progress "" +on early-init && property:init.mount_debugfs=1 + mount debugfs debugfs /sys/kernel/debug + chmod 0755 /sys/kernel/debug + # Migrate tasks again in case kernel threads are created during boot on property:sys.boot_completed=1 copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index a1e9b12..64a64d2 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc
@@ -48,6 +48,7 @@ /dev/pmsg0 0222 root log /dev/dma_heap/system 0444 system system /dev/dma_heap/system-uncached 0444 system system +/dev/dma_heap/system-secure 0444 system system # kms driver for drm based gpu /dev/dri/* 0666 root graphics
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp index 0819c21..635966f 100644 --- a/trusty/confirmationui/fuzz/Android.bp +++ b/trusty/confirmationui/fuzz/Android.bp
@@ -16,4 +16,8 @@ name: "trusty_confirmationui_fuzzer", defaults: ["trusty_fuzzer_defaults"], srcs: ["fuzz.cpp"], + + // The initial corpus for this fuzzer was derived by dumping bytes from + // ConfirmationUI VTS. + corpus: ["corpus/*"], }
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2 b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2 new file mode 100644 index 0000000..53fe0c9 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2 Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f b/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f new file mode 100644 index 0000000..d627b01 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq new file mode 100644 index 0000000..bda80fd --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3 b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3 new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3 Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR new file mode 100644 index 0000000..f5854f8 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T new file mode 100644 index 0000000..0dc6e91 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO new file mode 100644 index 0000000..927d64d --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-globJV b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD new file mode 100644 index 0000000..87870ca --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o new file mode 100644 index 0000000..0b274bf --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t new file mode 100644 index 0000000..87870ca --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI new file mode 100644 index 0000000..87870ca --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks new file mode 100644 index 0000000..b4a1c49 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN new file mode 100644 index 0000000..5adf905 --- /dev/null +++ b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN Binary files differ
diff --git a/trusty/confirmationui/fuzz/fuzz.cpp b/trusty/confirmationui/fuzz/fuzz.cpp index aa132e8..df2517c 100644 --- a/trusty/confirmationui/fuzz/fuzz.cpp +++ b/trusty/confirmationui/fuzz/fuzz.cpp
@@ -14,10 +14,7 @@ * limitations under the License. */ -#undef NDEBUG - -#include <assert.h> -#include <log/log.h> +#include <iostream> #include <stdlib.h> #include <trusty/coverage/coverage.h> #include <trusty/fuzz/counters.h> @@ -30,6 +27,7 @@ #define TIPC_DEV "/dev/trusty-ipc-dev0" #define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui" +#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf" /* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */ static struct uuid confirmationui_uuid = { @@ -39,16 +37,30 @@ {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b}, }; -static CoverageRecord record(TIPC_DEV, &confirmationui_uuid); +/* The format of the packets is as following: + * 16 bits (uint16_t, header) + payload bytes + * The 16 bits header spicify the number of bytes of payload (header excluded). + */ +struct data_packet { + uint16_t header; + uint8_t payload[]; +}; + +static CoverageRecord record(TIPC_DEV, &confirmationui_uuid, CONFIRMATIONUI_MODULE_NAME); extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) { auto ret = record.Open(); - assert(ret.ok()); + if (!ret.ok()) { + std::cerr << ret.error() << std::endl; + exit(-1); + } return 0; } +/* Each corpus contains one or more data packets. */ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static uint8_t buf[TIPC_MAX_MSG_SIZE]; + size_t data_idx = 0; ExtraCounters counters(&record); counters.Reset(); @@ -59,16 +71,22 @@ android::trusty::fuzz::Abort(); } - /* Write message to confirmationui server */ - ret = ta.Write(data, size); - if (!ret.ok()) { - return -1; - } + while (data_idx < size) { + struct data_packet* data_packet_ptr = (struct data_packet*)&data[data_idx]; + size_t payload_size = data_packet_ptr->header; + data_idx += data_packet_ptr->header + sizeof(data_packet_ptr->header); - /* Read message from confirmationui server */ - ret = ta.Read(&buf, sizeof(buf)); - if (!ret.ok()) { - return -1; + /* Write message to confirmationui server */ + ret = ta.Write(data_packet_ptr->payload, payload_size); + if (!ret.ok()) { + return -1; + } + + /* Read message from confirmationui server */ + ret = ta.Read(&buf, sizeof(buf)); + if (!ret.ok()) { + return -1; + } } return 0;
diff --git a/trusty/coverage/Android.bp b/trusty/coverage/Android.bp index 6038d44..daa6f03 100644 --- a/trusty/coverage/Android.bp +++ b/trusty/coverage/Android.bp
@@ -28,6 +28,7 @@ shared_libs: [ "libbase", "liblog", + "libdmabufheap", ], } @@ -43,6 +44,7 @@ shared_libs: [ "libbase", "liblog", + "libdmabufheap", ], require_root: true, }
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp index f383dd1..5eccdc5 100644 --- a/trusty/coverage/coverage.cpp +++ b/trusty/coverage/coverage.cpp
@@ -16,10 +16,12 @@ #define LOG_TAG "coverage" +#include <BufferAllocator/BufferAllocator.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <assert.h> +#include <log/log.h> #include <stdio.h> #include <sys/mman.h> #include <sys/uio.h> @@ -37,6 +39,8 @@ using android::base::ErrnoError; using android::base::Error; using std::string; +using std::to_string; +using std::unique_ptr; static inline uintptr_t RoundPageUp(uintptr_t val) { return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); @@ -46,12 +50,29 @@ : tipc_dev_(std::move(tipc_dev)), coverage_srv_fd_(-1), uuid_(*uuid), + sancov_filename_(), + record_len_(0), + shm_(NULL), + shm_len_(0) {} + +CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid, string module_name) + : tipc_dev_(std::move(tipc_dev)), + coverage_srv_fd_(-1), + uuid_(*uuid), + sancov_filename_(module_name + "." + to_string(getpid()) + ".sancov"), record_len_(0), shm_(NULL), shm_len_(0) {} CoverageRecord::~CoverageRecord() { if (shm_) { + if (sancov_filename_) { + auto res = SaveSancovFile(*sancov_filename_); + if (!res.ok()) { + ALOGE("Could not write sancov file for module: %s\n", sancov_filename_->c_str()); + } + } + munmap((void*)shm_, shm_len_); } } @@ -114,24 +135,23 @@ record_len_ = resp.open_args.record_len; shm_len_ = RoundPageUp(record_len_); - fd = memfd_create("trusty-coverage", 0); + BufferAllocator allocator; + + fd = allocator.Alloc("system", shm_len_); if (fd < 0) { - return ErrnoError() << "failed to create memfd: "; + return ErrnoError() << "failed to create dmabuf of size " << shm_len_ + << " err code: " << fd; } - unique_fd memfd(fd); + unique_fd dma_buf(fd); - if (ftruncate(memfd, shm_len_) < 0) { - return ErrnoError() << "failed to resize memfd: "; - } - - void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); + void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0); if (shm == MAP_FAILED) { return ErrnoError() << "failed to map memfd: "; } req.hdr.cmd = COVERAGE_CLIENT_CMD_SHARE_RECORD; req.share_record_args.shm_len = shm_len_; - ret = Rpc(&req, memfd, &resp); + ret = Rpc(&req, dma_buf, &resp); if (!ret.ok()) { return Error() << "failed to send shared memory: "; }
diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h index b6d46eb..5da68da 100644 --- a/trusty/coverage/include/trusty/coverage/coverage.h +++ b/trusty/coverage/include/trusty/coverage/coverage.h
@@ -16,6 +16,7 @@ #pragma once +#include <optional> #include <string> #include <android-base/result.h> @@ -32,7 +33,18 @@ class CoverageRecord { public: + /** + * Create a coverage record interface. Coverage will not be written to a + * sancov output file on completion. + */ CoverageRecord(std::string tipc_dev, struct uuid* uuid); + + /** + * Create a coverage record interface. On destruction, write this coverage + * to the given sancov filename. + */ + CoverageRecord(std::string tipc_dev, struct uuid* uuid, std::string module_name); + ~CoverageRecord(); Result<void> Open(); void ResetFullRecord(); @@ -58,6 +70,7 @@ std::string tipc_dev_; unique_fd coverage_srv_fd_; struct uuid uuid_; + std::optional<std::string> sancov_filename_; size_t record_len_; volatile void* shm_; size_t shm_len_;
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp index 8c79475..1e863ac 100644 --- a/trusty/fuzz/counters.cpp +++ b/trusty/fuzz/counters.cpp
@@ -21,6 +21,7 @@ #include <trusty/fuzz/counters.h> #include <android-base/logging.h> +#include <log/log.h> #include <trusty/coverage/coverage.h> #include <trusty/coverage/tipc.h> @@ -32,7 +33,8 @@ * We don't know how many counters the coverage record will contain. So, eyeball * the size of this section. */ -__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[PAGE_SIZE]; +static const size_t kMaxNumCounters = 0x4000; +__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters]; namespace android { namespace trusty { @@ -62,8 +64,16 @@ volatile uint8_t* end = NULL; record_->GetRawCounts(&begin, &end); + if (!begin || !end) { + ALOGE("Could not get raw counts from coverage record\n"); + return; + } size_t num_counters = end - begin; + if (num_counters > kMaxNumCounters) { + ALOGE("Too many counters (%zu) to fit in the extra counters section!\n", num_counters); + num_counters = kMaxNumCounters; + } for (size_t i = 0; i < num_counters; i++) { *(counters + i) = *(begin + i); }
diff --git a/trusty/fuzz/test/fuzz.cpp b/trusty/fuzz/test/fuzz.cpp index 28bb3f7..e7913db 100644 --- a/trusty/fuzz/test/fuzz.cpp +++ b/trusty/fuzz/test/fuzz.cpp
@@ -14,15 +14,12 @@ * limitations under the License. */ -#undef NDEBUG - -#include <assert.h> -#include <log/log.h> #include <stdlib.h> #include <trusty/coverage/coverage.h> #include <trusty/fuzz/counters.h> #include <trusty/fuzz/utils.h> #include <unistd.h> +#include <iostream> using android::trusty::coverage::CoverageRecord; using android::trusty::fuzz::ExtraCounters; @@ -43,7 +40,10 @@ extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) { auto ret = record.Open(); - assert(ret.ok()); + if (!ret.ok()) { + std::cerr << ret.error() << std::endl; + exit(-1); + } return 0; }
diff --git a/trusty/gatekeeper/fuzz/fuzz.cpp b/trusty/gatekeeper/fuzz/fuzz.cpp index c0e8abb..7bfd7d1 100644 --- a/trusty/gatekeeper/fuzz/fuzz.cpp +++ b/trusty/gatekeeper/fuzz/fuzz.cpp
@@ -14,15 +14,12 @@ * limitations under the License. */ -#undef NDEBUG - -#include <assert.h> -#include <log/log.h> #include <stdlib.h> #include <trusty/coverage/coverage.h> #include <trusty/fuzz/counters.h> #include <trusty/fuzz/utils.h> #include <unistd.h> +#include <iostream> using android::trusty::coverage::CoverageRecord; using android::trusty::fuzz::ExtraCounters; @@ -30,6 +27,7 @@ #define TIPC_DEV "/dev/trusty-ipc-dev0" #define GATEKEEPER_PORT "com.android.trusty.gatekeeper" +#define GATEKEEPER_MODULE_NAME "gatekeeper.syms.elf" /* Gatekeeper TA's UUID is 38ba0cdc-df0e-11e4-9869-233fb6ae4795 */ static struct uuid gatekeeper_uuid = { @@ -39,11 +37,14 @@ {0x98, 0x69, 0x23, 0x3f, 0xb6, 0xae, 0x47, 0x95}, }; -static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid); +static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid, GATEKEEPER_MODULE_NAME); extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) { auto ret = record.Open(); - assert(ret.ok()); + if (!ret.ok()) { + std::cerr << ret.error() << std::endl; + exit(-1); + } return 0; }
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp new file mode 100644 index 0000000..da9f9ec --- /dev/null +++ b/trusty/keymaster/fuzz/Android.bp
@@ -0,0 +1,19 @@ +// 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. + +cc_fuzz { + name: "trusty_keymaster_fuzzer", + defaults: ["trusty_fuzzer_defaults"], + srcs: ["fuzz.cpp"], +}
diff --git a/trusty/keymaster/fuzz/fuzz.cpp b/trusty/keymaster/fuzz/fuzz.cpp new file mode 100644 index 0000000..4ac97bb --- /dev/null +++ b/trusty/keymaster/fuzz/fuzz.cpp
@@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <trusty/coverage/coverage.h> +#include <trusty/fuzz/counters.h> +#include <trusty/fuzz/utils.h> +#include <unistd.h> +#include <iostream> + +using android::trusty::coverage::CoverageRecord; +using android::trusty::fuzz::ExtraCounters; +using android::trusty::fuzz::TrustyApp; + +#define TIPC_DEV "/dev/trusty-ipc-dev0" +#define KEYMASTER_PORT "com.android.trusty.keymaster" +#define KEYMASTER_MODULE_FILENAME "keymaster.syms.elf" + +/* Keymaster TA's UUID is 5f902ace-5e5c-4cd8-ae54-87b88c22ddaf */ +static struct uuid keymaster_uuid = { + 0x5f902ace, + 0x5e5c, + 0x4cd8, + {0xae, 0x54, 0x87, 0xb8, 0x8c, 0x22, 0xdd, 0xaf}, +}; + +static CoverageRecord record(TIPC_DEV, &keymaster_uuid, KEYMASTER_MODULE_FILENAME); + +extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) { + auto ret = record.Open(); + if (!ret.ok()) { + std::cerr << ret.error() << std::endl; + exit(-1); + } + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static uint8_t buf[TIPC_MAX_MSG_SIZE]; + + ExtraCounters counters(&record); + counters.Reset(); + + android::trusty::fuzz::TrustyApp ta(TIPC_DEV, KEYMASTER_PORT); + auto ret = ta.Connect(); + if (!ret.ok()) { + android::trusty::fuzz::Abort(); + } + + /* Send message to test server */ + ret = ta.Write(data, size); + if (!ret.ok()) { + return -1; + } + + /* Read message from test server */ + ret = ta.Read(&buf, sizeof(buf)); + if (!ret.ok()) { + return -1; + } + + return 0; +}