Merge UP1A.230503.003

Change-Id: I5f017b091cb2e8a9512d7812796f59a98b44cce6
This commit is contained in:
Erik Sanders 2023-05-05 21:52:36 +00:00
commit 7cbba88517
18 changed files with 965 additions and 103 deletions

View File

@ -20,6 +20,7 @@
#include <fcntl.h>
#include <linux/prctl.h>
#include <malloc.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/mman.h>
@ -2703,3 +2704,58 @@ TEST_F(CrasherTest, verify_build_id) {
}
ASSERT_TRUE(found_valid_elf) << "Did not find any elf files with valid BuildIDs to check.";
}
const char kLogMessage[] = "Should not see this log message.";
// Verify that the logd process does not read the log.
TEST_F(CrasherTest, logd_skips_reading_logs) {
StartProcess([]() {
pthread_setname_np(pthread_self(), "logd");
LOG(INFO) << kLogMessage;
abort();
});
unique_fd output_fd;
StartIntercept(&output_fd);
FinishCrasher();
AssertDeath(SIGABRT);
int intercept_result;
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
std::string result;
ConsumeFd(std::move(output_fd), &result);
// logd should not contain our log message.
ASSERT_NOT_MATCH(result, kLogMessage);
}
// Verify that the logd process does not read the log when the non-main
// thread crashes.
TEST_F(CrasherTest, logd_skips_reading_logs_not_main_thread) {
StartProcess([]() {
pthread_setname_np(pthread_self(), "logd");
LOG(INFO) << kLogMessage;
std::thread thread([]() {
pthread_setname_np(pthread_self(), "not_logd_thread");
// Raise the signal on the side thread.
raise_debugger_signal(kDebuggerdTombstone);
});
thread.join();
_exit(0);
});
unique_fd output_fd;
StartIntercept(&output_fd, kDebuggerdTombstone);
FinishCrasher();
AssertDeath(0);
int intercept_result;
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
ASSERT_NOT_MATCH(result, kLogMessage);
}

View File

@ -690,7 +690,14 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder*
// Only dump logs on debuggable devices.
if (android::base::GetBoolProperty("ro.debuggable", false)) {
dump_logcat(&result, main_thread.pid);
// Get the thread that corresponds to the main pid of the process.
const ThreadInfo& thread = threads.at(main_thread.pid);
// Do not attempt to dump logs of the logd process because the gathering
// of logs can hang until a timeout occurs.
if (thread.thread_name != "logd") {
dump_logcat(&result, main_thread.pid);
}
}
dump_open_fds(&result, open_files);

View File

@ -381,6 +381,7 @@ cc_test_host {
"socket_mock.cpp",
"socket_test.cpp",
"super_flash_helper_test.cpp",
"task_test.cpp",
"tcp_test.cpp",
"udp_test.cpp",
],

View File

@ -115,19 +115,6 @@ static bool g_disable_verification = false;
fastboot::FastBootDriver* fb = nullptr;
enum fb_buffer_type {
FB_BUFFER_FD,
FB_BUFFER_SPARSE,
};
struct fastboot_buffer {
enum fb_buffer_type type;
std::vector<SparsePtr> files;
int64_t sz;
unique_fd fd;
int64_t image_size;
};
static std::vector<Image> images = {
// clang-format off
{ "boot", "boot.img", "boot.sig", "boot", false, ImageType::BootCritical },
@ -1284,7 +1271,7 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer* buf,
}
}
static std::string get_current_slot() {
std::string get_current_slot() {
std::string current_slot;
if (fb->GetVar("current-slot", &current_slot) != fastboot::SUCCESS) return "";
if (current_slot[0] == '_') current_slot.erase(0, 1);
@ -1560,7 +1547,7 @@ static void CancelSnapshotIfNeeded() {
}
}
std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot) {
std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot) {
auto slot = entry.second;
if (slot.empty()) {
slot = current_slot;
@ -1574,23 +1561,215 @@ std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot)
return entry.first->part_name + "_" + slot;
}
class FlashAllTool {
public:
FlashAllTool(FlashingPlan* fp);
std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts) {
bool apply_vbmeta = false;
std::string slot = fp->slot_override;
std::string partition;
std::string img_name;
for (auto& part : parts) {
if (part == "--apply-vbmeta") {
apply_vbmeta = true;
} else if (part == "--slot-other") {
slot = fp->secondary_slot;
} else if (partition.empty()) {
partition = part;
} else if (img_name.empty()) {
img_name = part;
} else {
LOG(ERROR) << "unknown argument" << part
<< " in fastboot-info.txt. parts: " << android::base::Join(parts, " ");
return nullptr;
}
}
if (partition.empty()) {
LOG(ERROR) << "partition name not found when parsing fastboot-info.txt. parts: "
<< android::base::Join(parts, " ");
return nullptr;
}
if (img_name.empty()) {
img_name = partition + ".img";
}
return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta);
}
void Flash();
std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts) {
if (parts.empty()) return std::make_unique<RebootTask>(fp);
if (parts.size() > 1) {
LOG(ERROR) << "unknown arguments in reboot {target} in fastboot-info.txt: "
<< android::base::Join(parts, " ");
return nullptr;
}
return std::make_unique<RebootTask>(fp, parts[0]);
}
private:
void CheckRequirements();
void DetermineSlot();
void CollectImages();
void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
std::unique_ptr<WipeTask> ParseWipeCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts) {
if (parts.size() != 1) {
LOG(ERROR) << "unknown arguments in erase {partition} in fastboot-info.txt: "
<< android::base::Join(parts, " ");
return nullptr;
}
return std::make_unique<WipeTask>(fp, parts[0]);
}
std::vector<ImageEntry> boot_images_;
std::vector<ImageEntry> os_images_;
FlashingPlan* fp_;
};
std::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,
const std::vector<std::string>& command) {
if (command.size() == 0) {
return nullptr;
}
std::unique_ptr<Task> task;
if (command[0] == "flash") {
task = ParseFlashCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});
} else if (command[0] == "reboot") {
task = ParseRebootCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});
} else if (command[0] == "update-super" && command.size() == 1) {
task = std::make_unique<UpdateSuperTask>(fp);
} else if (command[0] == "erase" && command.size() == 2) {
task = ParseWipeCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});
}
if (!task) {
LOG(ERROR) << "unknown command parsing fastboot-info.txt line: "
<< android::base::Join(command, " ");
}
return task;
}
void AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>* tasks) {
// expands "resize-partitions" into individual commands : resize {os_partition_1}, resize
// {os_partition_2}, etc.
std::vector<std::unique_ptr<Task>> resize_tasks;
std::optional<size_t> loc;
for (size_t i = 0; i < tasks->size(); i++) {
if (auto flash_task = tasks->at(i)->AsFlashTask()) {
if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
if (!loc) {
loc = i;
}
resize_tasks.emplace_back(std::make_unique<ResizeTask>(
fp, flash_task->GetPartition(), "0", fp->slot_override));
}
}
}
// if no logical partitions (although should never happen since system will always need to be
// flashed)
if (!loc) {
return;
}
tasks->insert(tasks->begin() + loc.value(), std::make_move_iterator(resize_tasks.begin()),
std::make_move_iterator(resize_tasks.end()));
return;
}
static bool IsNumber(const std::string& s) {
bool period = false;
for (size_t i = 0; i < s.length(); i++) {
if (!isdigit(s[i])) {
if (!period && s[i] == '.' && i != 0 && i != s.length() - 1) {
period = true;
} else {
return false;
}
}
}
return true;
}
static bool IsIgnore(const std::vector<std::string>& command) {
if (command[0][0] == '#') {
return true;
}
return false;
}
bool CheckFastbootInfoRequirements(const std::vector<std::string>& command) {
if (command.size() != 2) {
LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
<< android::base::Join(command, " ");
return false;
}
if (command[0] != "version") {
LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
<< android::base::Join(command, " ");
return false;
}
if (!IsNumber(command[1])) {
LOG(ERROR) << "version number contains non-numeric values in fastboot-info.txt -> "
<< android::base::Join(command, " ");
return false;
}
LOG(VERBOSE) << "Checking 'fastboot-info.txt version'";
if (command[1] < PLATFORM_TOOLS_VERSION) {
return true;
}
LOG(ERROR) << "fasboot-info.txt version: " << command[1]
<< " not compatible with host tool version --> " << PLATFORM_TOOLS_VERSION;
return false;
}
std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,
const std::vector<std::string>& file) {
std::vector<std::unique_ptr<Task>> tasks;
// Get os_partitions that need to be resized
for (auto& text : file) {
std::vector<std::string> command = android::base::Tokenize(text, " ");
if (IsIgnore(command)) {
continue;
}
if (command.size() > 1 && command[0] == "version") {
if (!CheckFastbootInfoRequirements(command)) {
return {};
}
continue;
} else if (command.size() >= 2 && command[0] == "if-wipe") {
if (!fp->wants_wipe) {
continue;
}
command.erase(command.begin());
}
auto task = ParseFastbootInfoLine(fp, command);
if (!task) {
LOG(ERROR) << "Error when parsing fastboot-info.txt, falling back on Hardcoded list: "
<< text;
return {};
}
tasks.emplace_back(std::move(task));
}
if (auto flash_super_task = FlashSuperLayoutTask::InitializeFromTasks(fp, tasks)) {
auto it = tasks.begin();
for (size_t i = 0; i < tasks.size(); i++) {
if (auto flash_task = tasks[i]->AsFlashTask()) {
if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
break;
}
}
if (auto wipe_task = tasks[i]->AsWipeTask()) {
break;
}
it++;
}
tasks.insert(it, std::move(flash_super_task));
} else {
AddResizeTasks(fp, &tasks);
}
return tasks;
}
std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp, std::ifstream& fs) {
if (!fs || fs.eof()) return {};
std::string text;
std::vector<std::string> file;
// Get os_partitions that need to be resized
while (std::getline(fs, text)) {
file.emplace_back(text);
}
return ParseFastbootInfo(fp, file);
}
FlashAllTool::FlashAllTool(FlashingPlan* fp) : fp_(fp) {}
@ -1611,38 +1790,23 @@ void FlashAllTool::Flash() {
CancelSnapshotIfNeeded();
// First flash boot partitions. We allow this to happen either in userspace
// or in bootloader fastboot.
FlashImages(boot_images_);
std::vector<std::unique_ptr<Task>> tasks;
if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) {
tasks.emplace_back(std::move(flash_super_task));
} else {
// Sync the super partition. This will reboot to userspace fastboot if needed.
tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
// Resize any logical partition to 0, so each partition is reset to 0
// extents, and will achieve more optimal allocation.
for (const auto& [image, slot] : os_images_) {
// Retrofit devices have two super partitions, named super_a and super_b.
// On these devices, secondary slots must be flashed as physical
// partitions (otherwise they would not mount on first boot). To enforce
// this, we delete any logical partitions for the "other" slot.
if (is_retrofit_device()) {
std::string partition_name = image->part_name + "_"s + slot;
if (image->IsSecondary() && is_logical(partition_name)) {
fp_->fb->DeletePartition(partition_name);
}
tasks.emplace_back(std::make_unique<DeleteTask>(fp_, partition_name));
}
tasks.emplace_back(std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
}
std::string path = find_item_given_name("fastboot-info.txt");
std::ifstream stream(path);
std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp_, stream);
if (tasks.empty()) {
LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
"exist";
HardcodedFlash();
return;
}
LOG(VERBOSE) << "Flashing from fastboot-info.txt";
for (auto& task : tasks) {
task->Run();
}
FlashImages(os_images_);
if (fp_->wants_wipe) {
// avoid adding duplicate wipe tasks in fastboot main code.
fp_->wants_wipe = false;
}
}
void FlashAllTool::CheckRequirements() {
@ -1693,6 +1857,42 @@ void FlashAllTool::CollectImages() {
}
}
void FlashAllTool::HardcodedFlash() {
CollectImages();
// First flash boot partitions. We allow this to happen either in userspace
// or in bootloader fastboot.
FlashImages(boot_images_);
std::vector<std::unique_ptr<Task>> tasks;
if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) {
tasks.emplace_back(std::move(flash_super_task));
} else {
// Sync the super partition. This will reboot to userspace fastboot if needed.
tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
// Resize any logical partition to 0, so each partition is reset to 0
// extents, and will achieve more optimal allocation.
for (const auto& [image, slot] : os_images_) {
// Retrofit devices have two super partitions, named super_a and super_b.
// On these devices, secondary slots must be flashed as physical
// partitions (otherwise they would not mount on first boot). To enforce
// this, we delete any logical partitions for the "other" slot.
if (is_retrofit_device()) {
std::string partition_name = image->part_name + "_"s + slot;
if (image->IsSecondary() && should_flash_in_userspace(partition_name)) {
fp_->fb->DeletePartition(partition_name);
}
tasks.emplace_back(std::make_unique<DeleteTask>(fp_, partition_name));
}
tasks.emplace_back(std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
}
}
for (auto& i : tasks) {
i->Run();
}
FlashImages(os_images_);
}
void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
for (const auto& [image, slot] : images) {
fastboot_buffer buf;

View File

@ -29,7 +29,10 @@
#include <string>
#include "fastboot_driver.h"
#include "fastboot_driver_interface.h"
#include "filesystem.h"
#include "super_flash_helper.h"
#include "task.h"
#include "util.h"
#include <bootimg.h>
@ -47,6 +50,19 @@ class FastBootTool {
unsigned ParseFsOption(const char*);
};
enum fb_buffer_type {
FB_BUFFER_FD,
FB_BUFFER_SPARSE,
};
struct fastboot_buffer {
enum fb_buffer_type type;
std::vector<SparsePtr> files;
int64_t sz;
unique_fd fd;
int64_t image_size;
};
enum class ImageType {
// Must be flashed for device to boot into the kernel.
BootCritical,
@ -83,7 +99,27 @@ struct FlashingPlan {
std::string slot_override;
std::string current_slot;
std::string secondary_slot;
fastboot::FastBootDriver* fb;
fastboot::IFastBootDriver* fb;
};
class FlashAllTool {
public:
FlashAllTool(FlashingPlan* fp);
void Flash();
private:
void CheckRequirements();
void DetermineSlot();
void CollectImages();
void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
void HardcodedFlash();
std::vector<ImageEntry> boot_images_;
std::vector<ImageEntry> os_images_;
FlashingPlan* fp_;
};
bool should_flash_in_userspace(const std::string& partition_name);
@ -94,6 +130,21 @@ void do_for_partitions(const std::string& part, const std::string& slot,
std::string find_item(const std::string& item);
void reboot_to_userspace_fastboot();
void syntax_error(const char* fmt, ...);
std::string get_current_slot();
// Code for Parsing fastboot-info.txt
bool CheckFastbootInfoRequirements(const std::vector<std::string>& command);
std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts);
std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts);
std::unique_ptr<WipeTask> ParseWipeCommand(const FlashingPlan* fp,
const std::vector<std::string>& parts);
std::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,
const std::vector<std::string>& command);
void AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,
const std::vector<std::string>& file);
struct NetworkSerial {
Socket::Protocol protocol;
@ -103,7 +154,7 @@ struct NetworkSerial {
Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
bool supports_AB();
std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot_);
std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
int64_t get_sparse_limit(int64_t size);
std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);

View File

@ -33,7 +33,6 @@
#include <vector>
#include <android-base/endian.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <bootimg.h>
@ -41,21 +40,13 @@
#include <sparse/sparse.h>
#include "constants.h"
#include "fastboot_driver_interface.h"
#include "transport.h"
class Transport;
namespace fastboot {
enum RetCode : int {
SUCCESS = 0,
BAD_ARG,
IO_ERROR,
BAD_DEV_RESP,
DEVICE_FAIL,
TIMEOUT,
};
struct DriverCallbacks {
std::function<void(const std::string&)> prolog = [](const std::string&) {};
std::function<void(int)> epilog = [](int) {};
@ -63,7 +54,7 @@ struct DriverCallbacks {
std::function<void(const std::string&)> text = [](const std::string&) {};
};
class FastBootDriver {
class FastBootDriver : public IFastBootDriver {
friend class FastBootTest;
public:
@ -78,9 +69,10 @@ class FastBootDriver {
RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode CreatePartition(const std::string& partition, const std::string& size);
RetCode DeletePartition(const std::string& partition);
RetCode DeletePartition(const std::string& partition) override;
RetCode Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
std::string* response = nullptr, std::vector<std::string>* info = nullptr);
std::string* response = nullptr,
std::vector<std::string>* info = nullptr) override;
RetCode Download(android::base::borrowed_fd fd, size_t size, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Download(const std::string& name, const std::vector<char>& buf,
@ -93,16 +85,17 @@ class FastBootDriver {
RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Erase(const std::string& partition, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
std::vector<std::string>* info = nullptr) override;
RetCode Flash(const std::string& partition, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode GetVar(const std::string& key, std::string* val,
std::vector<std::string>* info = nullptr);
std::vector<std::string>* info = nullptr) override;
RetCode GetVarAll(std::vector<std::string>* response);
RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode Reboot(std::string* response = nullptr,
std::vector<std::string>* info = nullptr) override;
RetCode RebootTo(std::string target, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode ResizePartition(const std::string& partition, const std::string& size);
std::vector<std::string>* info = nullptr) override;
RetCode ResizePartition(const std::string& partition, const std::string& size) override;
RetCode SetActive(const std::string& slot, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Upload(const std::string& outfile, std::string* response = nullptr,
@ -116,7 +109,7 @@ class FastBootDriver {
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
RetCode FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
uint32_t sz);
uint32_t sz) override;
RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
size_t current, size_t total);
@ -128,7 +121,7 @@ class FastBootDriver {
void SetInfoCallback(std::function<void(const std::string&)> info);
static const std::string RCString(RetCode rc);
std::string Error();
RetCode WaitForDisconnect();
RetCode WaitForDisconnect() override;
// Note: set_transport will return the previous transport.
Transport* set_transport(Transport* transport);

View File

@ -0,0 +1,59 @@
//
// Copyright (C) 2023 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 <string>
#include "android-base/unique_fd.h"
class Transport;
namespace fastboot {
enum RetCode : int {
SUCCESS = 0,
BAD_ARG,
IO_ERROR,
BAD_DEV_RESP,
DEVICE_FAIL,
TIMEOUT,
};
class IFastBootDriver {
public:
RetCode virtual FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
uint32_t sz) = 0;
RetCode virtual DeletePartition(const std::string& partition) = 0;
RetCode virtual WaitForDisconnect() = 0;
RetCode virtual Reboot(std::string* response = nullptr,
std::vector<std::string>* info = nullptr) = 0;
RetCode virtual RebootTo(std::string target, std::string* response = nullptr,
std::vector<std::string>* info = nullptr) = 0;
RetCode virtual GetVar(const std::string& key, std::string* val,
std::vector<std::string>* info = nullptr) = 0;
RetCode virtual Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
std::string* response = nullptr,
std::vector<std::string>* info = nullptr) = 0;
RetCode virtual RawCommand(const std::string& cmd, const std::string& message,
std::string* response = nullptr,
std::vector<std::string>* info = nullptr, int* dsize = nullptr) = 0;
RetCode virtual ResizePartition(const std::string& partition, const std::string& size) = 0;
RetCode virtual Erase(const std::string& partition, std::string* response = nullptr,
std::vector<std::string>* info = nullptr) = 0;
virtual ~IFastBootDriver() = default;
};
} // namespace fastboot

View File

@ -0,0 +1,51 @@
//
// Copyright (C) 2023 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 <gmock/gmock.h>
#include "fastboot_driver_interface.h"
namespace fastboot {
class MockFastbootDriver : public IFastBootDriver {
public:
MOCK_METHOD(RetCode, FlashPartition,
(const std::string& partition, android::base::borrowed_fd fd, uint32_t sz),
(override));
MOCK_METHOD(RetCode, DeletePartition, (const std::string&), (override));
MOCK_METHOD(RetCode, Reboot, (std::string*, std::vector<std::string>*), (override));
MOCK_METHOD(RetCode, RebootTo, (std::string, std::string*, std::vector<std::string>*),
(override));
MOCK_METHOD(RetCode, GetVar, (const std::string&, std::string*, std::vector<std::string>*),
(override));
MOCK_METHOD(RetCode, Download,
(const std::string&, android::base::borrowed_fd, size_t, std::string*,
std::vector<std::string>*),
(override));
MOCK_METHOD(RetCode, RawCommand,
(const std::string&, const std::string&, std::string*, std::vector<std::string>*,
int*),
(override));
MOCK_METHOD(RetCode, ResizePartition, (const std::string&, const std::string&), (override));
MOCK_METHOD(RetCode, Erase, (const std::string&, std::string*, std::vector<std::string>*),
(override));
MOCK_METHOD(RetCode, WaitForDisconnect, (), (override));
};
} // namespace fastboot

View File

@ -14,12 +14,16 @@
// limitations under the License.
//
#include "task.h"
#include <iostream>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include "fastboot.h"
#include "filesystem.h"
#include "super_flash_helper.h"
#include <android-base/parseint.h>
#include "util.h"
using namespace std::string_literals;
FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname,
@ -42,8 +46,22 @@ void FlashTask::Run() {
do_for_partitions(pname_, slot_, flash, true);
}
RebootTask::RebootTask(FlashingPlan* fp) : fp_(fp){};
RebootTask::RebootTask(FlashingPlan* fp, const std::string& reboot_target)
std::string FlashTask::GetPartitionAndSlot() {
auto slot = slot_;
if (slot.empty()) {
slot = get_current_slot();
}
if (slot.empty()) {
return pname_;
}
if (slot == "all") {
LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
}
return pname_ + "_" + slot;
}
RebootTask::RebootTask(const FlashingPlan* fp) : fp_(fp){};
RebootTask::RebootTask(const FlashingPlan* fp, const std::string& reboot_target)
: reboot_target_(reboot_target), fp_(fp){};
void RebootTask::Run() {
@ -90,7 +108,7 @@ void FlashSuperLayoutTask::Run() {
}
std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
const FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
if (!supports_AB()) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
@ -153,7 +171,78 @@ std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
partition_size);
}
UpdateSuperTask::UpdateSuperTask(FlashingPlan* fp) : fp_(fp) {}
std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::InitializeFromTasks(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {
if (!supports_AB()) {
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return nullptr;
}
if (fp->slot_override == "all") {
LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
return nullptr;
}
// Does this device use dynamic partitions at all?
unique_fd fd = fp->source->OpenFile("super_empty.img");
if (fd < 0) {
LOG(VERBOSE) << "could not open super_empty.img";
return nullptr;
}
std::string super_name;
// Try to find whether there is a super partition.
if (fp->fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
super_name = "super";
}
uint64_t partition_size;
std::string partition_size_str;
if (fp->fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
return nullptr;
}
partition_size_str = fb_fix_numeric_var(partition_size_str);
if (!android::base::ParseUint(partition_size_str, &partition_size)) {
LOG(VERBOSE) << "Could not parse " << super_name << " size: " << partition_size_str;
return nullptr;
}
std::unique_ptr<SuperFlashHelper> helper = std::make_unique<SuperFlashHelper>(*fp->source);
if (!helper->Open(fd)) {
return nullptr;
}
for (const auto& task : tasks) {
if (auto flash_task = task->AsFlashTask()) {
if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
auto partition = flash_task->GetPartitionAndSlot();
if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) {
return nullptr;
}
}
}
}
auto s = helper->GetSparseLayout();
if (!s) return nullptr;
// Remove images that we already flashed, just in case we have non-dynamic OS images.
auto remove_if_callback = [&](const auto& task) -> bool {
if (auto flash_task = task->AsFlashTask()) {
return helper->WillFlash(flash_task->GetPartitionAndSlot());
} else if (auto update_super_task = task->AsUpdateSuperTask()) {
return true;
} else if (auto reboot_task = task->AsRebootTask()) {
return true;
}
return false;
};
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
partition_size);
}
UpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {}
void UpdateSuperTask::Run() {
unique_fd fd = fp_->source->OpenFile("super_empty.img");
@ -177,7 +266,7 @@ void UpdateSuperTask::Run() {
fp_->fb->RawCommand(command, "Updating super partition");
}
ResizeTask::ResizeTask(FlashingPlan* fp, const std::string& pname, const std::string& size,
ResizeTask::ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot)
: fp_(fp), pname_(pname), size_(size), slot_(slot) {}
@ -196,7 +285,7 @@ void DeleteTask::Run() {
fp_->fb->DeletePartition(pname_);
}
WipeTask::WipeTask(FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
WipeTask::WipeTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
void WipeTask::Run() {
std::string partition_type;

View File

@ -18,15 +18,28 @@
#include <sstream>
#include <string>
#include "fastboot.h"
#include "fastboot_driver.h"
#include "super_flash_helper.h"
#include "util.h"
struct FlashingPlan;
struct Image;
using ImageEntry = std::pair<const Image*, std::string>;
class FlashTask;
class RebootTask;
class UpdateSuperTask;
class WipeTask;
class Task {
public:
Task() = default;
virtual void Run() = 0;
virtual FlashTask* AsFlashTask() { return nullptr; }
virtual RebootTask* AsRebootTask() { return nullptr; }
virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
virtual WipeTask* AsWipeTask() { return nullptr; }
virtual ~Task() = default;
};
@ -34,7 +47,12 @@ class FlashTask : public Task {
public:
FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,
const bool apply_vbmeta);
virtual FlashTask* AsFlashTask() override { return this; }
std::string GetPartition() { return pname_; }
std::string GetImageName() { return fname_; }
std::string GetPartitionAndSlot();
std::string GetSlot() { return slot_; }
void Run() override;
private:
@ -46,8 +64,9 @@ class FlashTask : public Task {
class RebootTask : public Task {
public:
RebootTask(FlashingPlan* fp);
RebootTask(FlashingPlan* fp, const std::string& reboot_target);
RebootTask(const FlashingPlan* fp);
RebootTask(const FlashingPlan* fp, const std::string& reboot_target);
virtual RebootTask* AsRebootTask() override { return this; }
void Run() override;
private:
@ -59,8 +78,10 @@ class FlashSuperLayoutTask : public Task {
public:
FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
SparsePtr sparse_layout, uint64_t super_size);
static std::unique_ptr<FlashSuperLayoutTask> Initialize(FlashingPlan* fp,
static std::unique_ptr<FlashSuperLayoutTask> Initialize(const FlashingPlan* fp,
std::vector<ImageEntry>& os_images);
static std::unique_ptr<FlashSuperLayoutTask> InitializeFromTasks(
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
using ImageEntry = std::pair<const Image*, std::string>;
void Run() override;
@ -73,7 +94,8 @@ class FlashSuperLayoutTask : public Task {
class UpdateSuperTask : public Task {
public:
UpdateSuperTask(FlashingPlan* fp);
UpdateSuperTask(const FlashingPlan* fp);
virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }
void Run() override;
private:
@ -82,7 +104,7 @@ class UpdateSuperTask : public Task {
class ResizeTask : public Task {
public:
ResizeTask(FlashingPlan* fp, const std::string& pname, const std::string& size,
ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot);
void Run() override;
@ -105,7 +127,8 @@ class DeleteTask : public Task {
class WipeTask : public Task {
public:
WipeTask(FlashingPlan* fp, const std::string& pname);
WipeTask(const FlashingPlan* fp, const std::string& pname);
virtual WipeTask* AsWipeTask() override { return this; }
void Run() override;
private:

123
fastboot/task_test.cpp Normal file
View File

@ -0,0 +1,123 @@
//
// Copyright (C) 2023 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 "task.h"
#include "fastboot.h"
#include <gtest/gtest.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <unordered_map>
#include "android-base/strings.h"
using android::base::Split;
class ParseTest : public ::testing ::Test {
protected:
void SetUp() override {
fp = std::make_unique<FlashingPlan>();
fp->slot_override = "b";
fp->secondary_slot = "a";
fp->wants_wipe = false;
}
void TearDown() override {}
std::unique_ptr<FlashingPlan> fp;
private:
};
static std::vector<std::unique_ptr<Task>> collectTasks(FlashingPlan* fp,
const std::vector<std::string>& commands) {
std::vector<std::vector<std::string>> vec_commands;
for (auto& command : commands) {
vec_commands.emplace_back(android::base::Split(command, " "));
}
std::vector<std::unique_ptr<Task>> tasks;
for (auto& command : vec_commands) {
tasks.emplace_back(ParseFastbootInfoLine(fp, command));
}
return tasks;
}
std::unique_ptr<Task> ParseCommand(FlashingPlan* fp, std::string command) {
std::vector<std::string> vec_command = android::base::Split(command, " ");
return ParseFastbootInfoLine(fp, vec_command);
}
TEST_F(ParseTest, CORRECT_FlASH_TASK_FORMED) {
std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
"flash system", "flash --apply-vbmeta vbmeta"};
std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
std::vector<std::vector<std::string>> expected_values{
{"dtbo", "dtbo_b", "b", "dtbo.img"},
{"system", "system_a", "a", "system_other.img"},
{"system", "system_b", "b", "system.img"},
{"vbmeta", "vbmeta_b", "b", "vbmeta.img"}
};
for (auto& task : tasks) {
ASSERT_TRUE(task != nullptr);
}
for (size_t i = 0; i < tasks.size(); i++) {
auto task = tasks[i]->AsFlashTask();
ASSERT_TRUE(task != nullptr);
ASSERT_EQ(task->GetPartition(), expected_values[i][0]);
ASSERT_EQ(task->GetPartitionAndSlot(), expected_values[i][1]);
ASSERT_EQ(task->GetSlot(), expected_values[i][2]);
ASSERT_EQ(task->GetImageName(), expected_values[i][3]);
}
}
TEST_F(ParseTest, VERSION_CHECK_CORRRECT) {
std::vector<std::string> correct_versions = {
"version 1.0",
"version 22.00",
};
std::vector<std::string> bad_versions = {"version", "version .01", "version x1",
"version 1.0.1", "version 1.", "s 1.0",
"version 1.0 2.0"};
for (auto& version : correct_versions) {
ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
}
for (auto& version : bad_versions) {
ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
}
}
TEST_F(ParseTest, BAD_FASTBOOT_INFO_INPUT) {
ASSERT_EQ(ParseCommand(fp.get(), "flash"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "flash --slot-other --apply-vbmeta"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "flash --apply-vbmeta"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "if-wipe"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "if-wipe flash"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "wipe dtbo"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "update-super dtbo"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "flash system system.img system"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "reboot bootloader fastboot"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(),
"flash --slot-other --apply-vbmeta system system_other.img system"),
nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "erase"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "erase dtbo dtbo"), nullptr);
ASSERT_EQ(ParseCommand(fp.get(), "wipe this"), nullptr);
}

View File

@ -90,6 +90,8 @@ static const struct fs_path_config android_dirs[] = {
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
{ 00750, AID_ROOT, AID_SHELL, 0, "system/xbin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin" },
{ 00750, AID_ROOT, AID_SYSTEM, 0, "system_ext/apex/com.android.tethering/bin/for-system" },
{ 00750, AID_ROOT, AID_SYSTEM, 0, "system_ext/apex/com.android.tethering.inprocess/bin/for-system" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin" },
{ 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
@ -198,6 +200,8 @@ static const struct fs_path_config android_files[] = {
// in user builds.
{ 06755, AID_CLAT, AID_CLAT, 0, "system/apex/com.android.tethering/bin/for-system/clatd" },
{ 06755, AID_CLAT, AID_CLAT, 0, "system/apex/com.android.tethering.inprocess/bin/for-system/clatd" },
{ 06755, AID_CLAT, AID_CLAT, 0, "system_ext/apex/com.android.tethering/bin/for-system/clatd" },
{ 06755, AID_CLAT, AID_CLAT, 0, "system_ext/apex/com.android.tethering.inprocess/bin/for-system/clatd" },
{ 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
"system/bin/inputflinger" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |

View File

@ -96,6 +96,10 @@ void removeAllEmptyProcessGroups(void);
// Returns false in case of error, true in case of success
bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
// Check if a profile can be applied without failing.
// Returns true if it can be applied without failing, false otherwise
bool isProfileValidForProcess(const std::string& profile_name, int uid, int pid);
#endif // __ANDROID_VNDK__
__END_DECLS

View File

@ -657,3 +657,13 @@ bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
return CgroupGetAttributePathForTask(attr_name, tid, path);
}
bool isProfileValidForProcess(const std::string& profile_name, int uid, int pid) {
const TaskProfile* tp = TaskProfiles::GetInstance().GetProfile(profile_name);
if (tp == nullptr) {
return false;
}
return tp->IsValidForProcess(uid, pid);
}

View File

@ -259,6 +259,31 @@ bool SetAttributeAction::ExecuteForUID(uid_t uid) const {
return true;
}
bool SetAttributeAction::IsValidForProcess(uid_t, pid_t pid) const {
return IsValidForTask(pid);
}
bool SetAttributeAction::IsValidForTask(int tid) const {
std::string path;
if (!attribute_->GetPathForTask(tid, &path)) {
return false;
}
if (!access(path.c_str(), W_OK)) {
// operation will succeed
return true;
}
if (!access(path.c_str(), F_OK)) {
// file exists but not writable
return false;
}
// file does not exist, ignore if optional
return optional_;
}
SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
: controller_(c), path_(p) {
FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
@ -396,6 +421,39 @@ void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
FdCacheHelper::Drop(fd_[cache_type]);
}
bool SetCgroupAction::IsValidForProcess(uid_t uid, pid_t pid) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
return true;
}
if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
return false;
}
std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
return access(procs_path.c_str(), W_OK) == 0;
}
bool SetCgroupAction::IsValidForTask(int) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
return true;
}
if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
return false;
}
if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
// application-dependent path can't be used with tid
return false;
}
std::string tasks_path = controller()->GetTasksFilePath(path_);
return access(tasks_path.c_str(), W_OK) == 0;
}
WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
const std::string& value, bool logfailures)
: task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
@ -532,6 +590,37 @@ void WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {
FdCacheHelper::Drop(fd_[cache_type]);
}
bool WriteFileAction::IsValidForProcess(uid_t, pid_t) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
return true;
}
if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
return false;
}
return access(proc_path_.empty() ? task_path_.c_str() : proc_path_.c_str(), W_OK) == 0;
}
bool WriteFileAction::IsValidForTask(int) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
return true;
}
if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
return false;
}
if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
// application-dependent path can't be used with tid
return false;
}
return access(task_path_.c_str(), W_OK) == 0;
}
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
profile->ExecuteForProcess(uid, pid);
@ -558,6 +647,24 @@ void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
}
}
bool ApplyProfileAction::IsValidForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
if (!profile->IsValidForProcess(uid, pid)) {
return false;
}
}
return true;
}
bool ApplyProfileAction::IsValidForTask(int tid) const {
for (const auto& profile : profiles_) {
if (!profile->IsValidForTask(tid)) {
return false;
}
}
return true;
}
void TaskProfile::MoveTo(TaskProfile* profile) {
profile->elements_ = std::move(elements_);
profile->res_cached_ = res_cached_;
@ -620,6 +727,20 @@ void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_typ
res_cached_ = false;
}
bool TaskProfile::IsValidForProcess(uid_t uid, pid_t pid) const {
for (const auto& element : elements_) {
if (!element->IsValidForProcess(uid, pid)) return false;
}
return true;
}
bool TaskProfile::IsValidForTask(int tid) const {
for (const auto& element : elements_) {
if (!element->IsValidForTask(tid)) return false;
}
return true;
}
void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
for (auto& iter : profiles_) {
iter.second->DropResourceCaching(cache_type);

View File

@ -72,12 +72,14 @@ class ProfileAction {
virtual const char* Name() const = 0;
// Default implementations will fail
virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
virtual bool ExecuteForTask(int) const { return false; };
virtual bool ExecuteForUID(uid_t) const { return false; };
virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; }
virtual bool ExecuteForTask(int) const { return false; }
virtual bool ExecuteForUID(uid_t) const { return false; }
virtual void EnableResourceCaching(ResourceCacheType) {}
virtual void DropResourceCaching(ResourceCacheType) {}
virtual bool IsValidForProcess(uid_t uid, pid_t pid) const { return false; }
virtual bool IsValidForTask(int tid) const { return false; }
protected:
enum CacheUseResult { SUCCESS, FAIL, UNUSED };
@ -103,6 +105,8 @@ class SetTimerSlackAction : public ProfileAction {
const char* Name() const override { return "SetTimerSlack"; }
bool ExecuteForTask(int tid) const override;
bool IsValidForProcess(uid_t uid, pid_t pid) const override { return true; }
bool IsValidForTask(int tid) const override { return true; }
private:
unsigned long slack_;
@ -120,6 +124,8 @@ class SetAttributeAction : public ProfileAction {
bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
bool ExecuteForTask(int tid) const override;
bool ExecuteForUID(uid_t uid) const override;
bool IsValidForProcess(uid_t uid, pid_t pid) const override;
bool IsValidForTask(int tid) const override;
private:
const IProfileAttribute* attribute_;
@ -137,6 +143,8 @@ class SetCgroupAction : public ProfileAction {
bool ExecuteForTask(int tid) const override;
void EnableResourceCaching(ResourceCacheType cache_type) override;
void DropResourceCaching(ResourceCacheType cache_type) override;
bool IsValidForProcess(uid_t uid, pid_t pid) const override;
bool IsValidForTask(int tid) const override;
const CgroupController* controller() const { return &controller_; }
@ -161,6 +169,8 @@ class WriteFileAction : public ProfileAction {
bool ExecuteForTask(int tid) const override;
void EnableResourceCaching(ResourceCacheType cache_type) override;
void DropResourceCaching(ResourceCacheType cache_type) override;
bool IsValidForProcess(uid_t uid, pid_t pid) const override;
bool IsValidForTask(int tid) const override;
private:
std::string task_path_, proc_path_, value_;
@ -186,6 +196,8 @@ class TaskProfile {
bool ExecuteForUID(uid_t uid) const;
void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
bool IsValidForProcess(uid_t uid, pid_t pid) const;
bool IsValidForTask(int tid) const;
private:
const std::string name_;
@ -204,6 +216,8 @@ class ApplyProfileAction : public ProfileAction {
bool ExecuteForTask(int tid) const override;
void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
bool IsValidForProcess(uid_t uid, pid_t pid) const override;
bool IsValidForTask(int tid) const override;
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;

View File

@ -16,6 +16,7 @@
#include "task_profiles.h"
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
#include <mntent.h>
#include <processgroup/processgroup.h>
@ -29,13 +30,14 @@ using ::android::base::LogFunction;
using ::android::base::LogId;
using ::android::base::LogSeverity;
using ::android::base::SetLogger;
using ::android::base::Split;
using ::android::base::VERBOSE;
using ::testing::TestWithParam;
using ::testing::Values;
namespace {
bool IsCgroupV2Mounted() {
bool IsCgroupV2MountedRw() {
std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
if (!mnts) {
LOG(ERROR) << "Failed to open /proc/mounts";
@ -43,9 +45,11 @@ bool IsCgroupV2Mounted() {
}
struct mntent* mnt;
while ((mnt = getmntent(mnts.get()))) {
if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
return true;
if (strcmp(mnt->mnt_type, "cgroup2") != 0) {
continue;
}
const std::vector<std::string> options = Split(mnt->mnt_opts, ",");
return std::count(options.begin(), options.end(), "ro") == 0;
}
return false;
}
@ -145,8 +149,9 @@ class SetAttributeFixture : public TestWithParam<TestParam> {
};
TEST_P(SetAttributeFixture, SetAttribute) {
// Treehugger runs host tests inside a container without cgroupv2 support.
if (!IsCgroupV2Mounted()) {
// Treehugger runs host tests inside a container either without cgroupv2
// support or with the cgroup filesystem mounted read-only.
if (!IsCgroupV2MountedRw()) {
GTEST_SKIP();
return;
}
@ -170,6 +175,32 @@ TEST_P(SetAttributeFixture, SetAttribute) {
}
}
class TaskProfileFixture : public TestWithParam<TestParam> {
public:
~TaskProfileFixture() = default;
};
TEST_P(TaskProfileFixture, TaskProfile) {
// Treehugger runs host tests inside a container without cgroupv2 support.
if (!IsCgroupV2MountedRw()) {
GTEST_SKIP();
return;
}
const TestParam params = GetParam();
ProfileAttributeMock pa(params.attr_name);
// Test simple profile with one action
std::shared_ptr<TaskProfile> tp = std::make_shared<TaskProfile>("test_profile");
tp->Add(std::make_unique<SetAttributeAction>(&pa, params.attr_value, params.optional_attr));
EXPECT_EQ(tp->IsValidForProcess(getuid(), getpid()), params.result);
EXPECT_EQ(tp->IsValidForTask(getpid()), params.result);
// Test aggregate profile
TaskProfile tp2("meta_profile");
std::vector<std::shared_ptr<TaskProfile>> profiles = {tp};
tp2.Add(std::make_unique<ApplyProfileAction>(profiles));
EXPECT_EQ(tp2.IsValidForProcess(getuid(), getpid()), params.result);
EXPECT_EQ(tp2.IsValidForTask(getpid()), params.result);
}
// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
// exists }.
INSTANTIATE_TEST_SUITE_P(
@ -210,4 +241,28 @@ INSTANTIATE_TEST_SUITE_P(
.log_prefix = "Failed to write",
.log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
// Test TaskProfile IsValid calls.
INSTANTIATE_TEST_SUITE_P(
TaskProfileTestSuite, TaskProfileFixture,
Values(
// Test operating on non-existing cgroup attribute fails.
TestParam{.attr_name = "no-such-attribute",
.attr_value = ".",
.optional_attr = false,
.result = false},
// Test operating on optional non-existing cgroup attribute succeeds.
TestParam{.attr_name = "no-such-attribute",
.attr_value = ".",
.optional_attr = true,
.result = true},
// Test operating on existing cgroup attribute succeeds.
TestParam{.attr_name = "cgroup.procs",
.attr_value = ".",
.optional_attr = false,
.result = true},
// Test operating on optional existing cgroup attribute succeeds.
TestParam{.attr_name = "cgroup.procs",
.attr_value = ".",
.optional_attr = true,
.result = true}));
} // namespace

View File

@ -1,3 +1,4 @@
dir dev 0755 0 0
nod dev/null 0600 0 0 c 1 3
nod dev/console 0600 0 0 c 5 1
nod dev/urandom 0600 0 0 c 1 9