Merge "x86_64: mmap rnd bits: Allow min bits of less than 32" into main
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 3b8866e..2d55e5a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -461,6 +461,14 @@
{"reboot,smpl_timeout,pmic,main", 229},
{"reboot,ota,.*", 230},
{"reboot,periodic,.*", 231},
+ {"reboot,early,abl", 232},
+ {"reboot,early,bl2", 233},
+ {"reboot,longkey,pmic_cold", 234},
+ {"reboot,longkey,master_dc", 235},
+ {"reboot,ocp2,pmic,if", 236},
+ {"reboot,ocp,pmic,if", 237},
+ {"reboot,fship", 238},
+ {"reboot,ocp,.*", 239},
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
index a3e171b..82ef7b6 100644
--- a/debuggerd/common/include/dump_type.h
+++ b/debuggerd/common/include/dump_type.h
@@ -28,26 +28,24 @@
kDebuggerdTombstoneProto,
};
-inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
- switch (rhs) {
+inline const char* get_dump_type_name(const DebuggerdDumpType& dump_type) {
+ switch (dump_type) {
case kDebuggerdNativeBacktrace:
- stream << "kDebuggerdNativeBacktrace";
- break;
+ return "kDebuggerdNativeBacktrace";
case kDebuggerdTombstone:
- stream << "kDebuggerdTombstone";
- break;
+ return "kDebuggerdTombstone";
case kDebuggerdJavaBacktrace:
- stream << "kDebuggerdJavaBacktrace";
- break;
+ return "kDebuggerdJavaBacktrace";
case kDebuggerdAnyIntercept:
- stream << "kDebuggerdAnyIntercept";
- break;
+ return "kDebuggerdAnyIntercept";
case kDebuggerdTombstoneProto:
- stream << "kDebuggerdTombstoneProto";
- break;
+ return "kDebuggerdTombstoneProto";
default:
- stream << "[unknown]";
+ return "[unknown]";
}
+}
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+ stream << get_dump_type_name(rhs);
return stream;
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 19ff7eb..a6d8226 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -94,7 +94,7 @@
if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
err(1, "sigaction failed"); \
} \
- alarm(seconds); \
+ alarm(seconds * android::base::HwTimeoutMultiplier()); \
auto value = expr; \
int saved_errno = errno; \
if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
@@ -114,7 +114,7 @@
R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
- InterceptStatus* status, DebuggerdDumpType intercept_type) {
+ InterceptResponse* response, DebuggerdDumpType intercept_type) {
intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (intercept_fd->get() == -1) {
@@ -155,18 +155,15 @@
FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
}
- InterceptResponse response;
- rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), response, sizeof(*response)));
if (rc == -1) {
FAIL() << "failed to read response from tombstoned: " << strerror(errno);
} else if (rc == 0) {
FAIL() << "failed to read response from tombstoned (EOF)";
- } else if (rc != sizeof(response)) {
- FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+ } else if (rc != sizeof(*response)) {
+ FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(*response)
<< ", received " << rc;
}
-
- *status = response.status;
}
static bool pac_supported() {
@@ -225,9 +222,10 @@
FAIL() << "crasher hasn't been started";
}
- InterceptStatus status;
- tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &response, intercept_type);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
}
void CrasherTest::FinishIntercept(int* result) {
@@ -1744,9 +1742,10 @@
pid_t pid = 123'456'789 + i;
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
{
unique_fd tombstoned_socket, input_fd;
@@ -1778,9 +1777,10 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error messeage: " << response.error_message;
// Pretend to crash, and then immediately close the socket.
unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -1811,9 +1811,10 @@
pid_t pid = pid_base + dump;
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
{
unique_fd tombstoned_socket, input_fd;
@@ -1838,16 +1839,17 @@
}
}
-TEST(tombstoned, java_trace_intercept_smoke) {
+TEST(tombstoned, intercept_java_trace_smoke) {
// Using a "real" PID is a little dangerous here - if the test fails
// or crashes, we might end up getting a bogus / unreliable stack
// trace.
const pid_t self = getpid();
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
// First connect to tombstoned requesting a native tombstone. This
// should result in a "regular" FD and not the installed intercept.
@@ -1869,25 +1871,96 @@
ASSERT_STREQ("java", outbuf);
}
-TEST(tombstoned, multiple_intercepts) {
+TEST(tombstoned, intercept_multiple_dump_types) {
const pid_t fake_pid = 1'234'567;
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
unique_fd intercept_fd_2, output_fd_2;
- tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
- ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+ tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &response,
+ kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+}
+
+TEST(tombstoned, intercept_bad_pid) {
+ const pid_t fake_pid = -1;
+ unique_fd intercept_fd, output_fd;
+ InterceptResponse response = {};
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kFailed, response.status)
+ << "Error message: " << response.error_message;
+ ASSERT_MATCH(response.error_message, "bad pid");
+}
+
+TEST(tombstoned, intercept_bad_dump_types) {
+ const pid_t fake_pid = 1'234'567;
+ unique_fd intercept_fd, output_fd;
+ InterceptResponse response = {};
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response,
+ static_cast<DebuggerdDumpType>(20));
+ ASSERT_EQ(InterceptStatus::kFailed, response.status)
+ << "Error message: " << response.error_message;
+ ASSERT_MATCH(response.error_message, "bad dump type \\[unknown\\]");
+
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdAnyIntercept);
+ ASSERT_EQ(InterceptStatus::kFailed, response.status)
+ << "Error message: " << response.error_message;
+ ASSERT_MATCH(response.error_message, "bad dump type kDebuggerdAnyIntercept");
+
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstoneProto);
+ ASSERT_EQ(InterceptStatus::kFailed, response.status)
+ << "Error message: " << response.error_message;
+ ASSERT_MATCH(response.error_message, "bad dump type kDebuggerdTombstoneProto");
+}
+
+TEST(tombstoned, intercept_already_registered) {
+ const pid_t fake_pid = 1'234'567;
+ unique_fd intercept_fd1, output_fd1;
+ InterceptResponse response = {};
+ tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ unique_fd intercept_fd2, output_fd2;
+ tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, response.status)
+ << "Error message: " << response.error_message;
+ ASSERT_MATCH(response.error_message, "already registered, type kDebuggerdTombstone");
+}
+
+TEST(tombstoned, intercept_tombstone_proto_matched_to_tombstone) {
+ const pid_t fake_pid = 1'234'567;
+
+ unique_fd intercept_fd, output_fd;
+ InterceptResponse response = {};
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ const char data[] = "tombstone_proto";
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(
+ tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdTombstoneProto));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), data, sizeof(data)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+
+ char outbuf[sizeof(data)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+ ASSERT_STREQ("tombstone_proto", outbuf);
}
TEST(tombstoned, intercept_any) {
const pid_t fake_pid = 1'234'567;
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
const char any[] = "any";
unique_fd tombstoned_socket, input_fd;
@@ -1900,6 +1973,77 @@
ASSERT_STREQ("any", outbuf);
}
+TEST(tombstoned, intercept_any_failed_with_multiple_intercepts) {
+ const pid_t fake_pid = 1'234'567;
+
+ InterceptResponse response = {};
+ unique_fd intercept_fd1, output_fd1;
+ tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ unique_fd intercept_fd2, output_fd2;
+ tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_FALSE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+}
+
+TEST(tombstoned, intercept_multiple_verify_intercept) {
+ // Need to use our pid for java since that will verify the pid.
+ const pid_t fake_pid = getpid();
+
+ InterceptResponse response = {};
+ unique_fd intercept_fd1, output_fd1;
+ tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ unique_fd intercept_fd2, output_fd2;
+ tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ unique_fd intercept_fd3, output_fd3;
+ tombstoned_intercept(fake_pid, &intercept_fd3, &output_fd3, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
+
+ const char native_data[] = "native";
+ unique_fd tombstoned_socket1, input_fd1;
+ ASSERT_TRUE(
+ tombstoned_connect(fake_pid, &tombstoned_socket1, &input_fd1, kDebuggerdNativeBacktrace));
+ ASSERT_TRUE(android::base::WriteFully(input_fd1.get(), native_data, sizeof(native_data)));
+ tombstoned_notify_completion(tombstoned_socket1.get());
+
+ char native_outbuf[sizeof(native_data)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd1.get(), native_outbuf, sizeof(native_outbuf)));
+ ASSERT_STREQ("native", native_outbuf);
+
+ const char java_data[] = "java";
+ unique_fd tombstoned_socket2, input_fd2;
+ ASSERT_TRUE(
+ tombstoned_connect(fake_pid, &tombstoned_socket2, &input_fd2, kDebuggerdJavaBacktrace));
+ ASSERT_TRUE(android::base::WriteFully(input_fd2.get(), java_data, sizeof(java_data)));
+ tombstoned_notify_completion(tombstoned_socket2.get());
+
+ char java_outbuf[sizeof(java_data)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd2.get(), java_outbuf, sizeof(java_outbuf)));
+ ASSERT_STREQ("java", java_outbuf);
+
+ const char tomb_data[] = "tombstone";
+ unique_fd tombstoned_socket3, input_fd3;
+ ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket3, &input_fd3, kDebuggerdTombstone));
+ ASSERT_TRUE(android::base::WriteFully(input_fd3.get(), tomb_data, sizeof(tomb_data)));
+ tombstoned_notify_completion(tombstoned_socket3.get());
+
+ char tomb_outbuf[sizeof(tomb_data)];
+ ASSERT_TRUE(android::base::ReadFully(output_fd3.get(), tomb_outbuf, sizeof(tomb_outbuf)));
+ ASSERT_STREQ("tombstone", tomb_outbuf);
+}
+
TEST(tombstoned, interceptless_backtrace) {
// Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.
auto get_tombstone_timestamps = []() -> std::map<int, time_t> {
@@ -2132,10 +2276,11 @@
TEST(tombstoned, proto_intercept) {
const pid_t self = getpid();
unique_fd intercept_fd, output_fd;
- InterceptStatus status;
- tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
- ASSERT_EQ(InterceptStatus::kRegistered, status);
+ InterceptResponse response = {};
+ tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+ << "Error message: " << response.error_message;
unique_fd tombstoned_socket, text_fd, proto_fd;
ASSERT_TRUE(
@@ -2680,7 +2825,8 @@
}
prev_file = match[1];
- unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release());
+ auto elf_memory = unwindstack::Memory::CreateFileMemory(prev_file, 0);
+ unwindstack::Elf elf(elf_memory);
if (!elf.Init() || !elf.valid()) {
// Skipping invalid elf files.
continue;
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d71fc6c..15f09b3 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,8 +382,10 @@
return "SEGV_MTEAERR";
case SEGV_MTESERR:
return "SEGV_MTESERR";
+ case SEGV_CPERR:
+ return "SEGV_CPERR";
}
- static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code");
+ static_assert(NSIGSEGV == SEGV_CPERR, "missing SEGV_* si_code");
break;
case SIGSYS:
switch (si->si_code) {
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index b60cf5b..212d6dc 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -65,7 +65,7 @@
};
enum class InterceptStatus : uint8_t {
- // Returned when an intercept of a different type has already been
+ // Returned when an intercept of the same type has already been
// registered (and is active) for a given PID.
kFailedAlreadyRegistered,
// Returned in all other failure cases.
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 613e6f5..ac7b431 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -19,7 +19,10 @@
#include <inttypes.h>
#include <sys/types.h>
+#include <limits>
+#include <memory>
#include <unordered_map>
+#include <utility>
#include <event2/event.h>
#include <event2/listener.h>
@@ -36,8 +39,7 @@
using android::base::unique_fd;
static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
- auto intercept = reinterpret_cast<Intercept*>(arg);
- InterceptManager* intercept_manager = intercept->intercept_manager;
+ std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));
CHECK_EQ(sockfd, intercept->sockfd.get());
@@ -46,131 +48,108 @@
// Ownership of intercept differs based on whether we've registered it with InterceptManager.
if (!intercept->registered) {
- delete intercept;
- } else {
- auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
- if (it == intercept_manager->intercepts.end()) {
- LOG(FATAL) << "intercept close callback called after intercept was already removed?";
- }
- if (it->second.get() != intercept) {
- LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
- }
-
- const char* reason;
- if ((event & EV_TIMEOUT) != 0) {
- reason = "due to timeout";
- } else {
- reason = "due to input";
- }
-
- LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
- << intercept->dump_type << " terminated: " << reason;
- intercept_manager->intercepts.erase(it);
+ LOG(WARNING) << "intercept for pid " << intercept->pid << " and type " << intercept->dump_type
+ << " closed before being registered.";
+ return;
}
+
+ const char* reason = (event & EV_TIMEOUT) ? "due to timeout" : "due to input";
+ LOG(INFO) << "intercept for pid " << intercept->pid << " and type " << intercept->dump_type
+ << " terminated: " << reason;
}
-static bool is_intercept_request_valid(const InterceptRequest& request) {
- if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
- return false;
+void InterceptManager::Unregister(Intercept* intercept) {
+ CHECK(intercept->registered);
+ auto pid_entry = intercepts.find(intercept->pid);
+ if (pid_entry == intercepts.end()) {
+ LOG(FATAL) << "No intercepts found for pid " << intercept->pid;
+ }
+ auto& dump_type_hash = pid_entry->second;
+ auto dump_type_entry = dump_type_hash.find(intercept->dump_type);
+ if (dump_type_entry == dump_type_hash.end()) {
+ LOG(FATAL) << "Unknown intercept " << intercept->pid << " " << intercept->dump_type;
+ }
+ if (intercept != dump_type_entry->second) {
+ LOG(FATAL) << "Mismatch pointer trying to unregister intercept " << intercept->pid << " "
+ << intercept->dump_type;
}
- if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
- return false;
+ dump_type_hash.erase(dump_type_entry);
+ if (dump_type_hash.empty()) {
+ intercepts.erase(pid_entry);
}
-
- return true;
}
static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
- auto intercept = reinterpret_cast<Intercept*>(arg);
+ std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));
InterceptManager* intercept_manager = intercept->intercept_manager;
CHECK_EQ(sockfd, intercept->sockfd.get());
if ((ev & EV_TIMEOUT) != 0) {
LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
- goto fail;
+ return;
} else if ((ev & EV_READ) == 0) {
LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
- goto fail;
+ return;
}
- {
- unique_fd rcv_fd;
- InterceptRequest intercept_request;
- ssize_t result =
- ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+ unique_fd rcv_fd;
+ InterceptRequest intercept_request;
+ ssize_t result =
+ ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
- if (result == -1) {
- PLOG(WARNING) << "failed to read from intercept socket";
- goto fail;
- } else if (result != sizeof(intercept_request)) {
- LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
- << sizeof(intercept_request) << ")";
- goto fail;
- }
-
- // Move the received FD to the upper half, in order to more easily notice FD leaks.
- int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
- if (moved_fd == -1) {
- LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
- goto fail;
- }
- rcv_fd.reset(moved_fd);
-
- // We trust the other side, so only do minimal validity checking.
- if (!is_intercept_request_valid(intercept_request)) {
- InterceptResponse response = {};
- response.status = InterceptStatus::kFailed;
- snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
- TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
- goto fail;
- }
-
- intercept->intercept_pid = intercept_request.pid;
- intercept->dump_type = intercept_request.dump_type;
-
- // Check if it's already registered.
- if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
- InterceptResponse response = {};
- response.status = InterceptStatus::kFailedAlreadyRegistered;
- snprintf(response.error_message, sizeof(response.error_message),
- "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
- intercept_request.dump_type);
- TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
- LOG(WARNING) << response.error_message;
- goto fail;
- }
-
- // Let the other side know that the intercept has been registered, now that we know we can't
- // fail. tombstoned is single threaded, so this isn't racy.
- InterceptResponse response = {};
- response.status = InterceptStatus::kRegistered;
- if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
- PLOG(WARNING) << "failed to notify interceptor of registration";
- goto fail;
- }
-
- intercept->output_fd = std::move(rcv_fd);
- intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
- intercept->registered = true;
-
- LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
- << intercept_request.dump_type;
-
- // Register a different read event on the socket so that we can remove intercepts if the socket
- // closes (e.g. if a user CTRL-C's the process that requested the intercept).
- event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
- intercept_close_cb, arg);
-
- struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
- event_add(intercept->intercept_event, &timeout);
+ if (result == -1) {
+ PLOG(WARNING) << "failed to read from intercept socket";
+ return;
+ }
+ if (result != sizeof(intercept_request)) {
+ LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
+ << sizeof(intercept_request) << ")";
+ return;
}
- return;
+ // Move the received FD to the upper half, in order to more easily notice FD leaks.
+ int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
+ if (moved_fd == -1) {
+ LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
+ return;
+ }
+ rcv_fd.reset(moved_fd);
-fail:
- delete intercept;
+ // See if we can properly register the intercept.
+ InterceptResponse response = {};
+ if (!intercept_manager->CanRegister(intercept_request, response)) {
+ TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+ LOG(WARNING) << response.error_message;
+ return;
+ }
+
+ // Let the other side know that the intercept has been registered, now that we know we can't
+ // fail. tombstoned is single threaded, so this isn't racy.
+ response.status = InterceptStatus::kRegistered;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
+ PLOG(WARNING) << "failed to notify interceptor of registration";
+ return;
+ }
+
+ intercept->pid = intercept_request.pid;
+ intercept->dump_type = intercept_request.dump_type;
+ intercept->output_fd = std::move(rcv_fd);
+ intercept_manager->Register(intercept.get());
+
+ LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+ << intercept_request.dump_type;
+
+ // Register a different read event on the socket so that we can remove intercepts if the socket
+ // closes (e.g. if a user CTRL-C's the process that requested the intercept).
+ event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
+ intercept_close_cb, arg);
+
+ // If no request comes in, then this will close the intercept and free the pointer.
+ struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
+ event_add(intercept->intercept_event, &timeout);
+ intercept.release();
}
static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
@@ -187,41 +166,97 @@
event_add(intercept_event, &timeout);
}
+Intercept::~Intercept() {
+ event_free(intercept_event);
+ if (registered) {
+ CHECK(intercept_manager != nullptr);
+ intercept_manager->Unregister(this);
+ }
+}
+
InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
/* backlog */ -1, intercept_socket);
}
-bool dump_types_compatible(DebuggerdDumpType interceptor, DebuggerdDumpType dump) {
- if (interceptor == dump) {
- return true;
+static DebuggerdDumpType canonical_dump_type(const DebuggerdDumpType dump_type) {
+ // kDebuggerdTombstone and kDebuggerdTombstoneProto should be treated as
+ // a single dump_type for intercepts (kDebuggerdTombstone).
+ if (dump_type == kDebuggerdTombstoneProto) {
+ return kDebuggerdTombstone;
}
-
- if (interceptor == kDebuggerdTombstone && dump == kDebuggerdTombstoneProto) {
- return true;
- }
-
- return false;
+ return dump_type;
}
-bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
- android::base::unique_fd* out_fd) {
- auto it = this->intercepts.find(pid);
- if (it == this->intercepts.end()) {
+Intercept* InterceptManager::Get(const pid_t pid, const DebuggerdDumpType dump_type) {
+ auto pid_entry = intercepts.find(pid);
+ if (pid_entry == intercepts.end()) {
+ return nullptr;
+ }
+
+ const auto& dump_type_hash = pid_entry->second;
+ auto dump_type_entry = dump_type_hash.find(canonical_dump_type(dump_type));
+ if (dump_type_entry == dump_type_hash.end()) {
+ if (dump_type != kDebuggerdAnyIntercept) {
+ return nullptr;
+ }
+ // If doing a dump with an any intercept, only allow an any to match
+ // a single intercept. If there are multiple dump types with intercepts
+ // then there would be no way to figure out which to use.
+ if (dump_type_hash.size() != 1) {
+ LOG(WARNING) << "Cannot intercept using kDebuggerdAnyIntercept: there is more than one "
+ "intercept registered for pid "
+ << pid;
+ return nullptr;
+ }
+ dump_type_entry = dump_type_hash.begin();
+ }
+ return dump_type_entry->second;
+}
+
+bool InterceptManager::CanRegister(const InterceptRequest& request, InterceptResponse& response) {
+ if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+ response.status = InterceptStatus::kFailed;
+ snprintf(response.error_message, sizeof(response.error_message),
+ "invalid intercept request: bad pid %" PRId32, request.pid);
+ return false;
+ }
+ if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+ response.status = InterceptStatus::kFailed;
+ snprintf(response.error_message, sizeof(response.error_message),
+ "invalid intercept request: bad dump type %s", get_dump_type_name(request.dump_type));
return false;
}
- if (dump_type == kDebuggerdAnyIntercept) {
- LOG(INFO) << "found registered intercept of type " << it->second->dump_type
- << " for requested type kDebuggerdAnyIntercept";
- } else if (!dump_types_compatible(it->second->dump_type, dump_type)) {
- LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
- << " for requested type: " << dump_type;
+ if (Get(request.pid, request.dump_type) != nullptr) {
+ response.status = InterceptStatus::kFailedAlreadyRegistered;
+ snprintf(response.error_message, sizeof(response.error_message),
+ "pid %" PRId32 " already registered, type %s", request.pid,
+ get_dump_type_name(request.dump_type));
return false;
}
- auto intercept = std::move(it->second);
- this->intercepts.erase(it);
+ return true;
+}
+
+void InterceptManager::Register(Intercept* intercept) {
+ CHECK(!intercept->registered);
+ auto& dump_type_hash = intercepts[intercept->pid];
+ dump_type_hash[canonical_dump_type(intercept->dump_type)] = intercept;
+ intercept->registered = true;
+}
+
+bool InterceptManager::FindIntercept(pid_t pid, DebuggerdDumpType dump_type,
+ android::base::unique_fd* out_fd) {
+ Intercept* intercept = Get(pid, dump_type);
+ if (intercept == nullptr) {
+ return false;
+ }
+
+ if (dump_type != intercept->dump_type) {
+ LOG(INFO) << "found registered intercept of type " << intercept->dump_type
+ << " for requested type " << dump_type;
+ }
LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
<< " and type " << intercept->dump_type;
@@ -230,5 +265,8 @@
TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
*out_fd = std::move(intercept->output_fd);
+ // Delete the intercept data, which will unregister the intercept and remove the timeout event.
+ delete intercept;
+
return true;
}
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index a11d565..dc13aa9 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -28,17 +28,17 @@
#include "dump_type.h"
struct InterceptManager;
+struct InterceptRequest;
+struct InterceptResponse;
struct Intercept {
- ~Intercept() {
- event_free(intercept_event);
- }
+ ~Intercept();
InterceptManager* intercept_manager = nullptr;
event* intercept_event = nullptr;
android::base::unique_fd sockfd;
- pid_t intercept_pid = -1;
+ pid_t pid = -1;
android::base::unique_fd output_fd;
bool registered = false;
DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
@@ -46,12 +46,17 @@
struct InterceptManager {
event_base* base;
- std::unordered_map<pid_t, std::unique_ptr<Intercept>> intercepts;
+ std::unordered_map<pid_t, std::unordered_map<DebuggerdDumpType, Intercept*>> intercepts;
evconnlistener* listener = nullptr;
InterceptManager(event_base* _Nonnull base, int intercept_socket);
InterceptManager(InterceptManager& copy) = delete;
InterceptManager(InterceptManager&& move) = delete;
- bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
+ bool CanRegister(const InterceptRequest& request, InterceptResponse& response);
+ Intercept* Get(const pid_t pid, const DebuggerdDumpType dump_type);
+ void Register(Intercept* intercept);
+ void Unregister(Intercept* intercept);
+
+ bool FindIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
};
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 50558f7..cf7904f 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -283,9 +283,7 @@
static void perform_request(std::unique_ptr<Crash> crash) {
unique_fd output_fd;
- bool intercepted =
- intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
- if (intercepted) {
+ if (intercept_manager->FindIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
if (crash->crash_type == kDebuggerdTombstoneProto) {
crash->output.proto = CrashArtifact::devnull();
}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 56cac88..f85d1de 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -446,3 +446,14 @@
host_supported: true,
export_include_dirs: ["."],
}
+
+python_test_host {
+ name: "fastboot_integration_test",
+ main: "test_fastboot.py",
+ srcs: ["test_fastboot.py"],
+ data: [":fastboot"],
+ test_config: "fastboot_integration_test.xml",
+ test_options: {
+ unit_test: false,
+ },
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 56b90b9..ac2a20f 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1053,8 +1053,10 @@
return false;
}
sparse_file_destroy(s);
+ buf->file_type = FB_BUFFER_SPARSE;
} else {
buf->image_size = sz;
+ buf->file_type = FB_BUFFER_FD;
}
lseek(fd.get(), 0, SEEK_SET);
@@ -1191,6 +1193,15 @@
should_flash_in_userspace(source, partition)) {
return;
}
+
+ // If the image is sparse, moving the footer will simply corrupt the sparse
+ // format, so currently we don't support moving the footer on sparse files.
+ if (buf->file_type == FB_BUFFER_SPARSE) {
+ LOG(ERROR) << "Warning: skip copying " << partition << " image avb footer due to sparse "
+ << "image.";
+ return;
+ }
+
// If overflows and negative, it should be < buf->sz.
int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 2c40890..6a49970 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -57,7 +57,8 @@
};
struct fastboot_buffer {
- enum fb_buffer_type type;
+ fb_buffer_type type;
+ fb_buffer_type file_type;
std::vector<SparsePtr> files;
int64_t sz;
unique_fd fd;
diff --git a/fastboot/fastboot_integration_test.xml b/fastboot/fastboot_integration_test.xml
new file mode 100644
index 0000000..ad14cab
--- /dev/null
+++ b/fastboot/fastboot_integration_test.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config to run fastboot integration tests">
+ <option name="test-suite-tag" value="fastboot_unittests" />
+ <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+ <option name="par-file-name" value="fastboot_integration_test" />
+ <option name="test-timeout" value="1m" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/fastboot/test_fastboot.py b/fastboot/test_fastboot.py
new file mode 100644
index 0000000..adcaec7
--- /dev/null
+++ b/fastboot/test_fastboot.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+"""Tests for the fastboot command line utility.
+"""
+
+import re
+import subprocess
+import unittest
+
+
+class DevicesTest(unittest.TestCase):
+ """Tests for `fastboot devices`."""
+
+
+ def test_devices(self):
+ """Ensure that the format of `fastboot devices` does not change.
+
+ `fastboot devices` should alternate between a line containing the
+ device's serial number and fastboot state and an empty line
+ """
+ output = subprocess.check_output(["fastboot", "devices"])
+
+ previous_line_was_empty = True
+ for line in output.decode().splitlines():
+ if previous_line_was_empty:
+ if not re.match(r"[a-zA-Z\d]+\s+(bootloader|fastbootd)", line):
+ self.fail("%s does not match the expected format <serial no>\\s+(bootloader|fastbootd)" % line)
+ previous_line_was_empty = False
+ else:
+ if line:
+ self.fail("Expected an empty line. Received '%s'" % line)
+ previous_line_was_empty = True
+
+ if len(output) == 0:
+ self.fail("Output is empty. Are any devices connected?")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d55f8d3..faea5eb 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -828,7 +828,14 @@
<< ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
<< "(" << save_errno << ")";
}
- ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+
+ // Let's get the raw dm target, if it's a symlink, since some existing applications
+ // rely on /proc/mounts to find the userdata's dm target path. Don't break that assumption.
+ std::string real_source;
+ if (!android::base::Realpath(source, &real_source)) {
+ real_source = source;
+ }
+ ret = mount(real_source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
opts.c_str());
save_errno = errno;
if (try_f2fs_gc_allowance) gc_allowance += 10;
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
index 2cc0d2d..50d8280 100644
--- a/fs_mgr/fs_mgr_overlayfs_control.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -144,7 +144,8 @@
if (fs_mgr_overlayfs_already_mounted(mount_point)) {
return true;
}
- auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
+ const auto base = GetEncodedBaseDirForMountPoint(mount_point);
+ auto fsrec_mount_point = overlay + "/" + base + "/";
AutoSetFsCreateCon createcon(kOverlayfsFileContext);
if (!createcon.Ok()) {
@@ -286,10 +287,10 @@
}
auto cleanup_all = mount_point.empty();
- const auto partition_name = android::base::Basename(mount_point);
- const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
+ const auto base = GetEncodedBaseDirForMountPoint(mount_point);
+ const auto oldpath = top + (cleanup_all ? "" : ("/" + base));
const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir + ".teardown"
- : top + "/." + partition_name + ".teardown";
+ : top + "/." + base + ".teardown";
auto ret = fs_mgr_rm_all(newpath);
if (!rename(oldpath.c_str(), newpath.c_str())) {
if (change) *change = true;
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index 9f17c06..ae7ed4c 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -60,6 +60,7 @@
constexpr char kLowerdirOption[] = "lowerdir=";
constexpr char kUpperdirOption[] = "upperdir=";
+constexpr char kWorkdirOption[] = "workdir=";
bool fs_mgr_is_dsu_running() {
// Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
@@ -89,6 +90,14 @@
return {kScratchMountPoint, kCacheMountPoint};
}
+std::string GetEncodedBaseDirForMountPoint(const std::string& mount_point) {
+ std::string normalized_path;
+ if (mount_point.empty() || !android::base::Realpath(mount_point, &normalized_path)) {
+ return "";
+ }
+ return android::base::StringReplace(normalized_path, "/", "@", true);
+}
+
static bool fs_mgr_is_dir(const std::string& path) {
struct stat st;
return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
@@ -202,21 +211,6 @@
return has_shared_blocks;
}
-static std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
- if (!fs_mgr_is_dir(mount_point)) return "";
- const auto base = android::base::Basename(mount_point) + "/";
- for (const auto& overlay_mount_point : OverlayMountPoints()) {
- auto dir = overlay_mount_point + "/" + kOverlayTopDir + "/" + base;
- auto upper = dir + kUpperName;
- if (!fs_mgr_is_dir(upper)) continue;
- auto work = dir + kWorkName;
- if (!fs_mgr_is_dir(work)) continue;
- if (access(work.c_str(), R_OK | W_OK)) continue;
- return dir;
- }
- return "";
-}
-
const std::string fs_mgr_mount_point(const std::string& mount_point) {
if ("/"s != mount_point) return mount_point;
return "/system";
@@ -225,16 +219,30 @@
// default options for mount_point, returns empty string for none available.
static std::string fs_mgr_get_overlayfs_options(const FstabEntry& entry) {
const auto mount_point = fs_mgr_mount_point(entry.mount_point);
- auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
- if (candidate.empty()) return "";
- auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
- ",workdir=" + candidate + kWorkName + android::fs_mgr::CheckOverlayfs().mount_flags;
- for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
- if (android::base::StartsWith(flag, "context=")) {
- ret += "," + flag;
- }
+ if (!fs_mgr_is_dir(mount_point)) {
+ return "";
}
- return ret;
+ const auto base = GetEncodedBaseDirForMountPoint(mount_point);
+ if (base.empty()) {
+ return "";
+ }
+ for (const auto& overlay_mount_point : OverlayMountPoints()) {
+ const auto dir = overlay_mount_point + "/" + kOverlayTopDir + "/" + base + "/";
+ const auto upper = dir + kUpperName;
+ const auto work = dir + kWorkName;
+ if (!fs_mgr_is_dir(upper) || !fs_mgr_is_dir(work) || access(work.c_str(), R_OK | W_OK)) {
+ continue;
+ }
+ auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + upper + "," +
+ kWorkdirOption + work + android::fs_mgr::CheckOverlayfs().mount_flags;
+ for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
+ if (android::base::StartsWith(flag, "context=")) {
+ ret += "," + flag;
+ }
+ }
+ return ret;
+ }
+ return "";
}
bool AutoSetFsCreateCon::Set(const std::string& context) {
@@ -373,54 +381,86 @@
auto retval = true;
- struct move_entry {
+ struct MoveEntry {
std::string mount_point;
std::string dir;
bool shared_flag;
};
- std::vector<move_entry> move;
+
+ std::vector<MoveEntry> moved_mounts;
auto parent_private = false;
auto parent_made_private = false;
auto dev_private = false;
auto dev_made_private = false;
- for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+
+ // There could be multiple mount entries with the same mountpoint.
+ // Group these entries together with stable_sort, and keep only the last entry of a group.
+ // Only move mount the last entry in an over mount group, because the other entries are
+ // overshadowed and only the filesystem mounted with the last entry participates in file
+ // pathname resolution.
+ auto mountinfo = ReadMountinfoFromFile("/proc/self/mountinfo");
+ std::stable_sort(mountinfo.begin(), mountinfo.end(), [](const auto& lhs, const auto& rhs) {
+ return lhs.mount_point < rhs.mount_point;
+ });
+ std::reverse(mountinfo.begin(), mountinfo.end());
+ auto erase_from = std::unique(
+ mountinfo.begin(), mountinfo.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.mount_point == rhs.mount_point; });
+ mountinfo.erase(erase_from, mountinfo.end());
+ std::reverse(mountinfo.begin(), mountinfo.end());
+ // mountinfo is reversed twice, so still is in lexical sorted order.
+
+ for (const auto& entry : mountinfo) {
if ((entry.mount_point == mount_point) && !entry.shared_flag) {
parent_private = true;
}
if ((entry.mount_point == "/dev") && !entry.shared_flag) {
dev_private = true;
}
+ }
+ // Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
+ // This could happen if its parent mount is remounted later.
+ if (!parent_private) {
+ parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ }
+
+ for (const auto& entry : mountinfo) {
+ // Find all immediate submounts.
if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
continue;
}
- if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
- return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
- }) != move.end()) {
+ // Exclude duplicated or more specific entries.
+ if (std::find_if(moved_mounts.begin(), moved_mounts.end(), [&entry](const auto& it) {
+ return it.mount_point == entry.mount_point ||
+ android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+ }) != moved_mounts.end()) {
continue;
}
+ // mountinfo is in lexical order, so no need to worry about |entry| being a parent mount of
+ // entries of |moved_mounts|.
// use as the bound directory in /dev.
- AutoSetFsCreateCon createcon;
- auto new_context = fs_mgr_get_context(entry.mount_point);
- if (new_context.empty() || !createcon.Set(new_context)) {
- continue;
- }
- move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
- entry.shared_flag};
- const auto target = mkdtemp(new_entry.dir.data());
- if (!createcon.Restore()) {
- return false;
- }
- if (!target) {
- retval = false;
- PERROR << "temporary directory for MS_BIND";
- continue;
+ MoveEntry new_entry{entry.mount_point, "/dev/TemporaryDir-XXXXXX", entry.shared_flag};
+ {
+ AutoSetFsCreateCon createcon;
+ auto new_context = fs_mgr_get_context(entry.mount_point);
+ if (new_context.empty() || !createcon.Set(new_context)) {
+ continue;
+ }
+ const auto target = mkdtemp(new_entry.dir.data());
+ if (!target) {
+ retval = false;
+ PERROR << "temporary directory for MS_MOVE";
+ continue;
+ }
+ if (!createcon.Restore()) {
+ retval = false;
+ rmdir(new_entry.dir.c_str());
+ continue;
+ }
}
- if (!parent_private && !parent_made_private) {
- parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
- }
if (new_entry.shared_flag) {
new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
}
@@ -429,9 +469,10 @@
if (new_entry.shared_flag) {
fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
}
+ rmdir(new_entry.dir.c_str());
continue;
}
- move.emplace_back(std::move(new_entry));
+ moved_mounts.push_back(std::move(new_entry));
}
// hijack __mount() report format to help triage
@@ -455,7 +496,7 @@
}
// Move submounts back.
- for (const auto& entry : move) {
+ for (const auto& entry : moved_mounts) {
if (!dev_private && !dev_made_private) {
dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
}
@@ -650,22 +691,13 @@
!fs_mgr_wants_overlayfs(&new_entry)) {
continue;
}
- auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
- auto duplicate_or_more_specific = false;
- for (auto it = candidates.begin(); it != candidates.end();) {
- auto it_mount_point = fs_mgr_mount_point(it->mount_point);
- if ((it_mount_point == new_mount_point) ||
- (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
- duplicate_or_more_specific = true;
- break;
- }
- if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
- it = candidates.erase(it);
- } else {
- ++it;
- }
+ const auto new_mount_point = fs_mgr_mount_point(new_entry.mount_point);
+ if (std::find_if(candidates.begin(), candidates.end(), [&](const auto& it) {
+ return fs_mgr_mount_point(it.mount_point) == new_mount_point;
+ }) != candidates.end()) {
+ continue;
}
- if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+ candidates.push_back(std::move(new_entry));
}
return candidates;
}
@@ -699,7 +731,7 @@
return false;
}
auto ret = true;
- auto scratch_can_be_mounted = true;
+ auto scratch_can_be_mounted = !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
if (fs_mgr_is_verity_enabled(entry)) continue;
auto mount_point = fs_mgr_mount_point(entry.mount_point);
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.h b/fs_mgr/fs_mgr_overlayfs_mount.h
index f0afac1..98b9007 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.h
+++ b/fs_mgr/fs_mgr_overlayfs_mount.h
@@ -58,3 +58,4 @@
bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+std::string GetEncodedBaseDirForMountPoint(const std::string& mount_point);
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 4b3a5d3..7ba4d2b 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -88,17 +88,6 @@
return entry.mount_point;
}
-const FstabEntry* GetWrappedEntry(const Fstab& overlayfs_candidates, const FstabEntry& entry) {
- auto mount_point = system_mount_point(entry);
- auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
- [&mount_point](const auto& entry) {
- return android::base::StartsWith(mount_point,
- system_mount_point(entry) + "/");
- });
- if (it == overlayfs_candidates.end()) return nullptr;
- return &(*it);
-}
-
class MyLogger {
public:
explicit MyLogger(bool verbose) : verbose_(verbose) {}
@@ -169,15 +158,25 @@
// not checkpointing.
auto vold = GetVold();
bool checkpointing = false;
- if (!vold->isCheckpointing(&checkpointing).isOk()) {
- LOG(ERROR) << "Could not determine checkpointing status.";
- return false;
- }
- if (checkpointing) {
- LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
- LOG(ERROR) << "To force end checkpointing, call 'vdc checkpoint commitChanges'";
- LOG(ERROR) << "Warning: this can lead to data corruption if rolled back.";
- return false;
+ bool show_help = true;
+
+ while (true) {
+ if (!vold->isCheckpointing(&checkpointing).isOk()) {
+ LOG(ERROR) << "Could not determine checkpointing status.";
+ return false;
+ }
+ if (!checkpointing) {
+ break;
+ }
+ if (show_help) {
+ show_help = false;
+ std::cerr << "WARNING: Userdata checkpoint is in progress. To force end checkpointing, "
+ "call 'vdc checkpoint commitChanges'. This can lead to data corruption if "
+ "rolled back."
+ << std::endl;
+ LOG(INFO) << "Waiting for checkpoint to complete and then continue remount.";
+ }
+ std::this_thread::sleep_for(4s);
}
return true;
}
@@ -196,9 +195,6 @@
if (auto candidate_entry = GetEntryForMountPoint(&candidates, entry.mount_point)) {
return candidate_entry->fs_type == entry.fs_type;
}
- if (GetWrappedEntry(candidates, entry)) {
- return false;
- }
return true;
}
@@ -252,11 +248,6 @@
}
const FstabEntry* entry = &*it;
- if (auto wrap = GetWrappedEntry(candidates, *entry); wrap != nullptr) {
- LOG(INFO) << "partition " << arg << " covered by overlayfs for " << wrap->mount_point
- << ", switching";
- entry = wrap;
- }
// If it's already remounted, include it so it gets gracefully skipped
// later on.
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index bacfa4b..6b742e5 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -112,6 +112,7 @@
// properties are loaded.
static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
if (vndk_version.empty()) {
+ // Vendor overlay is disabled from VNDK deprecated devices.
LINFO << "vendor overlay: vndk version not defined";
return false;
}
diff --git a/fs_mgr/include_fstab b/fs_mgr/include_fstab
deleted file mode 120000
index 728737f..0000000
--- a/fs_mgr/include_fstab
+++ /dev/null
@@ -1 +0,0 @@
-libfstab/include
\ No newline at end of file
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 3a9ed9b..e261aa3 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -612,8 +612,12 @@
std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
static constexpr std::string_view kDevBlockPrefix("/dev/block/");
- if (android::base::StartsWith(path, kDevBlockPrefix)) {
- return path.substr(kDevBlockPrefix.length());
+ std::string real_path;
+ if (!android::base::Realpath(path, &real_path)) {
+ real_path = path;
+ }
+ if (android::base::StartsWith(real_path, kDevBlockPrefix)) {
+ return real_path.substr(kDevBlockPrefix.length());
}
return {};
}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 3e7ecc6..22c475f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -52,8 +52,8 @@
static constexpr uint64_t kSectorSize = 512;
-// Returns `path` without /dev/block prefix if and only if `path` starts with
-// that prefix.
+// Returns `path` without /dev/block prefix if `path` starts with that prefix.
+// Or, if `path` is a symlink, do the same with its real path.
std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
// This interface is for testing purposes. See DeviceMapper proper for what these methods do.
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 996ffd7..24eebdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -93,8 +93,8 @@
srcs: [
"builder_test.cpp",
"super_layout_builder_test.cpp",
- "test_partition_opener.cpp",
"utility_test.cpp",
+ ":TestPartitionOpener_group",
],
}
@@ -122,3 +122,8 @@
name: "vts_kernel_liblp_test",
defaults: ["liblp_test_defaults"],
}
+
+filegroup {
+ name: "TestPartitionOpener_group",
+ srcs: [ "test_partition_opener.cpp"],
+}
diff --git a/fs_mgr/liblp/fuzzer/Android.bp b/fs_mgr/liblp/fuzzer/Android.bp
new file mode 100644
index 0000000..a9e3509
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/Android.bp
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ *
+ */
+
+cc_defaults {
+ name: "liblp_fuzz_defaults",
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+ shared_libs: [
+ "liblp",
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libcutils",
+ ],
+ include_dirs: [
+ "system/core/fs_mgr/liblp",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 59148,
+ hotlists: ["4593311"],
+ description: "The fuzzers target the APIs of all liblp modules",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped"
+ }
+}
+
+cc_fuzz {
+ name: "liblp_builder_fuzzer",
+ srcs: ["liblp_builder_fuzzer.cpp"],
+ defaults: ["liblp_fuzz_defaults"],
+}
+
+cc_fuzz {
+ name: "liblp_super_layout_builder_fuzzer",
+ srcs: ["liblp_super_layout_builder_fuzzer.cpp"],
+ defaults: ["liblp_fuzz_defaults"],
+}
+
+python_binary_host {
+ name: "image_gen_rand",
+ srcs: ["image_gen_rand.py"],
+}
+
+genrule_defaults {
+ name: "test_data_gen_defaults",
+ tools: [
+ "image_gen_rand",
+ ],
+}
+
+// Fake dtb image.
+genrule {
+ name: "test_dtb",
+ defaults: ["test_data_gen_defaults"],
+ out: ["test_dtb.img"],
+ cmd: "$(location image_gen_rand) --seed dtb --length 1024 > $(out)",
+}
+
+// Fake bootconfig image.
+genrule {
+ name: "test_bootconfig",
+ defaults: ["test_data_gen_defaults"],
+ out: ["test_bootconfig.img"],
+ cmd: "$(location image_gen_rand) --seed bootconfig --length 1024 > $(out)",
+}
+
+// Fake vendor ramdisk with type "none".
+genrule {
+ name: "test_vendor_ramdisk_none",
+ defaults: ["test_data_gen_defaults"],
+ out: ["test_vendor_ramdisk_none.img"],
+ cmd: "$(location image_gen_rand) --seed vendor_ramdisk_none --length 1024 > $(out)",
+}
+
+// Fake vendor ramdisk with type "platform".
+genrule {
+ name: "test_vendor_ramdisk_platform",
+ defaults: ["test_data_gen_defaults"],
+ out: ["test_vendor_ramdisk_platform.img"],
+ cmd: "$(location image_gen_rand) --seed vendor_ramdisk_platform --length 1024 > $(out)",
+}
+
+// Fake replacement ramdisk.
+genrule {
+ name: "test_vendor_ramdisk_replace",
+ defaults: ["test_data_gen_defaults"],
+ out: ["test_vendor_ramdisk_replace.img"],
+ cmd: "$(location image_gen_rand) --seed replace --length 3072 > $(out)",
+}
+
+// Genrules for test vendor boot images.
+fastboot_sign_test_image = "$(location avbtool) add_hash_footer --salt 00 --image $(out) " +
+ "--partition_name vendor_boot --partition_size $$(( 1 * 1024 * 1024 ))"
+
+genrule_defaults {
+ name: "test_vendor_boot_gen_defaults",
+ defaults: ["test_data_gen_defaults"],
+ tools: [
+ "avbtool",
+ "mkbootimg",
+ ],
+}
+
+genrule {
+ name: "test_vendor_boot_v3",
+ defaults: ["test_vendor_boot_gen_defaults"],
+ out: ["test_vendor_boot_v3.img"],
+ srcs: [
+ ":test_dtb",
+ ":test_vendor_ramdisk_none",
+ ],
+ cmd: "$(location mkbootimg) --header_version 3 " +
+ "--vendor_ramdisk $(location :test_vendor_ramdisk_none) " +
+ "--dtb $(location :test_dtb) " +
+ "--vendor_boot $(out) && " +
+ fastboot_sign_test_image,
+}
+
+genrule {
+ name: "test_vendor_boot_v4_without_frag",
+ defaults: ["test_vendor_boot_gen_defaults"],
+ out: ["test_vendor_boot_v4_without_frag.img"],
+ srcs: [
+ ":test_dtb",
+ ":test_vendor_ramdisk_none",
+ ":test_bootconfig",
+ ],
+ cmd: "$(location mkbootimg) --header_version 4 " +
+ "--vendor_ramdisk $(location :test_vendor_ramdisk_none) " +
+ "--dtb $(location :test_dtb) " +
+ "--vendor_bootconfig $(location :test_bootconfig) " +
+ "--vendor_boot $(out) && " +
+ fastboot_sign_test_image,
+}
+
+genrule {
+ name: "test_vendor_boot_v4_with_frag",
+ defaults: ["test_vendor_boot_gen_defaults"],
+ out: ["test_vendor_boot_v4_with_frag.img"],
+ srcs: [
+ ":test_dtb",
+ ":test_vendor_ramdisk_none",
+ ":test_vendor_ramdisk_platform",
+ ":test_bootconfig",
+ ],
+ cmd: "$(location mkbootimg) --header_version 4 " +
+ "--dtb $(location :test_dtb) " +
+ "--vendor_bootconfig $(location :test_bootconfig) " +
+ "--ramdisk_type none --ramdisk_name none_ramdisk " +
+ "--vendor_ramdisk_fragment $(location :test_vendor_ramdisk_none) " +
+ "--ramdisk_type platform --ramdisk_name platform_ramdisk " +
+ "--vendor_ramdisk_fragment $(location :test_vendor_ramdisk_platform) " +
+ "--vendor_boot $(out) && " +
+ fastboot_sign_test_image,
+}
+
+cc_fuzz {
+ name: "liblp_apis_fuzzer",
+ srcs: [
+ "liblp_apis_fuzzer.cpp",
+ ":TestPartitionOpener_group",
+ ],
+ defaults: ["liblp_fuzz_defaults"],
+ shared_libs: [
+ "libsparse",
+ ],
+ data: [
+ ":test_dtb",
+ ":test_bootconfig",
+ ":test_vendor_ramdisk_none",
+ ":test_vendor_ramdisk_platform",
+ ":test_vendor_ramdisk_replace",
+ ":test_vendor_boot_v3",
+ ":test_vendor_boot_v4_without_frag",
+ ":test_vendor_boot_v4_with_frag",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+}
diff --git a/fs_mgr/liblp/fuzzer/README.md b/fs_mgr/liblp/fuzzer/README.md
new file mode 100644
index 0000000..015c59b
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/README.md
@@ -0,0 +1,130 @@
+# Fuzzers for liblp
+## Table of contents
++ [liblp_builder_fuzzer](#Builder)
++ [liblp_super_layout_builder_fuzzer](#SuperBuilder)
++ [liblp_apis_fuzzer](#APIs)
+
+# <a name="Builder"></a> Fuzzer for LiblpBuilder
+
+LiblpBuilder supports the following parameters:
+1. kAttributeTypes (parameter name: "attribute")
+2. blockDevSize (parameter name: "blockdev_size")
+3. metadataMaxSize (parameter name: "metadata_max_size")
+4. metadataSlotCount (parameter name: "metadata_slot_count")
+5. partitionName (parameter name: "partition_name")
+6. superBlockDeviceName (parameter name: "block_device_name")
+7. blockDeviceInfoSize (parameter name: "block_device_info_size")
+8. alignment (parameter name: "alignment")
+9. alignmentOffset (parameter name: "alignment_offset")
+10. logicalBlockSize (parameter name: "logical_block_size")
+11. maxMetadataSize (parameter name: "max_metadata_size")
+12. deviceIndex (parameter name: "device_index")
+13. start (parameter name: "start")
+14. end (parameter name: "end")
+15. addedGroupName (parameter name: "group_name")
+16. partitionGroupName (parameter name: "partition_name")
+17. numSectors (parameter name: "num_sectors")
+18. physicalSector (parameter name: "physical_sector")
+19. resizedPartitionSize (parameter name: "requested_size")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|
+|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|
+|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
+|`partitionName`| String |Value obtained from FuzzedDataProvider|
+|`superBlockDeviceName`| String |Value obtained from FuzzedDataProvider|
+|`blockDeviceInfoSize`| Integer |Value obtained from FuzzedDataProvider|
+|`alignment`| Integer |Value obtained from FuzzedDataProvider|
+|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|
+|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
+|`maxMetadataSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`deviceIndex`| Integer |Value obtained from FuzzedDataProvider|
+|`start`| Integer |Value obtained from FuzzedDataProvider|
+|`end`| Integer |Value obtained from FuzzedDataProvider|
+|`partitionGroupName`| String |Value obtained from FuzzedDataProvider|
+|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`resizedPartitionSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) liblp_builder_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/liblp_builder_fuzzer/liblp_builder_fuzzer
+```
+
+# <a name="SuperBuilder"></a> Fuzzer for LiblpSuperLayoutBuilder
+
+SuperLayoutBuilder supports the following parameters:
+1. kAttributeTypes (parameter name: "attribute")
+2. blockDevSize (parameter name: "blockdev_size")
+3. metadataMaxSize (parameter name: "metadata_max_size")
+4. partitionName (parameter name: "partition_name")
+5. data (parameter name: "data")
+6. imageName (parameter name: "image_name")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|
+|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|
+|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`partitionName`| String |Value obtained from FuzzedDataProvider|
+|`data`| String |Value obtained from FuzzedDataProvider|
+|`imageName`| String |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) liblp_super_layout_builder_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/liblp_super_layout_builder_fuzzer/liblp_super_layout_builder_fuzzer
+```
+
+# <a name="APIs"></a> Fuzzer for LiblpApis
+
+LiblpAPIs supports the following parameters:
+1. blockDeviceInfoSize (parameter name: "block_device_info_size")
+2. alignment (parameter name: "alignment")
+3. alignmentOffset (parameter name: "alignment_offset")
+4. logicalBlockSize (parameter name: "logical_block_size")
+5. blockDevSize (parameter name: "blockdev_size")
+6. metadataMaxSize (parameter name: "metadata_max_size")
+7. blockDeviceInfoName (parameter name: "block_device_info_name")
+8. numSectors (parameter name: "num_sectors")
+9. physicalSector (parameter name: "physical_sector")
+10. sparsify (parameter name: "sparsify")
+11. buffer (parameter name: "data")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`blockDeviceInfoSize`| Integer |Value obtained from FuzzedDataProvider|
+|`alignment`| Integer |Value obtained from FuzzedDataProvider|
+|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|
+|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
+|`blockDevSize`| Integer value in multiples of `LP_SECTOR_SIZE`|Value obtained from FuzzedDataProvider|
+|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`blockDeviceInfoName`| String |Value obtained from FuzzedDataProvider|
+|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`alignment`| Bool |Value obtained from FuzzedDataProvider|
+|`alignment`| Vector |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) liblp_apis_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/liblp_apis_fuzzer/liblp_apis_fuzzer
+```
diff --git a/fs_mgr/liblp/fuzzer/image_gen_rand.py b/fs_mgr/liblp/fuzzer/image_gen_rand.py
new file mode 100644
index 0000000..6e85472
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/image_gen_rand.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+# 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.
+
+"""
+Write given number of random bytes, generated with optional seed.
+"""
+
+import random, argparse
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--seed', help='Seed to random generator')
+ parser.add_argument('--length', type=int, required=True, help='Length of output')
+ args = parser.parse_args()
+
+ if args.seed:
+ random.seed(args.seed)
+
+ print(''.join(chr(random.randrange(0,0xff)) for _ in range(args.length)))
diff --git a/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
new file mode 100644
index 0000000..cb192ea
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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 <android-base/file.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <liblp/builder.h>
+#include <liblp/partition_opener.h>
+#include <linux/memfd.h>
+#include <sys/syscall.h>
+#include <writer.h>
+#include "images.h"
+#include "test_partition_opener.h"
+
+using namespace std;
+using namespace android;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+static constexpr size_t kDiskSize = 131072;
+static constexpr size_t kMetadataSize = 512;
+static constexpr size_t kMetadataSlots = 2;
+static constexpr uint32_t kMaxBytes = 20;
+static constexpr uint32_t kValidAlignment = 0;
+static constexpr uint32_t kValidAlignmentOffset = 0;
+static constexpr uint32_t kValidLogicalBlockSize = 4096;
+static constexpr uint32_t kMinMetadataSize = 0;
+static constexpr uint32_t kMaxMetadataSize = 10000;
+static constexpr uint32_t kMinFactor = 0;
+static constexpr uint32_t kMaxFactor = 10;
+static constexpr uint32_t kMetadataGeometrySize = 4096;
+static constexpr uint64_t kValidNumSectors = 1901568;
+static constexpr uint64_t kValidPhysicalSector = 3608576;
+static constexpr uint64_t kMinSectorValue = 1;
+static constexpr uint64_t kMaxSectorValue = 1000000;
+static constexpr uint64_t kMaxBufferSize = 100000;
+
+const string kImageFile = "image_file";
+const string kSuperName = "super";
+const string kSystemPartitionName = "system";
+const string kPartitionName = "builder_partition";
+const string kSuperPartitionName = "super_partition";
+
+const string kSuffix[] = {"_a", "_b", "a", "b"};
+
+class LiplpApisFuzzer {
+ public:
+ LiplpApisFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+ private:
+ void setupBuilder();
+ BlockDeviceInfo getBlockDevice();
+ FuzzedDataProvider mFdp;
+ unique_ptr<MetadataBuilder> mBuilder;
+ string mBlockDeviceInfoName;
+ string mSuperPartitionName;
+ string mPartitionName;
+ const string mImagePaths[10] = {
+ "data/test_dtb.img",
+ "data/test_bootconfig.img",
+ "data/test_vendor_ramdisk_none.img",
+ "data/test_vendor_ramdisk_platform.img",
+ "data/test_vendor_ramdisk_replace.img",
+ "data/test_vendor_boot_v4_with_frag.img",
+ "data/test_vendor_boot_v4_without_frag.img",
+ "data/test_vendor_boot_v3.img",
+ "dev/null",
+ mFdp.ConsumeRandomLengthString(kMaxBytes),
+ };
+};
+
+BlockDeviceInfo LiplpApisFuzzer::getBlockDevice() {
+ mBlockDeviceInfoName =
+ mFdp.ConsumeBool() ? kSuperName : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ uint64_t blockDeviceInfoSize =
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kDiskSize;
+ uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;
+ uint32_t alignmentOffset =
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;
+ uint32_t logicalBlockSize =
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidLogicalBlockSize;
+
+ BlockDeviceInfo superInfo{mBlockDeviceInfoName, blockDeviceInfoSize, alignment, alignmentOffset,
+ logicalBlockSize};
+ return superInfo;
+}
+
+void LiplpApisFuzzer::setupBuilder() {
+ uint64_t randomBlockDevSize =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinFactor, kMaxFactor) * LP_SECTOR_SIZE;
+ uint64_t blockDevSize = mFdp.ConsumeBool() ? randomBlockDevSize : kDiskSize;
+ uint32_t randomMetadataMaxSize =
+ mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataSize, kMaxMetadataSize);
+ uint32_t metadataMaxSize = mFdp.ConsumeBool() ? kMetadataSize : randomMetadataMaxSize;
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+ mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
+
+ if (mBuilder.get()) {
+ mBuilder->AddPartition(kSystemPartitionName, LP_PARTITION_ATTR_READONLY);
+
+ mPartitionName =
+ mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kPartitionName;
+ if (!mPartitionName.size()) {
+ mPartitionName = kPartitionName;
+ }
+ mSuperPartitionName = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes)
+ : kSuperPartitionName;
+ if (!mSuperPartitionName.size()) {
+ mSuperPartitionName = kSuperPartitionName;
+ }
+
+ Partition* super = mBuilder->AddPartition(mSuperPartitionName, LP_PARTITION_ATTR_READONLY);
+ mBuilder->AddPartition(mPartitionName, LP_PARTITION_ATTR_READONLY);
+
+ if (super) {
+ int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinSectorValue, kMaxSectorValue)
+ : kValidNumSectors;
+ int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinSectorValue, kMaxSectorValue)
+ : kValidPhysicalSector;
+ mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);
+ }
+ }
+}
+
+void LiplpApisFuzzer::process() {
+ BlockDeviceInfo superInfo = getBlockDevice();
+ unique_fd fd(syscall(__NR_memfd_create, "image_file", MFD_ALLOW_SEALING));
+ setupBuilder();
+
+ TestPartitionOpener opener(
+ {{mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kSuperName, fd}},
+ {{mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kSuperName,
+ superInfo}});
+
+ if (mBuilder.get()) {
+ unique_ptr<LpMetadata> metadata = mBuilder->Export();
+ const LpMetadata& metadataValue = *metadata.get();
+
+ map<string, string> images = {};
+ if (mFdp.ConsumeBool()) {
+ images[mSuperPartitionName] = mFdp.PickValueInArray(mImagePaths);
+ }
+
+ while (mFdp.remaining_bytes()) {
+ auto invokeAPIs = mFdp.PickValueInArray<const function<void()>>({
+ [&]() { WriteToImageFile(fd, metadataValue); },
+ [&]() { WriteToImageFile(kImageFile.c_str(), metadataValue); },
+ [&]() { FlashPartitionTable(opener, kSuperName, metadataValue); },
+ [&]() {
+ UpdatePartitionTable(opener, mPartitionName, metadataValue,
+ mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);
+ },
+ [&]() {
+ ReadMetadata(mPartitionName, mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);
+ },
+ [&]() { FlashPartitionTable(mPartitionName, metadataValue); },
+ [&]() {
+ UpdatePartitionTable(mPartitionName, metadataValue,
+ mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);
+ },
+ [&]() {
+ WriteToImageFile(kImageFile.c_str(), metadataValue,
+ metadata->geometry.logical_block_size, images,
+ mFdp.ConsumeBool() ? true : false /* sparsify */);
+ },
+
+ [&]() {
+ WriteSplitImageFiles(kImageFile.c_str(), metadataValue,
+ metadata->geometry.logical_block_size, images,
+ mFdp.ConsumeBool() ? true : false /* sparsify */);
+ },
+ [&]() { ReadFromImageFile(kImageFile.c_str()); },
+ [&]() { IsEmptySuperImage(kImageFile.c_str()); },
+ [&]() {
+ uint64_t bufferSize = mFdp.ConsumeIntegralInRange<uint64_t>(
+ 2 * kMetadataGeometrySize, kMaxBufferSize);
+ vector<uint8_t> buffer = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);
+ buffer.resize(bufferSize);
+ ReadFromImageBlob(buffer.data(), buffer.size());
+ },
+ [&]() {
+ uint32_t groupVectorSize = metadata->groups.size();
+ uint32_t randomGroupIndex =
+ mFdp.ConsumeIntegralInRange<uint32_t>(0, groupVectorSize - 1);
+ GetPartitionGroupName(metadata->groups[randomGroupIndex]);
+ },
+ [&]() {
+ uint32_t blockDeviceVectorSize = metadata->block_devices.size();
+ uint32_t randomBlockDeviceIndex =
+ mFdp.ConsumeIntegralInRange<uint32_t>(0, blockDeviceVectorSize - 1);
+ GetBlockDevicePartitionName(
+ metadata->block_devices[randomBlockDeviceIndex]);
+ },
+ [&]() { GetMetadataSuperBlockDevice(metadataValue); },
+ [&]() {
+ string suffix = mFdp.ConsumeBool()
+ ? mFdp.PickValueInArray<string>(kSuffix)
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ SlotNumberForSlotSuffix(suffix);
+ },
+ [&]() {
+ auto entry = FindPartition(metadataValue, kSystemPartitionName);
+ GetPartitionSize(metadataValue, *entry);
+ },
+ [&]() { GetPartitionSlotSuffix(mPartitionName); },
+ [&]() { FindPartition(metadataValue, mPartitionName); },
+ [&]() {
+ uint32_t partitionVectorSize = metadata->partitions.size();
+ uint32_t randomPartitionIndex =
+ mFdp.ConsumeIntegralInRange<uint32_t>(0, partitionVectorSize - 1);
+ GetPartitionName(metadata->partitions[randomPartitionIndex]);
+ },
+ [&]() { GetTotalSuperPartitionSize(metadataValue); },
+ [&]() { GetBlockDevicePartitionNames(metadataValue); },
+ });
+ invokeAPIs();
+ }
+ remove(kImageFile.c_str());
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ LiplpApisFuzzer liplpApisFuzzer(data, size);
+ liplpApisFuzzer.process();
+ return 0;
+}
diff --git a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
new file mode 100644
index 0000000..7f09ac8
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
@@ -0,0 +1,437 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+#include <liblp/builder.h>
+#include <liblp/property_fetcher.h>
+#include <storage_literals/storage_literals.h>
+
+using namespace android::fs_mgr;
+using namespace std;
+using namespace android::storage_literals;
+
+static constexpr uint64_t kValidBlockSize = 4096 * 50;
+static constexpr uint64_t kBlockDeviceInfoSize = 1024 * 1024;
+static constexpr uint64_t kValidBlockDeviceInfoSize = 8_GiB;
+static constexpr uint64_t kValidMaxGroupSize = 40960;
+static constexpr uint64_t kMinBlockDevValue = 0;
+static constexpr uint64_t kMaxBlockDevValue = 100000;
+static constexpr uint64_t kMinSectorValue = 1;
+static constexpr uint64_t kMaxSectorValue = 1000000;
+static constexpr uint64_t kMinValue = 0;
+static constexpr uint64_t kMaxValue = 10000;
+static constexpr uint64_t kValidNumSectors = 1901568;
+static constexpr uint64_t kValidPhysicalSector = 3608576;
+static constexpr uint64_t kMinElements = 0;
+static constexpr uint64_t kMaxElements = 10;
+static constexpr uint32_t kValidAlignment = 786432;
+static constexpr uint32_t kValidMetadataSize = 40960;
+static constexpr uint32_t kValidAlignmentOffset = 229376;
+static constexpr uint32_t kValidLogicalBlockSize = 4096;
+static constexpr uint32_t kValidMaxMetadataSize = 65536;
+static constexpr uint32_t kMinMetadataValue = 0;
+static constexpr uint32_t kMaxMetadataValue = 10000;
+static constexpr uint32_t kZeroAlignment = 0;
+static constexpr uint32_t kZeroAlignmentOffset = 0;
+static constexpr uint32_t kMaxBytes = 20;
+static constexpr uint32_t kMinBuilder = 0;
+static constexpr uint32_t kMaxBuilder = 4;
+
+const uint64_t kAttributeTypes[] = {
+ LP_PARTITION_ATTR_NONE, LP_PARTITION_ATTR_READONLY, LP_PARTITION_ATTR_SLOT_SUFFIXED,
+ LP_PARTITION_ATTR_UPDATED, LP_PARTITION_ATTR_DISABLED,
+};
+
+const string kFuzzPartitionName = "fuzz_partition_name";
+const string kSuperPartitionName = "super_partition";
+const string kDeviceInfoName = "super";
+const string kDefaultGroupName = "default";
+
+class BuilderFuzzer {
+ public:
+ BuilderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+ void invokeBuilderAPIs();
+ void selectRandomBuilder(int32_t randomBuilder, string superBlockDeviceName);
+ void setupBuilder(string superBlockDeviceName);
+ void callChangePartitionGroup();
+ void callVerifyExtentsAgainstSourceMetadata();
+ vector<BlockDeviceInfo> mBlockDevices;
+ unique_ptr<MetadataBuilder> mBuilder;
+ string mResizePartitionName;
+ string mGroupNames[4] = {
+ "default",
+ "group_a",
+ "group_b",
+ mFdp.ConsumeRandomLengthString(kMaxBytes),
+ };
+ string mPartitionNames[5] = {
+ "system_a",
+ "vendor_a",
+ "system_b",
+ "vendor_b",
+ mFdp.ConsumeRandomLengthString(kMaxBytes),
+ };
+ Partition* mPartition;
+ Partition* mFuzzPartition;
+ Partition* mResizePartition;
+ template <typename T>
+ T getParamValue(T validValue) {
+ T parameter = validValue;
+ if (mFdp.ConsumeBool()) {
+ parameter = mFdp.ConsumeIntegralInRange<T>(kMinValue, kMaxValue);
+ }
+ return parameter;
+ }
+};
+
+void BuilderFuzzer::selectRandomBuilder(int32_t randomBuilder, string superBlockDeviceName) {
+ switch (randomBuilder) {
+ case 0: {
+ uint32_t maxMetadataSize = getParamValue(kValidMaxMetadataSize);
+ uint32_t numSlots = mFdp.ConsumeBool() ? 0 : 1;
+ mBuilder = MetadataBuilder::New(mBlockDevices, superBlockDeviceName, maxMetadataSize,
+ numSlots);
+ break;
+ }
+ case 1: {
+ uint64_t blockDevSize =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
+ uint32_t metadataMaxSize =
+ mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+ mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
+ break;
+ }
+ case 2: {
+ uint64_t blockDevSize = getParamValue(kValidBlockSize);
+ uint32_t metadataSize = getParamValue(kValidMetadataSize);
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+ mBuilder = MetadataBuilder::New(blockDevSize, metadataSize, metadataSlotCount);
+ break;
+ }
+ case 3: {
+ string superPartitionName = mFdp.ConsumeBool()
+ ? kSuperPartitionName
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ mBuilder = MetadataBuilder::New(PartitionOpener(), superPartitionName,
+ mFdp.ConsumeIntegralInRange(0, 1) /* slot_number */);
+ break;
+ }
+ case 4: {
+ string superPartitionName = mFdp.ConsumeBool()
+ ? kSuperPartitionName
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ mBuilder = MetadataBuilder::New(
+ superPartitionName,
+ mFdp.ConsumeIntegralInRange<uint32_t>(0, 1) /* slot_number */);
+ break;
+ }
+ }
+}
+
+void BuilderFuzzer::setupBuilder(string superBlockDeviceName) {
+ uint64_t blockDeviceInfoSize =
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kValidBlockDeviceInfoSize;
+ uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;
+ uint32_t alignmentOffset =
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;
+ uint32_t logicalBlockSize =
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidLogicalBlockSize;
+ BlockDeviceInfo super(superBlockDeviceName, blockDeviceInfoSize, alignment, alignmentOffset,
+ logicalBlockSize);
+ mBlockDevices.push_back(super);
+
+ mBuilder->AddGroup(kDefaultGroupName, mFdp.ConsumeIntegral<uint64_t>() /* max_size */);
+ mPartition = mBuilder->AddPartition(kSuperPartitionName, LP_PARTITION_ATTR_READONLY);
+
+ mFuzzPartition = mBuilder->AddPartition(kFuzzPartitionName, kDefaultGroupName,
+ LP_PARTITION_ATTR_READONLY);
+
+ string mResizePartitionName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ if (!mResizePartitionName.size()) {
+ mResizePartitionName = "resize_partition";
+ }
+ mResizePartition = mBuilder->AddPartition(mResizePartitionName, kDefaultGroupName,
+ LP_PARTITION_ATTR_READONLY);
+
+ string changePartitionDeviceInfoName =
+ mFdp.ConsumeBool() ? kDeviceInfoName : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ BlockDeviceInfo changePartitionDeviceInfo(
+ changePartitionDeviceInfoName,
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kBlockDeviceInfoSize /* size */,
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
+ : kZeroAlignmentOffset /* alignment */,
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
+ : kZeroAlignmentOffset /* alignment_offset */,
+ mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
+ : kValidLogicalBlockSize /* logical_block_size */);
+ mBlockDevices.push_back(changePartitionDeviceInfo);
+}
+
+void BuilderFuzzer::callChangePartitionGroup() {
+ string group1 = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : "group1";
+ uint64_t group1Size = getParamValue(0);
+
+ string group2 = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : "group2";
+ uint64_t group2Size = getParamValue(0);
+
+ bool group1Added = mBuilder->AddGroup(group1, group1Size);
+ bool group2Added = mBuilder->AddGroup(group2, group2Size);
+
+ string changeGroupPartitionName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ if (changeGroupPartitionName.size() && group1Added && group2Added) {
+ Partition* changeGroupPartition = mBuilder->AddPartition(changeGroupPartitionName, group1,
+ LP_PARTITION_ATTR_READONLY);
+ if (changeGroupPartition) {
+ mBuilder->ChangePartitionGroup(changeGroupPartition, group2);
+ }
+ }
+}
+
+void BuilderFuzzer::callVerifyExtentsAgainstSourceMetadata() {
+ uint64_t sourceBlockDevSize = getParamValue(kValidBlockSize);
+ uint32_t sourceMetadataMaxSize = getParamValue(kValidMetadataSize);
+ uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
+ auto sourceBuilder =
+ MetadataBuilder::New(sourceBlockDevSize, sourceMetadataMaxSize, sourceSlotCount);
+
+ uint64_t targetBlockDevSize = getParamValue(kValidBlockSize);
+ uint32_t targetMetadataMaxSize = getParamValue(kValidMetadataSize);
+ uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
+ auto targetBuilder =
+ MetadataBuilder::New(targetBlockDevSize, targetMetadataMaxSize, targetSlotCount);
+
+ if (sourceBuilder && targetBuilder) {
+ int64_t sourceGroups = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ for (int64_t idx = 0; idx < sourceGroups; ++idx) {
+ sourceBuilder->AddGroup(
+ mFdp.PickValueInArray(mGroupNames),
+ mFdp.ConsumeBool() ? kValidMaxGroupSize : mFdp.ConsumeIntegral<uint64_t>());
+ }
+
+ int64_t sourcePartitions = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ for (int64_t idx = 0; idx < sourcePartitions; ++idx) {
+ sourceBuilder->AddPartition(mFdp.PickValueInArray(mPartitionNames),
+ LP_PARTITION_ATTR_READONLY);
+ }
+
+ int64_t targetGroups = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ for (int64_t idx = 0; idx < targetGroups; ++idx) {
+ targetBuilder->AddGroup(
+ mFdp.PickValueInArray(mGroupNames),
+ mFdp.ConsumeBool() ? kValidMaxGroupSize : mFdp.ConsumeIntegral<uint64_t>());
+ }
+
+ int64_t targetPartitions = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ for (int64_t idx = 0; idx < targetPartitions; ++idx) {
+ targetBuilder->AddPartition(mFdp.PickValueInArray(mPartitionNames),
+ LP_PARTITION_ATTR_READONLY);
+ }
+
+ MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+ *sourceBuilder, mFdp.ConsumeBool() ? 0 : 1 /* source_slot_number */, *targetBuilder,
+ mFdp.ConsumeBool() ? 0 : 1 /* target_slot_number */,
+ vector<string>{"system", "vendor", mFdp.ConsumeRandomLengthString(kMaxBytes)});
+ }
+}
+
+void BuilderFuzzer::invokeBuilderAPIs() {
+ string superBlockDeviceName =
+ mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kDeviceInfoName;
+ uint32_t randomBuilder = mFdp.ConsumeIntegralInRange<uint32_t>(kMinBuilder, kMaxBuilder);
+ selectRandomBuilder(randomBuilder, superBlockDeviceName);
+
+ if (mBuilder.get()) {
+ setupBuilder(superBlockDeviceName);
+
+ while (mFdp.remaining_bytes()) {
+ auto invokeAPIs = mFdp.PickValueInArray<const function<void()>>({
+ [&]() { callChangePartitionGroup(); },
+ [&]() {
+ string addedGroupName = mFdp.PickValueInArray(mGroupNames);
+ mBuilder->AddGroup(addedGroupName,
+ mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinValue, kMaxValue) /* max_size */);
+ },
+ [&]() {
+ string partitionName = mFdp.PickValueInArray(mPartitionNames);
+ Partition* addedPartition = mBuilder->AddPartition(
+ partitionName, mFdp.PickValueInArray(kAttributeTypes));
+ },
+ [&]() {
+ int64_t numSectors = mFdp.ConsumeBool()
+ ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinSectorValue, kMaxSectorValue)
+ : kValidNumSectors;
+ int64_t physicalSector = mFdp.ConsumeBool()
+ ? mFdp.ConsumeIntegralInRange<uint64_t>(
+ kMinSectorValue, kMaxSectorValue)
+ : kValidPhysicalSector;
+
+ int64_t numExtents =
+ mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ if (mFuzzPartition) {
+ bool extentAdded = false;
+ for (int64_t i = 0; i <= numExtents; ++i) {
+ extentAdded =
+ mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,
+ numSectors, physicalSector);
+ }
+
+ if (extentAdded) {
+ unique_ptr<LpMetadata> metadata = mBuilder->Export();
+ uint64_t alignedSize =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+ mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);
+ }
+ }
+ },
+ [&]() { callVerifyExtentsAgainstSourceMetadata(); },
+ [&]() { mBuilder->ListPartitionsInGroup(mFdp.PickValueInArray(mGroupNames)); },
+ [&]() {
+ int64_t maxSize = mFdp.ConsumeIntegral<uint64_t>();
+ mBuilder->ChangeGroupSize(mFdp.PickValueInArray(mGroupNames), maxSize);
+ },
+ [&]() {
+ string deviceInfoName = mFdp.ConsumeBool()
+ ? kDeviceInfoName
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ mBuilder->GetBlockDeviceInfo(deviceInfoName, &mBlockDevices[1]);
+ },
+ [&]() {
+ string deviceInfoName = mFdp.ConsumeBool()
+ ? kDeviceInfoName
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ mBuilder->UpdateBlockDeviceInfo(deviceInfoName, mBlockDevices[1]);
+ },
+ [&]() {
+ unique_ptr<LpMetadata> metadata = mBuilder->Export();
+ mBuilder->ImportPartitions(*metadata.get(),
+ {mFdp.PickValueInArray(mPartitionNames)});
+ },
+ [&]() { mBuilder->HasBlockDevice(mFdp.PickValueInArray(mPartitionNames)); },
+ [&]() { mBuilder->SetVirtualABDeviceFlag(); },
+ [&]() { mBuilder->SetAutoSlotSuffixing(); },
+ [&]() { mBuilder->ListGroups(); },
+ [&]() { mBuilder->UsedSpace(); },
+ [&]() { mBuilder->RequireExpandedMetadataHeader(); },
+ [&]() {
+ uint64_t resizedPartitionSize = getParamValue(0);
+ mBuilder->ResizePartition(mResizePartition, resizedPartitionSize);
+ },
+ [&]() {
+ uint32_t sourceSlot = mFdp.ConsumeBool() ? 0 : 1;
+ uint32_t targetSlot = mFdp.ConsumeBool() ? 0 : 1;
+ PartitionOpener partitionOpener;
+ string sourcePartition =
+ mFdp.ConsumeBool() ? kFuzzPartitionName : kDeviceInfoName;
+
+ MetadataBuilder::NewForUpdate(partitionOpener, sourcePartition, sourceSlot,
+ targetSlot);
+ partitionOpener.GetDeviceString(mFdp.PickValueInArray(mPartitionNames));
+ },
+ [&]() {
+ unique_ptr<LpMetadata> metadata = mBuilder->Export();
+ MetadataBuilder::New(*metadata.get());
+ },
+ [&]() { mBuilder->AllocatableSpace(); },
+ [&]() {
+ PartitionOpener pOpener;
+ string superPartitionName =
+ mFdp.ConsumeBool() ? kSuperPartitionName
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ pOpener.Open(superPartitionName, O_RDONLY);
+ pOpener.GetInfo(superPartitionName, &mBlockDevices[0]);
+ },
+ [&]() {
+ PartitionOpener pOpener;
+ string superPartitionName =
+ mFdp.ConsumeBool() ? kSuperPartitionName
+ : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ pOpener.Open(superPartitionName, O_RDONLY);
+ pOpener.GetDeviceString(superPartitionName);
+ },
+ [&]() {
+ Interval::Intersect(
+ Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+ mFdp.ConsumeIntegral<uint64_t>() /* start */,
+ mFdp.ConsumeIntegral<uint64_t>()) /* end */,
+ Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+ mFdp.ConsumeIntegral<uint64_t>() /* start */,
+ mFdp.ConsumeIntegral<uint64_t>() /* end */));
+ },
+ [&]() {
+ vector<Interval> intervalVectorA;
+ int64_t internalVectorAElements =
+ mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ for (int64_t idx = 0; idx < internalVectorAElements; ++idx) {
+ intervalVectorA.push_back(
+ Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+ mFdp.ConsumeIntegral<uint64_t>() /* start */,
+ mFdp.ConsumeIntegral<uint64_t>() /* end */));
+ }
+
+ vector<Interval> intervalVectorB;
+ int64_t internalVectorBElements =
+ mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+ for (int64_t idx = 0; idx < internalVectorBElements; ++idx) {
+ intervalVectorB.push_back(
+ Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+ mFdp.ConsumeIntegral<uint64_t>() /* start */,
+ mFdp.ConsumeIntegral<uint64_t>() /* end */));
+ }
+
+ Interval::Intersect(intervalVectorA, intervalVectorB);
+ },
+ [&]() {
+ uint64_t numSectors =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+ uint32_t deviceIndex =
+ mFdp.ConsumeIntegralInRange<uint32_t>(kMinValue, kMaxValue);
+ uint64_t physicalSector =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+ LinearExtent extent(numSectors, deviceIndex, physicalSector);
+ extent.AsInterval();
+ },
+ [&]() {
+ IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());
+ },
+ });
+ invokeAPIs();
+ }
+ if (mFdp.ConsumeBool()) {
+ mBuilder->RemoveGroupAndPartitions(mFdp.PickValueInArray(mGroupNames));
+ } else {
+ string removePartition = mFdp.PickValueInArray(mPartitionNames);
+ mBuilder->RemovePartition(removePartition);
+ }
+ }
+}
+
+void BuilderFuzzer::process() {
+ invokeBuilderAPIs();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ BuilderFuzzer builderFuzzer(data, size);
+ builderFuzzer.process();
+ return 0;
+}
diff --git a/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
new file mode 100644
index 0000000..a6642d7
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <liblp/metadata_format.h>
+#include <liblp/super_layout_builder.h>
+#include <linux/memfd.h>
+#include <storage_literals/storage_literals.h>
+#include <sys/syscall.h>
+
+using namespace android::fs_mgr;
+using namespace std;
+using unique_fd = android::base::unique_fd;
+using namespace android::storage_literals;
+
+static constexpr uint64_t kSuperLayoutValidBlockDevSize = 4_MiB;
+static constexpr uint64_t kMinBlockDevValue = 0;
+static constexpr uint64_t kMaxBlockDevValue = 100000;
+static constexpr uint64_t kMinElements = 0;
+static constexpr uint64_t kMaxElements = 10;
+static constexpr uint32_t kSuperLayoutValidMetadataSize = 8_KiB;
+static constexpr uint32_t kMinMetadataValue = 0;
+static constexpr uint32_t kMaxMetadataValue = 10000;
+static constexpr uint32_t kMaxBytes = 20;
+static constexpr uint32_t kMinOpen = 0;
+static constexpr uint32_t kMaxOpen = 2;
+
+const uint64_t kAttributeTypes[] = {
+ LP_PARTITION_ATTR_NONE, LP_PARTITION_ATTR_READONLY, LP_PARTITION_ATTR_SLOT_SUFFIXED,
+ LP_PARTITION_ATTR_UPDATED, LP_PARTITION_ATTR_DISABLED,
+};
+
+class SuperLayoutBuilderFuzzer {
+ public:
+ SuperLayoutBuilderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+
+ private:
+ FuzzedDataProvider mFdp;
+ void invokeSuperLayoutBuilderAPIs();
+ void callRandomOpen(int32_t open);
+ void addMultiplePartitions(int32_t numPartitions);
+ void setupSuperLayoutBuilder();
+ SuperLayoutBuilder mSuperLayoutBuilder;
+ unique_ptr<MetadataBuilder> mSuperBuilder;
+ unique_ptr<LpMetadata> mMetadata;
+ bool mOpenSuccess = false;
+};
+
+void SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder() {
+ uint64_t randomBlockDevSize =
+ mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
+ uint64_t blockDevSize = mFdp.ConsumeBool() ? kSuperLayoutValidBlockDevSize : randomBlockDevSize;
+ uint32_t randomMetadataMaxSize =
+ mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
+ uint32_t metadataMaxSize =
+ mFdp.ConsumeBool() ? kSuperLayoutValidMetadataSize : randomMetadataMaxSize;
+ uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+ mSuperBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
+
+ if (mSuperBuilder.get()) {
+ if (mFdp.ConsumeBool()) {
+ int32_t numPartitions =
+ mFdp.ConsumeIntegralInRange<int32_t>(kMinElements, kMaxElements);
+ addMultiplePartitions(numPartitions);
+ }
+
+ uint32_t randomOpen = mFdp.ConsumeIntegralInRange<uint32_t>(kMinOpen, kMaxOpen);
+ callRandomOpen(randomOpen);
+ }
+}
+
+void SuperLayoutBuilderFuzzer::addMultiplePartitions(int32_t numPartitions) {
+ for (int32_t idx = 0; idx < numPartitions; ++idx) {
+ string partitionName = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes)
+ : "builder_partition";
+ mSuperBuilder->AddPartition(partitionName, mFdp.PickValueInArray(kAttributeTypes));
+ }
+}
+
+void SuperLayoutBuilderFuzzer::callRandomOpen(int32_t open) {
+ mMetadata = mSuperBuilder->Export();
+ switch (open) {
+ case 0: {
+ vector<uint8_t> imageData = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);
+ mOpenSuccess = mSuperLayoutBuilder.Open((void*)(imageData.data()), imageData.size());
+ break;
+ }
+ case 1: {
+ mOpenSuccess = mSuperLayoutBuilder.Open(*mMetadata.get());
+ break;
+ }
+ case 2: {
+ unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+ WriteToImageFile(fd, *mMetadata.get());
+ mOpenSuccess = mSuperLayoutBuilder.Open(fd);
+ break;
+ }
+ }
+}
+
+void SuperLayoutBuilderFuzzer::invokeSuperLayoutBuilderAPIs() {
+ string imageName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+ string fuzzPartitionName =
+ mFdp.ConsumeBool() ? "builder_partition" : mFdp.ConsumeRandomLengthString(kMaxBytes);
+ if (!fuzzPartitionName.size()) {
+ fuzzPartitionName = "builder_partition";
+ }
+ setupSuperLayoutBuilder();
+ if (mOpenSuccess) {
+ while (mFdp.remaining_bytes()) {
+ auto invokeSuperAPIs = mFdp.PickValueInArray<const function<void()>>({
+ [&]() { mSuperLayoutBuilder.GetImageLayout(); },
+ [&]() {
+ mSuperLayoutBuilder.AddPartition(fuzzPartitionName, imageName,
+ mFdp.ConsumeIntegral<uint64_t>());
+ },
+ });
+ invokeSuperAPIs();
+ }
+ }
+}
+
+void SuperLayoutBuilderFuzzer::process() {
+ invokeSuperLayoutBuilderAPIs();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ SuperLayoutBuilderFuzzer superLayoutBuilderFuzzer(data, size);
+ superLayoutBuilderFuzzer.process();
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6fad662..a8a7716 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -198,6 +198,7 @@
"libsnapshot_cow/cow_format.cpp",
"libsnapshot_cow/cow_reader.cpp",
"libsnapshot_cow/parser_v2.cpp",
+ "libsnapshot_cow/parser_v3.cpp",
"libsnapshot_cow/snapshot_reader.cpp",
"libsnapshot_cow/writer_base.cpp",
"libsnapshot_cow/writer_v2.cpp",
@@ -419,6 +420,7 @@
},
data: [
"tools/testdata/cow_v2",
+ "tools/testdata/incompressible_block",
],
auto_gen_config: true,
require_root: false,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index c9777a3..75467cb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -94,13 +94,27 @@
} __attribute__((packed));
+// Resume point structure used for resume buffer
+struct ResumePoint {
+ // monotonically increasing value used by update_engine
+ uint64_t label;
+ // Index of last CowOperation guaranteed to be resumable
+ uint32_t op_index;
+} __attribute__((packed));
+
+static constexpr uint8_t kNumResumePoints = 4;
+
struct CowHeaderV3 : public CowHeader {
// Location of sequence buffer in COW.
uint64_t sequence_buffer_offset;
+ // number of currently written resume points
+ uint32_t resume_point_count;
// Size, in bytes, of the CowResumePoint buffer.
- uint32_t resume_buffer_size;
- // Size, in bytes, of the CowOperation buffer.
- uint32_t op_buffer_size;
+ uint32_t resume_point_max;
+ // Number of CowOperationV3 structs in the operation buffer, currently and total
+ // region size.
+ uint32_t op_count;
+ uint32_t op_count_max;
// Compression Algorithm
uint32_t compression_algorithm;
} __attribute__((packed));
@@ -218,6 +232,19 @@
return op.source_info & kCowOpSourceInfoDataMask;
}
+static constexpr off_t GetOpOffset(uint32_t op_index, const CowHeaderV3 header) {
+ return header.prefix.header_size + header.buffer_size +
+ (header.resume_point_max * sizeof(ResumePoint)) + (op_index * sizeof(CowOperationV3));
+}
+static constexpr off_t GetDataOffset(const CowHeaderV3 header) {
+ return header.prefix.header_size + header.buffer_size +
+ (header.resume_point_max * sizeof(ResumePoint)) +
+ header.op_count_max * sizeof(CowOperation);
+}
+static constexpr off_t GetResumeOffset(const CowHeaderV3 header) {
+ return header.prefix.header_size + header.buffer_size;
+}
+
struct CowFooter {
CowFooterOperation op;
uint8_t unused[64];
@@ -243,6 +270,8 @@
std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
+std::ostream& operator<<(std::ostream& os, ResumePoint const& arg);
+
int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 2721937..c87b32d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -16,6 +16,7 @@
#include <stdint.h>
+#include <deque>
#include <functional>
#include <memory>
#include <optional>
@@ -145,6 +146,7 @@
size_t ignore_bytes = 0) override;
CowHeader& GetHeader() override { return header_; }
+ const CowHeaderV3& header_v3() const { return header_; }
bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read);
bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
@@ -172,7 +174,7 @@
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
- CowHeader header_;
+ CowHeaderV3 header_;
std::optional<CowFooter> footer_;
uint64_t fd_size_;
std::optional<uint64_t> last_label_;
@@ -182,13 +184,14 @@
uint64_t num_total_data_ops_{};
uint64_t num_ordered_ops_to_merge_{};
bool has_seq_ops_{};
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_;
ReaderFlags reader_flag_;
bool is_merge_{};
- uint8_t compression_type_ = kCowCompressNone;
};
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
-
+// Though this function takes in a CowHeaderV3, the struct could be populated as a v1/v2 CowHeader.
+// The extra fields will just be filled as 0. V3 header is strictly a superset of v1/v2 header and
+// contains all of the latter's field
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header);
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 3016e93..5b1e56c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -54,6 +54,9 @@
// Batch write cluster ops
bool batch_write = false;
+
+ // Size of the cow operation buffer; used in v3 only.
+ uint32_t op_count_max = 0;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index e7b0020..08a79ba 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -405,15 +405,6 @@
// Add new public entries above this line.
- // Helpers for failure injection.
- using MergeConsistencyChecker =
- std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>;
-
- void set_merge_consistency_checker(MergeConsistencyChecker checker) {
- merge_consistency_checker_ = checker;
- }
- MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; }
-
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -657,8 +648,6 @@
MergeResult CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
MergeResult CheckTargetMergeState(LockedFile* lock, const std::string& name,
const SnapshotUpdateStatus& update_status);
- MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
- const SnapshotStatus& update_status);
auto UpdateStateToStr(enum UpdateState state);
// Get status or table information about a device-mapper node with a single target.
@@ -847,7 +836,6 @@
std::unique_ptr<SnapuserdClient> snapuserd_client_;
std::unique_ptr<LpMetadata> old_partition_metadata_;
std::optional<bool> is_snapshot_userspace_;
- MergeConsistencyChecker merge_consistency_checker_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index b905291..937065d 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -100,6 +100,11 @@
return os;
}
+std::ostream& operator<<(std::ostream& os, ResumePoint const& resume_point) {
+ os << "ResumePoint(" << resume_point.label << " , " << resume_point.op_index << ")";
+ return os;
+}
+
int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
if (op.type == kCowClusterOp) {
return op.source;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 607d610..8412879 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -29,11 +29,12 @@
#include "cow_decompress.h"
#include "parser_v2.h"
+#include "parser_v3.h"
namespace android {
namespace snapshot {
-bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header) {
if (lseek(fd.get(), 0, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek header failed";
return false;
@@ -49,9 +50,9 @@
<< "Expected: " << kCowMagicNumber;
return false;
}
- if (header->prefix.header_size > sizeof(CowHeader)) {
+ if (header->prefix.header_size > sizeof(CowHeaderV3)) {
LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
- << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
+ << " bytes, expected at most " << sizeof(CowHeaderV3) << " bytes)";
return false;
}
@@ -81,10 +82,9 @@
cow->merge_op_start_ = merge_op_start_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
- cow->data_loc_ = data_loc_;
+ cow->xor_data_loc_ = xor_data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
- cow->compression_type_ = compression_type_;
return cow;
}
@@ -103,11 +103,14 @@
PLOG(ERROR) << "lseek header failed";
return false;
}
- if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+
+ CHECK_GE(header_.prefix.header_size, sizeof(CowHeader));
+ CHECK_LE(header_.prefix.header_size, sizeof(header_));
+
+ if (!android::base::ReadFully(fd_, &header_, header_.prefix.header_size)) {
PLOG(ERROR) << "read header failed";
return false;
}
-
return true;
}
@@ -123,52 +126,35 @@
return false;
}
- CowParserV2 parser;
- if (!parser.Parse(fd, header_, label)) {
+ std::unique_ptr<CowParserBase> parser;
+ switch (header_.prefix.major_version) {
+ case 1:
+ case 2:
+ parser = std::make_unique<CowParserV2>();
+ break;
+ case 3:
+ parser = std::make_unique<CowParserV3>();
+ break;
+ default:
+ LOG(ERROR) << "Unknown version: " << header_.prefix.major_version;
+ return false;
+ }
+ if (!parser->Parse(fd, header_, label)) {
return false;
}
- footer_ = parser.footer();
- fd_size_ = parser.fd_size();
- last_label_ = parser.last_label();
- data_loc_ = parser.data_loc();
- ops_ = std::make_shared<std::vector<CowOperation>>(parser.ops()->size());
-
- // Translate the operation buffer from on disk to in memory
- for (size_t i = 0; i < parser.ops()->size(); i++) {
- const auto& v2_op = parser.ops()->at(i);
-
- auto& new_op = ops_->at(i);
- new_op.type = v2_op.type;
- new_op.data_length = v2_op.data_length;
-
- if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
- LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
- return false;
- }
- new_op.new_block = v2_op.new_block;
-
- uint64_t source_info = v2_op.source;
- if (new_op.type != kCowLabelOp) {
- source_info &= kCowOpSourceInfoDataMask;
- if (source_info != v2_op.source) {
- LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
- return false;
- }
- }
- if (v2_op.compression != kCowCompressNone) {
- if (compression_type_ == kCowCompressNone) {
- compression_type_ = v2_op.compression;
- } else if (compression_type_ != v2_op.compression) {
- LOG(ERROR) << "COW has mixed compression types which is not supported;"
- << " previously saw " << compression_type_ << ", got "
- << v2_op.compression << ", op: " << v2_op;
- return false;
- }
- }
- new_op.source_info = source_info;
+ TranslatedCowOps ops_info;
+ if (!parser->Translate(&ops_info)) {
+ return false;
}
+ header_ = ops_info.header;
+ ops_ = std::move(ops_info.ops);
+ footer_ = parser->footer();
+ fd_size_ = parser->fd_size();
+ last_label_ = parser->last_label();
+ xor_data_loc_ = parser->xor_data_loc();
+
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
return PrepMergeOps();
@@ -614,8 +600,8 @@
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
- len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) {
+ if (offset < header_.prefix.header_size || offset >= fd_size_ || offset + len > fd_size_ ||
+ len >= fd_size_) {
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
return false;
}
@@ -663,7 +649,7 @@
};
uint8_t CowReader::GetCompressionType() {
- return compression_type_;
+ return header_.compression_algorithm;
}
ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
@@ -695,12 +681,12 @@
uint64_t offset;
if (op->type == kCowXorOp) {
- offset = data_loc_->at(op->new_block);
+ offset = xor_data_loc_->at(op->new_block);
} else {
offset = GetCowOpSourceInfoData(*op);
}
-
- if (!decompressor) {
+ if (!decompressor ||
+ ((op->data_length == header_.block_size) && (header_.prefix.major_version == 3))) {
CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);
return stream.ReadFully(buffer, buffer_size);
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 83b5a12..993630b 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -74,15 +74,16 @@
}
}
-static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeader& header) {
+static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeaderV3& header) {
CowParserV2 parser;
if (!parser.Parse(fd, header)) {
LOG(ERROR) << "v2 parser failed";
return false;
}
- for (const auto& op : *parser.ops()) {
+ for (const auto& op : *parser.get_v2ops()) {
std::cout << op << "\n";
- if (auto iter = parser.data_loc()->find(op.new_block); iter != parser.data_loc()->end()) {
+ if (auto iter = parser.xor_data_loc()->find(op.new_block);
+ iter != parser.xor_data_loc()->end()) {
std::cout << " data loc: " << iter->second << "\n";
}
}
@@ -90,7 +91,7 @@
}
static bool ShowRawOpStream(borrowed_fd fd) {
- CowHeader header;
+ CowHeaderV3 header;
if (!ReadCowHeader(fd, &header)) {
LOG(ERROR) << "parse header failed";
return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
new file mode 100644
index 0000000..837b33e
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
@@ -0,0 +1,56 @@
+//
+// 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 <optional>
+#include <unordered_map>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct TranslatedCowOps {
+ CowHeaderV3 header;
+ std::shared_ptr<std::vector<CowOperationV3>> ops;
+};
+
+class CowParserBase {
+ public:
+ virtual ~CowParserBase() = default;
+
+ virtual bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+ std::optional<uint64_t> label = {}) = 0;
+ virtual bool Translate(TranslatedCowOps* out) = 0;
+ virtual std::optional<CowFooter> footer() const { return std::nullopt; }
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc() {
+ return xor_data_loc_;
+ };
+
+ uint64_t fd_size() const { return fd_size_; }
+ const std::optional<uint64_t>& last_label() const { return last_label_; }
+
+ protected:
+ CowHeaderV3 header_ = {};
+ uint64_t fd_size_;
+ std::optional<uint64_t> last_label_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_ = {};
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
index a7307bf..08a43a4 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -18,12 +18,14 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <libsnapshot/cow_format.h>
+
namespace android {
namespace snapshot {
using android::base::borrowed_fd;
-bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
+bool CowParserV2::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
auto pos = lseek(fd.get(), 0, SEEK_END);
if (pos < 0) {
PLOG(ERROR) << "lseek end failed";
@@ -46,18 +48,8 @@
LOG(ERROR) << "Clusters must contain at least two operations to function.";
return false;
}
- if (header_.op_size != sizeof(CowOperationV2)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperationV2);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
- return false;
- }
- if ((header_.prefix.major_version > kCowVersionMajor) ||
- (header_.prefix.minor_version != kCowVersionMinor)) {
+ if (header_.prefix.major_version > 2 || header_.prefix.minor_version != 0) {
LOG(ERROR) << "Header version mismatch, "
<< "major version: " << header_.prefix.major_version
<< ", expected: " << kCowVersionMajor
@@ -71,7 +63,7 @@
bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
uint64_t pos;
- auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+ auto xor_data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
// Skip the scratch space
if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
@@ -119,7 +111,7 @@
auto& current_op = ops_buffer->data()[current_op_num];
current_op_num++;
if (current_op.type == kCowXorOp) {
- data_loc->insert({current_op.new_block, data_pos});
+ xor_data_loc->insert({current_op.new_block, data_pos});
}
pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
@@ -199,9 +191,51 @@
}
}
- ops_ = ops_buffer;
- ops_->shrink_to_fit();
- data_loc_ = data_loc;
+ v2_ops_ = ops_buffer;
+ v2_ops_->shrink_to_fit();
+ xor_data_loc_ = xor_data_loc;
+ return true;
+}
+
+bool CowParserV2::Translate(TranslatedCowOps* out) {
+ out->ops = std::make_shared<std::vector<CowOperationV3>>(v2_ops_->size());
+
+ // Translate the operation buffer from on disk to in memory
+ for (size_t i = 0; i < out->ops->size(); i++) {
+ const auto& v2_op = v2_ops_->at(i);
+
+ auto& new_op = out->ops->at(i);
+ new_op.type = v2_op.type;
+ new_op.data_length = v2_op.data_length;
+
+ if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
+ return false;
+ }
+ new_op.new_block = v2_op.new_block;
+
+ uint64_t source_info = v2_op.source;
+ if (new_op.type != kCowLabelOp) {
+ source_info &= kCowOpSourceInfoDataMask;
+ if (source_info != v2_op.source) {
+ LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
+ return false;
+ }
+ }
+ if (v2_op.compression != kCowCompressNone) {
+ if (header_.compression_algorithm == kCowCompressNone) {
+ header_.compression_algorithm = v2_op.compression;
+ } else if (header_.compression_algorithm != v2_op.compression) {
+ LOG(ERROR) << "COW has mixed compression types which is not supported;"
+ << " previously saw " << header_.compression_algorithm << ", got "
+ << v2_op.compression << ", op: " << v2_op;
+ return false;
+ }
+ }
+ new_op.source_info = source_info;
+ }
+
+ out->header = header_;
return true;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
index f51ff88..f9ee2e5 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -17,36 +17,29 @@
#include <memory>
#include <optional>
-#include <unordered_map>
#include <vector>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
+#include <libsnapshot_cow/parser_base.h>
namespace android {
namespace snapshot {
-class CowParserV2 {
+class CowParserV2 final : public CowParserBase {
public:
- bool Parse(android::base::borrowed_fd fd, const CowHeader& header,
- std::optional<uint64_t> label = {});
+ bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+ std::optional<uint64_t> label = {}) override;
+ bool Translate(TranslatedCowOps* out) override;
+ std::optional<CowFooter> footer() const override { return footer_; }
const CowHeader& header() const { return header_; }
- const std::optional<CowFooter>& footer() const { return footer_; }
- std::shared_ptr<std::vector<CowOperationV2>> ops() { return ops_; }
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
- uint64_t fd_size() const { return fd_size_; }
- const std::optional<uint64_t>& last_label() const { return last_label_; }
+ std::shared_ptr<std::vector<CowOperationV2>> get_v2ops() { return v2_ops_; }
private:
bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
-
- CowHeader header_ = {};
+ std::shared_ptr<std::vector<CowOperationV2>> v2_ops_;
std::optional<CowFooter> footer_;
- std::shared_ptr<std::vector<CowOperationV2>> ops_;
- std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
- uint64_t fd_size_;
- std::optional<uint64_t> last_label_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
new file mode 100644
index 0000000..52c6348
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
@@ -0,0 +1,136 @@
+// 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 "parser_v3.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+bool CowParserV3::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
+ auto pos = lseek(fd.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+ header_ = header;
+
+ if (header_.footer_size != 0) {
+ LOG(ERROR) << "Footer size isn't 0, read " << header_.footer_size;
+ return false;
+ }
+
+ if (header_.op_size != sizeof(CowOperationV3)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperationV3);
+ return false;
+ }
+ if (header_.cluster_ops != 0) {
+ LOG(ERROR) << "Cluster ops not supported in v3";
+ return false;
+ }
+
+ if (header_.prefix.major_version != 3 || header_.prefix.minor_version != 0) {
+ LOG(ERROR) << "Header version mismatch, "
+ << "major version: " << header_.prefix.major_version
+ << ", expected: " << kCowVersionMajor
+ << ", minor version: " << header_.prefix.minor_version
+ << ", expected: " << kCowVersionMinor;
+ return false;
+ }
+
+ std::optional<uint32_t> op_index = header_.op_count;
+ if (label) {
+ if (!ReadResumeBuffer(fd)) {
+ PLOG(ERROR) << "Failed to read resume buffer";
+ return false;
+ }
+ op_index = FindResumeOp(label.value());
+ if (op_index == std::nullopt) {
+ LOG(ERROR) << "failed to get op index from given label: " << label.value();
+ return false;
+ }
+ }
+
+ return ParseOps(fd, op_index.value());
+}
+
+bool CowParserV3::ReadResumeBuffer(borrowed_fd fd) {
+ resume_points_ = std::make_shared<std::vector<ResumePoint>>(header_.resume_point_count);
+
+ return android::base::ReadFullyAtOffset(fd, resume_points_->data(),
+ header_.resume_point_count * sizeof(ResumePoint),
+ header_.prefix.header_size + header_.buffer_size);
+}
+
+std::optional<uint32_t> CowParserV3::FindResumeOp(const uint64_t label) {
+ for (auto& resume_point : *resume_points_) {
+ if (resume_point.label == label) {
+ return resume_point.op_index;
+ }
+ }
+ LOG(ERROR) << "failed to find label: " << label << "from following labels";
+ LOG(ERROR) << android::base::Join(*resume_points_, " ");
+
+ return std::nullopt;
+}
+
+bool CowParserV3::ParseOps(borrowed_fd fd, const uint32_t op_index) {
+ ops_ = std::make_shared<std::vector<CowOperationV3>>();
+ ops_->resize(op_index);
+
+ // read beginning of operation buffer -> so op_index = 0
+ const off_t offset = GetOpOffset(0, header_);
+ if (!android::base::ReadFullyAtOffset(fd, ops_->data(), ops_->size() * sizeof(CowOperationV3),
+ offset)) {
+ PLOG(ERROR) << "read ops failed";
+ return false;
+ }
+
+ // fill out mapping of XOR op data location
+ uint64_t data_pos = GetDataOffset(header_);
+
+ xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+ for (auto op : *ops_) {
+ if (op.type == kCowXorOp) {
+ xor_data_loc_->insert({op.new_block, data_pos});
+ }
+ data_pos += op.data_length;
+ }
+ // :TODO: sequence buffer & resume buffer follow
+ // Once we implement labels, we'll have to discard unused ops and adjust
+ // the header as needed.
+
+ ops_->shrink_to_fit();
+
+ return true;
+}
+
+bool CowParserV3::Translate(TranslatedCowOps* out) {
+ out->ops = ops_;
+ out->header = header_;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
new file mode 100644
index 0000000..afc01af
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
@@ -0,0 +1,60 @@
+// 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.
+// 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 <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot_cow/parser_base.h>
+
+namespace android {
+namespace snapshot {
+
+class CowParserV3 final : public CowParserBase {
+ public:
+ bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+ std::optional<uint64_t> label = {}) override;
+ bool Translate(TranslatedCowOps* out) override;
+ std::shared_ptr<std::vector<ResumePoint>> resume_points() const { return resume_points_; }
+
+ private:
+ bool ParseOps(android::base::borrowed_fd fd, const uint32_t op_index);
+ std::optional<uint32_t> FindResumeOp(const uint64_t label);
+ CowHeaderV3 header_ = {};
+ std::shared_ptr<std::vector<CowOperationV3>> ops_;
+ bool ReadResumeBuffer(android::base::borrowed_fd fd);
+ std::shared_ptr<std::vector<ResumePoint>> resume_points_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index 35d74ba..2709059 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -1522,6 +1522,37 @@
ASSERT_TRUE(reader.GetFooter(&footer));
}
+TEST_F(CowTest, DecompressIncompressibleBlock) {
+ auto fd = OpenTestFile("incompressible_block", O_RDONLY);
+ ASSERT_GE(fd, 0);
+
+ std::string original;
+ ASSERT_TRUE(android::base::ReadFdToString(fd, &original)) << strerror(errno);
+ ASSERT_EQ(original.size(), 4096);
+
+ CowOptions options;
+ options.compression = "gz";
+ auto writer = CreateCowWriter(2, options, GetCowFd());
+ ASSERT_NE(writer, nullptr);
+ ASSERT_TRUE(writer->AddRawBlocks(0, original.data(), original.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ std::string block(original.size(), '\0');
+ ASSERT_EQ(iter->Get()->data_length, 4096);
+ ASSERT_TRUE(ReadData(reader, iter->Get(), block.data(), block.size()));
+
+ for (size_t i = 0; i < block.size(); i++) {
+ ASSERT_EQ(block[i], original[i]) << "mismatch at byte " << i;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
index 2373d4d..c41e07c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -26,7 +26,9 @@
#include <libsnapshot/cow_writer.h>
#include "cow_decompress.h"
#include "libsnapshot/cow_format.h"
+#include "writer_v2.h"
#include "writer_v3.h"
+
using android::base::unique_fd;
using testing::AssertionFailure;
using testing::AssertionResult;
@@ -35,7 +37,7 @@
namespace android {
namespace snapshot {
-class CowOperationV3Test : public ::testing::Test {
+class CowTestV3 : public ::testing::Test {
protected:
virtual void SetUp() override {
cow_ = std::make_unique<TemporaryFile>();
@@ -49,5 +51,436 @@
std::unique_ptr<TemporaryFile> cow_;
};
+// Helper to check read sizes.
+static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
+ return reader.ReadData(op, buffer, size) == size;
+}
+
+TEST_F(CowTestV3, CowHeaderV2Test) {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ options.block_size = 4096;
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ auto writer_v2 = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer_v2->Initialize());
+ ASSERT_TRUE(writer_v2->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 2);
+ ASSERT_EQ(header.prefix.minor_version, 0);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.cluster_ops, options.cluster_ops);
+}
+
+TEST_F(CowTestV3, Header) {
+ CowOptions options;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.GetHeader();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, 0);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.cluster_ops, 0);
+}
+
+TEST_F(CowTestV3, MaxOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_FALSE(writer->AddZeroBlocks(1, 21));
+ ASSERT_FALSE(writer->AddZeroBlocks(1, 1));
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));
+
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_EQ(reader.header_v3().op_count, 20);
+}
+
+TEST_F(CowTestV3, ZeroOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ ASSERT_TRUE(writer->AddZeroBlocks(1, 2));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_EQ(reader.header_v3().op_count, 2);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 1);
+ ASSERT_EQ(op->source_info, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->AtEnd());
+ op = iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 2);
+ ASSERT_EQ(op->source_info, 0);
+}
+
+TEST_F(CowTestV3, ReplaceOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ options.scratch_space = false;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.op_count, 1);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ auto op = iter->Get();
+ std::string sink(data.size(), '\0');
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 5);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveReplaceOp) {
+ CowOptions options;
+ options.op_count_max = 20;
+ options.scratch_space = false;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+ std::string data;
+ data.resize(options.block_size * 5);
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = char(rand() % 256);
+ }
+
+ ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.op_count, 5);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+ std::string sink(data.size(), '\0');
+
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->data_length, options.block_size);
+ ASSERT_EQ(op->new_block, 5 + i);
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ ASSERT_EQ(i, 5);
+}
+
+TEST_F(CowTestV3, CopyOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ ASSERT_TRUE(writer->AddCopy(10, 1000, 100));
+ ASSERT_TRUE(writer->Finalize());
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 1000 + i);
+ iter->Next();
+ i += 1;
+ }
+
+ ASSERT_EQ(i, 100);
+}
+
+TEST_F(CowTestV3, XorOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data = "This is test data-1. Testing xor";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 1);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
+ std::string sink(data.size(), '\0');
+
+ ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314); // 4096 * 24 + 10
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveXorOp) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data;
+ data.resize(options.block_size * 5);
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = char(rand() % 256);
+ }
+
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 5);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ std::string sink(data.size(), '\0');
+ size_t i = 0;
+
+ while (!iter->AtEnd()) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314 + (i * options.block_size)); // 4096 * 24 + 10
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ ASSERT_EQ(i, 5);
+}
+
+TEST_F(CowTestV3, AllOpsWithCompression) {
+ CowOptions options;
+ options.compression = "gz";
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data;
+ data.resize(options.block_size * 5);
+ for (int i = 0; i < data.size(); i++) {
+ data[i] = char(rand() % 4);
+ }
+
+ ASSERT_TRUE(writer->AddZeroBlocks(10, 5));
+ ASSERT_TRUE(writer->AddCopy(15, 3, 5));
+ ASSERT_TRUE(writer->AddRawBlocks(18, data.data(), data.size()));
+ ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ const auto& header = reader.header_v3();
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, 3);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.buffer_size, BUFFER_REGION_DEFAULT_SIZE);
+ ASSERT_EQ(header.op_count, 20);
+ ASSERT_EQ(header.op_count_max, 100);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+
+ size_t i = 0;
+
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->new_block, 10 + i);
+ iter->Next();
+ i++;
+ }
+ i = 0;
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->new_block, 15 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 3 + i);
+ iter->Next();
+ i++;
+ }
+ i = 0;
+ std::string sink(data.size(), '\0');
+
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->new_block, 18 + i);
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+
+ i = 0;
+ std::fill(sink.begin(), sink.end(), '\0');
+ while (i < 5) {
+ auto op = iter->Get();
+ ASSERT_EQ(op->type, kCowXorOp);
+ ASSERT_EQ(op->new_block, 50 + i);
+ ASSERT_EQ(GetCowOpSourceInfoData(*op), 98314 + (i * options.block_size)); // 4096 * 24 + 10
+ ASSERT_TRUE(
+ ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+ iter->Next();
+ i++;
+ }
+ ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, GzCompression) {
+ CowOptions options;
+ options.op_count_max = 100;
+ options.compression = "gz";
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.compression_algorithm, kCowCompressGz);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->AtEnd());
+ auto op = iter->Get();
+
+ std::string sink(data.size(), '\0');
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->data_length, 56); // compressed!
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+ ASSERT_EQ(sink, data);
+
+ iter->Next();
+ ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, ResumePointTest) {
+ CowOptions options;
+ options.op_count_max = 100;
+ auto writer = CreateCowWriter(3, options, GetCowFd());
+
+ ASSERT_TRUE(writer->AddZeroBlocks(0, 15));
+ ASSERT_TRUE(writer->AddLabel(0));
+ ASSERT_TRUE(writer->AddZeroBlocks(15, 15));
+ ASSERT_TRUE(writer->Finalize());
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 30);
+
+ CowWriterV3 second_writer(options, GetCowFd());
+ ASSERT_TRUE(second_writer.Initialize(0));
+ ASSERT_TRUE(second_writer.Finalize());
+
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ header = reader.header_v3();
+ ASSERT_EQ(header.op_count, 15);
+}
} // namespace snapshot
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
index 709b248..5274456 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -62,6 +62,8 @@
bool InitFd();
bool ValidateNewBlock(uint64_t new_block);
+ bool IsEstimating() const { return is_dev_null_; }
+
CowOptions options_;
android::base::unique_fd fd_;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index a6f449f..37324c7 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -262,11 +262,6 @@
return false;
}
- if (lseek(fd_.get(), sizeof(CowHeader) + header_.buffer_size, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed";
- return false;
- }
-
InitPos();
InitBatchWrites();
@@ -274,12 +269,15 @@
}
bool CowWriterV2::OpenForAppend(uint64_t label) {
- if (!ReadCowHeader(fd_, &header_)) {
+ CowHeaderV3 header_v3;
+ if (!ReadCowHeader(fd_, &header_v3)) {
return false;
}
+ header_ = header_v3;
+
CowParserV2 parser;
- if (!parser.Parse(fd_, header_, {label})) {
+ if (!parser.Parse(fd_, header_v3, {label})) {
return false;
}
if (header_.prefix.major_version > 2) {
@@ -295,7 +293,7 @@
footer_.op.num_ops = 0;
InitPos();
- for (const auto& op : *parser.ops()) {
+ for (const auto& op : *parser.get_v2ops()) {
AddOperation(op);
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 2b9867e..6883c5e 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -34,6 +34,7 @@
#include <zlib.h>
#include <fcntl.h>
+#include <libsnapshot_cow/parser_v3.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -54,46 +55,263 @@
using android::base::unique_fd;
CowWriterV3::CowWriterV3(const CowOptions& options, unique_fd&& fd)
- : CowWriterBase(options, std::move(fd)) {}
+ : CowWriterBase(options, std::move(fd)) {
+ SetupHeaders();
+}
+
+void CowWriterV3::SetupHeaders() {
+ header_ = {};
+ header_.prefix.magic = kCowMagicNumber;
+ header_.prefix.major_version = 3;
+ header_.prefix.minor_version = 0;
+ header_.prefix.header_size = sizeof(CowHeaderV3);
+ header_.footer_size = 0;
+ header_.op_size = sizeof(CowOperationV3);
+ header_.block_size = options_.block_size;
+ header_.num_merge_ops = options_.num_merge_ops;
+ header_.cluster_ops = 0;
+ if (options_.scratch_space) {
+ header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;
+ }
+
+ // v3 specific fields
+ // WIP: not quite sure how some of these are calculated yet, assuming buffer_size is determined
+ // during COW size estimation
+ header_.sequence_buffer_offset = 0;
+ header_.resume_point_count = 0;
+ header_.resume_point_max = kNumResumePoints;
+ header_.op_count = 0;
+ header_.op_count_max = 0;
+ header_.compression_algorithm = kCowCompressNone;
+ return;
+}
+
+bool CowWriterV3::ParseOptions() {
+ num_compress_threads_ = std::max(options_.num_compress_threads, 1);
+ auto parts = android::base::Split(options_.compression, ",");
+ if (parts.size() > 2) {
+ LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+ << parts.size() << " " << options_.compression;
+ return false;
+ }
+ auto algorithm = CompressionAlgorithmFromString(parts[0]);
+ if (!algorithm) {
+ LOG(ERROR) << "unrecognized compression: " << options_.compression;
+ return false;
+ }
+ header_.compression_algorithm = *algorithm;
+
+ if (parts.size() > 1) {
+ if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+ LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+ return false;
+ }
+ } else {
+ compression_.compression_level =
+ CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+ }
+
+ compression_.algorithm = *algorithm;
+ compressor_ = ICompressor::Create(compression_, header_.block_size);
+ return true;
+}
CowWriterV3::~CowWriterV3() {}
bool CowWriterV3::Initialize(std::optional<uint64_t> label) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (label) return false;
- return false;
+ if (!InitFd() || !ParseOptions()) {
+ return false;
+ }
+ if (!label) {
+ if (!OpenForWrite()) {
+ return false;
+ }
+ } else {
+ if (!OpenForAppend(*label)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CowWriterV3::OpenForWrite() {
+ // This limitation is tied to the data field size in CowOperationV2.
+ // Keeping this for V3 writer <- although we
+ if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Block size is too large";
+ return false;
+ }
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+
+ // Headers are not complete, but this ensures the file is at the right
+ // position.
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+
+ if (options_.scratch_space) {
+ // Initialize the scratch space
+ std::string data(header_.buffer_size, 0);
+ if (!android::base::WriteFully(fd_, data.data(), header_.buffer_size)) {
+ PLOG(ERROR) << "writing scratch space failed";
+ return false;
+ }
+ }
+ header_.op_count_max = options_.op_count_max;
+ resume_points_ = std::make_shared<std::vector<ResumePoint>>();
+
+ if (!Sync()) {
+ LOG(ERROR) << "Header sync failed";
+ return false;
+ }
+ next_data_pos_ = GetDataOffset(header_);
+ return true;
+}
+
+bool CowWriterV3::OpenForAppend(uint64_t label) {
+ CowHeaderV3 header_v3;
+ if (!ReadCowHeader(fd_, &header_v3)) {
+ LOG(ERROR) << "Couldn't read Cow Header";
+ return false;
+ }
+
+ header_ = header_v3;
+
+ CHECK(label >= 0);
+ CowParserV3 parser;
+ if (!parser.Parse(fd_, header_, label)) {
+ PLOG(ERROR) << "unable to parse with given label: " << label;
+ return false;
+ }
+
+ resume_points_ = parser.resume_points();
+ options_.block_size = header_.block_size;
+ next_data_pos_ = GetDataOffset(header_);
+
+ TranslatedCowOps ops;
+ parser.Translate(&ops);
+ header_.op_count = ops.ops->size();
+
+ for (const auto& op : *ops.ops) {
+ next_data_pos_ += op.data_length;
+ }
+
+ return true;
}
bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (new_block || old_block || num_blocks) return false;
- return false;
+ for (size_t i = 0; i < num_blocks; i++) {
+ CowOperationV3 op = {};
+ op.type = kCowCopyOp;
+ op.new_block = new_block + i;
+ op.source_info = old_block + i;
+ if (!WriteOperation(op)) {
+ return false;
+ }
+ }
+
+ return true;
}
bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
-
- if (new_block_start || data || size) return false;
- return false;
+ return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
}
bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
uint32_t old_block, uint16_t offset) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (new_block_start || old_block || offset || data || size) return false;
- return false;
+ return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+ uint64_t old_block, uint16_t offset, uint8_t type) {
+ const size_t num_blocks = (size / header_.block_size);
+ for (size_t i = 0; i < num_blocks; i++) {
+ const uint8_t* const iter =
+ reinterpret_cast<const uint8_t*>(data) + (header_.block_size * i);
+
+ CowOperation op = {};
+ op.new_block = new_block_start + i;
+
+ op.type = type;
+ if (type == kCowXorOp) {
+ op.source_info = (old_block + i) * header_.block_size + offset;
+ } else {
+ op.source_info = next_data_pos_;
+ }
+ std::basic_string<uint8_t> compressed_data;
+ const void* out_data = iter;
+
+ op.data_length = header_.block_size;
+
+ if (compression_.algorithm) {
+ if (!compressor_) {
+ PLOG(ERROR) << "Compressor not initialized";
+ return false;
+ }
+ compressed_data = compressor_->Compress(out_data, header_.block_size);
+ if (compressed_data.size() < op.data_length) {
+ out_data = compressed_data.data();
+ op.data_length = compressed_data.size();
+ }
+ }
+ if (!WriteOperation(op, out_data, op.data_length)) {
+ PLOG(ERROR) << "AddRawBlocks with compression: write failed. new block: "
+ << new_block_start << " compression: " << compression_.algorithm;
+ return false;
+ }
+ }
+
+ return true;
}
bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (new_block_start && num_blocks) return false;
- return false;
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ CowOperationV3 op;
+ op.type = kCowZeroOp;
+ op.data_length = 0;
+ op.new_block = new_block_start + i;
+ op.source_info = 0;
+ if (!WriteOperation(op)) {
+ return false;
+ }
+ }
+ return true;
}
bool CowWriterV3::EmitLabel(uint64_t label) {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- if (label) return false;
- return false;
+ // remove all labels greater than this current one. we want to avoid the situation of adding
+ // in
+ // duplicate labels with differing op values
+ auto remove_if_callback = [&](const auto& resume_point) -> bool {
+ if (resume_point.label >= label) return true;
+ return false;
+ };
+ resume_points_->erase(
+ std::remove_if(resume_points_->begin(), resume_points_->end(), remove_if_callback),
+ resume_points_->end());
+
+ resume_points_->push_back({label, header_.op_count});
+ header_.resume_point_count++;
+ // remove the oldest resume point if resume_buffer is full
+ while (resume_points_->size() > header_.resume_point_max) {
+ resume_points_->erase(resume_points_->begin());
+ }
+
+ CHECK_LE(resume_points_->size(), header_.resume_point_max);
+
+ if (!android::base::WriteFullyAtOffset(fd_, resume_points_->data(),
+ resume_points_->size() * sizeof(ResumePoint),
+ GetResumeOffset(header_))) {
+ PLOG(ERROR) << "writing resume buffer failed";
+ return false;
+ }
+ return Sync();
}
bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
@@ -102,15 +320,49 @@
return false;
}
+bool CowWriterV3::WriteOperation(const CowOperationV3& op, const void* data, size_t size) {
+ if (IsEstimating()) {
+ header_.op_count++;
+ header_.op_count_max++;
+ next_data_pos_ += op.data_length;
+ return true;
+ }
+
+ if (header_.op_count + 1 > header_.op_count_max) {
+ LOG(ERROR) << "Maximum number of ops reached: " << header_.op_count_max;
+ return false;
+ }
+
+ const off_t offset = GetOpOffset(header_.op_count, header_);
+ if (!android::base::WriteFullyAtOffset(fd_, &op, sizeof(op), offset)) {
+ PLOG(ERROR) << "write failed for " << op << " at " << offset;
+ return false;
+ }
+ if (data && size > 0) {
+ if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
+ PLOG(ERROR) << "write failed for data of size: " << size
+ << " at offset: " << next_data_pos_;
+ return false;
+ }
+ }
+ header_.op_count++;
+ next_data_pos_ += op.data_length;
+ next_op_pos_ += sizeof(CowOperationV3);
+
+ return true;
+}
+
bool CowWriterV3::Finalize() {
- LOG(ERROR) << __LINE__ << " " << __FILE__ << " <- function here should never be called";
- return false;
+ CHECK_GE(header_.prefix.header_size, sizeof(CowHeaderV3));
+ CHECK_LE(header_.prefix.header_size, sizeof(header_));
+ if (!android::base::WriteFullyAtOffset(fd_, &header_, header_.prefix.header_size, 0)) {
+ return false;
+ }
+ return Sync();
}
uint64_t CowWriterV3::GetCowSize() {
- LOG(ERROR) << __LINE__ << " " << __FILE__
- << " <- Get Cow Size function here should never be called";
- return 0;
+ return next_data_pos_;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index ddd7287..3dfc33c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -14,7 +14,8 @@
#pragma once
-#include <future>
+#include <android-base/logging.h>
+
#include "writer_base.h"
namespace android {
@@ -37,6 +38,34 @@
virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
virtual bool EmitLabel(uint64_t label) override;
virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+
+ private:
+ void SetupHeaders();
+ bool ParseOptions();
+ bool OpenForWrite();
+ bool OpenForAppend(uint64_t label);
+ bool WriteOperation(const CowOperationV3& op, const void* data = nullptr, size_t size = 0);
+ bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+ uint16_t offset, uint8_t type);
+ bool CompressBlocks(size_t num_blocks, const void* data);
+
+ private:
+ CowHeaderV3 header_{};
+ CowCompression compression_;
+ // in the case that we are using one thread for compression, we can store and re-use the same
+ // compressor
+ std::unique_ptr<ICompressor> compressor_;
+ std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
+ // Resume points contain a laebl + cow_op_index.
+ std::shared_ptr<std::vector<ResumePoint>> resume_points_;
+
+ uint64_t next_op_pos_ = 0;
+ uint64_t next_data_pos_ = 0;
+ std::vector<std::basic_string<uint8_t>> compressed_buf_;
+
+ // in the case that we are using one thread for compression, we can store and re-use the same
+ // compressor
+ int num_compress_threads_ = 1;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 4743a42..e91e3b7 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -89,8 +89,6 @@
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
-MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status);
-
// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
SnapshotManager::~SnapshotManager() {}
@@ -120,9 +118,7 @@
}
SnapshotManager::SnapshotManager(IDeviceInfo* device)
- : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {
- merge_consistency_checker_ = android::snapshot::CheckMergeConsistency;
-}
+ : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
@@ -1365,13 +1361,6 @@
}
}
- // Merge is complete at this point
-
- auto code = CheckMergeConsistency(lock, name, snapshot_status);
- if (code != MergeFailureCode::Ok) {
- return MergeResult(UpdateState::MergeFailed, code);
- }
-
// Merging is done. First, update the status file to indicate the merge
// is complete. We do this before calling OnSnapshotMergeComplete, even
// though this means the write is potentially wasted work (since in the
@@ -1401,80 +1390,6 @@
return GetCowName(snapshot);
}
-MergeFailureCode SnapshotManager::CheckMergeConsistency(LockedFile* lock, const std::string& name,
- const SnapshotStatus& status) {
- CHECK(lock);
-
- return merge_consistency_checker_(name, status);
-}
-
-MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
- if (!status.using_snapuserd()) {
- // Do not try to verify old-style COWs yet.
- return MergeFailureCode::Ok;
- }
-
- auto& dm = DeviceMapper::Instance();
-
- std::string cow_image_name = GetMappedCowDeviceName(name, status);
- std::string cow_image_path;
- if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
- LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
- return MergeFailureCode::GetCowPathConsistencyCheck;
- }
-
- // First pass, count # of ops.
- size_t num_ops = 0;
- {
- unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open " << cow_image_name;
- return MergeFailureCode::OpenCowConsistencyCheck;
- }
-
- CowReader reader;
- if (!reader.Parse(std::move(fd))) {
- LOG(ERROR) << "Failed to parse cow " << cow_image_path;
- return MergeFailureCode::ParseCowConsistencyCheck;
- }
-
- num_ops = reader.get_num_total_data_ops();
- }
-
- // Second pass, try as hard as we can to get the actual number of blocks
- // the system thinks is merged.
- unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_DIRECT | O_SYNC | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open direct " << cow_image_name;
- return MergeFailureCode::OpenCowDirectConsistencyCheck;
- }
-
- void* addr;
- size_t page_size = getpagesize();
- if (posix_memalign(&addr, page_size, page_size) < 0) {
- PLOG(ERROR) << "posix_memalign with page size " << page_size;
- return MergeFailureCode::MemAlignConsistencyCheck;
- }
-
- // COWs are always at least 2MB, this is guaranteed in snapshot creation.
- std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
- if (!android::base::ReadFully(fd, buffer.get(), page_size)) {
- PLOG(ERROR) << "Direct read failed " << cow_image_name;
- return MergeFailureCode::DirectReadConsistencyCheck;
- }
-
- auto header = reinterpret_cast<CowHeader*>(buffer.get());
- if (header->num_merge_ops != num_ops) {
- LOG(ERROR) << "COW consistency check failed, expected " << num_ops << " to be merged, "
- << "but " << header->num_merge_ops << " were actually recorded.";
- LOG(ERROR) << "Aborting merge progress for snapshot " << name
- << ", will try again next boot";
- return MergeFailureCode::WrongMergeCountConsistencyCheck;
- }
-
- return MergeFailureCode::Ok;
-}
-
MergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) {
@@ -3598,6 +3513,11 @@
return Return::Error();
}
+ if (!android::fs_mgr::WaitForFile(cow_path, 6s)) {
+ LOG(ERROR) << "Timed out waiting for device to appear: " << cow_path;
+ return Return::Error();
+ }
+
if (it->second.using_snapuserd()) {
unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
@@ -3743,7 +3663,7 @@
return nullptr;
}
- CowHeader header;
+ CowHeaderV3 header;
if (!ReadCowHeader(cow_fd, &header)) {
LOG(ERROR) << "OpenCompressedSnapshotWriter: read header failed";
return nullptr;
@@ -4419,32 +4339,30 @@
}
bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
- auto slot = GetCurrentSlot();
- if (slot == Slot::Target) {
- // Merge in-progress
- if (IsSnapuserdRequired()) {
+ // We cannot grab /metadata/ota lock here as this
+ // is in reboot path. See b/308900853
+ //
+ // Check if any of the partitions are mounted
+ // off dm-user block device. If so, then we are certain
+ // that OTA update in progress.
+ auto current_suffix = device_->GetSlotSuffix();
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ LOG(ERROR) << "No dm-enabled block device is found.";
+ return false;
+ }
+ for (auto& partition : dm_block_devices) {
+ std::string partition_name = partition.first + current_suffix;
+ DeviceMapper::TargetInfo snap_target;
+ if (!GetSingleTarget(partition_name, TableQuery::Status, &snap_target)) {
+ return false;
+ }
+ auto type = DeviceMapper::GetTargetType(snap_target.spec);
+ if (type == "user") {
return true;
}
}
-
- // Let's check more deeper to see if snapshots are mounted
- auto lock = LockExclusive();
- if (!lock) {
- return false;
- }
-
- std::vector<std::string> snapshots;
- if (!ListSnapshots(lock.get(), &snapshots)) {
- return false;
- }
-
- for (const auto& snapshot : snapshots) {
- // Active snapshot and daemon is alive
- if (IsSnapshotDevice(snapshot) && EnsureSnapuserdConnected(2s)) {
- return true;
- }
- }
-
return false;
}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index e506110..4e6b5e1 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1601,97 +1601,6 @@
}
}
-// Test that a transient merge consistency check failure can resume properly.
-TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
- if (!snapuserd_required_) {
- // b/179111359
- GTEST_SKIP() << "Skipping snapuserd test";
- }
-
- auto old_sys_size = GetSize(sys_);
- auto old_prd_size = GetSize(prd_);
-
- // Grow |sys| but shrink |prd|.
- SetSize(sys_, old_sys_size * 2);
- sys_->set_estimate_cow_size(8_MiB);
- SetSize(prd_, old_prd_size / 2);
- prd_->set_estimate_cow_size(1_MiB);
-
- AddOperationForPartitions();
-
- ASSERT_TRUE(sm->BeginUpdate());
- ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
- ASSERT_TRUE(WriteSnapshotAndHash(sys_));
- ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
- ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
-
- sync();
-
- // Assert that source partitions aren't affected.
- for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name));
- }
-
- ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
- // Simulate shutting down the device.
- ASSERT_TRUE(UnmapAll());
-
- // After reboot, init does first stage mount.
- auto init = NewManagerForFirstStageMount("_b");
- ASSERT_NE(init, nullptr);
- ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
- // Check that the target partitions have the same content.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name));
- }
-
- auto old_checker = init->merge_consistency_checker();
-
- init->set_merge_consistency_checker(
- [](const std::string&, const SnapshotStatus&) -> MergeFailureCode {
- return MergeFailureCode::WrongMergeCountConsistencyCheck;
- });
-
- // Initiate the merge and wait for it to be completed.
- if (ShouldSkipLegacyMerging()) {
- LOG(INFO) << "Skipping legacy merge in test";
- return;
- }
- ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
- {
- // Check that the merge phase is FIRST_PHASE until at least one call
- // to ProcessUpdateState() occurs.
- ASSERT_TRUE(AcquireLock());
- auto local_lock = std::move(lock_);
- auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
- ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
- }
-
- // Merge should have failed.
- ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
-
- // Simulate shutting down the device and creating partitions again.
- ASSERT_TRUE(UnmapAll());
-
- // Restore the checker.
- init->set_merge_consistency_checker(std::move(old_checker));
-
- ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
- // Complete the merge.
- ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-
- // Check that the target partitions have the same content after the merge.
- for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
- ASSERT_TRUE(IsPartitionUnchanged(name))
- << "Content of " << name << " changes after the merge";
- }
-}
-
// Test that if new system partitions uses empty space in super, that region is not snapshotted.
TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
GTEST_SKIP() << "b/141889746";
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
index d979e20..ffd7a4b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -51,11 +51,10 @@
std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
const std::string& misc_name, const std::string& cow_device_path,
const std::string& backing_device, const std::string& base_path_merge,
- std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
- bool perform_verification) {
+ std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring) {
auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
base_path_merge, opener, num_worker_threads,
- use_iouring, perform_verification);
+ use_iouring, perform_verification_);
if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
return nullptr;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
index b1605f0..ff6ee8f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -57,8 +57,7 @@
const std::string& backing_device,
const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener,
- int num_worker_threads, bool use_iouring,
- bool perform_verification) = 0;
+ int num_worker_threads, bool use_iouring) = 0;
// Start serving requests on a snapshot handler.
virtual bool StartHandler(const std::string& misc_name) = 0;
@@ -84,6 +83,9 @@
// Returns whether all snapshots have verified.
virtual bool GetVerificationStatus() = 0;
+
+ // Disable partition verification
+ virtual void DisableVerification() = 0;
};
class SnapshotHandlerManager final : public ISnapshotHandlerManager {
@@ -94,8 +96,7 @@
const std::string& backing_device,
const std::string& base_path_merge,
std::shared_ptr<IBlockServerOpener> opener,
- int num_worker_threads, bool use_iouring,
- bool perform_verification) override;
+ int num_worker_threads, bool use_iouring) override;
bool StartHandler(const std::string& misc_name) override;
bool DeleteHandler(const std::string& misc_name) override;
bool InitiateMerge(const std::string& misc_name) override;
@@ -104,6 +105,7 @@
void TerminateMergeThreads() override;
double GetMergePercentage() override;
bool GetVerificationStatus() override;
+ void DisableVerification() override { perform_verification_ = false; }
private:
bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
@@ -128,6 +130,7 @@
int num_partitions_merge_complete_ = 0;
std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
android::base::unique_fd monitor_merge_event_fd_;
+ bool perform_verification_ = true;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 474ba7d..950d771 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -82,6 +82,10 @@
SNAP_LOG(DEBUG) << "Merge-complete %: " << merge_completion_percentage_
<< " num_merge_ops: " << ch->num_merge_ops
<< " total-ops: " << reader_->get_num_total_data_ops();
+
+ if (ch->num_merge_ops == reader_->get_num_total_data_ops()) {
+ MarkMergeComplete();
+ }
}
bool SnapshotHandler::CommitMerge(int num_merge_ops) {
@@ -108,7 +112,7 @@
return false;
}
- if (!android::base::WriteFully(cow_fd_, &header, sizeof(CowHeader))) {
+ if (!android::base::WriteFully(cow_fd_, &header, header.prefix.header_size)) {
SNAP_PLOG(ERROR) << "Write to header failed";
return false;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 6a1dab8..fa1e7a0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -159,6 +159,7 @@
bool ShouldReconstructDataFromCow() { return populate_data_from_cow_; }
void FinishReconstructDataFromCow() { populate_data_from_cow_ = false; }
+ void MarkMergeComplete();
// Return the snapshot status
std::string GetMergeStatus();
@@ -245,6 +246,7 @@
int num_worker_threads_ = kNumWorkerThreads;
bool perform_verification_ = true;
bool resume_merge_ = false;
+ bool merge_complete_ = false;
std::unique_ptr<UpdateVerify> update_verify_;
std::shared_ptr<IBlockServerOpener> block_server_opener_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 13b9a00..6eee357 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -360,16 +360,15 @@
num_worker_threads = 1;
}
- bool perform_verification = true;
- if (android::base::EndsWith(misc_name, "-init") || is_socket_present_) {
- perform_verification = false;
+ if (android::base::EndsWith(misc_name, "-init") || is_socket_present_ ||
+ (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {
+ handlers_->DisableVerification();
}
auto opener = block_server_factory_->CreateOpener(misc_name);
return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
- opener, num_worker_threads, io_uring_enabled_,
- perform_verification);
+ opener, num_worker_threads, io_uring_enabled_);
}
bool UserSnapshotServer::WaitForSocket() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index be28541..9926071 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -40,6 +40,8 @@
static constexpr uint32_t kMaxPacketSize = 512;
static constexpr uint8_t kMaxMergeThreads = 2;
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+ "/metadata/ota/snapshot-boot-without-slot-switch";
class UserSnapshotServer {
private:
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index bed71cf..73c3cbf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -627,9 +627,10 @@
void SnapuserdTest::InitCowDevice() {
auto factory = harness_->GetBlockServerFactory();
auto opener = factory->CreateOpener(system_device_ctrl_name_);
+ handlers_->DisableVerification();
auto handler =
handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
- base_dev_->GetPath(), opener, 1, GetParam(), false);
+ base_dev_->GetPath(), opener, 1, GetParam());
ASSERT_NE(handler, nullptr);
ASSERT_NE(handler->snapuserd(), nullptr);
#ifdef __ANDROID__
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 8d090bf..2ad4ea1 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -386,10 +386,16 @@
}
}
+void SnapshotHandler::MarkMergeComplete() {
+ std::lock_guard<std::mutex> lock(lock_);
+ merge_complete_ = true;
+}
+
std::string SnapshotHandler::GetMergeStatus() {
bool merge_not_initiated = false;
bool merge_monitored = false;
bool merge_failed = false;
+ bool merge_complete = false;
{
std::lock_guard<std::mutex> lock(lock_);
@@ -405,10 +411,9 @@
if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) {
merge_failed = true;
}
- }
- struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
- bool merge_complete = (ch->num_merge_ops == reader_->get_num_total_data_ops());
+ merge_complete = merge_complete_;
+ }
if (merge_not_initiated) {
// Merge was not initiated yet; however, we have merge completion
@@ -444,7 +449,6 @@
return "snapshot-merge-failed";
}
- // Merge complete
if (merge_complete) {
return "snapshot-merge-complete";
}
diff --git a/fs_mgr/libsnapshot/tools/Android.bp b/fs_mgr/libsnapshot/tools/Android.bp
index 0f08286..a1c92c0 100644
--- a/fs_mgr/libsnapshot/tools/Android.bp
+++ b/fs_mgr/libsnapshot/tools/Android.bp
@@ -23,17 +23,18 @@
cc_binary {
- name: "basic_v2_cow_writer",
+ name: "write_cow",
host_supported: true,
defaults: [
"fs_mgr_defaults",
"libsnapshot_cow_defaults",
],
- srcs: ["basic_v2_cow_writer.cpp"],
+ srcs: ["write_cow.cpp"],
static_libs: [
"libsnapshot_cow",
+ "libgflags",
],
shared_libs: [
diff --git a/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp b/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp
deleted file mode 100644
index 72fb0f5..0000000
--- a/fs_mgr/libsnapshot/tools/basic_v2_cow_writer.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// 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 <android-base/file.h>
-#include <android-base/logging.h>
-#include <libsnapshot/cow_compress.h>
-#include <libsnapshot/cow_format.h>
-#include <libsnapshot/cow_writer.h>
-#include <filesystem>
-
-#include "android-base/unique_fd.h"
-
-using namespace android::snapshot;
-
-// This writes a simple cow v2 file in the current directory. This file will serve as testdata for
-// ensuring our v3 cow reader will be able to read a cow file created by the v2 writer.
-//
-// WARNING: We should not be overriding this test file, as it will serve as historic marker for what
-// a device with old writer_v2 will write as a cow.
-void write_cow_v2() {
- CowOptions options;
- options.cluster_ops = 5;
- options.num_merge_ops = 1;
- std::string data = "This is some data, believe it";
- data.resize(options.block_size, '\0');
-
- char cwd_buffer[1024];
- size_t cwd_buffer_size = sizeof(cwd_buffer);
-
- // Get the current working directory path.
- char* err = getcwd(cwd_buffer, cwd_buffer_size);
- if (!err) {
- LOG(ERROR) << "Couldn't get current directory";
- }
- android::base::unique_fd fd(open(strcat(cwd_buffer, "/cow_v2"), O_CREAT | O_RDWR, 0666));
- if (fd.get() == -1) {
- LOG(FATAL) << "couldn't open tmp_cow";
- }
- std::unique_ptr<ICowWriter> writer = CreateCowWriter(2, options, std::move(fd));
- writer->AddCopy(0, 5);
- writer->AddRawBlocks(2, data.data(), data.size());
- writer->AddLabel(1);
- writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
- writer->AddZeroBlocks(5, 10);
- writer->AddLabel(2);
- writer->Finalize();
-}
-
-int main() {
- write_cow_v2();
-}
diff --git a/fs_mgr/libsnapshot/tools/testdata/incompressible_block b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
new file mode 100644
index 0000000..cc45cd0
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
Binary files differ
diff --git a/fs_mgr/libsnapshot/tools/write_cow.cpp b/fs_mgr/libsnapshot/tools/write_cow.cpp
new file mode 100644
index 0000000..bd51174
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/write_cow.cpp
@@ -0,0 +1,114 @@
+//
+// 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_writer.h>
+
+#include <gflags/gflags.h>
+#include <iostream>
+
+#include "android-base/unique_fd.h"
+
+DEFINE_bool(silent, false, "Run silently");
+DEFINE_int32(writer_version, 2, "which version of COW writer to be used");
+DEFINE_bool(write_legacy, false,
+ "Writes a legacy cow_v2 in current directory, this cow was used to test backwards "
+ "compatibility between version 2 and version 3");
+DEFINE_bool(write_header, false, "Test reading/writing just the header");
+using namespace android::snapshot;
+
+// This writes a simple cow v2 file in the current directory. This file will serve as testdata for
+// ensuring our v3 cow reader will be able to read a cow file created by the v2 writer.
+//
+// WARNING: We should not be overriding this test file, as it will serve as historic marker for what
+// a device with old writer_v2 will write as a cow.
+static void write_legacy_cow_v2() {
+ CowOptions options;
+ options.cluster_ops = 5;
+ options.num_merge_ops = 1;
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ char cwd_buffer[1024];
+ size_t cwd_buffer_size = sizeof(cwd_buffer);
+
+ // Get the current working directory path.
+ char* err = getcwd(cwd_buffer, cwd_buffer_size);
+ if (!err) {
+ LOG(ERROR) << "Couldn't get current directory";
+ }
+ android::base::unique_fd fd(open(strcat(cwd_buffer, "/cow_v2"), O_CREAT | O_RDWR, 0666));
+ if (fd.get() == -1) {
+ LOG(FATAL) << "couldn't open tmp_cow";
+ }
+ std::unique_ptr<ICowWriter> writer = CreateCowWriter(2, options, std::move(fd));
+ writer->AddCopy(0, 5);
+ writer->AddRawBlocks(2, data.data(), data.size());
+ writer->AddLabel(1);
+ writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
+ writer->AddZeroBlocks(5, 10);
+ writer->AddLabel(2);
+ writer->Finalize();
+}
+
+static bool WriteCow(const std::string& path) {
+ android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ fd.reset(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));
+ if (fd < 0) {
+ PLOG(ERROR) << "could not open " << path << " for writing";
+ return false;
+ }
+ CowOptions options;
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ std::unique_ptr<ICowWriter> writer =
+ CreateCowWriter(FLAGS_writer_version, options, std::move(fd));
+ if (!writer) {
+ return false;
+ }
+
+ writer->AddCopy(0, 5);
+ writer->AddRawBlocks(2, data.data(), data.size());
+ writer->AddLabel(1);
+ writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
+ writer->AddZeroBlocks(5, 10);
+ writer->AddLabel(2);
+ writer->Finalize();
+
+ if (!FLAGS_silent) {
+ std::cout << "Writing COW with writer v" << FLAGS_writer_version << "\n";
+ }
+
+ return true;
+}
+
+int main(int argc, char** argv) {
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_write_legacy) {
+ write_legacy_cow_v2();
+ return 0;
+ }
+ if (argc < 2) {
+ gflags::ShowUsageWithFlags(argv[0]);
+ return 1;
+ }
+ if (!WriteCow(argv[1])) {
+ return 1;
+ }
+}
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
index f08cab2..91f235c 100644
--- a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -21,10 +21,8 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
-
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
@@ -38,9 +36,15 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public class VendorOverlayHostTest extends BaseHostJUnit4Test {
boolean wasRoot = false;
+ String vndkVersion = null;
@Before
public void setup() throws DeviceNotAvailableException {
+ vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+ Assume.assumeTrue(
+ "Vendor Overlay is disabled for VNDK deprecated devices",
+ vndkVersion != null && !vndkVersion.trim().isEmpty());
+
wasRoot = getDevice().isAdbRoot();
if (!wasRoot) {
Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
@@ -74,8 +78,6 @@
*/
@Test
public void testVendorOverlay() throws DeviceNotAvailableException {
- String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
-
// Create files and modify policy
CommandResult result = getDevice().executeShellV2Command(
"echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 32947b5..8f15220 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -32,6 +32,24 @@
return android::base::GetIntProperty("ro.vendor.api_level", -1);
}
+// Returns true iff the device has the specified feature.
+bool DeviceSupportsFeature(const char* feature) {
+ bool device_supports_feature = false;
+ FILE* p = popen("pm list features", "re");
+ if (p) {
+ char* line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, p) > 0) {
+ if (strstr(line, feature)) {
+ device_supports_feature = true;
+ break;
+ }
+ }
+ pclose(p);
+ }
+ return device_supports_feature;
+}
+
TEST(fs, ErofsSupported) {
// T-launch GKI kernels and higher must support EROFS.
if (GetVsrLevel() < __ANDROID_API_T__) {
@@ -82,7 +100,8 @@
ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
std::vector<std::string> must_be_f2fs = {"/data"};
- if (vsr_level >= __ANDROID_API_U__) {
+ if (vsr_level >= __ANDROID_API_U__ &&
+ !DeviceSupportsFeature("android.hardware.type.automotive")) {
must_be_f2fs.emplace_back("/metadata");
}
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index f070776..662185c 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -108,9 +108,9 @@
static passwd static_passwd = {
.pw_name = static_name,
.pw_dir = static_dir,
- .pw_shell = static_shell,
.pw_uid = 0,
.pw_gid = 0,
+ .pw_shell = static_shell,
};
for (size_t n = 0; n < android_id_count; ++n) {
diff --git a/init/init.cpp b/init/init.cpp
index 40e2169..83cb78b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -108,6 +108,7 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::base::unique_fd;
using android::fs_mgr::AvbHandle;
using android::snapshot::SnapshotManager;
@@ -116,7 +117,8 @@
static int property_triggers_enabled = 0;
-static int signal_fd = -1;
+int sigchld_fd = -1;
+static int sigterm_fd = -1;
static int property_fd = -1;
struct PendingControlMessage {
@@ -713,8 +715,9 @@
HandlePowerctlMessage("shutdown,container");
}
-static void HandleSignalFd() {
+static void HandleSignalFd(int signal) {
signalfd_siginfo siginfo;
+ const int signal_fd = signal == SIGCHLD ? sigchld_fd : sigterm_fd;
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
if (bytes_read != sizeof(siginfo)) {
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
@@ -748,10 +751,28 @@
}
}
+static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, signal);
+ unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+ if (signal_fd == -1) {
+ return ErrnoError() << "failed to create signalfd for signal " << signal;
+ }
+
+ auto result = epoll->RegisterHandler(
+ signal_fd.get(), [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
+ if (!result.ok()) {
+ return result.error();
+ }
+
+ return signal_fd.release();
+}
+
static void InstallSignalFdHandler(Epoll* epoll) {
// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
// SIGCHLD when a child process stops or continues (b/77867680#comment9).
- const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
+ const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };
sigaction(SIGCHLD, &act, nullptr);
sigset_t mask;
@@ -774,14 +795,19 @@
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
- signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
- if (signal_fd == -1) {
- PLOG(FATAL) << "failed to create signalfd";
+ Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGCHLD);
+ if (!cs_result.ok()) {
+ PLOG(FATAL) << cs_result.error();
}
+ sigchld_fd = cs_result.value();
+ Service::SetSigchldFd(sigchld_fd);
- constexpr int flags = EPOLLIN | EPOLLPRI;
- if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
- LOG(FATAL) << result.error();
+ if (sigismember(&mask, SIGTERM)) {
+ Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
+ if (!cs_result.ok()) {
+ PLOG(FATAL) << cs_result.error();
+ }
+ sigterm_fd = cs_result.value();
}
}
@@ -1059,8 +1085,8 @@
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
- am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
+ am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
diff --git a/init/init.h b/init/init.h
index 9c7e918..b781167 100644
--- a/init/init.h
+++ b/init/init.h
@@ -28,6 +28,8 @@
namespace android {
namespace init {
+extern int sigchld_fd;
+
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 8efb72c..6f8a4de 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -46,6 +46,13 @@
constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
+void AddPersistentProperty(const std::string& name, const std::string& value,
+ PersistentProperties* persistent_properties) {
+ auto persistent_property_record = persistent_properties->add_properties();
+ persistent_property_record->set_name(name);
+ persistent_property_record->set_value(value);
+}
+
Result<PersistentProperties> LoadLegacyPersistentProperties() {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
if (!dir) {
@@ -115,24 +122,6 @@
}
}
-PersistentProperties LoadPersistentPropertiesFromMemory() {
- PersistentProperties persistent_properties;
- __system_property_foreach(
- [](const prop_info* pi, void* cookie) {
- __system_property_read_callback(
- pi,
- [](void* cookie, const char* name, const char* value, unsigned serial) {
- if (StartsWith(name, "persist.")) {
- auto properties = reinterpret_cast<PersistentProperties*>(cookie);
- AddPersistentProperty(name, value, properties);
- }
- },
- cookie);
- },
- &persistent_properties);
- return persistent_properties;
-}
-
Result<std::string> ReadPersistentPropertyFile() {
const std::string temp_filename = persistent_property_filename + ".tmp";
if (access(temp_filename.c_str(), F_OK) == 0) {
@@ -164,13 +153,6 @@
} // namespace
-void AddPersistentProperty(const std::string& name, const std::string& value,
- PersistentProperties* persistent_properties) {
- auto persistent_property_record = persistent_properties->add_properties();
- persistent_property_record->set_name(name);
- persistent_property_record->set_value(value);
-}
-
Result<PersistentProperties> LoadPersistentPropertyFile() {
auto file_contents = ReadPersistentPropertyFile();
if (!file_contents.ok()) return file_contents.error();
@@ -221,6 +203,24 @@
return {};
}
+PersistentProperties LoadPersistentPropertiesFromMemory() {
+ PersistentProperties persistent_properties;
+ __system_property_foreach(
+ [](const prop_info* pi, void* cookie) {
+ __system_property_read_callback(
+ pi,
+ [](void* cookie, const char* name, const char* value, unsigned serial) {
+ if (StartsWith(name, "persist.")) {
+ auto properties = reinterpret_cast<PersistentProperties*>(cookie);
+ AddPersistentProperty(name, value, properties);
+ }
+ },
+ cookie);
+ },
+ &persistent_properties);
+ return persistent_properties;
+}
+
// Persistent properties are not written often, so we rather not keep any data in memory and read
// then rewrite the persistent property file for each update.
void WritePersistentProperty(const std::string& name, const std::string& value) {
@@ -266,8 +266,57 @@
}
}
- return *persistent_properties;
+ // loop over to find all staged props
+ auto const staged_prefix = std::string_view("next_boot.");
+ auto staged_props = std::unordered_map<std::string, std::string>();
+ for (const auto& property_record : persistent_properties->properties()) {
+ auto const& prop_name = property_record.name();
+ auto const& prop_value = property_record.value();
+ if (StartsWith(prop_name, staged_prefix)) {
+ auto actual_prop_name = prop_name.substr(staged_prefix.size());
+ staged_props[actual_prop_name] = prop_value;
+ }
+ }
+
+ if (staged_props.empty()) {
+ return *persistent_properties;
+ }
+
+ // if has staging, apply staging and perserve the original prop order
+ PersistentProperties updated_persistent_properties;
+ for (const auto& property_record : persistent_properties->properties()) {
+ auto const& prop_name = property_record.name();
+ auto const& prop_value = property_record.value();
+
+ // don't include staged props anymore
+ if (StartsWith(prop_name, staged_prefix)) {
+ continue;
+ }
+
+ auto iter = staged_props.find(prop_name);
+ if (iter != staged_props.end()) {
+ AddPersistentProperty(prop_name, iter->second, &updated_persistent_properties);
+ staged_props.erase(iter);
+ } else {
+ AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
+ }
+ }
+
+ // add any additional staged props
+ for (auto const& [prop_name, prop_value] : staged_props) {
+ AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
+ }
+
+ // write current updated persist prop file
+ auto result = WritePersistentPropertyFile(updated_persistent_properties);
+ if (!result.ok()) {
+ LOG(ERROR) << "Could not store persistent property: " << result.error();
+ }
+
+ return updated_persistent_properties;
}
+
+
} // namespace init
} // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
index a6f80e6..11083da 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -25,10 +25,9 @@
namespace android {
namespace init {
-void AddPersistentProperty(const std::string& name, const std::string& value,
- PersistentProperties* persistent_properties);
PersistentProperties LoadPersistentProperties();
void WritePersistentProperty(const std::string& name, const std::string& value);
+PersistentProperties LoadPersistentPropertiesFromMemory();
// Exposed only for testing
Result<PersistentProperties> LoadPersistentPropertyFile();
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index e5d26db..5763050 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -178,5 +178,37 @@
EXPECT_FALSE(it == read_back_properties.properties().end());
}
+TEST(persistent_properties, StagedPersistProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"next_boot.persist.test.numbers", "54321"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.test.numbers", "12345"},
+ {"next_boot.persist.test.extra", "abc"},
+ };
+
+ ASSERT_RESULT_OK(
+ WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+ std::vector<std::pair<std::string, std::string>> expected_persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.test.numbers", "54321"},
+ {"persist.test.extra", "abc"},
+ };
+
+ // lock down that staged props are applied
+ auto first_read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(expected_persistent_properties, first_read_back_properties);
+
+ // lock down that other props are not overwritten
+ auto second_read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(expected_persistent_properties, second_read_back_properties);
+}
+
} // namespace init
} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 38cbea3..bd74358 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -76,6 +76,10 @@
#include "system/core/init/property_service.pb.h"
#include "util.h"
+static constexpr char APPCOMPAT_OVERRIDE_PROP_FOLDERNAME[] =
+ "/dev/__properties__/appcompat_override";
+static constexpr char APPCOMPAT_OVERRIDE_PROP_TREE_FILE[] =
+ "/dev/__properties__/appcompat_override/property_info";
using namespace std::literals;
using android::base::ErrnoError;
@@ -1279,11 +1283,17 @@
return;
}
- constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
- if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
+ if (!WriteStringToFile(serialized_contexts, PROP_TREE_FILE, 0444, 0, 0, false)) {
PLOG(ERROR) << "Unable to write serialized property infos to file";
}
- selinux_android_restorecon(kPropertyInfosPath, 0);
+ selinux_android_restorecon(PROP_TREE_FILE, 0);
+
+ mkdir(APPCOMPAT_OVERRIDE_PROP_FOLDERNAME, S_IRWXU | S_IXGRP | S_IXOTH);
+ if (!WriteStringToFile(serialized_contexts, APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0444, 0, 0,
+ false)) {
+ PLOG(ERROR) << "Unable to write vendor overrides to file";
+ }
+ selinux_android_restorecon(APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0);
}
static void ExportKernelBootProps() {
@@ -1396,42 +1406,12 @@
switch (init_message.msg_case()) {
case InitMessage::kLoadPersistentProperties: {
load_override_properties();
- // Read persistent properties after all default values have been loaded.
- // Apply staged and persistent properties
- bool has_staged_prop = false;
- auto const staged_prefix = std::string_view("next_boot.");
- auto const staged_persist_prefix = std::string_view("next_boot.persist.");
- auto persist_props_map = std::unordered_map<std::string, std::string>();
auto persistent_properties = LoadPersistentProperties();
for (const auto& property_record : persistent_properties.properties()) {
auto const& prop_name = property_record.name();
auto const& prop_value = property_record.value();
-
- if (StartsWith(prop_name, staged_prefix)) {
- has_staged_prop = true;
- auto actual_prop_name = prop_name.substr(staged_prefix.size());
- InitPropertySet(actual_prop_name, prop_value);
- if (StartsWith(prop_name, staged_persist_prefix)) {
- persist_props_map[actual_prop_name] = prop_value;
- }
- } else if (!persist_props_map.count(prop_name)) {
- InitPropertySet(prop_name, prop_value);
- }
- }
-
- // Update persist prop file if there are staged props
- if (has_staged_prop) {
- PersistentProperties updated_persist_props;
- for (auto const& [prop_name, prop_value] : persist_props_map) {
- AddPersistentProperty(prop_name, prop_value, &updated_persist_props);
- }
-
- // write current updated persist prop file
- auto result = WritePersistentPropertyFile(updated_persist_props);
- if (!result.ok()) {
- LOG(ERROR) << "Could not store persistent property: " << result.error();
- }
+ InitPropertySet(prop_name, prop_value);
}
// Apply debug ramdisk special settings after persistent properties are loaded.
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 3351c4c..5757922 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -563,7 +563,7 @@
}
}
if (timeout > 0ms) {
- WaitToBeReaped(pids, timeout);
+ WaitToBeReaped(sigchld_fd, pids, timeout);
} else {
// Even if we don't to wait for services to stop, we still optimistically reap zombies.
ReapAnyOutstandingChildren();
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
index 476a46a..c2a3fa1 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -30,10 +30,14 @@
// Builtins and service definitions both have their arguments start at 1 and finish at 3.
Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {
static const std::vector<std::pair<const char*, int>> text_to_resources = {
- {"cpu", 0}, {"fsize", 1}, {"data", 2}, {"stack", 3},
- {"core", 4}, {"rss", 5}, {"nproc", 6}, {"nofile", 7},
- {"memlock", 8}, {"as", 9}, {"locks", 10}, {"sigpending", 11},
- {"msgqueue", 12}, {"nice", 13}, {"rtprio", 14}, {"rttime", 15},
+ {"cpu", RLIMIT_CPU}, {"fsize", RLIMIT_FSIZE},
+ {"data", RLIMIT_DATA}, {"stack", RLIMIT_STACK},
+ {"core", RLIMIT_CORE}, {"rss", RLIMIT_RSS},
+ {"nproc", RLIMIT_NPROC}, {"nofile", RLIMIT_NOFILE},
+ {"memlock", RLIMIT_MEMLOCK}, {"as", RLIMIT_AS},
+ {"locks", RLIMIT_LOCKS}, {"sigpending", RLIMIT_SIGPENDING},
+ {"msgqueue", RLIMIT_MSGQUEUE}, {"nice", RLIMIT_NICE},
+ {"rtprio", RLIMIT_RTPRIO}, {"rttime", RLIMIT_RTTIME},
};
int resource;
@@ -49,6 +53,8 @@
std::string resource_string;
if (StartsWith(args[1], "RLIM_")) {
resource_string = args[1].substr(5);
+ } else if (StartsWith(args[1], "RLIMIT_")) {
+ resource_string = args[1].substr(7);
} else {
resource_string = args[1];
}
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index 3c3f848..a6955f6 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -67,6 +67,7 @@
{{"rtprio", "10", "10"}, {14, {10, 10}}},
{{"rttime", "10", "10"}, {15, {10, 10}}},
+ // For some reason, we spelled these wrong.
{{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
{{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
{{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
@@ -84,6 +85,24 @@
{{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
{{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
+ // These are the correct spellings.
+ {{"RLIMIT_CPU", "10", "10"}, {0, {10, 10}}},
+ {{"RLIMIT_FSIZE", "10", "10"}, {1, {10, 10}}},
+ {{"RLIMIT_DATA", "10", "10"}, {2, {10, 10}}},
+ {{"RLIMIT_STACK", "10", "10"}, {3, {10, 10}}},
+ {{"RLIMIT_CORE", "10", "10"}, {4, {10, 10}}},
+ {{"RLIMIT_RSS", "10", "10"}, {5, {10, 10}}},
+ {{"RLIMIT_NPROC", "10", "10"}, {6, {10, 10}}},
+ {{"RLIMIT_NOFILE", "10", "10"}, {7, {10, 10}}},
+ {{"RLIMIT_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+ {{"RLIMIT_AS", "10", "10"}, {9, {10, 10}}},
+ {{"RLIMIT_LOCKS", "10", "10"}, {10, {10, 10}}},
+ {{"RLIMIT_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+ {{"RLIMIT_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+ {{"RLIMIT_NICE", "10", "10"}, {13, {10, 10}}},
+ {{"RLIMIT_RTPRIO", "10", "10"}, {14, {10, 10}}},
+ {{"RLIMIT_RTTIME", "10", "10"}, {15, {10, 10}}},
+
{{"0", "10", "10"}, {0, {10, 10}}},
{{"1", "10", "10"}, {1, {10, 10}}},
{{"2", "10", "10"}, {2, {10, 10}}},
@@ -113,6 +132,7 @@
{{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
{{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
{{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+ {{"RLIMIT_", "10", "10"}, "Could not parse resource 'RLIMIT_'"},
{{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
{{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
{{"cpu", "unlimit", "10"}, "Could not parse soft limit 'unlimit'"},
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 9095b85..1f211dd 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -503,10 +503,6 @@
<< ") failed";
}
}
-
- if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
- LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
- }
}
constexpr size_t kKlogMessageSize = 1024;
diff --git a/init/service.cpp b/init/service.cpp
index 5e900ee..d351a8f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -136,6 +136,7 @@
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
+int Service::sigchld_fd_ = -1;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::string& filename, const std::vector<std::string>& args)
@@ -194,28 +195,20 @@
}
}
-void Service::KillProcessGroup(int signal, bool report_oneshot) {
- // If we've already seen a successful result from killProcessGroup*(), then we have removed
- // the cgroup already and calling these functions a second time will simply result in an error.
- // This is true regardless of which signal was sent.
- // These functions handle their own logging, so no additional logging is needed.
- if (!process_cgroup_empty_) {
+void Service::KillProcessGroup(int signal) {
+ // Always attempt the process kill if process is still running.
+ // Cgroup clean up routines are idempotent. It's safe to call
+ // killProcessGroup repeatedly. During shutdown, `init` will
+ // call this function to send SIGTERM/SIGKILL to all processes.
+ // These signals must be sent for a successful shutdown.
+ if (!process_cgroup_empty_ || IsRunning()) {
LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
<< ") process group...";
- int max_processes = 0;
int r;
if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid(), pid_, signal, &max_processes);
+ r = killProcessGroupOnce(uid(), pid_, signal);
} else {
- r = killProcessGroup(uid(), pid_, signal, &max_processes);
- }
-
- if (report_oneshot && max_processes > 0) {
- LOG(WARNING)
- << "Killed " << max_processes
- << " additional processes from a oneshot process group for service '" << name_
- << "'. This is new behavior, previously child processes would not be killed in "
- "this case.";
+ r = killProcessGroup(uid(), pid_, signal);
}
if (r == 0) process_cgroup_empty_ = true;
@@ -265,7 +258,7 @@
void Service::Reap(const siginfo_t& siginfo) {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
- KillProcessGroup(SIGKILL, false);
+ KillProcessGroup(SIGKILL);
} else {
// Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
// kill the process group in this case.
@@ -273,7 +266,7 @@
// The new behavior in Android R is to kill these process groups in all cases. The
// 'true' parameter instructions KillProcessGroup() to report a warning message where it
// detects a difference in behavior has occurred.
- KillProcessGroup(SIGKILL, true);
+ KillProcessGroup(SIGKILL);
}
}
diff --git a/init/service.h b/init/service.h
index 9f09cef..13c8b5f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -156,11 +156,12 @@
const Subcontext* subcontext() const { return subcontext_; }
const std::string& filename() const { return filename_; }
void set_filename(const std::string& name) { filename_ = name; }
+ static void SetSigchldFd(int sigchld_fd) { sigchld_fd_ = sigchld_fd; }
private:
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
- void KillProcessGroup(int signal, bool report_oneshot = false);
+ void KillProcessGroup(int signal);
void SetProcessAttributesAndCaps(InterprocessFifo setsid_finished);
void ResetFlagsForStart();
Result<void> CheckConsole();
@@ -168,8 +169,10 @@
void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
InterprocessFifo setsid_finished);
void SetMountNamespace();
+
static unsigned long next_start_order_;
static bool is_exec_service_running_;
+ static int sigchld_fd_;
const std::string name_;
std::set<std::string> classnames_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index a1b2cc5..92e350b 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -179,8 +179,9 @@
if (!ParseInt(args[1], &service_->proc_attr_.priority,
static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
- ANDROID_PRIORITY_LOWEST);
+ return Errorf("process priority value must be range {} - {}",
+ static_cast<int>(ANDROID_PRIORITY_HIGHEST),
+ static_cast<int>(ANDROID_PRIORITY_LOWEST));
}
return {};
}
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index f8c501f..9d4c7c8 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -18,6 +18,7 @@
#include <signal.h>
#include <string.h>
+#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -31,6 +32,7 @@
#include <thread>
+#include "epoll.h"
#include "init.h"
#include "service.h"
#include "service_list.h"
@@ -121,8 +123,23 @@
}
}
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout) {
+static void DiscardSiginfo(int signal_fd) {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ LOG(WARNING) << "Unexpected: " << __func__ << " read " << bytes_read << " bytes instead of "
+ << sizeof(siginfo);
+ }
+}
+
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+ std::chrono::milliseconds timeout) {
Timer t;
+ Epoll epoll;
+ // The init process passes a valid sigchld_fd argument but unit tests do not.
+ if (sigchld_fd >= 0) {
+ epoll.RegisterHandler(sigchld_fd, [sigchld_fd]() { DiscardSiginfo(sigchld_fd); });
+ }
std::vector<pid_t> alive_pids(pids.begin(), pids.end());
while (!alive_pids.empty() && t.duration() < timeout) {
pid_t pid;
@@ -135,14 +152,18 @@
if (alive_pids.empty()) {
break;
}
- std::this_thread::sleep_for(50ms);
+ if (sigchld_fd >= 0) {
+ epoll.Wait(std::max(timeout - t.duration(), 0ms));
+ } else {
+ std::this_thread::sleep_for(50ms);
+ }
}
LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
<< alive_pids.size() << " of them still running";
- for (pid_t pid : pids) {
+ for (pid_t pid : alive_pids) {
std::string status = "(no-such-pid)";
ReadFileToString(StringPrintf("/proc/%d/status", pid), &status);
- LOG(INFO) << "Still running: " << pid << ' ' << status;
+ LOG(INFO) << "Still running: " << pid << '\n' << status;
}
}
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index fac1020..e07a7d6 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -25,7 +25,8 @@
void ReapAnyOutstandingChildren();
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout);
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+ std::chrono::milliseconds timeout);
} // namespace init
} // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index dbaeb93..a319c63 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,8 @@
__BEGIN_DECLS
-static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
+static constexpr const char* CGROUPV2_HIERARCHY_NAME = "cgroup2";
+[[deprecated]] static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
bool CgroupsAvailable();
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
@@ -67,14 +68,11 @@
// Return 0 and removes the cgroup if there are no longer any processes in it.
// Returns -1 in the case of an error occurring or if there are processes still running
// even after retrying for up to 200ms.
-// If max_processes is not nullptr, it returns the maximum number of processes seen in the cgroup
-// during the killing process. Note that this can be 0 if all processes from the process group have
-// already been terminated.
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroup(uid_t uid, int initialPid, int signal);
// Returns the same as killProcessGroup(), however it does not retry, which means
// that it only returns 0 in the case that the cgroup exists and it contains no processes.
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
// Sends the provided signal to all members of a process group, but does not wait for processes to
// exit, or for the cgroup to be removed. Callers should also ensure that killProcessGroup is called
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index cc2565f..d352f0e 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -129,7 +129,7 @@
}
if (!attr->GetPathForTask(tid, path)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return false;
}
@@ -213,7 +213,7 @@
return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
}
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
+static int RemoveCgroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
int ret = 0;
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
@@ -233,7 +233,7 @@
return ret;
}
-static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
+static bool RemoveUidCgroups(const std::string& uid_path, bool empty_only) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
bool empty = true;
if (uid != NULL) {
@@ -275,11 +275,11 @@
return empty;
}
-void removeAllProcessGroupsInternal(bool empty_only) {
+static void removeAllProcessGroupsInternal(bool empty_only) {
std::vector<std::string> cgroups;
std::string path, memcg_apps_path;
- if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
+ if (CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &path)) {
cgroups.push_back(path);
}
if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
@@ -302,7 +302,7 @@
}
auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- if (!RemoveUidProcessGroups(path, empty_only)) {
+ if (!RemoveUidCgroups(path, empty_only)) {
LOG(VERBOSE) << "Skip removing " << path;
continue;
}
@@ -455,29 +455,21 @@
return (!fd || feof(fd.get())) ? processes : -1;
}
-static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
- int* max_processes) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
CHECK_GE(uid, 0);
CHECK_GT(initialPid, 0);
std::string hierarchy_root_path;
if (CgroupsAvailable()) {
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
}
const char* cgroup = hierarchy_root_path.c_str();
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- if (max_processes != nullptr) {
- *max_processes = 0;
- }
-
int retry = retries;
int processes;
while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
- if (max_processes != nullptr && processes > *max_processes) {
- *max_processes = processes;
- }
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (!CgroupsAvailable()) {
// makes no sense to retry, because there are no cgroup_procs file
@@ -519,12 +511,12 @@
}
// 400 retries correspond to 2 secs max timeout
- int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
+ int err = RemoveCgroup(cgroup, uid, initialPid, 400);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
std::string memcg_apps_path;
if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
- RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
+ RemoveCgroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
return -1;
}
}
@@ -540,18 +532,18 @@
}
}
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes) {
- return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/, max_processes);
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+ return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes) {
- return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
int sendSignalToProcessGroup(uid_t uid, int initialPid, int signal) {
std::string hierarchy_root_path;
if (CgroupsAvailable()) {
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
}
const char* cgroup = hierarchy_root_path.c_str();
return DoKillProcessGroupOnce(cgroup, uid, initialPid, signal);
@@ -609,7 +601,7 @@
CHECK_GT(initialPid, 0);
if (memControl && !UsePerAppMemcg()) {
- PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
+ LOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
return -EINVAL;
}
@@ -625,19 +617,19 @@
}
std::string cgroup;
- CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
+ CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cgroup);
return createProcessGroupInternal(uid, initialPid, cgroup, true);
}
static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
if (!isMemoryCgroupSupported()) {
- PLOG(ERROR) << "Memcg is not mounted.";
+ LOG(ERROR) << "Memcg is not mounted.";
return false;
}
std::string path;
if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
- PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+ LOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
return false;
}
@@ -672,4 +664,4 @@
}
return tp->IsValidForProcess(uid, pid);
-}
\ No newline at end of file
+}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 3e4393d..d013ec8 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,13 +1,6 @@
{
"Cgroups": [
{
- "Controller": "blkio",
- "Path": "/dev/blkio",
- "Mode": "0775",
- "UID": "system",
- "GID": "system"
- },
- {
"Controller": "cpu",
"Path": "/dev/cpuctl",
"Mode": "0755",
@@ -39,6 +32,12 @@
{
"Controller": "freezer",
"Path": "."
+ },
+ {
+ "Controller": "io",
+ "Path": ".",
+ "NeedsActivation": true,
+ "Optional": true
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 1fc66ba..2c08b0b 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -76,6 +76,21 @@
"Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
+ },
+ {
+ "Name": "BfqWeight",
+ "Controller": "io",
+ "File": "io.bfq.weight"
+ },
+ {
+ "Name": "CfqGroupIdle",
+ "Controller": "io",
+ "File": "io.group_idle"
+ },
+ {
+ "Name": "CfqWeight",
+ "Controller": "io",
+ "File": "io.weight"
}
],
@@ -439,11 +454,30 @@
"Name": "LowIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": "background"
+ "Name": "BfqWeight",
+ "Value": "10",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "200",
+ "Optional": "true"
}
}
]
@@ -452,11 +486,30 @@
"Name": "NormalIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": ""
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
}
}
]
@@ -465,11 +518,30 @@
"Name": "HighIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": ""
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
}
}
]
@@ -478,11 +550,30 @@
"Name": "MaxIoPriority",
"Actions": [
{
- "Name": "JoinCgroup",
+ "Name": "SetAttribute",
"Params":
{
- "Controller": "blkio",
- "Path": ""
+ "Name": "BfqWeight",
+ "Value": "100",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqGroupIdle",
+ "Value": "0",
+ "Optional": "true"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "CfqWeight",
+ "Value": "1000",
+ "Optional": "true"
}
}
]
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index fbeedf9..4e44c91 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -212,7 +212,7 @@
if (root.isMember("Cgroups2")) {
const Json::Value& cgroups2 = root["Cgroups2"];
std::string root_path = cgroups2["Path"].asString();
- MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_CONTROLLER_NAME, "", 2);
+ MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME, "", 2);
const Json::Value& childGroups = cgroups2["Controllers"];
for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {
@@ -358,7 +358,7 @@
const format::CgroupController* controller = descriptor.controller();
if (controller->version() == 2) {
- if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
+ if (!strcmp(controller->name(), CGROUPV2_HIERARCHY_NAME)) {
return MountV2CgroupController(descriptor);
} else {
return ActivateV2CgroupController(descriptor);
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index f51b076..d5bd47c 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -316,7 +316,7 @@
FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
}
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd, ResourceCacheType cache_type) const {
if (tid <= 0) {
return true;
}
@@ -332,6 +332,7 @@
return true;
}
+ const char* controller_name = controller()->name();
// 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
@@ -345,7 +346,8 @@
<< "' into cpuset because all cpus in that cpuset are offline";
empty_cpuset_reported = true;
} else {
- PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+ PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; path=" << path_ << "; "
+ << (cache_type == RCT_TASK ? "task" : "process");
}
return false;
@@ -356,7 +358,7 @@
std::lock_guard<std::mutex> lock(fd_mutex_);
if (FdCacheHelper::IsCached(fd_[cache_type])) {
// fd is cached, reuse it
- if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+ if (!AddTidToCgroup(id, fd_[cache_type], cache_type)) {
LOG(ERROR) << "Failed to add task into cgroup";
return ProfileAction::FAIL;
}
@@ -391,7 +393,7 @@
PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
return false;
}
- if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
+ if (!AddTidToCgroup(pid, tmp_fd, RCT_PROCESS)) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -412,7 +414,7 @@
PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << tasks_path;
return false;
}
- if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
+ if (!AddTidToCgroup(tid, tmp_fd, RCT_TASK)) {
LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -866,7 +868,13 @@
auto controller = cg_map.FindController(controller_name);
if (controller.HasValue()) {
- profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+ if (controller.version() == 1) {
+ profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+ } else {
+ LOG(WARNING) << "A JoinCgroup action in the " << profile_name
+ << " profile is used for controller " << controller_name
+ << " in the cgroup v2 hierarchy and will be ignored";
+ }
} else {
LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 4663f64..16ffe63 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -160,7 +160,7 @@
android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
mutable std::mutex fd_mutex_;
- static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+ bool AddTidToCgroup(int tid, int fd, ResourceCacheType cache_type) const;
CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
};
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index 99d819a..b17e695 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -116,7 +116,7 @@
}
bool GetPathForTask(int tid, std::string* path) const override {
#ifdef __ANDROID__
- CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+ CHECK(CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, path));
CHECK_GT(path->length(), 0);
if (path->rbegin()[0] != '/') {
*path += "/";
diff --git a/libutils/Android.bp b/libutils/Android.bp
index b3ddda3..4d4294b 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -68,13 +68,6 @@
"-Wno-exit-time-destructors",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
- header_libs: [
- "libbase_headers",
- "libutils_headers",
- ],
- export_header_lib_headers: [
- "libutils_headers",
- ],
shared_libs: [
"libcutils",
@@ -134,6 +127,14 @@
whole_static_libs: ["libutils_binder"],
+ header_libs: [
+ "libbase_headers",
+ "libutils_headers",
+ ],
+ export_header_lib_headers: [
+ "libutils_headers",
+ ],
+
srcs: [
"FileMap.cpp",
"JenkinsHash.cpp",
@@ -221,6 +222,14 @@
support_system_process: true,
},
+ header_libs: [
+ "libbase_headers",
+ "libutils_headers",
+ ],
+ export_header_lib_headers: [
+ "libutils_headers",
+ ],
+
srcs: [
"CallStack.cpp",
],
diff --git a/libutils/binder/Android.bp b/libutils/binder/Android.bp
index e2eddb3..a049f3d 100644
--- a/libutils/binder/Android.bp
+++ b/libutils/binder/Android.bp
@@ -10,6 +10,7 @@
],
native_bridge_supported: true,
+ export_include_dirs: ["include"],
srcs: [
"Errors.cpp",
"RefBase.cpp",
diff --git a/libutils/binder/RefBase.cpp b/libutils/binder/RefBase.cpp
index c7055fb..2d2e40b 100644
--- a/libutils/binder/RefBase.cpp
+++ b/libutils/binder/RefBase.cpp
@@ -20,8 +20,6 @@
#include <memory>
#include <mutex>
-#include <android-base/macros.h>
-
#include <fcntl.h>
#include <log/log.h>
@@ -57,15 +55,17 @@
// log all reference counting operations
#define PRINT_REFS 0
+#if !defined(ANDROID_UTILS_CALLSTACK_ENABLED)
#if defined(__linux__)
// CallStack is only supported on linux type platforms.
-#define CALLSTACK_ENABLED 1
+#define ANDROID_UTILS_CALLSTACK_ENABLED 1
#else
-#define CALLSTACK_ENABLED 0
-#endif
+#define ANDROID_UTILS_CALLSTACK_ENABLED 0
+#endif // defined(__linux__)
+#endif // !defined(ANDROID_UTILS_CALLSTACK_ENABLED)
-#if CALLSTACK_ENABLED
-#include <utils/CallStack.h>
+#if ANDROID_UTILS_CALLSTACK_ENABLED
+#include "../../include/utils/CallStack.h"
#endif
// ---------------------------------------------------------------------------
@@ -232,7 +232,7 @@
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
@@ -246,7 +246,7 @@
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
@@ -254,7 +254,7 @@
}
if (dumpStack) {
ALOGE("above errors at:");
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
}
@@ -343,7 +343,7 @@
{
ref_entry* next;
const void* id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::CallStackUPtr stack;
#endif
int32_t ref;
@@ -360,7 +360,7 @@
// decrement the reference count.
ref->ref = mRef;
ref->id = id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
ref->stack = CallStack::getCurrent(2);
#endif
ref->next = *refs;
@@ -396,7 +396,7 @@
ref = ref->next;
}
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
}
@@ -424,7 +424,7 @@
snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
inc, refs->id, refs->ref);
out->append(buf);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
out->append(CallStack::stackToString("\t\t", refs->stack.get()));
#else
out->append("\t\t(call stacks disabled)");
@@ -536,7 +536,7 @@
case INITIAL_STRONG_VALUE:
refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
- FALLTHROUGH_INTENDED;
+ [[fallthrough]];
case 0:
refs->mBase->onFirstRef();
}
@@ -790,7 +790,7 @@
"object.",
mRefs->mWeak.load(), this);
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
} else if (strongs != 0) {
diff --git a/libutils/binder/String8.cpp b/libutils/binder/String8.cpp
index 6a75484..749bfcb 100644
--- a/libutils/binder/String8.cpp
+++ b/libutils/binder/String8.cpp
@@ -19,7 +19,6 @@
#include <utils/String8.h>
-#include <utils/Compat.h>
#include <log/log.h>
#include <utils/String16.h>
@@ -430,6 +429,13 @@
// ---------------------------------------------------------------------------
// Path functions
+// TODO: we should remove all the path functions from String8
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
String8 String8::getPathDir(void) const
{
const char* cp;
diff --git a/libutils/binder/Unicode.cpp b/libutils/binder/Unicode.cpp
index 364a177..2ed2d4f 100644
--- a/libutils/binder/Unicode.cpp
+++ b/libutils/binder/Unicode.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "unicode"
-#include <android-base/macros.h>
#include <limits.h>
#include <utils/Unicode.h>
@@ -92,11 +91,11 @@
switch (bytes)
{ /* note: everything falls through. */
case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
- FALLTHROUGH_INTENDED;
+ [[fallthrough]];
case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
- FALLTHROUGH_INTENDED;
+ [[fallthrough]];
case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
- FALLTHROUGH_INTENDED;
+ [[fallthrough]];
case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
}
}
@@ -304,15 +303,15 @@
while (in < end) {
char16_t w = *in++;
- if (LIKELY(w < 0x0080)) {
+ if (w < 0x0080) [[likely]] {
utf8_len += 1;
continue;
}
- if (LIKELY(w < 0x0800)) {
+ if (w < 0x0800) [[likely]] {
utf8_len += 2;
continue;
}
- if (LIKELY(!is_any_surrogate(w))) {
+ if (!is_any_surrogate(w)) [[likely]] {
utf8_len += 3;
continue;
}
@@ -345,20 +344,20 @@
while (in < in_end) {
char16_t w = *in++;
- if (LIKELY(w < 0x0080)) {
+ if (w < 0x0080) [[likely]] {
if (out + 1 > out_end)
return err_out();
*out++ = (char)(w & 0xff);
continue;
}
- if (LIKELY(w < 0x0800)) {
+ if (w < 0x0800) [[likely]] {
if (out + 2 > out_end)
return err_out();
*out++ = (char)(0xc0 | ((w >> 6) & 0x1f));
*out++ = (char)(0x80 | ((w >> 0) & 0x3f));
continue;
}
- if (LIKELY(!is_any_surrogate(w))) {
+ if (!is_any_surrogate(w)) [[likely]] {
if (out + 3 > out_end)
return err_out();
*out++ = (char)(0xe0 | ((w >> 12) & 0xf));
@@ -420,25 +419,25 @@
while (in < in_end) {
uint8_t c = *in;
utf16_len++;
- if (LIKELY((c & 0x80) == 0)) {
+ if ((c & 0x80) == 0) [[likely]] {
in++;
continue;
}
- if (UNLIKELY(c < 0xc0)) {
+ if (c < 0xc0) [[unlikely]] {
ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
in++;
continue;
}
- if (LIKELY(c < 0xe0)) {
+ if (c < 0xe0) [[likely]] {
in += 2;
continue;
}
- if (LIKELY(c < 0xf0)) {
+ if (c < 0xf0) [[likely]] {
in += 3;
continue;
} else {
uint8_t c2, c3, c4;
- if (UNLIKELY(c >= 0xf8)) {
+ if (c >= 0xf8) [[unlikely]] {
ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
}
c2 = in[1]; c3 = in[2]; c4 = in[3];
@@ -487,25 +486,25 @@
while (in < in_end && out < out_end) {
c = *in++;
- if (LIKELY((c & 0x80) == 0)) {
+ if ((c & 0x80) == 0) [[likely]] {
*out++ = (char16_t)(c);
continue;
}
- if (UNLIKELY(c < 0xc0)) {
+ if (c < 0xc0) [[unlikely]] {
ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
*out++ = (char16_t)(c);
continue;
}
- if (LIKELY(c < 0xe0)) {
- if (UNLIKELY(in + 1 > in_end)) {
+ if (c < 0xe0) [[likely]] {
+ if (in + 1 > in_end) [[unlikely]] {
return err_in();
}
c2 = *in++;
*out++ = (char16_t)(((c & 0x1f) << 6) | (c2 & 0x3f));
continue;
}
- if (LIKELY(c < 0xf0)) {
- if (UNLIKELY(in + 2 > in_end)) {
+ if (c < 0xf0) [[likely]] {
+ if (in + 2 > in_end) [[unlikely]] {
return err_in();
}
c2 = *in++; c3 = *in++;
@@ -513,19 +512,19 @@
((c2 & 0x3f) << 6) | (c3 & 0x3f));
continue;
} else {
- if (UNLIKELY(in + 3 > in_end)) {
+ if (in + 3 > in_end) [[unlikely]] {
return err_in();
}
- if (UNLIKELY(c >= 0xf8)) {
+ if (c >= 0xf8) [[unlikely]] {
ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
}
// Multiple UTF16 characters with surrogates
c2 = *in++; c3 = *in++; c4 = *in++;
w = utf8_4b_to_utf32(c, c2, c3, c4);
- if (UNLIKELY(w < 0x10000)) {
+ if (w < 0x10000) [[unlikely]] {
*out++ = (char16_t)(w);
} else {
- if (UNLIKELY(out + 2 > out_end)) {
+ if (out + 2 > out_end) [[unlikely]] {
// Ooops.... not enough room for this surrogate pair.
return out;
}
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/binder/include/utils/LightRefBase.h
similarity index 100%
rename from libutils/include/utils/LightRefBase.h
rename to libutils/binder/include/utils/LightRefBase.h
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/binder/include/utils/TypeHelpers.h
similarity index 100%
rename from libutils/include/utils/TypeHelpers.h
rename to libutils/binder/include/utils/TypeHelpers.h
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
new file mode 120000
index 0000000..9da2cf0
--- /dev/null
+++ b/libutils/include/utils/LightRefBase.h
@@ -0,0 +1 @@
+../../binder/include/utils/LightRefBase.h
\ No newline at end of file
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
new file mode 120000
index 0000000..848f6d7
--- /dev/null
+++ b/libutils/include/utils/TypeHelpers.h
@@ -0,0 +1 @@
+../../binder/include/utils/TypeHelpers.h
\ No newline at end of file
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
index ad4fb31..b2b257e 100644
--- a/libvndksupport/linker.cpp
+++ b/libvndksupport/linker.cpp
@@ -75,7 +75,7 @@
}
return handle;
} else {
- ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+ ALOGW("Loading %s from current namespace instead of sphal namespace.", name);
return dlopen(name, flag);
}
}
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
index 0548021..65705ac 100644
--- a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -20,6 +20,8 @@
#include <stdint.h>
#include <stdlib.h>
+static constexpr char PROP_TREE_FILE[] = "/dev/__properties__/property_info";
+
namespace android {
namespace properties {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 317f809..fb64736 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -219,26 +219,6 @@
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
- # Create blkio group and apply initial settings.
- # This feature needs kernel to support it, and the
- # device's init.rc must actually set the correct values.
- mkdir /dev/blkio/background
- chown system system /dev/blkio
- chown system system /dev/blkio/background
- chown system system /dev/blkio/tasks
- chown system system /dev/blkio/background/tasks
- chown system system /dev/blkio/cgroup.procs
- chown system system /dev/blkio/background/cgroup.procs
- chmod 0664 /dev/blkio/tasks
- chmod 0664 /dev/blkio/background/tasks
- chmod 0664 /dev/blkio/cgroup.procs
- chmod 0664 /dev/blkio/background/cgroup.procs
- write /dev/blkio/blkio.weight 1000
- write /dev/blkio/background/blkio.weight 200
- write /dev/blkio/background/blkio.bfq.weight 10
- write /dev/blkio/blkio.group_idle 0
- write /dev/blkio/background/blkio.group_idle 0
-
restorecon_recursive /mnt
mount configfs none /config nodev noexec nosuid
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index cc92c68..32057b4 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -76,9 +76,6 @@
error(1, errno, "couldn't stat %s", path);
}
- // /data/user/0 is a known safe symlink.
- if (strcmp("/data/user/0", path) == 0) return;
-
// Must be a real directory, not a symlink.
if (!S_ISDIR(st.st_mode)) {
error(1, 0, "%s not a directory: %o", path, st.st_mode);
@@ -191,27 +188,17 @@
}
// Retrieve package information from system, switching egid so we can read the file.
+ pkg_info info = {.name = pkgname};
gid_t old_egid = getegid();
if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed");
- pkg_info info;
- memset(&info, 0, sizeof(info));
- info.name = pkgname;
if (!packagelist_parse(packagelist_parse_callback, &info)) {
error(1, errno, "packagelist_parse failed");
}
-
- // Handle a multi-user data path
- if (userId > 0) {
- free(info.data_dir);
- if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
- error(1, errno, "asprintf failed");
- }
- }
+ if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
if (info.uid == 0) {
error(1, 0, "unknown package: %s", pkgname);
}
- if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
// Verify that user id is not too big.
if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
@@ -231,6 +218,12 @@
error(1, 0, "package not debuggable: %s", pkgname);
}
+ // Ensure we have the right data path for the specific user.
+ free(info.data_dir);
+ if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+ error(1, errno, "asprintf failed");
+ }
+
// Check that the data directory path is valid.
check_data_path(pkgname, info.data_dir, userAppId);
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
index 5f24bc6..b10f727 100644
--- a/trusty/keymaster/fuzz/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -26,7 +26,9 @@
"-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
],
fuzz_config: {
- cc: ["trong@google.com"],
+ cc: ["trong@google.com", "drysdale@google.com"],
+ componentid: 1084733,
+ hotlists: ["4271696"],
},
// The initial corpus for this fuzzer was derived by dumping messages from
diff --git a/trusty/keymint/fuzz/Android.bp b/trusty/keymint/fuzz/Android.bp
new file mode 100644
index 0000000..de73db7
--- /dev/null
+++ b/trusty/keymint/fuzz/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+ name: "trusty_keymint_fuzzer",
+ defaults: ["trusty_fuzzer_defaults"],
+ srcs: [":trusty_tipc_fuzzer"],
+ cflags: [
+ "-DTRUSTY_APP_PORT=\"com.android.trusty.keymint\"",
+ "-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
+ "-DTRUSTY_APP_FILENAME=\"keymint_app.syms.elf\"",
+ ],
+ fuzz_config: {
+ cc: ["drysdale@google.com"],
+ componentid: 1084733,
+ hotlists: ["4271696"],
+ },
+
+ // The initial corpus for this fuzzer was derived by dumping messages from
+ // the HAL service while running the VTS tests for KeyMint.
+ corpus: ["corpus/*"],
+}
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-821180-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-821180-0
new file mode 100644
index 0000000..18fce25
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-821180-0
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0
new file mode 100644
index 0000000..906f640
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0
@@ -0,0 +1 @@
+@
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0
new file mode 100644
index 0000000..d629eaa
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0
@@ -0,0 +1 @@
+Cfoo
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0
new file mode 100644
index 0000000..3aa32ab
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0
@@ -0,0 +1 @@
+X@TÄdý£%r³õ«ã1:áéz¹ð7êXØÿ$éÓûõv`ô4¨ÂÝrc«ØJ!Rq¯»3?ZD
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1
new file mode 100644
index 0000000..1937534
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1
@@ -0,0 +1,2 @@
+X@vE)Y¼0jIê»: <q*÷Ö㺧
+#ï}øê4CT$dñ¬ô"£YOǨSÖò; eP´
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-0
new file mode 100644
index 0000000..0490f22
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-1
new file mode 100644
index 0000000..ea95a46
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-2
new file mode 100644
index 0000000..ffe61a4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-3
new file mode 100644
index 0000000..1e27673
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-0
new file mode 100644
index 0000000..8ab6a9b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-1
new file mode 100644
index 0000000..dfac273
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-2
new file mode 100644
index 0000000..3f0d405
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-3
new file mode 100644
index 0000000..aef020a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-0
new file mode 100644
index 0000000..1036580
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-1
new file mode 100644
index 0000000..b0ead45
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-2
new file mode 100644
index 0000000..8375478
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-3
new file mode 100644
index 0000000..32224ee
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-0
new file mode 100644
index 0000000..0d3285a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-1
new file mode 100644
index 0000000..73a0a5b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-2
new file mode 100644
index 0000000..135ba17
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-3
new file mode 100644
index 0000000..827854e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-0
new file mode 100644
index 0000000..668b59c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-1
new file mode 100644
index 0000000..e20dcdb
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-2
new file mode 100644
index 0000000..b23c619
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-3
new file mode 100644
index 0000000..0a51e01
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-0
new file mode 100644
index 0000000..be73ca3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-1
new file mode 100644
index 0000000..625ce23
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-2
new file mode 100644
index 0000000..5ae5b06
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-3
new file mode 100644
index 0000000..665818c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-0
new file mode 100644
index 0000000..021de9d
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-1
new file mode 100644
index 0000000..85394c2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-2
new file mode 100644
index 0000000..1c07c9f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-3
new file mode 100644
index 0000000..03094b3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-0
new file mode 100644
index 0000000..3fda800
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-1
new file mode 100644
index 0000000..9bedcf6
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-2
new file mode 100644
index 0000000..49f8faf
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-3
new file mode 100644
index 0000000..38f360c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-0
new file mode 100644
index 0000000..16388b3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-1
new file mode 100644
index 0000000..5238d4a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-2
new file mode 100644
index 0000000..ce98ac3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-3
new file mode 100644
index 0000000..b0bdc3e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-0
new file mode 100644
index 0000000..58cfa24
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-1
new file mode 100644
index 0000000..ffe3bb3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-2
new file mode 100644
index 0000000..93f7d1c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-3
new file mode 100644
index 0000000..ad6744f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-0
new file mode 100644
index 0000000..ce8515f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-1
new file mode 100644
index 0000000..dd1d192
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-2
new file mode 100644
index 0000000..c82d9a1
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-3
new file mode 100644
index 0000000..93246f8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-0
new file mode 100644
index 0000000..8865ca7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-1
new file mode 100644
index 0000000..36369f7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138292-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138292-0
new file mode 100644
index 0000000..ffcffbf
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138292-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-0
new file mode 100644
index 0000000..3741bdf
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-1
new file mode 100644
index 0000000..74f74cd
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-2
new file mode 100644
index 0000000..0ed4888
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-3
new file mode 100644
index 0000000..e933e1c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-0
new file mode 100644
index 0000000..88c2106
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-1
new file mode 100644
index 0000000..004c63a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-2
new file mode 100644
index 0000000..939c477
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-3
new file mode 100644
index 0000000..2a7a322
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-0
new file mode 100644
index 0000000..4d4ed45
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-1
new file mode 100644
index 0000000..5a5d90d
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-2
new file mode 100644
index 0000000..d966664
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-3
new file mode 100644
index 0000000..746a2df
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-0
new file mode 100644
index 0000000..fbde04e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-1
new file mode 100644
index 0000000..a8ec9fe
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-2
new file mode 100644
index 0000000..e00aa32
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-3
new file mode 100644
index 0000000..1c414e4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-0
new file mode 100644
index 0000000..6ce48a5
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-1
new file mode 100644
index 0000000..623ba78
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-2
new file mode 100644
index 0000000..3e1a756
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-3
new file mode 100644
index 0000000..9fda99a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-0
new file mode 100644
index 0000000..9d8c18f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-1
new file mode 100644
index 0000000..c88132a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-2
new file mode 100644
index 0000000..8b735f4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-3
new file mode 100644
index 0000000..33148a1
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848b-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848b-0
new file mode 100644
index 0000000..a43a7fc
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848b-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-0
new file mode 100644
index 0000000..66898a7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-1
new file mode 100644
index 0000000..9c2b989
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-2
new file mode 100644
index 0000000..8b2a6dd
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-3
new file mode 100644
index 0000000..60bb113
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0
new file mode 100644
index 0000000..e68e281
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0
@@ -0,0 +1 @@
+X4just some garbage data which is not a valid key blob:oÿý¦Hclientid:oÿýCGappdata
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-1
new file mode 100644
index 0000000..6b94220
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-0
new file mode 100644
index 0000000..8c41bfd
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-1
new file mode 100644
index 0000000..69305d2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-2
new file mode 100644
index 0000000..c09cc84
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-3
new file mode 100644
index 0000000..63d07cb
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-0
new file mode 100644
index 0000000..5d7d27a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-1
new file mode 100644
index 0000000..79515cc
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-2
new file mode 100644
index 0000000..c5f577c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-3
new file mode 100644
index 0000000..c93b24c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-0
new file mode 100644
index 0000000..cf90447
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-1
new file mode 100644
index 0000000..02233d7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-2
new file mode 100644
index 0000000..f39c953
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-3
new file mode 100644
index 0000000..438b2be
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-0
new file mode 100644
index 0000000..3f81237
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-1
new file mode 100644
index 0000000..3fad357
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-2
new file mode 100644
index 0000000..5967cad
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-3
new file mode 100644
index 0000000..705537b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0
new file mode 100644
index 0000000..204f7a8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0
@@ -0,0 +1 @@
+1?TöÖÉDFfoobar
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1
new file mode 100644
index 0000000..239a31b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1
@@ -0,0 +1 @@
+1;OiìÁÈ^Æ'Ffoobar
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2
new file mode 100644
index 0000000..88586f0
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2
@@ -0,0 +1 @@
+1YéÍ. Ffoobar
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3
new file mode 100644
index 0000000..06fb12a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3
@@ -0,0 +1 @@
+1úbxAN`Ffoobar
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0
new file mode 100644
index 0000000..9f819e8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0
@@ -0,0 +1 @@
+2;*Æõ(ÚÐ÷Yøaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-1
new file mode 100644
index 0000000..305a056
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2
new file mode 100644
index 0000000..509b24b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2
@@ -0,0 +1 @@
+2_ÛÈK5]rY÷aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3
new file mode 100644
index 0000000..75d713d
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3
@@ -0,0 +1,2 @@
+2;
+E=¤®þYùaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-0
new file mode 100644
index 0000000..b96374f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1
new file mode 100644
index 0000000..e07515e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1
@@ -0,0 +1 @@
+3Á6Ø.[LHello World!@
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-2
new file mode 100644
index 0000000..fb70126
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-3
new file mode 100644
index 0000000..2ec8700
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0
new file mode 100644
index 0000000..7f4913f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0
@@ -0,0 +1 @@
+4;zÁ«Êñ
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1
new file mode 100644
index 0000000..9a89501
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1
@@ -0,0 +1 @@
+4ßÂ÷6
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2
new file mode 100644
index 0000000..219dbb2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3
new file mode 100644
index 0000000..f3875ac
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3
@@ -0,0 +1 @@
+4 訢%
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0
new file mode 100644
index 0000000..a0a6c55
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0
@@ -0,0 +1 @@
+A
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0
new file mode 100644
index 0000000..775bb4b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0
@@ -0,0 +1 @@
+Bô
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00035504-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00035504-0
new file mode 100644
index 0000000..8900169
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00035504-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-001e170d-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-001e170d-0
new file mode 100644
index 0000000..1d6adc0
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-001e170d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00303031-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00303031-0
new file mode 100644
index 0000000..05965d9
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00303031-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00313563-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00313563-0
new file mode 100644
index 0000000..00d3a67
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00313563-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00333233-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00333233-0
new file mode 100644
index 0000000..608e178
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00333233-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00365a17-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00365a17-0
new file mode 100644
index 0000000..2b4acb2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00365a17-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-003cc0cc-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-003cc0cc-0
new file mode 100644
index 0000000..2cfcec8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-003cc0cc-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-003e7b1a-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-003e7b1a-0
new file mode 100644
index 0000000..7beac8c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-003e7b1a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0042-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0042-0
new file mode 100644
index 0000000..57302ad
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0042-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00646630-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00646630-0
new file mode 100644
index 0000000..4ecce85
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00646630-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-0
new file mode 100644
index 0000000..3d4a9d3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-1 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-1
new file mode 100644
index 0000000..9a6182a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-2 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-2
new file mode 100644
index 0000000..3375a2e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-3 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-3
new file mode 100644
index 0000000..41359da
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822180-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822180-0
new file mode 100644
index 0000000..0ebe5ae
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822180-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822280-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822280-0
new file mode 100644
index 0000000..a797c20
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822280-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822580-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822580-0
new file mode 100644
index 0000000..3691795
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822580-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822680-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822680-0
new file mode 100644
index 0000000..dd5cbf3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822680-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822780-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822780-0
new file mode 100644
index 0000000..4b79060
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822780-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822880-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822880-0
new file mode 100644
index 0000000..e7eab4c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822880-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822980-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822980-0
new file mode 100644
index 0000000..0072b55
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822980-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822a80-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822a80-0
new file mode 100644
index 0000000..9a60d67
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822a80-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822b80-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822b80-0
new file mode 100644
index 0000000..88cba54
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822b80-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822c80-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822c80-0
new file mode 100644
index 0000000..deeb51f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822c80-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823080-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823080-0
new file mode 100644
index 0000000..15f1535
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823080-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823480-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823480-0
new file mode 100644
index 0000000..291df50
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823480-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823819-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823819-0
new file mode 100644
index 0000000..75cefc8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823819-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082381d-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381d-0
new file mode 100644
index 0000000..ce61cd9
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082381e-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381e-0
new file mode 100644
index 0000000..9aa2664
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823820-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823820-0
new file mode 100644
index 0000000..151c1f4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823820-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823825-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823825-0
new file mode 100644
index 0000000..0356eee
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823825-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823827-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823827-0
new file mode 100644
index 0000000..26ea013
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823827-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082382b-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082382b-0
new file mode 100644
index 0000000..c2ca980
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082382b-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823833-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823833-0
new file mode 100644
index 0000000..1d8f61e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823833-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823836-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823836-0
new file mode 100644
index 0000000..98dd9b0
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823836-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823837-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823837-0
new file mode 100644
index 0000000..da79a71
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823837-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823838-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823838-0
new file mode 100644
index 0000000..101d1be
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823838-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823839-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823839-0
new file mode 100644
index 0000000..9297110
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823839-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082383a-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383a-0
new file mode 100644
index 0000000..4cfc804
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082383e-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383e-0
new file mode 100644
index 0000000..6fbc978
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823840-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823840-0
new file mode 100644
index 0000000..4f73211
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823840-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823841-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823841-0
new file mode 100644
index 0000000..eb9bc2a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823841-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823846-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823846-0
new file mode 100644
index 0000000..8f40430
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823846-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082384d-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384d-0
new file mode 100644
index 0000000..242516e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082384e-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384e-0
new file mode 100644
index 0000000..72f2930
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082384f-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384f-0
new file mode 100644
index 0000000..7cb67c3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384f-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823850-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823850-0
new file mode 100644
index 0000000..25251cc
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823850-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823903-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823903-0
new file mode 100644
index 0000000..42e4206
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823903-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-009a81fa-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-009a81fa-0
new file mode 100644
index 0000000..b5ffa78
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-009a81fa-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00b5ae79-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00b5ae79-0
new file mode 100644
index 0000000..c5f085b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00b5ae79-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-0
new file mode 100644
index 0000000..598a377
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-1 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-1
new file mode 100644
index 0000000..37f4af5
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-2 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-2
new file mode 100644
index 0000000..5568f4b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-3 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-3
new file mode 100644
index 0000000..34a5f23
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-3
Binary files differ