Merge "Addressing error-code propagation for remount service."
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 8f84346..1e3c559 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -16,6 +16,8 @@
#pragma once
+#include <memory>
+
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Unwinder.h>
@@ -31,7 +33,7 @@
}
void MockSetBuildID(uint64_t offset, const std::string& build_id) {
- unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
+ std::shared_ptr<unwindstack::MapInfo> map_info = GetMaps()->Find(offset);
if (map_info != nullptr) {
map_info->SetBuildID(std::string(build_id));
}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 534d7be..1835f0e 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -107,7 +107,7 @@
// In this case, the sp will be in either an invalid map if triggered
// on the main thread, or in a guard map if in another thread, which
// will be the first case or second case from below.
- unwindstack::MapInfo* map_info = maps->Find(sp);
+ auto map_info = maps->Find(sp);
if (map_info == nullptr) {
return "stack pointer is in a non-existent map; likely due to stack overflow.";
} else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -158,7 +158,7 @@
}
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
- unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+ auto map_info = maps->Find(fault_addr);
if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
} else {
@@ -396,7 +396,7 @@
regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
std::string label{"memory near "s + reg_name};
if (maps) {
- unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
+ auto map_info = maps->Find(untag_address(reg_value));
if (map_info != nullptr && !map_info->name().empty()) {
label += " (" + map_info->name() + ")";
}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 6c380a1..b1c4ef3 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -103,7 +103,7 @@
// In this case, the sp will be in either an invalid map if triggered
// on the main thread, or in a guard map if in another thread, which
// will be the first case or second case from below.
- unwindstack::MapInfo* map_info = maps->Find(sp);
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(sp);
if (map_info == nullptr) {
return "stack pointer is in a non-existent map; likely due to stack overflow.";
} else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -226,7 +226,7 @@
cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
}
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
- unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+ auto map_info = maps->Find(fault_addr);
if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
} else {
@@ -342,8 +342,8 @@
f->set_file_map_offset(frame.map_elf_start_offset);
- unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
- if (map_info) {
+ auto map_info = maps->Find(frame.map_start);
+ if (map_info.get() != nullptr) {
f->set_build_id(map_info->GetPrintableBuildID());
}
}
@@ -370,7 +370,7 @@
MemoryDump dump;
dump.set_register_name(name);
- unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
if (map_info) {
dump.set_mapping_name(map_info->name());
}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 2c70778..339f392 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -408,3 +408,9 @@
":fastboot_test_vendor_boot_v4_with_frag"
],
}
+
+cc_library_headers {
+ name: "fastboot_headers",
+ host_supported: true,
+ export_include_dirs: ["."],
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 0e918a3..322fe5c 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,10 +18,10 @@
# Package fastboot-related executables.
#
-my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
+my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
+my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
new file mode 100644
index 0000000..fcd3bd6
--- /dev/null
+++ b/fastboot/fuzzer/Android.bp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+cc_fuzz {
+ name: "fastboot_fuzzer",
+ host_supported: true,
+ device_supported: false,
+ srcs: [
+ "fastboot_fuzzer.cpp",
+ "socket_mock_fuzz.cpp",
+ ],
+ header_libs: [
+ "bootimg_headers",
+ "fastboot_headers",
+ ],
+ static_libs: [
+ "libext4_utils",
+ "libcrypto",
+ "libfastboot",
+ "libbuildversion",
+ "libbase",
+ "libziparchive",
+ "libsparse",
+ "libutils",
+ "liblog",
+ "libz",
+ "libdiagnose_usb",
+ "libbase",
+ "libcutils",
+ "libgtest",
+ "libgtest_main",
+ "libbase",
+ "libadb_host",
+ "liblp",
+ "liblog",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 533764,
+ },
+}
diff --git a/fastboot/fuzzer/README.md b/fastboot/fuzzer/README.md
new file mode 100644
index 0000000..10b06ea
--- /dev/null
+++ b/fastboot/fuzzer/README.md
@@ -0,0 +1,51 @@
+# Fuzzer for libfastboot
+
+## Plugin Design Considerations
+The fuzzer plugin for libfastboot is designed based on the understanding of the
+source code and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libfastboot supports the following parameters:
+1. Year (parameter name: `year`)
+2. Month (parameter name: `month`)
+3. Day (parameter name: `day`)
+4. Version (parameter name: `version`)
+5. Fs Option (parameter name: `fsOption`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `year` | `2000` to `2127` | Value obtained from FuzzedDataProvider|
+| `month` | `1` to `12` | Value obtained from FuzzedDataProvider|
+| `day` | `1` to `31` | Value obtained from FuzzedDataProvider|
+| `version` | `0` to `127` | Value obtained from FuzzedDataProvider|
+| `fsOption` | 0. `casefold` 1. `projid` 2. `compress` | Value obtained from FuzzedDataProvider|
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build fastboot_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) fastboot_fuzzer_fuzzer
+```
+#### Steps to run
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/${TARGET_ARCH}/fastboot_fuzzer/fastboot_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/fastboot/fuzzer/fastboot_fuzzer.cpp b/fastboot/fuzzer/fastboot_fuzzer.cpp
new file mode 100644
index 0000000..60940fe
--- /dev/null
+++ b/fastboot/fuzzer/fastboot_fuzzer.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <android-base/file.h>
+#include "fastboot.h"
+#include "socket.h"
+#include "socket_mock_fuzz.h"
+#include "tcp.h"
+#include "udp.h"
+#include "vendor_boot_img_utils.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace std;
+
+const size_t kYearMin = 2000;
+const size_t kYearMax = 2127;
+const size_t kMonthMin = 1;
+const size_t kMonthMax = 12;
+const size_t kDayMin = 1;
+const size_t kDayMax = 31;
+const size_t kVersionMin = 0;
+const size_t kVersionMax = 127;
+const size_t kMaxStringSize = 100;
+const size_t kMinTimeout = 10;
+const size_t kMaxTimeout = 3000;
+const uint16_t kValidUdpPacketSize = 512;
+const uint16_t kMinUdpPackets = 1;
+const uint16_t kMaxUdpPackets = 10;
+
+const string kValidTcpHandshakeString = "FB01";
+const string kInvalidTcpHandshakeString = "FB00";
+const string kValidRamdiskName = "default";
+const string kVendorBootFile = "/tmp/vendorBootFile";
+const string kRamdiskFile = "/tmp/ramdiskFile";
+const char* kFsOptionsArray[] = {"casefold", "projid", "compress"};
+
+class FastbootFuzzer {
+ public:
+ void Process(const uint8_t* data, size_t size);
+
+ private:
+ void InvokeParseApi();
+ void InvokeSocket();
+ void InvokeTcp();
+ void InvokeUdp();
+ void InvokeVendorBootImgUtils(const uint8_t* data, size_t size);
+ bool MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,
+ unique_ptr<Socket>* client, const string& hostname);
+ unique_ptr<FuzzedDataProvider> fdp_ = nullptr;
+};
+
+void FastbootFuzzer::InvokeParseApi() {
+ boot_img_hdr_v1 hdr = {};
+ FastBootTool fastBoot;
+
+ int32_t year = fdp_->ConsumeIntegralInRange<int32_t>(kYearMin, kYearMax);
+ int32_t month = fdp_->ConsumeIntegralInRange<int32_t>(kMonthMin, kMonthMax);
+ int32_t day = fdp_->ConsumeIntegralInRange<int32_t>(kDayMin, kDayMax);
+ string date = to_string(year) + "-" + to_string(month) + "-" + to_string(day);
+ fastBoot.ParseOsPatchLevel(&hdr, date.c_str());
+
+ int32_t major = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
+ int32_t minor = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
+ int32_t patch = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
+ string version = to_string(major) + "." + to_string(minor) + "." + to_string(patch);
+ fastBoot.ParseOsVersion(&hdr, version.c_str());
+
+ fastBoot.ParseFsOption(fdp_->PickValueInArray(kFsOptionsArray));
+}
+
+bool FastbootFuzzer::MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,
+ unique_ptr<Socket>* client,
+ const string& hostname = "localhost") {
+ *server = Socket::NewServer(protocol, 0);
+ if (*server == nullptr) {
+ return false;
+ }
+ *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);
+ if (*client == nullptr) {
+ return false;
+ }
+ if (protocol == Socket::Protocol::kTcp) {
+ *server = (*server)->Accept();
+ if (*server == nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FastbootFuzzer::InvokeSocket() {
+ unique_ptr<Socket> server, client;
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ if (MakeConnectedSockets(protocol, &server, &client)) {
+ string message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+ client->Send(message.c_str(), message.length());
+ string received(message.length(), '\0');
+ if (fdp_->ConsumeBool()) {
+ client->Close();
+ }
+ if (fdp_->ConsumeBool()) {
+ server->Close();
+ }
+ server->ReceiveAll(&received[0], received.length(),
+ /* timeout_ms */
+ fdp_->ConsumeIntegralInRange<size_t>(kMinTimeout, kMaxTimeout));
+ server->Close();
+ client->Close();
+ }
+ }
+}
+
+void FastbootFuzzer::InvokeTcp() {
+ /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */
+ SocketMockFuzz* tcp_mock = new SocketMockFuzz;
+ tcp_mock->ExpectSend(fdp_->ConsumeBool() ? kValidTcpHandshakeString
+ : kInvalidTcpHandshakeString);
+ tcp_mock->AddReceive(fdp_->ConsumeBool() ? kValidTcpHandshakeString
+ : kInvalidTcpHandshakeString);
+
+ string error;
+ unique_ptr<Transport> transport = tcp::internal::Connect(unique_ptr<Socket>(tcp_mock), &error);
+
+ if (transport.get()) {
+ string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+ if (fdp_->ConsumeBool()) {
+ tcp_mock->ExpectSend(write_message);
+ } else {
+ tcp_mock->ExpectSendFailure(write_message);
+ }
+ string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+ if (fdp_->ConsumeBool()) {
+ tcp_mock->AddReceive(read_message);
+ } else {
+ tcp_mock->AddReceiveFailure();
+ }
+
+ transport->Write(write_message.data(), write_message.length());
+
+ string buffer(read_message.length(), '\0');
+ transport->Read(&buffer[0], buffer.length());
+
+ transport->Close();
+ }
+}
+
+static string PacketValue(uint16_t value) {
+ return string{static_cast<char>(value >> 8), static_cast<char>(value)};
+}
+
+static string ErrorPacket(uint16_t sequence, const string& message = "",
+ char flags = udp::internal::kFlagNone) {
+ return string{udp::internal::kIdError, flags} + PacketValue(sequence) + message;
+}
+
+static string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
+ return string{udp::internal::kIdInitialization, udp::internal::kFlagNone} +
+ PacketValue(sequence) + PacketValue(version) + PacketValue(max_packet_size);
+}
+
+static string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
+ return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence) +
+ PacketValue(new_sequence);
+}
+
+static string QueryPacket(uint16_t sequence) {
+ return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence);
+}
+
+static string FastbootPacket(uint16_t sequence, const string& data = "",
+ char flags = udp::internal::kFlagNone) {
+ return string{udp::internal::kIdFastboot, flags} + PacketValue(sequence) + data;
+}
+
+void FastbootFuzzer::InvokeUdp() {
+ /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */
+ SocketMockFuzz* udp_mock = new SocketMockFuzz;
+ uint16_t starting_sequence = fdp_->ConsumeIntegral<uint16_t>();
+ int32_t device_max_packet_size = fdp_->ConsumeBool() ? kValidUdpPacketSize
+ : fdp_->ConsumeIntegralInRange<uint16_t>(
+ 0, kValidUdpPacketSize - 1);
+ udp_mock->ExpectSend(QueryPacket(0));
+ udp_mock->AddReceive(QueryPacket(0, starting_sequence));
+ udp_mock->ExpectSend(InitPacket(starting_sequence, udp::internal::kProtocolVersion,
+ udp::internal::kHostMaxPacketSize));
+ udp_mock->AddReceive(
+ InitPacket(starting_sequence, udp::internal::kProtocolVersion, device_max_packet_size));
+
+ string error;
+ unique_ptr<Transport> transport = udp::internal::Connect(unique_ptr<Socket>(udp_mock), &error);
+ bool is_transport_initialized = transport != nullptr && error.empty();
+
+ if (is_transport_initialized) {
+ uint16_t num_packets =
+ fdp_->ConsumeIntegralInRange<uint16_t>(kMinUdpPackets, kMaxUdpPackets);
+
+ for (uint16_t i = 0; i < num_packets; ++i) {
+ string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+ string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+ if (fdp_->ConsumeBool()) {
+ udp_mock->ExpectSend(FastbootPacket(i, write_message));
+ } else {
+ udp_mock->ExpectSend(ErrorPacket(i, write_message));
+ }
+
+ if (fdp_->ConsumeBool()) {
+ udp_mock->AddReceive(FastbootPacket(i, read_message));
+ } else {
+ udp_mock->AddReceive(ErrorPacket(i, read_message));
+ }
+ transport->Write(write_message.data(), write_message.length());
+ string buffer(read_message.length(), '\0');
+ transport->Read(&buffer[0], buffer.length());
+ }
+ transport->Close();
+ }
+}
+
+void FastbootFuzzer::InvokeVendorBootImgUtils(const uint8_t* data, size_t size) {
+ int32_t vendor_boot_fd = open(kVendorBootFile.c_str(), O_CREAT | O_RDWR, 0644);
+ if (vendor_boot_fd < 0) {
+ return;
+ }
+ int32_t ramdisk_fd = open(kRamdiskFile.c_str(), O_CREAT | O_RDWR, 0644);
+ if (ramdisk_fd < 0) {
+ return;
+ }
+ write(vendor_boot_fd, data, size);
+ write(ramdisk_fd, data, size);
+ string ramdisk_name = fdp_->ConsumeBool() ? kValidRamdiskName
+ : fdp_->ConsumeRandomLengthString(kMaxStringSize);
+ string content_vendor_boot_fd = {};
+ string content_ramdisk_fd = {};
+ lseek(vendor_boot_fd, 0, SEEK_SET);
+ lseek(ramdisk_fd, 0, SEEK_SET);
+ android::base::ReadFdToString(vendor_boot_fd, &content_vendor_boot_fd);
+ android::base::ReadFdToString(ramdisk_fd, &content_ramdisk_fd);
+ uint64_t vendor_boot_size =
+ fdp_->ConsumeBool() ? content_vendor_boot_fd.size() : fdp_->ConsumeIntegral<uint64_t>();
+ uint64_t ramdisk_size =
+ fdp_->ConsumeBool() ? content_ramdisk_fd.size() : fdp_->ConsumeIntegral<uint64_t>();
+ (void)replace_vendor_ramdisk(vendor_boot_fd, vendor_boot_size, ramdisk_name, ramdisk_fd,
+ ramdisk_size);
+ close(vendor_boot_fd);
+ close(ramdisk_fd);
+}
+
+void FastbootFuzzer::Process(const uint8_t* data, size_t size) {
+ fdp_ = make_unique<FuzzedDataProvider>(data, size);
+ InvokeParseApi();
+ InvokeSocket();
+ InvokeTcp();
+ InvokeUdp();
+ InvokeVendorBootImgUtils(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FastbootFuzzer fastbootFuzzer;
+ fastbootFuzzer.Process(data, size);
+ return 0;
+}
diff --git a/fastboot/fuzzer/socket_mock_fuzz.cpp b/fastboot/fuzzer/socket_mock_fuzz.cpp
new file mode 100644
index 0000000..df96eb0
--- /dev/null
+++ b/fastboot/fuzzer/socket_mock_fuzz.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "socket_mock_fuzz.h"
+
+SocketMockFuzz::SocketMockFuzz() : Socket(INVALID_SOCKET) {}
+
+SocketMockFuzz::~SocketMockFuzz() {}
+
+bool SocketMockFuzz::Send(const void* data, size_t length) {
+ if (events_.empty()) {
+ return false;
+ }
+
+ if (events_.front().type != EventType::kSend) {
+ return false;
+ }
+
+ std::string message(reinterpret_cast<const char*>(data), length);
+ if (events_.front().message != message) {
+ return false;
+ }
+
+ bool return_value = events_.front().status;
+ events_.pop();
+ return return_value;
+}
+
+// Mock out multi-buffer send to be one large send, since that's what it should looks like from
+// the user's perspective.
+bool SocketMockFuzz::Send(std::vector<cutils_socket_buffer_t> buffers) {
+ std::string data;
+ for (const auto& buffer : buffers) {
+ data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);
+ }
+ return Send(data.data(), data.size());
+}
+
+ssize_t SocketMockFuzz::Receive(void* data, size_t length, int /*timeout_ms*/) {
+ if (events_.empty()) {
+ return -1;
+ }
+
+ const Event& event = events_.front();
+ if (event.type != EventType::kReceive) {
+ return -1;
+ }
+
+ const std::string& message = event.message;
+ if (message.length() > length) {
+ return -1;
+ }
+
+ receive_timed_out_ = event.status;
+ ssize_t return_value = message.length();
+
+ // Empty message indicates failure.
+ if (message.empty()) {
+ return_value = -1;
+ } else {
+ memcpy(data, message.data(), message.length());
+ }
+
+ events_.pop();
+ return return_value;
+}
+
+int SocketMockFuzz::Close() {
+ return 0;
+}
+
+std::unique_ptr<Socket> SocketMockFuzz::Accept() {
+ if (events_.empty()) {
+ return nullptr;
+ }
+
+ if (events_.front().type != EventType::kAccept) {
+ return nullptr;
+ }
+
+ std::unique_ptr<Socket> sock = std::move(events_.front().sock);
+ events_.pop();
+ return sock;
+}
+
+void SocketMockFuzz::ExpectSend(std::string message) {
+ events_.push(Event(EventType::kSend, std::move(message), true, nullptr));
+}
+
+void SocketMockFuzz::ExpectSendFailure(std::string message) {
+ events_.push(Event(EventType::kSend, std::move(message), false, nullptr));
+}
+
+void SocketMockFuzz::AddReceive(std::string message) {
+ events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
+}
+
+void SocketMockFuzz::AddReceiveTimeout() {
+ events_.push(Event(EventType::kReceive, "", true, nullptr));
+}
+
+void SocketMockFuzz::AddReceiveFailure() {
+ events_.push(Event(EventType::kReceive, "", false, nullptr));
+}
+
+void SocketMockFuzz::AddAccept(std::unique_ptr<Socket> sock) {
+ events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
+}
+
+SocketMockFuzz::Event::Event(EventType _type, std::string _message, ssize_t _status,
+ std::unique_ptr<Socket> _sock)
+ : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/fuzzer/socket_mock_fuzz.h b/fastboot/fuzzer/socket_mock_fuzz.h
new file mode 100644
index 0000000..67bd0d6
--- /dev/null
+++ b/fastboot/fuzzer/socket_mock_fuzz.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "socket.h"
+
+class SocketMockFuzz : public Socket {
+ public:
+ SocketMockFuzz();
+ ~SocketMockFuzz() override;
+
+ bool Send(const void* data, size_t length) override;
+ bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
+ ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+ int Close() override;
+ virtual std::unique_ptr<Socket> Accept();
+
+ // Adds an expectation for Send().
+ void ExpectSend(std::string message);
+
+ // Adds an expectation for Send() that returns false.
+ void ExpectSendFailure(std::string message);
+
+ // Adds data to provide for Receive().
+ void AddReceive(std::string message);
+
+ // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
+ void AddReceiveTimeout();
+
+ // Adds a Receive() failure after which ReceiveTimedOut() will return false.
+ void AddReceiveFailure();
+
+ // Adds a Socket to return from Accept().
+ void AddAccept(std::unique_ptr<Socket> sock);
+
+ private:
+ enum class EventType { kSend, kReceive, kAccept };
+
+ struct Event {
+ Event(EventType _type, std::string _message, ssize_t _status,
+ std::unique_ptr<Socket> _sock);
+
+ EventType type;
+ std::string message;
+ bool status; // Return value for Send() or timeout status for Receive().
+ std::unique_ptr<Socket> sock;
+ };
+
+ std::queue<Event> events_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketMockFuzz);
+};
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9b6c3dd..609bd11 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -130,10 +130,12 @@
if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
const auto arg = flag.substr(equal_sign + 1);
if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
- if (!ParseInt(arg, &entry->reserved_size)) {
+ off64_t size_in_4k_blocks;
+ if (!ParseInt(arg, &size_in_4k_blocks, static_cast<off64_t>(0),
+ std::numeric_limits<off64_t>::max() >> 12)) {
LWARNING << "Warning: reserve_root= flag malformed: " << arg;
} else {
- entry->reserved_size <<= 12;
+ entry->reserved_size = size_in_4k_blocks << 12;
}
} else if (StartsWith(flag, "lowerdir=")) {
entry->lowerdir = std::move(arg);
@@ -442,7 +444,81 @@
return "";
}
-bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ * /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ * /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
+ std::set<std::string> boot_devices;
+
+ for (const auto& entry : fstab) {
+ std::string blk_device = entry.blk_device;
+ // Skips blk_device that doesn't conform to the format.
+ if (!android::base::StartsWith(blk_device, "/dev/block") ||
+ android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+ android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+ continue;
+ }
+ // Skips non-by_name blk_device.
+ // /dev/block/<type>/<device>/by-name/<partition>
+ // ^ slash_by_name
+ auto slash_by_name = blk_device.find("/by-name");
+ if (slash_by_name == std::string::npos) continue;
+ blk_device.erase(slash_by_name); // erases /by-name/<partition>
+
+ // Erases /dev/block/, now we have <type>/<device>
+ blk_device.erase(0, std::string("/dev/block/").size());
+
+ // <type>/<device>
+ // ^ first_slash
+ auto first_slash = blk_device.find('/');
+ if (first_slash == std::string::npos) continue;
+
+ auto boot_device = blk_device.substr(first_slash + 1);
+ if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+ }
+
+ return boot_devices;
+}
+
+FstabEntry BuildDsuUserdataFstabEntry() {
+ constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+
+ FstabEntry userdata = {
+ .blk_device = "userdata_gsi",
+ .mount_point = "/data",
+ .fs_type = "ext4",
+ .flags = kFlags,
+ .reserved_size = 128 * 1024 * 1024,
+ };
+ userdata.fs_mgr_flags.wait = true;
+ userdata.fs_mgr_flags.check = true;
+ userdata.fs_mgr_flags.logical = true;
+ userdata.fs_mgr_flags.quota = true;
+ userdata.fs_mgr_flags.late_mount = true;
+ userdata.fs_mgr_flags.formattable = true;
+ return userdata;
+}
+
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+ auto iter = std::remove_if(fstab->begin(), fstab->end(),
+ [&](const auto& entry) { return entry.mount_point == mount_point; });
+ if (iter != fstab->end()) {
+ fstab->erase(iter, fstab->end());
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
ssize_t len;
size_t alloc_len = 0;
char *line = NULL;
@@ -528,80 +604,6 @@
return false;
}
-/* Extracts <device>s from the by-name symlinks specified in a fstab:
- * /dev/block/<type>/<device>/by-name/<partition>
- *
- * <type> can be: platform, pci or vbd.
- *
- * For example, given the following entries in the input fstab:
- * /dev/block/platform/soc/1da4000.ufshc/by-name/system
- * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
- * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
- */
-std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
- std::set<std::string> boot_devices;
-
- for (const auto& entry : fstab) {
- std::string blk_device = entry.blk_device;
- // Skips blk_device that doesn't conform to the format.
- if (!android::base::StartsWith(blk_device, "/dev/block") ||
- android::base::StartsWith(blk_device, "/dev/block/by-name") ||
- android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
- continue;
- }
- // Skips non-by_name blk_device.
- // /dev/block/<type>/<device>/by-name/<partition>
- // ^ slash_by_name
- auto slash_by_name = blk_device.find("/by-name");
- if (slash_by_name == std::string::npos) continue;
- blk_device.erase(slash_by_name); // erases /by-name/<partition>
-
- // Erases /dev/block/, now we have <type>/<device>
- blk_device.erase(0, std::string("/dev/block/").size());
-
- // <type>/<device>
- // ^ first_slash
- auto first_slash = blk_device.find('/');
- if (first_slash == std::string::npos) continue;
-
- auto boot_device = blk_device.substr(first_slash + 1);
- if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
- }
-
- return boot_devices;
-}
-
-FstabEntry BuildDsuUserdataFstabEntry() {
- constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
-
- FstabEntry userdata = {
- .blk_device = "userdata_gsi",
- .mount_point = "/data",
- .fs_type = "ext4",
- .flags = kFlags,
- .reserved_size = 128 * 1024 * 1024,
- };
- userdata.fs_mgr_flags.wait = true;
- userdata.fs_mgr_flags.check = true;
- userdata.fs_mgr_flags.logical = true;
- userdata.fs_mgr_flags.quota = true;
- userdata.fs_mgr_flags.late_mount = true;
- userdata.fs_mgr_flags.formattable = true;
- return userdata;
-}
-
-bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
- auto iter = std::remove_if(fstab->begin(), fstab->end(),
- [&](const auto& entry) { return entry.mount_point == mount_point; });
- if (iter != fstab->end()) {
- fstab->erase(iter, fstab->end());
- return true;
- }
- return false;
-}
-
-} // namespace
-
void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
const std::vector<std::string>& dsu_partitions) {
static constexpr char kDsuKeysDir[] = "/avb";
@@ -707,7 +709,7 @@
bool is_proc_mounts = path == "/proc/mounts";
Fstab fstab;
- if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
+ if (!ReadFstabFromFp(fstab_file.get(), is_proc_mounts, &fstab)) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
return false;
}
@@ -762,7 +764,7 @@
return false;
}
- if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
+ if (!ReadFstabFromFp(fstab_file.get(), false, fstab)) {
if (verbose) {
LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
<< fstab_buf;
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
new file mode 100644
index 0000000..f0afd28
--- /dev/null
+++ b/fs_mgr/fuzz/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_fuzz {
+ name: "libfstab_fuzzer",
+ srcs: [
+ "fs_mgr_fstab_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libfstab",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+
+ dictionary: "fstab.dict",
+ fuzz_config: {
+ cc: [
+ "yochiang@google.com",
+ ],
+ },
+}
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
new file mode 100644
index 0000000..1fddbf8
--- /dev/null
+++ b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <cstdio>
+
+#include <fstab/fstab.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+ fmemopen(static_cast<void*>(const_cast<uint8_t*>(data)), size, "r"), fclose);
+ if (fstab_file == nullptr) {
+ return 0;
+ }
+ android::fs_mgr::Fstab fstab;
+ android::fs_mgr::ReadFstabFromFp(fstab_file.get(), /* proc_mounts= */ false, &fstab);
+ return 0;
+}
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/fuzz/fstab.dict
new file mode 100644
index 0000000..84dddf7
--- /dev/null
+++ b/fs_mgr/fuzz/fstab.dict
@@ -0,0 +1,70 @@
+"#"
+"="
+","
+"f2fs"
+
+# mount flags
+"noatime"
+"noexec"
+"nosuid"
+"nodev"
+"nodiratime"
+"ro"
+"rw"
+"sync"
+"remount"
+"bind"
+"rec"
+"unbindable"
+"private"
+"slave"
+"shared"
+"defaults"
+
+# fs_mgr flags
+"wait"
+"check"
+"nonremovable"
+"recoveryonly"
+"noemulatedsd"
+"notrim"
+"verify"
+"formattable"
+"slotselect"
+"latemount"
+"nofail"
+"verifyatboot"
+"quota"
+"avb"
+"logical"
+"checkpoint=block"
+"checkpoint=fs"
+"first_stage_mount"
+"slotselect_other"
+"fsverity"
+"metadata_csum"
+"fscompress"
+"overlayfs_remove_missing_lowerdir"
+
+# fs_mgr flags that expect an argument
+"reserve_root="
+"lowerdir="
+"encryptable="
+"voldmanaged="
+"length="
+"swapprio="
+"zramsize="
+"forceencrypt="
+"fileencryption="
+"forcefdeorfbe="
+"max_comp_streams="
+"reservedsize="
+"readahead_size_kb="
+"eraseblk="
+"logicalblk="
+"avb_keys="
+"avb="
+"keydirectory="
+"metadata_encryption="
+"sysfs_path="
+"zram_backingdev_size="
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 9a4ed46..d0f32a3 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -99,6 +99,9 @@
// Unless explicitly requested, a lookup on mount point should always return the 1st one.
using Fstab = std::vector<FstabEntry>;
+// Exported for testability. Regular users should use ReadFstabFromfile().
+bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out);
+
bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
bool ReadDefaultFstab(Fstab* fstab);
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 8acb885..71dadd0 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -498,24 +498,6 @@
return IsFilePinned(fd, file_path, sfs.f_type);
}
-static bool CountFiemapExtents(int file_fd, const std::string& file_path, uint32_t* num_extents) {
- struct fiemap fiemap = {};
- fiemap.fm_start = 0;
- fiemap.fm_length = UINT64_MAX;
- fiemap.fm_flags = FIEMAP_FLAG_SYNC;
- fiemap.fm_extent_count = 0;
-
- if (ioctl(file_fd, FS_IOC_FIEMAP, &fiemap)) {
- PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
- return false;
- }
-
- if (num_extents) {
- *num_extents = fiemap.fm_mapped_extents;
- }
- return true;
-}
-
static bool IsValidExtent(const fiemap_extent* extent, std::string_view file_path) {
if (extent->fe_flags & kUnsupportedExtentFlags) {
LOG(ERROR) << "Extent at location " << extent->fe_logical << " of file " << file_path
@@ -530,12 +512,12 @@
}
static bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,
- uint32_t num_extents, std::string_view file_path) {
- if (num_extents == 0) return false;
-
+ std::string_view file_path) {
+ uint32_t num_extents = fiemap->fm_mapped_extents;
const struct fiemap_extent* last_extent = &fiemap->fm_extents[num_extents - 1];
if (!IsLastExtent(last_extent)) {
- LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path;
+ LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path
+ << " num_extents=" << num_extents << " max_extents=" << kMaxExtents;
return false;
}
@@ -577,21 +559,7 @@
static bool ReadFiemap(int file_fd, const std::string& file_path,
std::vector<struct fiemap_extent>* extents) {
- uint32_t num_extents;
- if (!CountFiemapExtents(file_fd, file_path, &num_extents)) {
- return false;
- }
- if (num_extents == 0) {
- LOG(ERROR) << "File " << file_path << " has zero extents";
- return false;
- }
- if (num_extents > kMaxExtents) {
- LOG(ERROR) << "File has " << num_extents << ", maximum is " << kMaxExtents << ": "
- << file_path;
- return false;
- }
-
- uint64_t fiemap_size = sizeof(struct fiemap) + num_extents * sizeof(struct fiemap_extent);
+ uint64_t fiemap_size = sizeof(struct fiemap) + kMaxExtents * sizeof(struct fiemap_extent);
auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
if (buffer == nullptr) {
LOG(ERROR) << "Failed to allocate memory for fiemap";
@@ -603,19 +571,13 @@
fiemap->fm_length = UINT64_MAX;
// make sure file is synced to disk before we read the fiemap
fiemap->fm_flags = FIEMAP_FLAG_SYNC;
- fiemap->fm_extent_count = num_extents;
+ fiemap->fm_extent_count = kMaxExtents;
if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
return false;
}
- if (fiemap->fm_mapped_extents != num_extents) {
- LOG(ERROR) << "FIEMAP returned unexpected extent count (" << num_extents
- << " expected, got " << fiemap->fm_mapped_extents << ") for file: " << file_path;
- return false;
- }
-
- return FiemapToExtents(fiemap, extents, num_extents, file_path);
+ return FiemapToExtents(fiemap, extents, file_path);
}
static bool ReadFibmap(int file_fd, const std::string& file_path,
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 801c446..37319fe 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 30545
balsini@google.com
dvander@google.com
elsk@google.com
diff --git a/init/README.md b/init/README.md
index 64c6b1c..6c29b07 100644
--- a/init/README.md
+++ b/init/README.md
@@ -733,7 +733,10 @@
`verity_update_state`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
- because fs\_mgr can't set them directly itself.
+ because fs\_mgr can't set them directly itself. This is required since
+ Android 12, because CtsNativeVerifiedBootTestCases will read property
+ "partition.${partition}.verified.hash_alg" to check that sha1 is not used.
+ See https://r.android.com/1546980 for more details.
`wait <path> [ <timeout> ]`
> Poll for the existence of the given file and return when found,
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index ac6b7b2..5f34cc4 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -99,7 +99,7 @@
std::string vbmeta_digest = GetProperty("ro.boot.vbmeta.digest", "");
ASSERT_GE(vbmeta_digest.size(), 8u);
- std::string build_id = GetProperty("ro.boot.build.id", "");
+ std::string build_id = GetProperty("ro.build.id", "");
// Check that the build id is constructed with the prefix of vbmeta digest
std::string expected_build_id = legacy_build_id + "." + vbmeta_digest.substr(0, 8);
ASSERT_EQ(expected_build_id, build_id);
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index c471fa0..e65fe92 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -130,6 +130,7 @@
#define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */
#define AID_ARTD 1082 /* ART Service daemon */
#define AID_UWB 1083 /* UWB subsystem */
+#define AID_THREAD_NETWORK 1084 /* Thread Network subsystem */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 449a505..45d3c7c 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -183,7 +183,19 @@
}
]
},
-
+ {
+ "Name": "Dex2oatPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpu",
+ "Path": "dex2oat"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
"Actions": [
@@ -638,7 +650,7 @@
},
{
"Name": "Dex2OatBootComplete",
- "Profiles": [ "SCHED_SP_BACKGROUND" ]
+ "Profiles": [ "Dex2oatPerformance", "LowIoPriority", "TimerSlackHigh" ]
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
index 56053e0..e7be548 100644
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -117,7 +117,19 @@
}
]
},
-
+ {
+ "Name": "Dex2oatPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
"Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
index 52279b8..6174c8d 100644
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -117,7 +117,19 @@
}
]
},
-
+ {
+ "Name": "Dex2oatPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
"Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
index 56053e0..e7be548 100644
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -117,7 +117,19 @@
}
]
},
-
+ {
+ "Name": "Dex2oatPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
"Actions": [
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index cf74e65..e935f99 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -194,22 +194,39 @@
fd_.reset(FDS_NOT_CACHED);
}
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
if (tid <= 0) {
return true;
}
std::string value = std::to_string(tid);
- if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
- // If the thread is in the process of exiting, don't flag an error
- if (errno != ESRCH) {
- PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
- return false;
- }
+ if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {
+ return true;
}
- return true;
+ // If the thread is in the process of exiting, don't flag an error
+ if (errno == ESRCH) {
+ return true;
+ }
+
+ // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
+ if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
+ // This is an abnormal case happening only in testing, so report it only once
+ static bool empty_cpuset_reported = false;
+
+ if (empty_cpuset_reported) {
+ return true;
+ }
+
+ LOG(ERROR) << "Failed to add task '" << value
+ << "' into cpuset because all cpus in that cpuset are offline";
+ empty_cpuset_reported = true;
+ } else {
+ PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+ }
+
+ return false;
}
bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
@@ -219,7 +236,7 @@
PLOG(WARNING) << "Failed to open " << procs_path;
return false;
}
- if (!AddTidToCgroup(pid, tmp_fd)) {
+ if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -231,7 +248,7 @@
std::lock_guard<std::mutex> lock(fd_mutex_);
if (IsFdValid()) {
// fd is cached, reuse it
- if (!AddTidToCgroup(tid, fd_)) {
+ if (!AddTidToCgroup(tid, fd_, controller()->name())) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -256,7 +273,7 @@
PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
return false;
}
- if (!AddTidToCgroup(tid, tmp_fd)) {
+ if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 25a84b0..97c38f4 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -134,7 +134,7 @@
mutable std::mutex fd_mutex_;
static bool IsAppDependentPath(const std::string& path);
- static bool AddTidToCgroup(int tid, int fd);
+ static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
};
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 3b6cfd8..515cc10 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -31,14 +31,41 @@
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/personality.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <android-base/parseint.h>
+#include <log/log.h>
+#include <sysutils/NetlinkEvent.h>
+
+using android::base::ParseInt;
/* From kernel's net/netfilter/xt_quota2.c */
const int LOCAL_QLOG_NL_EVENT = 112;
const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
-/* From deprecated ipt_ULOG.h to parse QLOG_NL_EVENT. */
+/******************************************************************************
+ * WARNING: HERE BE DRAGONS! *
+ * *
+ * This is here to provide for compatibility with both 32 and 64-bit kernels *
+ * from 32-bit userspace. *
+ * *
+ * The kernel definition of this struct uses types (like long) that are not *
+ * the same across 32-bit and 64-bit builds, and there is no compatibility *
+ * layer to fix it up before it reaches userspace. *
+ * As such we need to detect the bit-ness of the kernel and deal with it. *
+ * *
+ ******************************************************************************/
+
+/*
+ * This is the verbatim kernel declaration from net/netfilter/xt_quota2.c,
+ * it is *NOT* of a well defined layout and is included here for compile
+ * time assertions only.
+ *
+ * It got there from deprecated ipt_ULOG.h to parse QLOG_NL_EVENT.
+ */
#define ULOG_MAC_LEN 80
#define ULOG_PREFIX_LEN 32
typedef struct ulog_packet_msg {
@@ -55,11 +82,117 @@
unsigned char payload[0];
} ulog_packet_msg_t;
-#include <android-base/parseint.h>
-#include <log/log.h>
-#include <sysutils/NetlinkEvent.h>
+// On Linux int is always 32 bits, while sizeof(long) == sizeof(void*),
+// thus long on a 32-bit Linux kernel is 32-bits, like int always is
+typedef int long32;
+typedef unsigned int ulong32;
+static_assert(sizeof(long32) == 4);
+static_assert(sizeof(ulong32) == 4);
-using android::base::ParseInt;
+// Here's the same structure definition with the assumption the kernel
+// is compiled for 32-bits.
+typedef struct {
+ ulong32 mark;
+ long32 timestamp_sec;
+ long32 timestamp_usec;
+ unsigned int hook;
+ char indev_name[IFNAMSIZ];
+ char outdev_name[IFNAMSIZ];
+ ulong32 data_len;
+ char prefix[ULOG_PREFIX_LEN];
+ unsigned char mac_len;
+ unsigned char mac[ULOG_MAC_LEN];
+ unsigned char payload[0];
+} ulog_packet_msg32_t;
+
+// long on a 64-bit kernel is 64-bits with 64-bit alignment,
+// while long long is 64-bit but may have 32-bit aligment.
+typedef long long __attribute__((__aligned__(8))) long64;
+typedef unsigned long long __attribute__((__aligned__(8))) ulong64;
+static_assert(sizeof(long64) == 8);
+static_assert(sizeof(ulong64) == 8);
+
+// Here's the same structure definition with the assumption the kernel
+// is compiled for 64-bits.
+typedef struct {
+ ulong64 mark;
+ long64 timestamp_sec;
+ long64 timestamp_usec;
+ unsigned int hook;
+ char indev_name[IFNAMSIZ];
+ char outdev_name[IFNAMSIZ];
+ ulong64 data_len;
+ char prefix[ULOG_PREFIX_LEN];
+ unsigned char mac_len;
+ unsigned char mac[ULOG_MAC_LEN];
+ unsigned char payload[0];
+} ulog_packet_msg64_t;
+
+// One expects the 32-bit version to be smaller than the 64-bit version.
+static_assert(sizeof(ulog_packet_msg32_t) < sizeof(ulog_packet_msg64_t));
+// And either way the 'native' version should match either the 32 or 64 bit one.
+static_assert(sizeof(ulog_packet_msg_t) == sizeof(ulog_packet_msg32_t) ||
+ sizeof(ulog_packet_msg_t) == sizeof(ulog_packet_msg64_t));
+
+// In practice these sizes are always simply (for both x86 and arm):
+static_assert(sizeof(ulog_packet_msg32_t) == 168);
+static_assert(sizeof(ulog_packet_msg64_t) == 192);
+
+// Figure out the bitness of userspace.
+// Trivial and known at compile time.
+static bool isUserspace64bit(void) {
+ return sizeof(long) == 8;
+}
+
+// Figure out the bitness of the kernel.
+static bool isKernel64Bit(void) {
+ // a 64-bit userspace requires a 64-bit kernel
+ if (isUserspace64bit()) return true;
+
+ static bool init = false;
+ static bool cache = false;
+ if (init) return cache;
+
+ // Retrieve current personality - on Linux this system call *cannot* fail.
+ int p = personality(0xffffffff);
+ // But if it does just assume kernel and userspace (which is 32-bit) match...
+ if (p == -1) return false;
+
+ // This will effectively mask out the bottom 8 bits, and switch to 'native'
+ // personality, and then return the previous personality of this thread
+ // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
+ int q = personality((p & ~PER_MASK) | PER_LINUX);
+ // Per man page this theoretically could error out with EINVAL,
+ // but kernel code analysis suggests setting PER_LINUX cannot fail.
+ // Either way, assume kernel and userspace (which is 32-bit) match...
+ if (q != p) return false;
+
+ struct utsname u;
+ (void)uname(&u); // only possible failure is EFAULT, but u is on stack.
+
+ // Switch back to previous personality.
+ // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
+ // but then we wouldn't have fetched 'p' from the kernel in the first place.
+ // Either way there's nothing meaningul we can do in case of error.
+ // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
+ // really hurt us either. We're really just switching back to be 'clean'.
+ (void)personality(p);
+
+ // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
+ // x86_64 i686 aarch64 armv7l
+ // additionally observed on arm device:
+ // armv8l
+ // presumably also might just be possible:
+ // i386 i486 i586
+ // and there might be other weird arm32 cases.
+ // We note that the 64 is present in both 64-bit archs,
+ // and in general is likely to be present in only 64-bit archs.
+ cache = !!strstr(u.machine, "64");
+ init = true;
+ return cache;
+}
+
+/******************************************************************************/
NetlinkEvent::NetlinkEvent() {
mAction = Action::kUnknown;
@@ -280,13 +413,22 @@
* Parse a QLOG_NL_EVENT message.
*/
bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {
- const char *devname;
- ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh);
- if (!checkRtNetlinkLength(nh, sizeof(*pm)))
- return false;
+ const char* alert;
+ const char* devname;
- devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
- asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
+ if (isKernel64Bit()) {
+ ulog_packet_msg64_t* pm64 = (ulog_packet_msg64_t*)NLMSG_DATA(nh);
+ if (!checkRtNetlinkLength(nh, sizeof(*pm64))) return false;
+ alert = pm64->prefix;
+ devname = pm64->indev_name[0] ? pm64->indev_name : pm64->outdev_name;
+ } else {
+ ulog_packet_msg32_t* pm32 = (ulog_packet_msg32_t*)NLMSG_DATA(nh);
+ if (!checkRtNetlinkLength(nh, sizeof(*pm32))) return false;
+ alert = pm32->prefix;
+ devname = pm32->indev_name[0] ? pm32->indev_name : pm32->outdev_name;
+ }
+
+ asprintf(&mParams[0], "ALERT_NAME=%s", alert);
asprintf(&mParams[1], "INTERFACE=%s", devname);
mSubsystem = strdup("qlog");
mAction = Action::kChange;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3831693..27fa059 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -155,6 +155,7 @@
mkdir /dev/cpuctl/rt
mkdir /dev/cpuctl/system
mkdir /dev/cpuctl/system-background
+ mkdir /dev/cpuctl/dex2oat
chown system system /dev/cpuctl
chown system system /dev/cpuctl/foreground
chown system system /dev/cpuctl/background
@@ -162,6 +163,7 @@
chown system system /dev/cpuctl/rt
chown system system /dev/cpuctl/system
chown system system /dev/cpuctl/system-background
+ chown system system /dev/cpuctl/dex2oat
chown system system /dev/cpuctl/tasks
chown system system /dev/cpuctl/foreground/tasks
chown system system /dev/cpuctl/background/tasks
@@ -169,6 +171,7 @@
chown system system /dev/cpuctl/rt/tasks
chown system system /dev/cpuctl/system/tasks
chown system system /dev/cpuctl/system-background/tasks
+ chown system system /dev/cpuctl/dex2oat/tasks
chmod 0664 /dev/cpuctl/tasks
chmod 0664 /dev/cpuctl/foreground/tasks
chmod 0664 /dev/cpuctl/background/tasks
@@ -176,6 +179,7 @@
chmod 0664 /dev/cpuctl/rt/tasks
chmod 0664 /dev/cpuctl/system/tasks
chmod 0664 /dev/cpuctl/system-background/tasks
+ chmod 0664 /dev/cpuctl/dex2oat/tasks
# Create a cpu group for NNAPI HAL processes
mkdir /dev/cpuctl/nnapi-hal
@@ -1097,6 +1101,9 @@
# Define default initial receive window size in segments.
setprop net.tcp_def_init_rwnd 60
+ # Update dm-verity state and set partition.*.verified properties.
+ verity_update_state
+
# Start standard binderized HAL daemons
class_start hal
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 7bd1d10..99d9e56 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -117,6 +117,7 @@
"libkeymint",
"liblog",
"libtrusty",
+ "libutils",
],
required: [
"android.hardware.hardware_keystore.xml",
@@ -142,6 +143,7 @@
"libtrusty",
"libhardware",
"libkeymaster_messages",
+ "libutils",
"libxml2",
],
export_include_dirs: ["include"],
@@ -169,6 +171,7 @@
"libtrusty",
"libhardware",
"libkeymaster_messages",
+ "libutils",
"libxml2",
],
cflags: [
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 2d44009..db1a9f4 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -19,6 +19,7 @@
// TODO: make this generic in libtrusty
#include <errno.h>
+#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
@@ -33,11 +34,15 @@
#include <trusty_keymaster/ipc/keymaster_ipc.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+#include <utils/Timers.h>
#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
static int handle_ = -1;
+static const int timeout_ms = 10 * 1000;
+static const int max_timeout_ms = 60 * 1000;
+
int trusty_keymaster_connect() {
int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
if (rc < 0) {
@@ -84,7 +89,38 @@
msg->cmd = cmd;
memcpy(msg->payload, in, in_size);
+ nsecs_t start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);
+ bool timed_out = false;
+ int poll_timeout_ms = timeout_ms;
+ while (true) {
+ struct pollfd pfd;
+ pfd.fd = handle_;
+ pfd.events = POLLOUT;
+ pfd.revents = 0;
+
+ int p = poll(&pfd, 1, poll_timeout_ms);
+ if (p == 0) {
+ ALOGW("write for cmd %d is taking more than %lld nsecs", cmd,
+ (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+ timed_out = true;
+ poll_timeout_ms *= 2;
+ if (poll_timeout_ms > max_timeout_ms) {
+ poll_timeout_ms = max_timeout_ms;
+ }
+ continue;
+ } else if (p < 0) {
+ ALOGE("write poll error: %d", errno);
+ } else if (pfd.revents != POLLOUT) {
+ ALOGW("unexpected poll() result: %d", pfd.revents);
+ }
+ break;
+ }
+
ssize_t rc = write(handle_, msg, msg_size);
+ if (timed_out) {
+ ALOGW("write for cmd %d finished after %lld nsecs", cmd,
+ (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+ }
free(msg);
if (rc < 0) {
@@ -122,8 +158,37 @@
return -EOVERFLOW;
}
iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};
+ start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);
+ timed_out = false;
+ poll_timeout_ms = timeout_ms;
+ while (true) {
+ struct pollfd pfd;
+ pfd.fd = handle_;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ int p = poll(&pfd, 1, poll_timeout_ms);
+ if (p == 0) {
+ ALOGW("readv for cmd %d is taking more than %lld nsecs", cmd,
+ (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+ timed_out = true;
+ poll_timeout_ms *= 2;
+ if (poll_timeout_ms > max_timeout_ms) {
+ poll_timeout_ms = max_timeout_ms;
+ }
+ continue;
+ } else if (p < 0) {
+ ALOGE("read poll error: %d", errno);
+ } else if (pfd.revents != POLLIN) {
+ ALOGW("unexpected poll() result: %d", pfd.revents);
+ }
+ break;
+ }
rc = readv(handle_, iov, 2);
+ if (timed_out) {
+ ALOGW("readv for cmd %d finished after %lld nsecs", cmd,
+ (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+ }
if (rc < 0) {
ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
strerror(errno));
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index 4060278..d5a77fb 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -31,7 +31,7 @@
template <typename T, class... Args>
std::shared_ptr<T> addService(Args&&... args) {
- std::shared_ptr<T> service = std::make_shared<T>(std::forward<Args>(args)...);
+ std::shared_ptr<T> service = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
auto instanceName = std::string(T::descriptor) + "/default";
LOG(ERROR) << "Adding service instance: " << instanceName;
auto status = AServiceManager_addService(service->asBinder().get(), instanceName.c_str());