Merge "[incremental/adb] fix signature size check"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 7933629..89bd66a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -16,16 +16,25 @@
"name": "adb_tls_connection_test"
},
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "CtsInitTestCases"
},
{
+ "name": "CtsLiblogTestCases"
+ },
+ {
+ "name": "CtsLogdTestCases"
+ },
+ {
"name": "debuggerd_test"
},
{
- "name": "CtsFsMgrTestCases"
+ "name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "fs_mgr_vendor_overlay_test"
+ "name": "init_kill_services_test"
},
{
"name": "libpackagelistparser_test"
@@ -58,5 +67,10 @@
{
"name": "propertyinfoserializer_tests"
}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/tests/StagedInstallTest"
+ }
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index 2fc205f..87ac54d 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -12,6 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+tidy_errors = [
+ "-*",
+ "bugprone-inaccurate-erase",
+]
+
cc_defaults {
name: "adb_defaults",
@@ -73,6 +78,10 @@
],
},
},
+
+ tidy: true,
+ tidy_checks: tidy_errors,
+ tidy_checks_as_errors: tidy_errors,
}
cc_defaults {
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index d6f536e..ea50f59 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -876,7 +876,7 @@
"-S",
std::to_string(sb.st_size),
session_id_str,
- android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+ android::base::StringPrintf("%d_%s", i, android::base::Basename(split).c_str()),
"-",
};
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index b674a81..db4c479 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -400,12 +400,12 @@
for (const std::string& path : paths) {
int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
if (wd < 0) {
- PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
+ PLOG(ERROR) << "failed to inotify_add_watch on path '" << path << "'";
continue;
}
g_monitored_paths[wd] = path;
- LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
+ LOG(INFO) << "watch descriptor " << wd << " registered for '" << path << "'";
}
fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 9611212..a0fc9ca 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -249,6 +249,9 @@
return false;
}
+ // Remove any services with the same instance name, as it may be a stale registration.
+ removeDNSService(regType_.c_str(), serviceName_.c_str());
+
// Add to the service registry before trying to auto-connect, since socket_spec_connect will
// check these registries for the ip address when connecting via mdns instance name.
int adbSecureServiceType = serviceIndex();
@@ -268,13 +271,6 @@
return false;
}
- if (!services->empty()) {
- // Remove the previous resolved service, if any.
- services->erase(std::remove_if(services->begin(), services->end(),
- [&](std::unique_ptr<ResolvedService>& service) {
- return (serviceName_ == service->serviceName());
- }));
- }
services->push_back(std::unique_ptr<ResolvedService>(this));
if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
@@ -327,6 +323,8 @@
static bool connectByServiceName(const ServiceRegistry& services,
const std::string& service_name);
+ static void removeDNSService(const char* regType, const char* serviceName);
+
private:
int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
std::string addr_format_;
@@ -396,6 +394,37 @@
return false;
}
+// static
+void ResolvedService::removeDNSService(const char* regType, const char* serviceName) {
+ D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
+ int index = adb_DNSServiceIndexByName(regType);
+ ServiceRegistry* services;
+ switch (index) {
+ case kADBTransportServiceRefIndex:
+ services = sAdbTransportServices;
+ break;
+ case kADBSecurePairingServiceRefIndex:
+ services = sAdbSecurePairingServices;
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ services = sAdbSecureConnectServices;
+ break;
+ default:
+ return;
+ }
+
+ if (services->empty()) {
+ return;
+ }
+
+ std::string sName(serviceName);
+ services->erase(std::remove_if(services->begin(), services->end(),
+ [&sName](std::unique_ptr<ResolvedService>& service) {
+ return (sName == service->serviceName());
+ }),
+ services->end());
+}
+
void adb_secure_foreach_pairing_service(const char* service_name,
adb_secure_foreach_service_callback cb) {
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
@@ -481,35 +510,6 @@
std::string regType_;
};
-static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
- D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
- int index = adb_DNSServiceIndexByName(regType);
- ResolvedService::ServiceRegistry* services;
- switch (index) {
- case kADBTransportServiceRefIndex:
- services = ResolvedService::sAdbTransportServices;
- break;
- case kADBSecurePairingServiceRefIndex:
- services = ResolvedService::sAdbSecurePairingServices;
- break;
- case kADBSecureConnectServiceRefIndex:
- services = ResolvedService::sAdbSecureConnectServices;
- break;
- default:
- return;
- }
-
- if (services->empty()) {
- return;
- }
-
- std::string sName(serviceName);
- services->erase(std::remove_if(services->begin(), services->end(),
- [&sName](std::unique_ptr<ResolvedService>& service) {
- return (sName == service->serviceName());
- }));
-}
-
// Returns the version the device wanted to advertise,
// or -1 if parsing fails.
static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
@@ -612,7 +612,7 @@
} else {
D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
regtype, domain);
- adb_RemoveDNSService(regtype, serviceName);
+ ResolvedService::removeDNSService(regtype, serviceName);
}
}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index b9f0d54..a32d875 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -618,21 +618,37 @@
finally:
zeroconf_ctx.unregister_service(info)
+@contextlib.contextmanager
+def zeroconf_register_services(zeroconf_ctx, infos):
+ """Context manager for multiple zeroconf services
+
+ Registers all services given and unregisters all on cleanup. Returns the ServiceInfo
+ list supplied.
+ """
+
+ try:
+ for info in infos:
+ zeroconf_ctx.register_service(info)
+ yield infos
+ finally:
+ for info in infos:
+ zeroconf_ctx.unregister_service(info)
+
"""Should match the service names listed in adb_mdns.h"""
class MdnsTest:
"""Tests for adb mdns."""
+ @staticmethod
+ def _mdns_services(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+ @staticmethod
+ def _devices(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
class Base(unittest.TestCase):
- @staticmethod
- def _mdns_services(port):
- output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
- return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
-
- @staticmethod
- def _devices(port):
- output = subprocess.check_output(["adb", "-P", str(port), "devices"])
- return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
-
@contextlib.contextmanager
def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
"""Context manager for an ADB connection.
@@ -691,6 +707,50 @@
for line in MdnsTest._mdns_services(server_port)))
@unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+ def test_mdns_services_register_unregister_multiple(self):
+ """Ensure that `adb mdns services` correctly adds and removes multiple services
+ """
+ from zeroconf import IPVersion, ServiceInfo
+
+ with adb_server() as server_port:
+ output = subprocess.check_output(["adb", "-P", str(server_port),
+ "mdns", "services"]).strip()
+ self.assertTrue(output.startswith(b"List of discovered mdns services"))
+
+ """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
+ """Register/Unregister a service"""
+ with zeroconf_context(IPVersion.V4Only) as zc:
+ srvs = {
+ 'mdns_name': ["testservice0", "testservice1", "testservice2"],
+ 'mdns_type': "_" + self.service_name + "._tcp.",
+ 'ipaddr': [
+ socket.inet_aton("192.168.0.1"),
+ socket.inet_aton("10.0.0.255"),
+ socket.inet_aton("172.16.1.100")],
+ 'port': [10000, 20000, 65535]}
+ srv_infos = []
+ for i in range(len(srvs['mdns_name'])):
+ srv_infos.append(ServiceInfo(
+ srvs['mdns_type'] + "local.",
+ name=srvs['mdns_name'][i] + "." + srvs['mdns_type'] + "local.",
+ addresses=[srvs['ipaddr'][i]],
+ port=srvs['port'][i]))
+
+ """ Register all devices, then unregister"""
+ with zeroconf_register_services(zc, srv_infos) as infos:
+ """Give adb some time to register the service"""
+ time.sleep(1)
+ for i in range(len(srvs['mdns_name'])):
+ self.assertTrue(any((srvs['mdns_name'][i] in line and srvs['mdns_type'] in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+ """Give adb some time to unregister the service"""
+ time.sleep(1)
+ for i in range(len(srvs['mdns_name'])):
+ self.assertFalse(any((srvs['mdns_name'][i] in line and srvs['mdns_type'] in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+ @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
def test_mdns_connect(self):
"""Ensure that `adb connect` by mdns instance name works (for non-pairing services)
"""
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 5d6cee4..8979e9a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -436,6 +436,8 @@
{"reboot,userspace_failed,watchdog_fork", 188},
{"reboot,userspace_failed,*", 189},
{"reboot,mount_userdata_failed", 190},
+ {"reboot,forcedsilent", 191},
+ {"reboot,forcednonsilent", 192},
};
// Converts a string value representing the reason the system booted to an
diff --git a/code_coverage/seccomp_policy/code_coverage.arm.policy b/code_coverage/seccomp_policy/code_coverage.arm.policy
index d6784e3..b80910f 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm.policy
@@ -6,6 +6,7 @@
write: 1
fcntl64: 1
fstat64: 1
+ftruncate64: 1
geteuid32: 1
_llseek: 1
mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.arm64.policy b/code_coverage/seccomp_policy/code_coverage.arm64.policy
index 4c3dd26..7040ea2 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm64.policy
@@ -6,6 +6,7 @@
write: 1
fcntl: 1
fstat: 1
+ftruncate: 1
geteuid: 1
lseek: 1
mmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.policy.def b/code_coverage/seccomp_policy/code_coverage.policy.def
index f136084..599c4a4 100644
--- a/code_coverage/seccomp_policy/code_coverage.policy.def
+++ b/code_coverage/seccomp_policy/code_coverage.policy.def
@@ -22,6 +22,7 @@
#if defined(__LP64__)
fcntl: 1
fstat: 1
+ftruncate: 1
geteuid: 1
lseek: 1
mmap: 1
@@ -29,6 +30,7 @@
#else
fcntl64: 1
fstat64: 1
+ftruncate64: 1
geteuid32: 1
_llseek: 1
mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86.policy b/code_coverage/seccomp_policy/code_coverage.x86.policy
index 24ff8b9..f8e0cc0 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86.policy
@@ -6,6 +6,7 @@
write: 1
fcntl64: 1
fstat64: 1
+ftruncate64: 1
geteuid32: 1
_llseek: 1
mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86_64.policy b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
index 3081036..dcf2f9a 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86_64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
@@ -6,6 +6,7 @@
write: 1
fcntl: 1
fstat: 1
+ftruncate: 1
geteuid: 1
lseek: 1
mmap: 1
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 31c2d5d..ad10a1f 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -253,7 +253,6 @@
"libcutils",
"libdebuggerd_client",
"liblog",
- "libminijail",
"libnativehelper",
"libunwindstack",
],
@@ -261,6 +260,7 @@
static_libs: [
"libdebuggerd",
"libgmock",
+ "libminijail",
],
header_libs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 5c02738..6bfb5f2 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -70,36 +70,6 @@
tv->tv_usec = static_cast<long>(microseconds.count());
}
-static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
- struct tm now;
- time_t t = time(nullptr);
- localtime_r(&t, &now);
- char timestamp[32];
- strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
- std::string time_now(timestamp);
-
- std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
-
- char proc_name_buf[1024];
- const char* proc_name = nullptr;
- std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
-
- if (fp) {
- proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
- }
-
- if (!proc_name) {
- proc_name = "<unknown>";
- }
-
- buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
- << "Cmd line: " << proc_name << "\n";
-}
-
-static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
- buffer << "----- end " << std::to_string(pid) << " -----\n";
-}
-
/**
* Returns the wchan data for each thread in the process,
* or empty string if unable to obtain any data.
@@ -125,9 +95,10 @@
}
if (std::string str = data.str(); !str.empty()) {
- get_wchan_header(pid, buffer);
+ buffer << "\n----- Waiting Channels: pid " << pid << " at " << get_timestamp() << " -----\n"
+ << "Cmd line: " << get_process_name(pid) << "\n";
buffer << "\n" << str << "\n";
- get_wchan_footer(pid, buffer);
+ buffer << "----- end " << std::to_string(pid) << " -----\n";
buffer << "\n";
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9d7658e..108787e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <sys/capability.h>
+#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
@@ -172,6 +173,8 @@
void StartCrasher(const std::string& crash_type);
void FinishCrasher();
void AssertDeath(int signo);
+
+ static void Trap(void* ptr);
};
CrasherTest::CrasherTest() {
@@ -334,6 +337,48 @@
R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
}
+// Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the
+// compiler could still clobber the argument register before trapping, but that's unlikely.
+__attribute__((weak)) void CrasherTest::Trap(void* ptr ATTRIBUTE_UNUSED) {
+ __builtin_trap();
+}
+
+TEST_F(CrasherTest, heap_addr_in_register) {
+#if defined(__i386__)
+ GTEST_SKIP() << "architecture does not pass arguments in registers";
+#endif
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ // Crash with a heap pointer in the first argument register.
+ Trap(malloc(1));
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ int status;
+ ASSERT_EQ(crasher_pid, TIMEOUT(30, waitpid(crasher_pid, &status, 0)));
+ ASSERT_TRUE(WIFSIGNALED(status)) << "crasher didn't terminate via a signal";
+ // Don't test the signal number because different architectures use different signals for
+ // __builtin_trap().
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+#if defined(__aarch64__)
+ ASSERT_MATCH(result, "memory near x0");
+#elif defined(__arm__)
+ ASSERT_MATCH(result, "memory near r0");
+#elif defined(__x86_64__)
+ ASSERT_MATCH(result, "memory near rdi");
+#else
+ ASSERT_TRUE(false) << "unsupported architecture";
+#endif
+}
+
#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
static void SetTagCheckingLevelSync() {
int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
@@ -512,6 +557,55 @@
#endif
}
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+static uintptr_t CreateTagMapping() {
+ uintptr_t mapping =
+ reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+ return 0;
+ }
+ __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+ :
+ : "r"(mapping + (1ULL << 56))
+ : "memory");
+ return mapping;
+}
+#endif
+
+TEST_F(CrasherTest, mte_tag_dump) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ Trap(reinterpret_cast<void *>(CreateTagMapping()));
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGTRAP);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(memory near x0:
+.*
+.*
+ 01.............0 0000000000000000 0000000000000000 ................
+ 00.............0)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
TEST_F(CrasherTest, LD_PRELOAD) {
int intercept_result;
unique_fd output_fd;
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index c606970..f5a873c 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -27,7 +27,6 @@
#include <string.h>
#include <sys/ptrace.h>
#include <sys/types.h>
-#include <time.h>
#include <unistd.h>
#include <map>
@@ -40,14 +39,10 @@
#include "libdebuggerd/types.h"
#include "libdebuggerd/utility.h"
+#include "util.h"
static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
- time_t t = time(NULL);
- struct tm tm;
- localtime_r(&t, &tm);
- char timestr[64];
- strftime(timestr, sizeof(timestr), "%F %T", &tm);
- _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
+ _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, get_timestamp().c_str());
if (process_name) {
_LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", process_name);
@@ -106,9 +101,8 @@
log.tfd = output_fd;
log.amfd_data = nullptr;
- char process_name[128];
- read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
- dump_process_header(&log, getpid(), process_name);
+ pid_t pid = getpid();
+ dump_process_header(&log, pid, get_process_name(pid).c_str());
}
void dump_backtrace_footer(int output_fd) {
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 7bfcf5d..76155b1 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -83,8 +83,6 @@
void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
-void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
-
void drop_capabilities();
bool signal_has_sender(const siginfo_t*, pid_t caller_pid);
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index be39582..f16f578 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -30,39 +30,39 @@
const char g_expected_full_dump[] =
"\nmemory near r1:\n"
#if defined(__LP64__)
-" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+" 0000000012345650 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 0000000012345660 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345670 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345680 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345690 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00000000123456a0 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00000000123456b0 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00000000123456c0 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00000000123456d0 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00000000123456e0 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00000000123456f0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000012345700 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000012345710 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000012345720 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000012345730 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000012345740 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+" 12345650 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 12345660 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345670 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345680 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345690 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 123456a0 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 123456b0 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" 123456c0 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" 123456d0 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 123456e0 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 123456f0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 12345700 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 12345710 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 12345720 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 12345730 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 12345740 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
const char g_expected_partial_dump[] = \
@@ -112,7 +112,10 @@
if (last_read_addr_ > 0) {
offset = addr - last_read_addr_;
}
- size_t bytes_available = buffer_.size() - offset;
+ size_t bytes_available = 0;
+ if (offset < buffer_.size()) {
+ bytes_available = buffer_.size() - offset;
+ }
if (partial_read_) {
bytes = std::min(bytes, bytes_partial_read_);
@@ -258,44 +261,7 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near pc:\n"
-#if defined(__LP64__)
-" 00000000a2345658 ---------------- ---------------- ................\n"
-" 00000000a2345668 ---------------- ---------------- ................\n"
-" 00000000a2345678 ---------------- ---------------- ................\n"
-" 00000000a2345688 ---------------- ---------------- ................\n"
-" 00000000a2345698 ---------------- ---------------- ................\n"
-" 00000000a23456a8 ---------------- ---------------- ................\n"
-" 00000000a23456b8 ---------------- ---------------- ................\n"
-" 00000000a23456c8 ---------------- ---------------- ................\n"
-" 00000000a23456d8 ---------------- ---------------- ................\n"
-" 00000000a23456e8 ---------------- ---------------- ................\n"
-" 00000000a23456f8 ---------------- ---------------- ................\n"
-" 00000000a2345708 ---------------- ---------------- ................\n"
-" 00000000a2345718 ---------------- ---------------- ................\n"
-" 00000000a2345728 ---------------- ---------------- ................\n"
-" 00000000a2345738 ---------------- ---------------- ................\n"
-" 00000000a2345748 ---------------- ---------------- ................\n";
-#else
-" a2345658 -------- -------- -------- -------- ................\n"
-" a2345668 -------- -------- -------- -------- ................\n"
-" a2345678 -------- -------- -------- -------- ................\n"
-" a2345688 -------- -------- -------- -------- ................\n"
-" a2345698 -------- -------- -------- -------- ................\n"
-" a23456a8 -------- -------- -------- -------- ................\n"
-" a23456b8 -------- -------- -------- -------- ................\n"
-" a23456c8 -------- -------- -------- -------- ................\n"
-" a23456d8 -------- -------- -------- -------- ................\n"
-" a23456e8 -------- -------- -------- -------- ................\n"
-" a23456f8 -------- -------- -------- -------- ................\n"
-" a2345708 -------- -------- -------- -------- ................\n"
-" a2345718 -------- -------- -------- -------- ................\n"
-" a2345728 -------- -------- -------- -------- ................\n"
-" a2345738 -------- -------- -------- -------- ................\n"
-" a2345748 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+ ASSERT_STREQ("", tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -429,57 +395,17 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMemoryTest, memory_address_too_low) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- memory_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, memory_mock_.get(), 0, "memory near r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
TEST_F(DumpMemoryTest, memory_address_too_high) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), -32, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), -208, "memory near r1");
#else
- dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near r1");
-#endif
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_address_would_overflow) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- memory_mock_->SetReadData(buffer, sizeof(buffer));
-
-#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near r1");
-#else
- dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x100000000 - 32, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x100000000 - 208, "memory near r1");
#endif
std::string tombstone_contents;
@@ -500,9 +426,9 @@
memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near r4");
+ dump_memory(&log_, memory_mock_.get(), -224, "memory near r4");
#else
- dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near r4");
+ dump_memory(&log_, memory_mock_.get(), 0x100000000 - 224, "memory near r4");
#endif
std::string tombstone_contents;
@@ -510,40 +436,57 @@
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#if defined(__aarch64__)
+" 00ffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 00ffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 00ffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 00ffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 00ffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00ffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00ffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00ffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00ffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00ffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00ffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 00ffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 00ffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 00ffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 00ffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 00fffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#elif defined(__LP64__)
+" ffffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" ffffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" ffffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" ffffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" ffffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" ffffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" ffffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" ffffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" ffffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" ffffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" ffffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" ffffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" ffffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" ffffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" ffffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" fffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+" ffffff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" ffffff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" ffffff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" ffffff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" ffffff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" ffffff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" ffffff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" ffffff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" ffffff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" ffffff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" ffffffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" ffffffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" ffffffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" ffffffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" ffffffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" fffffff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -570,39 +513,41 @@
const char* expected_dump = \
"\nmemory near r4:\n"
#if defined(__LP64__)
-" 0000000010000f88 ---------------- ---------------- ................\n"
-" 0000000010000f98 ---------------- ---------------- ................\n"
-" 0000000010000fa8 ---------------- ---------------- ................\n"
-" 0000000010000fb8 ---------------- ---------------- ................\n"
-" 0000000010000fc8 ---------------- ---------------- ................\n"
-" 0000000010000fd8 ---------------- ---------------- ................\n"
-" 0000000010000fe8 ---------------- ---------------- ................\n"
-" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
-" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+R"( 0000000010000f80 ---------------- ---------------- ................
+ 0000000010000f90 ---------------- ---------------- ................
+ 0000000010000fa0 ---------------- ---------------- ................
+ 0000000010000fb0 ---------------- ---------------- ................
+ 0000000010000fc0 ---------------- ---------------- ................
+ 0000000010000fd0 ---------------- ---------------- ................
+ 0000000010000fe0 ---------------- ---------------- ................
+ 0000000010000ff0 ---------------- ---------------- ................
+ 0000000010001000 8786858483828180 8f8e8d8c8b8a8988 ................
+ 0000000010001010 9796959493929190 9f9e9d9c9b9a9998 ................
+ 0000000010001020 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................
+ 0000000010001030 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................
+ 0000000010001040 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................
+ 0000000010001050 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................
+ 0000000010001060 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................
+ 0000000010001070 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................
+)";
#else
-" 10000f88 -------- -------- -------- -------- ................\n"
-" 10000f98 -------- -------- -------- -------- ................\n"
-" 10000fa8 -------- -------- -------- -------- ................\n"
-" 10000fb8 -------- -------- -------- -------- ................\n"
-" 10000fc8 -------- -------- -------- -------- ................\n"
-" 10000fd8 -------- -------- -------- -------- ................\n"
-" 10000fe8 -------- -------- -------- -------- ................\n"
-" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
-" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+R"( 10000f80 -------- -------- -------- -------- ................
+ 10000f90 -------- -------- -------- -------- ................
+ 10000fa0 -------- -------- -------- -------- ................
+ 10000fb0 -------- -------- -------- -------- ................
+ 10000fc0 -------- -------- -------- -------- ................
+ 10000fd0 -------- -------- -------- -------- ................
+ 10000fe0 -------- -------- -------- -------- ................
+ 10000ff0 -------- -------- -------- -------- ................
+ 10001000 83828180 87868584 8b8a8988 8f8e8d8c ................
+ 10001010 93929190 97969594 9b9a9998 9f9e9d9c ................
+ 10001020 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................
+ 10001030 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................
+ 10001040 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................
+ 10001050 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................
+ 10001060 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................
+ 10001070 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................
+)";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -684,44 +629,7 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000000 ---------------- ---------------- ................\n"
-" 0000000010000010 ---------------- ---------------- ................\n"
-" 0000000010000020 ---------------- ---------------- ................\n"
-" 0000000010000030 ---------------- ---------------- ................\n"
-" 0000000010000040 ---------------- ---------------- ................\n"
-" 0000000010000050 ---------------- ---------------- ................\n"
-" 0000000010000060 ---------------- ---------------- ................\n"
-" 0000000010000070 ---------------- ---------------- ................\n"
-" 0000000010000080 ---------------- ---------------- ................\n"
-" 0000000010000090 ---------------- ---------------- ................\n"
-" 00000000100000a0 ---------------- ---------------- ................\n"
-" 00000000100000b0 ---------------- ---------------- ................\n"
-" 00000000100000c0 ---------------- ---------------- ................\n"
-" 00000000100000d0 ---------------- ---------------- ................\n"
-" 00000000100000e0 ---------------- ---------------- ................\n"
-" 00000000100000f0 ---------------- ---------------- ................\n";
-#else
-" 10000000 -------- -------- -------- -------- ................\n"
-" 10000010 -------- -------- -------- -------- ................\n"
-" 10000020 -------- -------- -------- -------- ................\n"
-" 10000030 -------- -------- -------- -------- ................\n"
-" 10000040 -------- -------- -------- -------- ................\n"
-" 10000050 -------- -------- -------- -------- ................\n"
-" 10000060 -------- -------- -------- -------- ................\n"
-" 10000070 -------- -------- -------- -------- ................\n"
-" 10000080 -------- -------- -------- -------- ................\n"
-" 10000090 -------- -------- -------- -------- ................\n"
-" 100000a0 -------- -------- -------- -------- ................\n"
-" 100000b0 -------- -------- -------- -------- ................\n"
-" 100000c0 -------- -------- -------- -------- ................\n"
-" 100000d0 -------- -------- -------- -------- ................\n"
-" 100000e0 -------- -------- -------- -------- ................\n"
-" 100000f0 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+ ASSERT_STREQ("", tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -744,44 +652,7 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000f00 ---------------- ---------------- ................\n"
-" 0000000010000f10 ---------------- ---------------- ................\n"
-" 0000000010000f20 ---------------- ---------------- ................\n"
-" 0000000010000f30 ---------------- ---------------- ................\n"
-" 0000000010000f40 ---------------- ---------------- ................\n"
-" 0000000010000f50 ---------------- ---------------- ................\n"
-" 0000000010000f60 ---------------- ---------------- ................\n"
-" 0000000010000f70 ---------------- ---------------- ................\n"
-" 0000000010000f80 ---------------- ---------------- ................\n"
-" 0000000010000f90 ---------------- ---------------- ................\n"
-" 0000000010000fa0 ---------------- ---------------- ................\n"
-" 0000000010000fb0 ---------------- ---------------- ................\n"
-" 0000000010000fc0 ---------------- ---------------- ................\n"
-" 0000000010000fd0 ---------------- ---------------- ................\n"
-" 0000000010000fe0 ---------------- ---------------- ................\n"
-" 0000000010000ff0 ---------------- ---------------- ................\n";
-#else
-" 10000f00 -------- -------- -------- -------- ................\n"
-" 10000f10 -------- -------- -------- -------- ................\n"
-" 10000f20 -------- -------- -------- -------- ................\n"
-" 10000f30 -------- -------- -------- -------- ................\n"
-" 10000f40 -------- -------- -------- -------- ................\n"
-" 10000f50 -------- -------- -------- -------- ................\n"
-" 10000f60 -------- -------- -------- -------- ................\n"
-" 10000f70 -------- -------- -------- -------- ................\n"
-" 10000f80 -------- -------- -------- -------- ................\n"
-" 10000f90 -------- -------- -------- -------- ................\n"
-" 10000fa0 -------- -------- -------- -------- ................\n"
-" 10000fb0 -------- -------- -------- -------- ................\n"
-" 10000fc0 -------- -------- -------- -------- ................\n"
-" 10000fd0 -------- -------- -------- -------- ................\n"
-" 10000fe0 -------- -------- -------- -------- ................\n"
-" 10000ff0 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+ ASSERT_STREQ("", tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index aec8c60..b42d70c 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -359,13 +359,6 @@
ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
}
-TEST_F(TombstoneTest, dump_timestamp) {
- setenv("TZ", "UTC", 1);
- tzset();
- dump_timestamp(&log_, 0);
- ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
-}
-
class GwpAsanCrashDataTest : public GwpAsanCrashData {
public:
GwpAsanCrashDataTest(
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index ab65dd1..face02b 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -58,6 +58,7 @@
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
+#include "util.h"
#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
@@ -80,15 +81,6 @@
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
-static void dump_timestamp(log_t* log, time_t time) {
- struct tm tm;
- localtime_r(&time, &tm);
-
- char buf[strlen("1970-01-01 00:00:00+0830") + 1];
- strftime(buf, sizeof(buf), "%F %T%z", &tm);
- _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
-}
-
static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
unwindstack::Maps* maps) {
static constexpr uint64_t kMaxDifferenceBytes = 256;
@@ -507,10 +499,9 @@
// (although in this case the pid is redundant).
char timeBuf[32];
time_t sec = static_cast<time_t>(log_entry.entry.sec);
- struct tm tmBuf;
- struct tm* ptm;
- ptm = localtime_r(&sec, &tmBuf);
- strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+ tm tm;
+ localtime_r(&sec, &tm);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", &tm);
char* msg = log_entry.msg();
if (msg == nullptr) {
@@ -571,23 +562,20 @@
log.tfd = tombstone_fd;
log.amfd_data = nullptr;
- char thread_name[16];
- char process_name[128];
-
- read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
- read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
+ std::string thread_name = get_thread_name(tid);
+ std::string process_name = get_process_name(pid);
std::unique_ptr<unwindstack::Regs> regs(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
std::map<pid_t, ThreadInfo> threads;
- threads[gettid()] = ThreadInfo{
+ threads[tid] = ThreadInfo{
.registers = std::move(regs),
.uid = uid,
.tid = tid,
- .thread_name = thread_name,
+ .thread_name = thread_name.c_str(),
.pid = pid,
- .process_name = process_name,
+ .process_name = process_name.c_str(),
.siginfo = siginfo,
};
@@ -606,8 +594,8 @@
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
std::string* amfd_data) {
- // don't copy log messages to tombstone unless this is a dev device
- bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
+ // Don't copy log messages to tombstone unless this is a development device.
+ bool want_logs = GetBoolProperty("ro.debuggable", false);
log_t log;
log.current_tid = target_thread;
@@ -617,7 +605,7 @@
_LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_header_info(&log);
- dump_timestamp(&log, time(nullptr));
+ _LOG(&log, logtype::HEADER, "Timestamp: %s\n", get_timestamp().c_str());
auto it = threads.find(target_thread);
if (it == threads.end()) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index c8a3431..f43092c 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -129,24 +129,23 @@
#define MEMORY_BYTES_PER_LINE 16
void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
- // Align the address to sizeof(long) and start 32 bytes before the address.
- addr &= ~(sizeof(long) - 1);
+ // Align the address to the number of bytes per line to avoid confusing memory tag output if
+ // memory is tagged and we start from a misaligned address. Start 32 bytes before the address.
+ addr &= ~(MEMORY_BYTES_PER_LINE - 1);
if (addr >= 4128) {
addr -= 32;
}
- // Don't bother if the address looks too low, or looks too high.
- if (addr < 4096 ||
-#if defined(__LP64__)
- addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
-#else
- addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
-#endif
+ // We don't want the address tag to appear in the addresses in the memory dump.
+ addr = untag_address(addr);
+
+ // Don't bother if the address would overflow, taking tag bits into account. Note that
+ // untag_address truncates to 32 bits on 32-bit platforms as a side effect of returning a
+ // uintptr_t, so this also checks for 32-bit overflow.
+ if (untag_address(addr + MEMORY_BYTES_TO_DUMP - 1) < addr) {
return;
}
- _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
-
// Dump 256 bytes
uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
memset(data, 0, MEMORY_BYTES_TO_DUMP);
@@ -187,6 +186,15 @@
}
}
+ // If we were unable to read anything, it probably means that the register doesn't contain a
+ // valid pointer. In that case, skip the output for this register entirely rather than emitting 16
+ // lines of dashes.
+ if (bytes == 0) {
+ return;
+ }
+
+ _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
+
// Dump the code around memory as:
// addr contents ascii
// 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
@@ -197,8 +205,13 @@
size_t current = 0;
size_t total_bytes = start + bytes;
for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
+ uint64_t tagged_addr = addr;
+ long tag = memory->ReadTag(addr);
+ if (tag >= 0) {
+ tagged_addr |= static_cast<uint64_t>(tag) << 56;
+ }
std::string logline;
- android::base::StringAppendF(&logline, " %" PRIPTR, addr);
+ android::base::StringAppendF(&logline, " %" PRIPTR, tagged_addr);
addr += MEMORY_BYTES_PER_LINE;
std::string ascii;
@@ -226,23 +239,6 @@
}
}
-void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
- unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
- if (fd != -1) {
- int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
- if (rc != -1) {
- buf[rc] = '\0';
-
- // Trim trailing newlines.
- if (rc > 0 && buf[rc - 1] == '\n') {
- buf[rc - 1] = '\0';
- }
- return;
- }
- }
- strcpy(buf, default_value);
-}
-
void drop_capabilities() {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index a37b3b9..9d09210 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -17,6 +17,7 @@
#include "util.h"
#include <sys/socket.h>
+#include <time.h>
#include <string>
#include <utility>
@@ -38,3 +39,19 @@
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result);
return android::base::Trim(result);
}
+
+std::string get_timestamp() {
+ timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ tm tm;
+ localtime_r(&ts.tv_sec, &tm);
+
+ char buf[strlen("1970-01-01 00:00:00.123456789+0830") + 1];
+ char* s = buf;
+ size_t sz = sizeof(buf), n;
+ n = strftime(s, sz, "%F %H:%M", &tm), s += n, sz -= n;
+ n = snprintf(s, sz, ":%02d.%09ld", tm.tm_sec, ts.tv_nsec), s += n, sz -= n;
+ n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
+ return buf;
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index e964423..07e7e99 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -23,3 +23,5 @@
std::string get_process_name(pid_t pid);
std::string get_thread_name(pid_t tid);
+
+std::string get_timestamp();
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index cb1d354..406e8b8 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $i -eq $COMP_CWORD ]]; then
- partitions="boot bootloader dtbo modem odm oem product radio recovery system vbmeta vendor"
+ partitions="boot bootloader dtbo modem odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
else
_fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 86cf30d..d33c987 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -145,6 +145,7 @@
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, ImageType::BootCritical },
{ "dts", "dt.img", "dt.sig", "dts", true, ImageType::BootCritical },
{ "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal },
+ { "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
@@ -166,6 +167,10 @@
"vendor_boot.img", "vendor_boot.sig",
"vendor_boot",
true, ImageType::BootCritical },
+ { "vendor_dlkm",
+ "vendor_dlkm.img", "vendor_dlkm.sig",
+ "vendor_dlkm",
+ true, ImageType::Normal },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index e7f785b..34ab32c 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -1286,7 +1286,7 @@
ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
- // Sanity check of hash
+ // Validity check of hash
EXPECT_NE(hash_before, hash_buf)
<< "Writing a random buffer to 'userdata' had the same hash as after erasing it";
SetLockState(true); // Lock the device
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index cd64599..ac784b2 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -149,6 +149,14 @@
darwin: {
enabled: false,
},
+ vendor: {
+ cflags: [
+ // Skipping entries in fstab should only be done in a system
+ // process as the config file is in /system_ext.
+ // Remove the op from the vendor variant.
+ "-DNO_SKIP_MOUNT",
+ ],
+ },
},
export_include_dirs: ["include_fstab"],
header_libs: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 0ae5787..88bb234 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -607,7 +607,7 @@
LINFO << "Enabling ext4 metadata_csum on " << blk_device;
- // requires to give last_fsck_time to current to avoid insane time.
+ // Must give `-T now` to prevent last_fsck_time from growing too large,
// otherwise, tune2fs won't enable metadata_csum.
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
"-T", "now", blk_device.c_str()};
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f333a85..54102ec 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -696,7 +696,9 @@
TransformFstabForDsu(fstab, Split(lp_names, ","));
}
+#ifndef NO_SKIP_MOUNT
SkipMountingPartitions(fstab);
+#endif
EnableMandatoryFlags(fstab);
return true;
@@ -726,11 +728,14 @@
return false;
}
+#ifndef NO_SKIP_MOUNT
SkipMountingPartitions(fstab);
+#endif
return true;
}
+#ifndef NO_SKIP_MOUNT
// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
@@ -762,6 +767,7 @@
return true;
}
+#endif
// Loads the fstab file and combines with fstab entries passed in from device tree.
bool ReadDefaultFstab(Fstab* fstab) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 1fa1aa1..a7704de 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -809,15 +809,26 @@
entry.fs_type = mnt_type;
if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
- entry.flags = MS_NOATIME;
- if (readonly) {
- entry.flags |= MS_RDONLY;
- } else {
+ entry.flags = MS_NOATIME | MS_RDONLY;
+ auto mounted = true;
+ if (!readonly) {
+ if (entry.fs_type == "ext4") {
+ // check if ext4 de-dupe
+ entry.flags |= MS_RDONLY;
+ auto save_errno = errno;
+ mounted = fs_mgr_do_mount_one(entry) == 0;
+ if (mounted) {
+ mounted = !fs_mgr_has_shared_blocks(entry.mount_point, entry.blk_device);
+ fs_mgr_overlayfs_umount_scratch();
+ }
+ errno = save_errno;
+ }
+ entry.flags &= ~MS_RDONLY;
fs_mgr_set_blk_ro(device_path, false);
}
entry.fs_mgr_flags.check = true;
auto save_errno = errno;
- auto mounted = fs_mgr_do_mount_one(entry) == 0;
+ if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
if ((entry.fs_type == "f2fs") && ext4) {
entry.fs_type = "ext4";
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 58241b3..e425284 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -42,6 +42,7 @@
enabled: false,
},
},
+ ramdisk_available: true,
}
filegroup {
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 250cb82..8788b5a 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -280,5 +280,12 @@
return android::base::Join(argv, " ");
}
+std::string DmTargetUser::GetParameterString() const {
+ std::vector<std::string> argv;
+ argv.push_back(std::to_string(start()));
+ argv.push_back(std::to_string(size()));
+ return android::base::Join(argv, " ");
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index f986cfe..57e3884 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -309,6 +309,14 @@
bool is_hw_wrapped_ = false;
};
+class DmTargetUser final : public DmTarget {
+ public:
+ DmTargetUser(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+ std::string name() const override { return "user"; }
+ std::string GetParameterString() const override;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 4dd4bcc..621031a 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -45,7 +45,7 @@
using namespace android::dm;
-// We cap the maximum number of extents as a sanity measure.
+// We cap the maximum number of extents as a robustness measure.
static constexpr uint32_t kMaxExtents = 50000;
// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 12c7397..36bb3df 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -266,7 +266,7 @@
cursor_file_pos_ += bytes_to_write;
}
- // If we've reached the end of the current file, close it for sanity.
+ // If we've reached the end of the current file, close it.
if (cursor_file_pos_ == file->size()) {
cursor_fd_ = {};
}
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index bbb0510..c189855 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -139,8 +139,7 @@
}
*bdev_name = ::android::base::Basename(sysfs_bdev);
- // Paranoid sanity check to make sure we just didn't get the
- // input in return as-is.
+ // Check that the symlink doesn't point to itself.
if (sysfs_bdev == *bdev_name) {
LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
return false;
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index c192bf5..46072bb 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -52,16 +52,16 @@
partition, offset, num_bytes, buffer, out_num_read);
}
-static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
- size_t rollback_index_location ATTRIBUTE_UNUSED,
- uint64_t* out_rollback_index) {
+static AvbIOResult no_op_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
+ size_t rollback_index_location ATTRIBUTE_UNUSED,
+ uint64_t* out_rollback_index) {
// rollback_index has been checked in bootloader phase.
// In user-space, returns the smallest value 0 to pass the check.
*out_rollback_index = 0;
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_validate_vbmeta_public_key(
+static AvbIOResult no_op_validate_vbmeta_public_key(
AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
size_t public_key_length ATTRIBUTE_UNUSED,
const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
@@ -76,8 +76,8 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
- bool* out_is_unlocked) {
+static AvbIOResult no_op_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
+ bool* out_is_unlocked) {
// The function is for bootloader to update the value into
// androidboot.vbmeta.device_state in kernel cmdline.
// In user-space, returns true as we don't need to update it anymore.
@@ -85,9 +85,9 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- char* guid_buf, size_t guid_buf_size) {
+static AvbIOResult no_op_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ char* guid_buf, size_t guid_buf_size) {
// The function is for bootloader to set the correct UUID
// for a given partition in kernel cmdline.
// In user-space, returns a faking one as we don't need to update
@@ -96,9 +96,9 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- uint64_t* out_size_num_byte) {
+static AvbIOResult no_op_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ uint64_t* out_size_num_byte) {
// The function is for bootloader to load entire content of AVB HASH partitions.
// In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
*out_size_num_byte = 0;
@@ -123,15 +123,15 @@
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
- // user-space so we set them as dummy operations. Also zero the entire
+ // user-space so we set them as no-op operations. Also zero the entire
// struct so operations added in the future will be set to NULL.
memset(&avb_ops_, 0, sizeof(AvbOps));
avb_ops_.read_from_partition = read_from_partition;
- avb_ops_.read_rollback_index = dummy_read_rollback_index;
- avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
- avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
- avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
- avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
+ avb_ops_.read_rollback_index = no_op_read_rollback_index;
+ avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key;
+ avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked;
+ avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition;
+ avb_ops_.get_size_of_partition = no_op_get_size_of_partition;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 5d504ab..49333a1 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -226,7 +226,7 @@
return nullptr;
}
- // Sanity check here because we have to use vbmeta_images_[0] below.
+ // Validity check here because we have to use vbmeta_images_[0] below.
if (avb_handle->vbmeta_images_.size() < 1) {
LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded";
return nullptr;
@@ -405,11 +405,11 @@
// - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).
// Might occur in either the top-level vbmeta or a chained vbmeta.
// - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).
- // Could only occur in a chained vbmeta. Because we have *dummy* operations in
+ // Could only occur in a chained vbmeta. Because we have *no-op* operations in
// FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate
// the public key of the top-level vbmeta always pass in userspace here.
//
- // The following verify result won't happen, because the *dummy* operation
+ // The following verify result won't happen, because the *no-op* operation
// avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked
// vbmeta images, which should be caught in the bootloader stage, won't be detected here.
// - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index 5c388aa..a52a00d 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -222,7 +222,7 @@
base::FilePath test_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
- // Generates dummy files to list.
+ // Generates test files to list.
base::FilePath file_path_1 = test_dir.Append("1.txt");
ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
base::FilePath file_path_2 = test_dir.Append("2.txt");
@@ -253,7 +253,7 @@
base::FilePath test_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
- // Generates dummy files to list.
+ // Generates test files to list.
base::FilePath file_path_1 = test_dir.Append("1.txt");
ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
base::FilePath file_path_2 = test_dir.Append("2.txt");
@@ -281,7 +281,7 @@
base::FilePath tmp_dir;
ASSERT_TRUE(GetTempDir(&tmp_dir));
- // Generates dummy files to list.
+ // Generates test files to list.
base::FilePath no_such_dir = tmp_dir.Append("not_such_dir");
auto fail = ListFiles(no_such_dir.value());
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index c37d70e..623293e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -31,6 +31,22 @@
namespace android {
namespace fs_mgr {
+std::ostream& operator<<(std::ostream& os, const Extent& extent) {
+ switch (extent.GetExtentType()) {
+ case ExtentType::kZero: {
+ os << "type: Zero";
+ break;
+ }
+ case ExtentType::kLinear: {
+ auto linear_extent = static_cast<const LinearExtent*>(&extent);
+ os << "type: Linear, physical sectors: " << linear_extent->physical_sector()
+ << ", end sectors: " << linear_extent->end_sector();
+ break;
+ }
+ }
+ return os;
+}
+
bool LinearExtent::AddTo(LpMetadata* out) const {
if (device_index_ >= out->block_devices.size()) {
LERROR << "Extent references unknown block device.";
@@ -41,6 +57,17 @@
return true;
}
+bool LinearExtent::operator==(const android::fs_mgr::Extent& other) const {
+ if (other.GetExtentType() != ExtentType::kLinear) {
+ return false;
+ }
+
+ auto other_ptr = static_cast<const LinearExtent*>(&other);
+ return num_sectors_ == other_ptr->num_sectors_ &&
+ physical_sector_ == other_ptr->physical_sector_ &&
+ device_index_ == other_ptr->device_index_;
+}
+
bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
if (device_index_ != other.device_index()) {
return false;
@@ -64,6 +91,10 @@
return true;
}
+bool ZeroExtent::operator==(const android::fs_mgr::Extent& other) const {
+ return other.GetExtentType() == ExtentType::kZero && num_sectors_ == other.num_sectors();
+}
+
Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
: name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
@@ -205,11 +236,18 @@
}
}
- if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false) &&
- !always_keep_source_slot) {
- if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
- target_slot_number)) {
- return nullptr;
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ if (always_keep_source_slot) {
+ // always_keep_source_slot implies the target build does not support snapshots.
+ // Clear unsupported attributes.
+ SetMetadataHeaderV0(metadata.get());
+ } else {
+ // !always_keep_source_slot implies the target build supports snapshots. Do snapshot
+ // updates.
+ if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
+ target_slot_number)) {
+ return nullptr;
+ }
}
}
@@ -511,7 +549,7 @@
return partitions_.back().get();
}
-Partition* MetadataBuilder::FindPartition(std::string_view name) {
+Partition* MetadataBuilder::FindPartition(std::string_view name) const {
for (const auto& partition : partitions_) {
if (partition->name() == name) {
return partition.get();
@@ -520,7 +558,7 @@
return nullptr;
}
-PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) const {
for (const auto& group : groups_) {
if (group->name() == group_name) {
return group.get();
@@ -1263,5 +1301,50 @@
return geometry_.logical_block_size;
}
+bool MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+ const MetadataBuilder& source_metadata, uint32_t source_slot_number,
+ const MetadataBuilder& target_metadata, uint32_t target_slot_number,
+ const std::vector<std::string>& partitions) {
+ for (const auto& base_name : partitions) {
+ // Find the partition in metadata with the slot suffix.
+ auto target_partition_name = base_name + SlotSuffixForSlotNumber(target_slot_number);
+ const auto target_partition = target_metadata.FindPartition(target_partition_name);
+ if (!target_partition) {
+ LERROR << "Failed to find partition " << target_partition_name << " in metadata slot "
+ << target_slot_number;
+ return false;
+ }
+
+ auto source_partition_name = base_name + SlotSuffixForSlotNumber(source_slot_number);
+ const auto source_partition = source_metadata.FindPartition(source_partition_name);
+ if (!source_partition) {
+ LERROR << "Failed to find partition " << source_partition << " in metadata slot "
+ << source_slot_number;
+ return false;
+ }
+
+ // We expect the partitions in the target metadata to have the identical extents as the
+ // one in the source metadata. Because they are copied in NewForUpdate.
+ if (target_partition->extents().size() != source_partition->extents().size()) {
+ LERROR << "Extents count mismatch for partition " << base_name << " target slot has "
+ << target_partition->extents().size() << ", source slot has "
+ << source_partition->extents().size();
+ return false;
+ }
+
+ for (size_t i = 0; i < target_partition->extents().size(); i++) {
+ const auto& src_extent = *source_partition->extents()[i];
+ const auto& tgt_extent = *target_partition->extents()[i];
+ if (tgt_extent != src_extent) {
+ LERROR << "Extents " << i << " is different for partition " << base_name;
+ LERROR << "tgt extent " << tgt_extent << "; src extent " << src_extent;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 1a3250a..e4b617a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -234,7 +234,7 @@
EXPECT_EQ(lba, aligned_lba);
}
- // Sanity check one extent.
+ // Check one extent.
EXPECT_EQ(exported->extents.back().target_data, 3072);
}
@@ -947,9 +947,10 @@
}
static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
- const std::string& partition_name, uint64_t num_sectors,
- uint64_t start_sector, std::vector<Interval>* intervals) {
- Partition* p = builder->AddPartition(partition_name, "group", 0);
+ const std::string& partition_name, const std::string& group_name,
+ uint64_t num_sectors, uint64_t start_sector,
+ std::vector<Interval>* intervals = nullptr) {
+ Partition* p = builder->AddPartition(partition_name, group_name, 0);
ASSERT_NE(p, nullptr);
ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
ASSERT_EQ(p->extents().size(), 1);
@@ -977,17 +978,17 @@
ASSERT_TRUE(builder->AddGroup("group", 0));
std::vector<Interval> old_intervals;
- AddPartition(builder, "system", 10229008, 2048, &old_intervals);
- AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
- AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
- AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
- AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
- AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
- AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
- AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+ AddPartition(builder, "system", "group", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", "group", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", "group", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", "group", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", "group", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", "group", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", "group", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", "group", 888, 13477888, &old_intervals);
// Don't track the first vendor interval, since it will get extended.
- AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+ AddPartition(builder, "vendor", "group", 2477920, 10231808, nullptr);
std::vector<Interval> new_intervals;
@@ -1066,3 +1067,30 @@
ASSERT_NE(p, nullptr);
ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
}
+
+TEST_F(BuilderTest, VerifyExtent) {
+ auto source_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
+ ASSERT_NE(source_builder, nullptr);
+ ASSERT_TRUE(source_builder->AddGroup("test_group_a", 40960));
+ ASSERT_TRUE(source_builder->AddGroup("test_group_b", 40960));
+ AddPartition(source_builder, "system_a", "test_group_a", 8192, 2048);
+ AddPartition(source_builder, "vendor_a", "test_group_a", 10240, 10240);
+ AddPartition(source_builder, "system_b", "test_group_b", 8192, 20480);
+
+ auto target_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
+ ASSERT_NE(target_builder, nullptr);
+ ASSERT_TRUE(target_builder->AddGroup("test_group_b", 40960));
+ AddPartition(target_builder, "system_b", "test_group_b", 8192, 2048);
+ AddPartition(target_builder, "vendor_b", "test_group_b", 10240, 10240);
+
+ ASSERT_TRUE(MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+ *source_builder, 0, *target_builder, 1, std::vector<std::string>{"system", "vendor"}));
+
+ target_builder->RemovePartition("vendor_b");
+ ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
+ *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
+
+ AddPartition(target_builder, "vendor_b", "test_group_b", 1000, 10240);
+ ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
+ *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 6af9d94..236fd8d 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -47,7 +47,7 @@
BlockDeviceInfo device_info;
ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
- // Sanity check that the device doesn't give us some weird inefficient
+ // Check that the device doesn't give us some weird inefficient
// alignment.
EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 89a47b1..54f31bc 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -42,6 +42,11 @@
// Name of the default group in a metadata.
static constexpr std::string_view kDefaultGroup = "default";
+enum class ExtentType {
+ kZero,
+ kLinear,
+};
+
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
@@ -50,6 +55,10 @@
virtual bool AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
+ virtual ExtentType GetExtentType() const = 0;
+
+ virtual bool operator==(const Extent& other) const = 0;
+ virtual bool operator!=(const Extent& other) const { return !(*this == other); }
uint64_t num_sectors() const { return num_sectors_; }
void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
@@ -58,6 +67,8 @@
uint64_t num_sectors_;
};
+std::ostream& operator<<(std::ostream& os, const Extent& extent);
+
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
@@ -66,6 +77,9 @@
bool AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
+ ExtentType GetExtentType() const override { return ExtentType::kLinear; }
+
+ bool operator==(const Extent& other) const override;
uint64_t physical_sector() const { return physical_sector_; }
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
@@ -87,6 +101,9 @@
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
bool AddTo(LpMetadata* out) const override;
+ ExtentType GetExtentType() const override { return ExtentType::kZero; }
+
+ bool operator==(const Extent& other) const override;
};
class PartitionGroup final {
@@ -208,8 +225,10 @@
// metadata may not have the target slot's devices listed yet, in which
// case, it is automatically upgraded to include all available block
// devices.
- // If |always_keep_source_slot| is set, on a Virtual A/B device, source slot
- // partitions are kept. This is useful when applying a downgrade package.
+ // If |always_keep_source_slot| is set, on a Virtual A/B device
+ // - source slot partitions are kept.
+ // - UPDATED flag is cleared.
+ // This is useful when applying a downgrade package.
static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
const std::string& source_partition,
uint32_t source_slot_number,
@@ -241,6 +260,14 @@
return New(device_info, metadata_max_size, metadata_slot_count);
}
+ // Verifies that the given partitions in the metadata have the same extents as the source
+ // metadata.
+ static bool VerifyExtentsAgainstSourceMetadata(const MetadataBuilder& source_metadata,
+ uint32_t source_slot_number,
+ const MetadataBuilder& target_metadata,
+ uint32_t target_slot_number,
+ const std::vector<std::string>& partitions);
+
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@@ -265,10 +292,10 @@
void RemovePartition(std::string_view name);
// Find a partition by name. If no partition is found, nullptr is returned.
- Partition* FindPartition(std::string_view name);
+ Partition* FindPartition(std::string_view name) const;
// Find a group by name. If no group is found, nullptr is returned.
- PartitionGroup* FindGroup(std::string_view name);
+ PartitionGroup* FindGroup(std::string_view name) const;
// Add a predetermined extent to a partition.
bool AddLinearExtent(Partition* partition, const std::string& block_device,
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 1d4db85..3d3dde6 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -49,7 +49,7 @@
// Dynamic System Update is installed to an sdcard, which won't be in
// the boot device list.
//
- // We whitelist because most devices in /dev/block are not valid for
+ // mmcblk* is allowed because most devices in /dev/block are not valid for
// storing fiemaps.
if (android::base::StartsWith(path, "mmcblk")) {
return "/dev/block/" + path;
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index e6fd9f7..24ccc0f 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -174,7 +174,7 @@
return false;
}
- // Do basic sanity checks before computing the checksum.
+ // Do basic validity checks before computing the checksum.
if (header.magic != LP_METADATA_HEADER_MAGIC) {
LERROR << "Logical partition metadata has invalid magic value.";
return false;
@@ -255,7 +255,7 @@
LpMetadataHeader& header = metadata->header;
- // Sanity check the table size.
+ // Check the table size.
if (header.tables_size > geometry.metadata_max_size) {
LERROR << "Invalid partition metadata header table size.";
return nullptr;
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 48c5c83..d8e171b 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
#include <stdint.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -29,6 +30,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
@@ -285,5 +287,42 @@
return true;
}
+inline std::string ToHexString(uint64_t value) {
+ return android::base::StringPrintf("0x%" PRIx64, value);
+}
+
+void SetMetadataHeaderV0(LpMetadata* metadata) {
+ if (metadata->header.minor_version <= LP_METADATA_MINOR_VERSION_MIN) {
+ return;
+ }
+ LINFO << "Forcefully setting metadata header version " << LP_METADATA_MAJOR_VERSION << "."
+ << metadata->header.minor_version << " to " << LP_METADATA_MAJOR_VERSION << "."
+ << LP_METADATA_MINOR_VERSION_MIN;
+ metadata->header.minor_version = LP_METADATA_MINOR_VERSION_MIN;
+ metadata->header.header_size = sizeof(LpMetadataHeaderV1_0);
+
+ // Retrofit Virtual A/B devices should have version 10.1, so flags shouldn't be set.
+ // Warn if this is the case, but zero it out anyways.
+ if (metadata->header.flags) {
+ LWARN << "Zeroing unexpected flags: " << ToHexString(metadata->header.flags);
+ }
+
+ // Zero out all fields beyond LpMetadataHeaderV0.
+ static_assert(sizeof(metadata->header) > sizeof(LpMetadataHeaderV1_0));
+ memset(reinterpret_cast<uint8_t*>(&metadata->header) + sizeof(LpMetadataHeaderV1_0), 0,
+ sizeof(metadata->header) - sizeof(LpMetadataHeaderV1_0));
+
+ // Clear partition attributes unknown to V0.
+ // On retrofit Virtual A/B devices, UPDATED flag may be set, so only log info here.
+ for (auto& partition : metadata->partitions) {
+ if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK_V0) {
+ LINFO << "Clearing " << GetPartitionName(partition)
+ << " partition attribute: " << ToHexString(partition.attributes);
+ }
+
+ partition.attributes &= LP_PARTITION_ATTRIBUTE_MASK_V0;
+ }
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index c4fe3ed..aa3a6a0 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -103,6 +103,10 @@
bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
uint32_t target_slot_number);
+// Forcefully set metadata header version to 1.0, clearing any incompatible flags and attributes
+// so that when downgrading to a build with liblp V0, the device still boots.
+void SetMetadataHeaderV0(LpMetadata* metadata);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 8bf1ee9..2708efa 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -81,8 +81,8 @@
return header_blob + tables;
}
-// Perform sanity checks so we don't accidentally overwrite valid metadata
-// with potentially invalid metadata, or random partition data with metadata.
+// Perform checks so we don't accidentally overwrite valid metadata with
+// potentially invalid metadata, or random partition data with metadata.
static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
const LpMetadata& metadata, const std::string& slot_suffix,
std::string* blob) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 95301ff..eaef180 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -310,3 +310,36 @@
auto_gen_config: true,
require_root: true,
}
+
+cc_defaults {
+ name: "snapuserd_defaults",
+ srcs: [
+ "snapuserd.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror"
+ ],
+
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libdm",
+ ],
+}
+
+cc_binary {
+ name: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+}
+
+cc_binary {
+ name: "snapuserd_ramdisk",
+ stem: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+
+ ramdisk: true,
+ static_executable: true,
+ system_shared_libs: [],
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 3c2c776..a4a3150 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -553,9 +553,8 @@
// This should only be called in recovery.
bool UnmapAllPartitions();
- // Sanity check no snapshot overflows. Note that this returns false negatives if the snapshot
- // overflows, then is remapped and not written afterwards. Hence, the function may only serve
- // as a sanity check.
+ // Check no snapshot overflows. Note that this returns false negatives if the snapshot
+ // overflows, then is remapped and not written afterwards.
bool EnsureNoOverflowSnapshot(LockedFile* lock);
enum class Slot { Unknown, Source, Target };
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 55214f5..b49f99e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -300,9 +300,9 @@
LOG(ERROR) << "SnapshotStatus has no name.";
return false;
}
- // Sanity check these sizes. Like liblp, we guarantee the partition size
- // is respected, which means it has to be sector-aligned. (This guarantee
- // is useful for locating avb footers correctly). The COW file size, however,
+ // Check these sizes. Like liblp, we guarantee the partition size is
+ // respected, which means it has to be sector-aligned. (This guarantee is
+ // useful for locating avb footers correctly). The COW file size, however,
// can be arbitrarily larger than specified, so we can safely round it up.
if (status->device_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << status->name()
@@ -351,7 +351,6 @@
}
// The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
- // Sanity check this.
if (status.cow_file_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
<< status.cow_file_size();
@@ -1846,7 +1845,7 @@
PLOG(ERROR) << "Open failed: " << file;
return nullptr;
}
- if (lock_flags != 0 && flock(fd, lock_flags) < 0) {
+ if (lock_flags != 0 && TEMP_FAILURE_RETRY(flock(fd, lock_flags)) < 0) {
PLOG(ERROR) << "Acquire flock failed: " << file;
return nullptr;
}
@@ -1857,7 +1856,7 @@
}
SnapshotManager::LockedFile::~LockedFile() {
- if (flock(fd_, LOCK_UN) < 0) {
+ if (TEMP_FAILURE_RETRY(flock(fd_, LOCK_UN)) < 0) {
PLOG(ERROR) << "Failed to unlock file: " << path_;
}
}
@@ -2520,7 +2519,19 @@
LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
}
- return AutoUnmountDevice::New(device_->GetMetadataDir());
+ auto ret = AutoUnmountDevice::New(device_->GetMetadataDir());
+ if (ret == nullptr) return nullptr;
+
+ // In rescue mode, it is possible to erase and format metadata, but /metadata/ota is not
+ // created to execute snapshot updates. Hence, subsequent calls is likely to fail because
+ // Lock*() fails. By failing early and returning nullptr here, update_engine_sideload can
+ // treat this case as if /metadata is not mounted.
+ if (!LockShared()) {
+ LOG(WARNING) << "/metadata is mounted, but errors occur when acquiring a shared lock. "
+ "Subsequent calls to SnapshotManager will fail. Unmounting /metadata now.";
+ return nullptr;
+ }
+ return ret;
}
bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 5b145c3..aced3ed 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -141,7 +141,7 @@
const RecoveryCreateSnapshotDevicesArgs& args) {
std::unique_ptr<AutoDevice> device;
if (args.has_metadata_device_object()) {
- device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
+ device = std::make_unique<NoOpAutoDevice>(args.metadata_mounted());
}
return snapshot->RecoveryCreateSnapshotDevices(device);
}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index fa327b8..5319e69 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -35,9 +35,9 @@
class AutoMemBasedDir;
class SnapshotFuzzDeviceInfo;
-class DummyAutoDevice : public AutoDevice {
+class NoOpAutoDevice : public AutoDevice {
public:
- DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
+ NoOpAutoDevice(bool mounted) : AutoDevice(mounted ? "no_op" : "") {}
};
struct SnapshotTestModule {
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
index 051584c..12101a2 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
@@ -173,9 +173,9 @@
if (iter != groups_.end()) {
continue;
}
- // Update package metadata doesn't have this group. Before deleting it, sanity check that it
- // doesn't have any partitions left. Update metadata shouldn't assign any partitions to this
- // group, so all partitions that originally belong to this group should be moved by
+ // Update package metadata doesn't have this group. Before deleting it, check that it
+ // doesn't have any partitions left. Update metadata shouldn't assign any partitions to
+ // this group, so all partitions that originally belong to this group should be moved by
// MovePartitionsToDefault at this point.
auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);
if (!existing_partitions_in_group.empty()) {
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
new file mode 100644
index 0000000..a6ff4fd
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+using android::base::unique_fd;
+
+#define DM_USER_MAP_READ 0
+#define DM_USER_MAP_WRITE 1
+
+struct dm_user_message {
+ __u64 seq;
+ __u64 type;
+ __u64 flags;
+ __u64 sector;
+ __u64 len;
+ __u8 buf[];
+};
+
+using namespace android::dm;
+
+static int daemon_main(const std::string& device) {
+ unique_fd block_fd(open(device.c_str(), O_RDWR));
+ if (block_fd < 0) {
+ PLOG(ERROR) << "Unable to open " << device;
+ return 1;
+ }
+
+ unique_fd ctrl_fd(open("/dev/dm-user", O_RDWR));
+ if (ctrl_fd < 0) {
+ PLOG(ERROR) << "Unable to open /dev/dm-user";
+ return 1;
+ }
+
+ size_t buf_size = 1UL << 16;
+ auto buf = std::make_unique<char>(buf_size);
+
+ /* Just keeps pumping messages between userspace and the kernel. We won't
+ * actually be doing anything, but the sequence numbers line up so it'll at
+ * least make forward progress. */
+ while (true) {
+ struct dm_user_message* msg = (struct dm_user_message*)buf.get();
+
+ memset(buf.get(), 0, buf_size);
+
+ ssize_t readed = read(ctrl_fd.get(), buf.get(), buf_size);
+ if (readed < 0) {
+ PLOG(ERROR) << "Control read failed, trying with more space";
+ buf_size *= 2;
+ buf = std::make_unique<char>(buf_size);
+ continue;
+ }
+
+ LOG(DEBUG) << android::base::StringPrintf("read() from dm-user returned %d bytes:",
+ (int)readed);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->seq: 0x%016llx", msg->seq);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->type: 0x%016llx", msg->type);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->flags: 0x%016llx", msg->flags);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->sector: 0x%016llx", msg->sector);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->len: 0x%016llx", msg->len);
+
+ switch (msg->type) {
+ case DM_USER_MAP_READ: {
+ LOG(DEBUG) << android::base::StringPrintf(
+ "Responding to read of sector %lld with %lld bytes data", msg->sector,
+ msg->len);
+
+ if ((sizeof(*msg) + msg->len) > buf_size) {
+ auto old_buf = std::move(buf);
+ buf_size = sizeof(*msg) + msg->len;
+ buf = std::make_unique<char>(buf_size);
+ memcpy(buf.get(), old_buf.get(), sizeof(*msg));
+ msg = (struct dm_user_message*)buf.get();
+ }
+
+ if (lseek(block_fd.get(), msg->sector * 512, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed: " << device;
+ return 7;
+ }
+ if (!android::base::ReadFully(block_fd.get(), msg->buf, msg->len)) {
+ PLOG(ERROR) << "read failed: " << device;
+ return 7;
+ }
+
+ if (!android::base::WriteFully(ctrl_fd.get(), buf.get(), sizeof(*msg) + msg->len)) {
+ PLOG(ERROR) << "write control failed";
+ return 3;
+ }
+ break;
+ }
+
+ case DM_USER_MAP_WRITE:
+ abort();
+ break;
+ }
+
+ LOG(DEBUG) << "read() finished, next message";
+ }
+
+ return 0;
+}
+
+int main([[maybe_unused]] int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ daemon_main(argv[1]);
+ return 0;
+}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 82c4262..d56f7f2 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -15,13 +15,17 @@
adb remount tests
---color Dress output with highlighting colors
---help This help
---no-wait-screen Do not wait for display screen to settle
---print-time Report the test duration
---serial Specify device (must if multiple are present)
---wait-adb <duration> adb wait timeout
---wait-fastboot <duration> fastboot wait timeout
+-c --color Dress output with highlighting colors
+-h --help This help
+-D --no-wait-screen Do not wait for display screen to settle
+-t --print-time Report the test duration
+-s --serial Specify device (must if multiple are present)"
+if [ -n "`which timeout`" ]; then
+ USAGE="${USAGE}
+-a --wait-adb <duration> adb wait timeout
+-f --wait-fastboot <duration> fastboot wait timeout"
+fi
+USAGE="${USAGE}
Conditions:
- Must be a userdebug build.
@@ -46,10 +50,10 @@
ESCAPE="`echo | tr '\n' '\033'`"
# A _real_ embedded carriage return character
CR="`echo | tr '\n' '\r'`"
-GREEN="${ESCAPE}[38;5;40m"
-RED="${ESCAPE}[38;5;196m"
-ORANGE="${ESCAPE}[38;5;255:165:0m"
-BLUE="${ESCAPE}[35m"
+GREEN="${ESCAPE}[32m"
+RED="${ESCAPE}[31m"
+YELLOW="${ESCAPE}[33m"
+BLUE="${ESCAPE}[34m"
NORMAL="${ESCAPE}[0m"
TMPDIR=${TMPDIR:-/tmp}
print_time=false
@@ -72,7 +76,7 @@
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
- wc -l | grep '^1$' >/dev/null
+ wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null
fi
}
@@ -85,7 +89,7 @@
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
- wc -l | grep '^1$' >/dev/null
+ wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null
fi
}
@@ -100,7 +104,7 @@
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
return ${?}
fi
- if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
+ if echo "${list}" | wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null; then
echo "${list}" |
grep "[${SPACE}${TAB}]recovery\$" >/dev/null
return ${?}
@@ -143,7 +147,7 @@
adb logcat "${@}" </dev/null |
tr -d '\r' |
grep -v 'logd : logdr: UID=' |
- sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
+ sed -e '${ /------- beginning of kernel/d }' -e 's/^[0-1][0-9]-[0-3][0-9] //'
}
[ "USAGE: avc_check >/dev/stderr
@@ -160,7 +164,7 @@
if [ -z "${L}" ]; then
return
fi
- echo "${ORANGE}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
echo "${L}" | sed "s/^/${INDENT}/" >&2
}
@@ -284,7 +288,7 @@
local start=`date +%s`
local duration=
local ret
- if [ -n "${1}" ]; then
+ if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
duration=`format_duration ${1}`
echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
@@ -299,7 +303,7 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
fi
fi
local end=`date +%s`
@@ -359,18 +363,22 @@
echo "(In adb mode `adb_user`)"
else
echo "(USB stack borken for ${USB_ADDRESS})"
- USB_DEVICE=`usb_devnum`
- if [ -n "${USB_DEVICE}" ]; then
- echo "# lsusb -v -s ${USB_DEVICE#dev}"
- local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
- if [ -n "${D}" ]; then
- echo "${D}"
- else
- lsusb -v
+ if [ -n "`which usb_devnum`" ]; then
+ USB_DEVICE=`usb_devnum`
+ if [ -n "`which lsusb`" ]; then
+ if [ -n "${USB_DEVICE}" ]; then
+ echo "# lsusb -v -s ${USB_DEVICE#dev}"
+ local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
+ if [ -n "${D}" ]; then
+ echo "${D}"
+ else
+ lsusb -v
+ fi
+ else
+ echo "# lsusb -v (expected device missing)"
+ lsusb -v
+ fi
fi
- else
- echo "# lsusb -v (expected device missing)"
- lsusb -v
fi >&2
fi
}
@@ -382,7 +390,7 @@
local ret
# fastboot has no wait-for-device, but it does an automatic
# wait and requires (even a nonsensical) command to do so.
- if [ -n "${1}" ]; then
+ if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
@@ -398,7 +406,7 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi >&2
fi
return ${ret}
@@ -409,7 +417,7 @@
Returns: waits until the device has returned for recovery or optional timeout" ]
recovery_wait() {
local ret
- if [ -n "${1}" ]; then
+ if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
@@ -423,7 +431,7 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi >&2
fi
return ${ret}
@@ -732,6 +740,7 @@
grep -v \
-e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e "^\(ramdumpfs\) " \
-e " functionfs " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
@@ -753,13 +762,28 @@
## MAINLINE
##
-OPTIONS=`getopt --alternative --unquoted \
- --longoptions help,serial:,colour,color,no-colour,no-color \
- --longoptions wait-adb:,wait-fastboot: \
- --longoptions wait-screen,wait-display \
- --longoptions no-wait-screen,no-wait-display \
- --longoptions gtest_print_time,print-time \
- -- "?hs:" ${*}` ||
+HOSTOS=`uname`
+GETOPTS="--alternative --unquoted
+ --longoptions help,serial:,colour,color,no-colour,no-color
+ --longoptions wait-adb:,wait-fastboot:
+ --longoptions wait-screen,wait-display
+ --longoptions no-wait-screen,no-wait-display
+ --longoptions gtest_print_time,print-time
+ --"
+if [ "Darwin" = "${HOSTOS}" ]; then
+ GETOPTS=
+ USAGE="`echo \"${USAGE}\" |
+ sed 's/--color/ /g
+ 1s/--help/-h/
+ s/--help/ /g
+ s/--no-wait-screen/ /g
+ s/--print-time/ /g
+ 1s/--serial/-s/
+ s/--serial/ /g
+ s/--wait-adb/ /g
+ s/--wait-fastboot/ /g'`"
+fi
+OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:t" ${*}` ||
( echo "${USAGE}" >&2 ; false ) ||
die "getopt failure"
set -- ${OPTIONS}
@@ -775,26 +799,26 @@
export ANDROID_SERIAL=${2}
shift
;;
- --color | --colour)
+ -c | --color | --colour)
color=true
;;
- --no-color | --no-colour)
+ -C | --no-color | --no-colour)
color=false
;;
- --no-wait-display | --no-wait-screen)
+ -D | --no-wait-display | --no-wait-screen)
screen_wait=false
;;
- --wait-display | --wait-screen)
+ -d | --wait-display | --wait-screen)
screen_wait=true
;;
- --print-time | --gtest_print_time)
+ -t | --print-time | --gtest_print_time)
print_time=true
;;
- --wait-adb)
+ -a | --wait-adb)
ADB_WAIT=${2}
shift
;;
- --wait-fastboot)
+ -f | --wait-fastboot)
FASTBOOT_WAIT=${2}
shift
;;
@@ -815,7 +839,7 @@
if ! ${color}; then
GREEN=""
RED=""
- ORANGE=""
+ YELLOW=""
BLUE=""
NORMAL=""
fi
@@ -827,14 +851,14 @@
inFastboot && die "device in fastboot mode"
inRecovery && die "device in recovery mode"
if ! inAdb; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} device not in adb mode" >&2
adb_wait ${ADB_WAIT}
fi
inAdb || die "specified device not in adb mode"
isDebuggable || die "device not a debug build"
enforcing=true
if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
enforcing=false
fi
@@ -846,9 +870,13 @@
[ -n "${D}" ] || D=`get_property ro.boot.serialno`
[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
USB_SERIAL=
-[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
- grep usb |
- xargs -r grep -l ${ANDROID_SERIAL}`
+if [ -n "${ANDROID_SERIAL}" -a "Darwin" != "${HOSTOS}" ]; then
+ USB_SERIAL="`find /sys/devices -name serial | grep usb`"
+ if [ -n "${USB_SERIAL}" ]; then
+ USB_SERIAL=`echo "${USB_SERIAL}" |
+ xargs grep -l ${ANDROID_SERIAL}`
+ fi
+fi
USB_ADDRESS=
if [ -n "${USB_SERIAL}" ]; then
USB_ADDRESS=${USB_SERIAL%/serial}
@@ -860,13 +888,16 @@
BUILD_DESCRIPTION=`get_property ro.build.description`
[ -z "${BUILD_DESCRIPTION}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+KERNEL_VERSION="`adb_su cat /proc/version </dev/null 2>/dev/null`"
+[ -z "${KERNEL_VERSION}" ] ||
+ echo "${BLUE}[ INFO ]${NORMAL} ${KERNEL_VERSION}" >&2
ACTIVE_SLOT=`get_active_slot`
[ -z "${ACTIVE_SLOT}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
# Acquire list of system partitions
-PARTITIONS=`adb_su cat /vendor/etc/fstab* |
+PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
skip_administrative_mounts |
sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
sort -u |
@@ -903,9 +934,12 @@
done
# If reboot too soon after fresh flash, could trip device update failure logic
+if ${screen_wait}; then
+ echo "${YELLOW}[ WARNING ]${NORMAL} waiting for screen to come up. Consider --no-wait-screen option" >&2
+fi
if ! wait_for_screen && ${screen_wait}; then
screen_wait=false
- echo "${ORANGE}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
fi
# Can we test remount -R command?
@@ -954,7 +988,7 @@
adb_su remount -R system </dev/null
err=${?}
if [ "${err}" != 0 ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
T="-t ${T}"
else
# Rebooted, logcat will be meaningless, and last logcat will likely be clear
@@ -980,7 +1014,7 @@
adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
(
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
false
) ||
overlayfs_supported=false
@@ -989,7 +1023,7 @@
echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
case `adb_sh uname -r </dev/null` in
4.[456789].* | 4.[1-9][0-9]* | [56789].*)
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
overlayfs_supported=false
;;
*)
@@ -1011,14 +1045,14 @@
reboot=false
for d in ${OVERLAYFS_BACKING}; do
if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
adb_sh rm -rf /${d}/overlay </dev/null ||
die "/${d}/overlay wipe"
reboot=true
fi
done
if ${reboot}; then
- echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} rebooting before test" >&2
adb_reboot &&
adb_wait ${ADB_WAIT} ||
die "lost device after reboot after wipe `usb_status`"
@@ -1030,7 +1064,7 @@
D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
echo "${H}" &&
echo "${D}" &&
- echo "${ORANGE}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
overlayfs_needed=true
D=`adb_sh cat /proc/mounts </dev/null |
@@ -1083,7 +1117,7 @@
if [ X"${D}" != X"${H}" ]; then
echo "${H}"
if [ X"${D}" != X"${D##*setup failed}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
fi
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
@@ -1130,7 +1164,7 @@
elif ${rebooted}; then
echo "${GREEN}[ OK ]${NORMAL} verity already disabled" >&2
else
- echo "${ORANGE}[ WARNING ]${NORMAL} verity already disabled" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} verity already disabled" >&2
fi
echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
@@ -1160,7 +1194,7 @@
die -t ${T} "overlay takeover failed"
fi
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
if [ -z "${virtual_ab}" ]; then
scratch_partition=scratch
fi
@@ -1292,7 +1326,7 @@
fixup_from_recovery() {
inRecovery || return 1
- echo "${ORANGE}[ ERROR ]${NORMAL} Device in recovery" >&2
+ echo "${YELLOW}[ ERROR ]${NORMAL} Device in recovery" >&2
adb reboot </dev/null
adb_wait ${ADB_WAIT}
}
@@ -1312,7 +1346,7 @@
adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
skip_administrative_mounts |
grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
fi
@@ -1373,20 +1407,20 @@
is_userspace_fastboot=false
if ! ${is_bootloader_fastboot}; then
- echo "${ORANGE}[ WARNING ]${NORMAL} does not support fastboot, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} does not support fastboot, skipping"
elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} build tree not setup, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} build tree not setup, skipping"
elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} vendor image missing, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} vendor image missing, skipping"
elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} wrong vendor image, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} wrong vendor image, skipping"
elif [ -z "${ANDROID_HOST_OUT}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} please run lunch, skipping"
elif ! (
adb_cat /vendor/build.prop |
cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
) >/dev/null 2>/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
else
wait_for_screen
avc_check
@@ -1432,7 +1466,7 @@
fi
fastboot reboot ||
die "can not reboot out of fastboot"
- echo "${ORANGE}[ WARNING ]${NORMAL} adb after fastboot"
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb after fastboot"
adb_wait ${ADB_WAIT} ||
fixup_from_recovery ||
die "did not reboot after formatting ${scratch_partition} `usb_status`"
@@ -1449,8 +1483,8 @@
if ${is_userspace_fastboot}; then
die "overlay supposed to be minus /vendor takeover after flash vendor"
else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
fi
fi
B="`adb_cat /system/hello`"
@@ -1468,7 +1502,7 @@
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
vendor content after flash vendor
else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
--warning vendor content after flash vendor
fi
@@ -1489,7 +1523,7 @@
L=
D="${H%?Now reboot your device for settings to take effect*}"
if [ X"${H}" != X"${D}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
adb_reboot &&
adb_wait ${ADB_WAIT} &&
@@ -1547,7 +1581,7 @@
err=${?}
if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
then
- echo "${ORANGE}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
adb_reboot &&
adb_wait ${ADB_WAIT} &&
adb_root ||
@@ -1580,9 +1614,9 @@
if [ -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${YELLOW}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
else
- echo "${ORANGE}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
+ echo "${YELLOW}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
fi >&2
fastboot --set-active=${ACTIVE_SLOT}
fi
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 2738457..7a3d9a9 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -174,6 +174,8 @@
}
return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
cow_device, mode, chunk_size);
+ } else if (target_type == "user") {
+ return std::make_unique<DmTargetUser>(start_sector, num_sectors);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
diff --git a/init/Android.bp b/init/Android.bp
index edf9099..3f2cd07 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -130,6 +130,7 @@
"libpropertyinfoserializer",
"libpropertyinfoparser",
"libsnapshot_init",
+ "libxml2",
"lib_apex_manifest_proto_lite",
"update_metadata-protos",
],
@@ -164,6 +165,9 @@
"selinux_policy_version",
],
srcs: init_common_sources + init_device_sources,
+ generated_sources: [
+ "apex-info-list",
+ ],
whole_static_libs: [
"libcap",
"com.android.sysprop.apex",
@@ -178,6 +182,12 @@
target: {
recovery: {
cflags: ["-DRECOVERY"],
+ exclude_static_libs: [
+ "libxml2",
+ ],
+ exclude_generated_sources: [
+ "apex-info-list",
+ ],
exclude_shared_libs: [
"libbinder",
"libutils",
@@ -212,6 +222,9 @@
target: {
recovery: {
cflags: ["-DRECOVERY"],
+ exclude_static_libs: [
+ "libxml2",
+ ],
exclude_shared_libs: [
"libbinder",
"libutils",
diff --git a/init/README.md b/init/README.md
index 188f19b..c3b64f6 100644
--- a/init/README.md
+++ b/init/README.md
@@ -197,11 +197,14 @@
Currently defaults to root. (??? probably should default to nobody)
`interface <interface name> <instance name>`
-> Associates this service with a list of the HIDL services that it provides. The interface name
- must be a fully-qualified name and not a value name. For instance, this is used to allow
- hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
- be used multiple times.
- For example: interface vendor.foo.bar@1.0::IBaz default
+> Associates this service with a list of the AIDL or HIDL services that it provides. The interface
+ name must be a fully-qualified name and not a value name. For instance, this is used to allow
+ servicemanager or hwservicemanager to lazily start services. When multiple interfaces are served,
+ this tag should be used multiple times. An example of an entry for a HIDL
+ interface is `interface vendor.foo.bar@1.0::IBaz default`. For an AIDL interface, use
+ `interface aidl <instance name>`. The instance name for an AIDL interface is
+ whatever is registered with servicemanager, and these can be listed with `adb
+ shell dumpsys -l`.
`ioprio <class> <priority>`
> Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index b423f86..8db9793 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -37,7 +37,15 @@
}
bool BlockDevInitializer::InitDeviceMapper() {
- const std::string dm_path = "/devices/virtual/misc/device-mapper";
+ return InitMiscDevice("device-mapper");
+}
+
+bool BlockDevInitializer::InitDmUser() {
+ return InitMiscDevice("dm-user");
+}
+
+bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
+ const std::string dm_path = "/devices/virtual/misc/" + name;
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
@@ -49,13 +57,13 @@
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
- LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ LOG(INFO) << name << " device not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(dm_callback, 10s);
- LOG(INFO) << "Wait for device-mapper returned after " << t;
+ LOG(INFO) << "Wait for " << name << " returned after " << t;
}
if (!found) {
- LOG(ERROR) << "device-mapper device not found after polling timeout";
+ LOG(ERROR) << name << " device not found after polling timeout";
return false;
}
return true;
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index 0d4c6e9..b8dd3f1 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -27,12 +27,15 @@
BlockDevInitializer();
bool InitDeviceMapper();
+ bool InitDmUser();
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
+ bool InitMiscDevice(const std::string& name);
+
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
diff --git a/init/devices.cpp b/init/devices.cpp
index 9fbec64..53ca875 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -193,7 +193,8 @@
while (directory != "/" && directory != ".") {
std::string subsystem_link_path;
if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
- subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+ (subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
+ subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
// We need to remove the mount point that we added above before returning.
directory.erase(0, sysfs_mount_point_.size());
*platform_device_path = directory;
diff --git a/init/init.cpp b/init/init.cpp
index 631db8e..cb5bbba 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <fcntl.h>
+#include <paths.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
@@ -727,6 +728,12 @@
InitSecondStageLogging(argv);
LOG(INFO) << "init second stage started!";
+ // Update $PATH in the case the second stage init is newer than first stage init, where it is
+ // first set.
+ if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
+ PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
+ }
+
// Init should not crash because of a dependence on any other process, therefore we ignore
// SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
// is inherited across exec, but custom signal handlers are not. Since we do not want to
@@ -868,6 +875,8 @@
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
+ // Restore prio before main loop
+ setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 07b4724..fa65740 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,6 +17,7 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <gtest/gtest.h>
#include "action.h"
@@ -32,6 +33,8 @@
#include "service_parser.h"
#include "util.h"
+using android::base::GetIntProperty;
+
namespace android {
namespace init {
@@ -240,6 +243,10 @@
}
TEST(init, RejectsCriticalAndOneshotService) {
+ if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
+ GTEST_SKIP() << "Test only valid for devices launching with R or later";
+ }
+
std::string init_script =
R"init(
service A something
diff --git a/init/main.cpp b/init/main.cpp
index 38bc74b..23f5530 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -52,7 +52,8 @@
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
-
+ // Boost prio which will be restored later
+ setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index b9d5d67..59cc140 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,58 +27,34 @@
#include <android-base/properties.h>
#include <android-base/result.h>
#include <android-base/unique_fd.h>
-#include <apex_manifest.pb.h>
#include "util.h"
+#ifndef RECOVERY
+#define ACTIVATE_FLATTENED_APEX 1
+#endif
+
+#ifdef ACTIVATE_FLATTENED_APEX
+#include <apex_manifest.pb.h>
+#include <com_android_apex.h>
+#include <selinux/android.h>
+#endif // ACTIVATE_FLATTENED_APEX
+
namespace android {
namespace init {
namespace {
-static bool BindMount(const std::string& source, const std::string& mount_point,
- bool recursive = false) {
- unsigned long mountflags = MS_BIND;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+static bool BindMount(const std::string& source, const std::string& mount_point) {
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, MS_BIND | MS_REC, nullptr) == -1) {
PLOG(ERROR) << "Failed to bind mount " << source;
return false;
}
return true;
}
-static bool MakeShared(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_SHARED;
- if (recursive) {
- mountflags |= MS_REC;
- }
+static bool ChangeMount(const std::string& mount_point, unsigned long mountflags) {
if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to shared";
- return false;
- }
- return true;
-}
-
-static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_SLAVE;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to slave";
- return false;
- }
- return true;
-}
-
-static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_PRIVATE;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to private";
+ PLOG(ERROR) << "Failed to remount " << mount_point << " as " << std::hex << mountflags;
return false;
}
return true;
@@ -106,6 +82,8 @@
return updatable;
}
+#ifdef ACTIVATE_FLATTENED_APEX
+
static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
return ErrnoError() << "Could not create mount point " << mount_path;
@@ -116,7 +94,7 @@
return {};
}
-static Result<std::string> GetApexName(const std::string& apex_dir) {
+static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
const std::string manifest_path = apex_dir + "/apex_manifest.pb";
std::string content;
if (!android::base::ReadFileToString(manifest_path, &content)) {
@@ -126,11 +104,12 @@
if (!manifest.ParseFromString(content)) {
return Error() << "Can't parse manifest file: " << manifest_path;
}
- return manifest.name();
+ return manifest;
}
+template <typename Fn>
static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
- const std::string& to_dir) {
+ const std::string& to_dir, Fn on_activate) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
if (!dir) {
return {};
@@ -140,15 +119,16 @@
if (entry->d_name[0] == '.') continue;
if (entry->d_type == DT_DIR) {
const std::string apex_path = from_dir + "/" + entry->d_name;
- const auto apex_name = GetApexName(apex_path);
- if (!apex_name.ok()) {
- LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+ const auto apex_manifest = GetApexManifest(apex_path);
+ if (!apex_manifest.ok()) {
+ LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
continue;
}
- const std::string mount_path = to_dir + "/" + (*apex_name);
+ const std::string mount_path = to_dir + "/" + apex_manifest->name();
if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
return result;
}
+ on_activate(apex_path, *apex_manifest);
}
}
return {};
@@ -167,15 +147,37 @@
"/vendor/apex",
};
+ std::vector<com::android::apex::ApexInfo> apex_infos;
+ auto on_activate = [&](const std::string& apex_path,
+ const apex::proto::ApexManifest& apex_manifest) {
+ apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
+ apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true);
+ };
+
for (const auto& dir : kBuiltinDirsForApexes) {
- if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result.ok()) {
+ if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
LOG(ERROR) << result.error();
return false;
}
}
+
+ std::ostringstream oss;
+ com::android::apex::ApexInfoList apex_info_list(apex_infos);
+ com::android::apex::write(oss, apex_info_list);
+ const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
+ if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
+ PLOG(ERROR) << "Failed to write " << kApexInfoList;
+ return false;
+ }
+ if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
+ PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
+ }
+
return true;
}
+#endif // ACTIVATE_FLATTENED_APEX
+
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -190,17 +192,17 @@
// needed for /foo/bar, then we will make /foo/bar as a mount point (by
// bind-mounting by to itself) and set the propagation type of the mount
// point to private.
- if (!MakeShared("/", true /*recursive*/)) return false;
+ if (!ChangeMount("/", MS_SHARED | MS_REC)) return false;
// /apex is a private mountpoint to give different sets of APEXes for
// the bootstrap and default mount namespaces. The processes running with
// the bootstrap namespace get APEXes from the read-only partition.
- if (!(MakePrivate("/apex"))) return false;
+ if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
// /linkerconfig is a private mountpoint to give a different linker configuration
// based on the mount namespace. Subdirectory will be bind-mounted based on current mount
// namespace
- if (!(MakePrivate("/linkerconfig"))) return false;
+ if (!(ChangeMount("/linkerconfig", MS_PRIVATE))) return false;
// The two mount namespaces present challenges for scoped storage, because
// vold, which is responsible for most of the mounting, lives in the
@@ -231,15 +233,15 @@
if (!mkdir_recursive("/mnt/user", 0755)) return false;
if (!mkdir_recursive("/mnt/installer", 0755)) return false;
if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
- if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
- if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/installer"))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable"))) return false;
// First, make /mnt/installer and /mnt/androidwritable a slave bind mount
- if (!(MakeSlave("/mnt/installer"))) return false;
- if (!(MakeSlave("/mnt/androidwritable"))) return false;
+ if (!(ChangeMount("/mnt/installer", MS_SLAVE))) return false;
+ if (!(ChangeMount("/mnt/androidwritable", MS_SLAVE))) return false;
// Then, make it shared again - effectively creating a new peer group, that
// will be inherited by new mount namespaces.
- if (!(MakeShared("/mnt/installer"))) return false;
- if (!(MakeShared("/mnt/androidwritable"))) return false;
+ if (!(ChangeMount("/mnt/installer", MS_SHARED))) return false;
+ if (!(ChangeMount("/mnt/androidwritable", MS_SHARED))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
@@ -269,9 +271,9 @@
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
}
-
+#ifdef ACTIVATE_FLATTENED_APEX
success &= ActivateFlattenedApexesIfPossible();
-
+#endif
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 612854d..a1e0969 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -51,6 +51,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -74,6 +75,7 @@
using namespace std::literals;
using android::base::GetProperty;
+using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -630,9 +632,11 @@
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
- static constexpr const char* const kVendorPathPrefixes[2] = {
+ static constexpr const char* const kVendorPathPrefixes[4] = {
"/vendor",
"/odm",
+ "/vendor_dlkm",
+ "/odm_dlkm",
};
const char* context = kInitContext;
@@ -886,24 +890,62 @@
load_properties_from_file("/prop.default", nullptr, &properties);
}
+ // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
+ // Falling back to /<part>/defalt.prop and /<part>/build.prop only when legacy path has to
+ // be supported, which is controlled by the support_legacy_path_until argument.
+ const auto load_properties_from_partition = [&properties](const std::string& partition,
+ int support_legacy_path_until) {
+ auto path = "/" + partition + "/etc/build.prop";
+ if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+ return;
+ }
+ // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
+ // separate map. Then by comparing its value with legacy_version, we know that if the
+ // partition is old enough so that we need to respect the legacy paths.
+ std::map<std::string, std::string> temp;
+ auto legacy_path1 = "/" + partition + "/default.prop";
+ auto legacy_path2 = "/" + partition + "/build.prop";
+ load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
+ load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
+ bool support_legacy_path = false;
+ auto version_prop_name = "ro." + partition + ".build.version.sdk";
+ auto it = temp.find(version_prop_name);
+ if (it == temp.end()) {
+ // This is embarassing. Without the prop, we can't determine how old the partition is.
+ // Let's be conservative by assuming it is very very old.
+ support_legacy_path = true;
+ } else if (int value;
+ ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
+ support_legacy_path = true;
+ }
+ if (support_legacy_path) {
+ // We don't update temp into properties directly as it might skip any (future) logic
+ // for resolving duplicates implemented in load_properties_from_file. Instead, read
+ // the files again into the properties map.
+ load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
+ load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
+ } else {
+ LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
+ << "because " << version_prop_name << "(" << it->second << ") is newer "
+ << "than " << support_legacy_path_until;
+ }
+ };
+
+ // Order matters here. The more the partition is specific to a product, the higher its
+ // precedence is.
load_properties_from_file("/system/build.prop", nullptr, &properties);
- load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
-
- // TODO(b/117892318): uncomment the following condition when vendor.imgs for
- // aosp_* targets are all updated.
-// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
- load_properties_from_file("/vendor/default.prop", nullptr, &properties);
-// }
+ load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+ // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
+ // all updated.
+ // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+ load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+ // }
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+ load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
+ load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
+ load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
+ load_properties_from_partition("product", /* support_legacy_path_until */ 30);
- if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
- load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
- } else {
- load_properties_from_file("/odm/default.prop", nullptr, &properties);
- load_properties_from_file("/odm/build.prop", nullptr, &properties);
- }
-
- load_properties_from_file("/product/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
if (access(kDebugRamdiskProp, R_OK) == 0) {
@@ -952,7 +994,7 @@
&property_infos)) {
return;
}
- // Don't check for failure here, so we always have a sane list of properties.
+ // Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 05e632b..f2383d7 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -60,13 +60,14 @@
Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // Recursively remount / as slave like zygote does so unmounting and mounting /proc
- // doesn't interfere with the parent namespace's /proc mount. This will also
- // prevent any other mounts/unmounts initiated by the service from interfering
- // with the parent namespace but will still allow mount events from the parent
+ // Recursively remount / as MS_SLAVE like zygote does so that
+ // unmounting and mounting /proc doesn't interfere with the parent
+ // namespace's /proc mount. This will also prevent any other
+ // mounts/unmounts initiated by the service from interfering with the
+ // parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- return ErrnoError() << "Could not remount(/) recursively as slave";
+ return ErrnoError() << "Could not remount(/) recursively as MS_SLAVE";
}
// umount() then mount() /proc and/or /sys
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3dd538..9d4ea8c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,6 +18,8 @@
#include <fcntl.h>
#include <poll.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <unistd.h>
#include <android-base/file.h>
@@ -181,6 +183,8 @@
trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+ // Restore prio before main loop
+ setpriority(PRIO_PROCESS, 0, 0);
subcontext_process.MainLoop();
return 0;
}
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 7e543f2..66a3328 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -54,7 +54,7 @@
TEST_P(InitKillServicesTest, KillCriticalProcesses) {
ExpectKillingServiceRecovers(GetParam());
- // sanity check init is still responding
+ // Ensure that init is still responding
EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
EXPECT_TRUE(SetProperty("test.death.test", ""));
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index d8d9b36..7cd396a 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -95,20 +95,18 @@
fcntl(device_fd_, F_SETFL, O_NONBLOCK);
}
-bool UeventListener::ReadUevent(Uevent* uevent) const {
+ReadUeventResult UeventListener::ReadUevent(Uevent* uevent) const {
char msg[UEVENT_MSG_LEN + 2];
int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
if (n <= 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
PLOG(ERROR) << "Error reading from Uevent Fd";
}
- return false;
+ return ReadUeventResult::kFailed;
}
if (n >= UEVENT_MSG_LEN) {
LOG(ERROR) << "Uevent overflowed buffer, discarding";
- // Return true here even if we discard as we may have more uevents pending and we
- // want to keep processing them.
- return true;
+ return ReadUeventResult::kInvalid;
}
msg[n] = '\0';
@@ -116,7 +114,7 @@
ParseEvent(msg, uevent);
- return true;
+ return ReadUeventResult::kSuccess;
}
// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
@@ -137,7 +135,10 @@
close(fd);
Uevent uevent;
- while (ReadUevent(&uevent)) {
+ ReadUeventResult result;
+ while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {
+ // Skip processing the uevent if it is invalid.
+ if (result == ReadUeventResult::kInvalid) continue;
if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
}
}
@@ -212,7 +213,10 @@
// We're non-blocking, so if we receive a poll event keep processing until
// we have exhausted all uevent messages.
Uevent uevent;
- while (ReadUevent(&uevent)) {
+ ReadUeventResult result;
+ while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {
+ // Skip processing the uevent if it is invalid.
+ if (result == ReadUeventResult::kInvalid) continue;
if (callback(uevent) == ListenerAction::kStop) return;
}
}
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index aea094e..2772860 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -27,7 +27,7 @@
#include "uevent.h"
-#define UEVENT_MSG_LEN 2048
+#define UEVENT_MSG_LEN 8192
namespace android {
namespace init {
@@ -37,6 +37,12 @@
kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
};
+enum class ReadUeventResult {
+ kSuccess = 0, // Uevent was successfully read.
+ kFailed, // Uevent reading has failed.
+ kInvalid, // An Invalid Uevent was read (like say, the msg received is >= UEVENT_MSG_LEN).
+};
+
using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
class UeventListener {
@@ -50,7 +56,7 @@
const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
private:
- bool ReadUevent(Uevent* uevent) const;
+ ReadUeventResult ReadUevent(Uevent* uevent) const;
ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
android::base::unique_fd device_fd_;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 7514b61..54659c5 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -321,6 +321,8 @@
while (waitpid(-1, nullptr, WNOHANG) > 0) {
}
+ // Restore prio before main loop
+ setpriority(PRIO_PROCESS, 0, 0);
uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
for (auto& uevent_handler : uevent_handlers) {
uevent_handler->HandleUevent(uevent);
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index f4191b9..cc32b6d 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -1584,7 +1584,7 @@
// Verify the flag is set.
ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
- // Quick sanity checks.
+ // Quick basic checks of functionality.
uint64_t offset;
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 8c232f0..20cd659 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -122,7 +122,8 @@
return true;
}
- /* If its not a number, assume string, but check if its a sane string */
+ // Non-numeric should be a single ASCII character. Characters after the
+ // first are ignored.
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
vndk_version.c_str());
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5805a4d..b9fc82e 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -203,6 +203,7 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index b94d134..2fe877c 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -38,24 +38,24 @@
int (*netdDeleteTagData)(uint32_t, uid_t);
};
-int dummyTagSocket(int, uint32_t, uid_t) {
+int stubTagSocket(int, uint32_t, uid_t) {
return -EREMOTEIO;
}
-int dummyUntagSocket(int) {
+int stubUntagSocket(int) {
return -EREMOTEIO;
}
-int dummySetCounterSet(uint32_t, uid_t) {
+int stubSetCounterSet(uint32_t, uid_t) {
return -EREMOTEIO;
}
-int dummyDeleteTagData(uint32_t, uid_t) {
+int stubDeleteTagData(uint32_t, uid_t) {
return -EREMOTEIO;
}
netdHandler initHandler(void) {
- netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3497d63..2a0230f 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -20,6 +20,7 @@
extern "C" {
#endif
+/* Returns `1` if the device is debuggable or `0` if not. */
int __android_log_is_debuggable();
#ifdef __cplusplus
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index d3b72bc..de4c430 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -144,14 +144,6 @@
int __android_log_security_bswrite(int32_t tag, const char* payload);
int __android_log_security(); /* Device Owner is present */
-#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
-#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */
-#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */
-#define BOOL_DEFAULT_FLAG_PERSIST 0x2 /* <key>, persist.<key>, ro.<key> */
-#define BOOL_DEFAULT_FLAG_ENG 0x4 /* off for user */
-#define BOOL_DEFAULT_FLAG_SVELTE 0x8 /* off for low_ram */
-bool __android_logger_property_get_bool(const char* key, int flag);
-
#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform \
*/
#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 161fcf1..2e01101 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -85,7 +85,6 @@
__android_log_pmsg_file_read;
__android_log_pmsg_file_write;
__android_logger_get_buffer_size;
- __android_logger_property_get_bool;
android_openEventTagMap;
android_log_processBinaryLogBuffer;
android_log_processLogBuffer;
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f5e060c..9990137 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -294,33 +294,12 @@
}
int __android_log_is_debuggable() {
- static uint32_t serial;
- static struct cache_char tag_cache;
- static const char key[] = "ro.debuggable";
- int ret;
+ static int is_debuggable = [] {
+ char value[PROP_VALUE_MAX] = {};
+ return __system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1");
+ }();
- if (tag_cache.c) { /* ro property does not change after set */
- ret = tag_cache.c == '1';
- } else if (lock()) {
- struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
- refresh_cache(&temp_cache, key);
- ret = temp_cache.c == '1';
- } else {
- int change_detected = check_cache(&tag_cache.cache);
- uint32_t current_serial = __system_property_area_serial();
- if (current_serial != serial) {
- change_detected = 1;
- }
- if (change_detected) {
- refresh_cache(&tag_cache, key);
- serial = current_serial;
- }
- ret = tag_cache.c == '1';
-
- unlock();
- }
-
- return ret;
+ return is_debuggable;
}
/*
@@ -390,21 +369,6 @@
* need not guess our intentions.
*/
-/* Property helper */
-static bool check_flag(const char* prop, const char* flag) {
- const char* cp = strcasestr(prop, flag);
- if (!cp) {
- return false;
- }
- /* We only will document comma (,) */
- static const char sep[] = ",:;|+ \t\f";
- if ((cp != prop) && !strchr(sep, cp[-1])) {
- return false;
- }
- cp += strlen(flag);
- return !*cp || !!strchr(sep, *cp);
-}
-
/* cache structure */
struct cache_property {
struct cache cache;
@@ -422,88 +386,8 @@
__system_property_read(cache->cache.pinfo, 0, cache->property);
}
-/* get boolean with the logger twist that supports eng adjustments */
-bool __android_logger_property_get_bool(const char* key, int flag) {
- struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
- if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
- char newkey[strlen("persist.") + strlen(key) + 1];
- snprintf(newkey, sizeof(newkey), "ro.%s", key);
- refresh_cache_property(&property, newkey);
- property.cache.pinfo = NULL;
- property.cache.serial = 0xFFFFFFFF;
- snprintf(newkey, sizeof(newkey), "persist.%s", key);
- refresh_cache_property(&property, newkey);
- property.cache.pinfo = NULL;
- property.cache.serial = 0xFFFFFFFF;
- }
-
- refresh_cache_property(&property, key);
-
- if (check_flag(property.property, "true")) {
- return true;
- }
- if (check_flag(property.property, "false")) {
- return false;
- }
- if (property.property[0]) {
- flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
- }
- if (check_flag(property.property, "eng")) {
- flag |= BOOL_DEFAULT_FLAG_ENG;
- }
- /* this is really a "not" flag */
- if (check_flag(property.property, "svelte")) {
- flag |= BOOL_DEFAULT_FLAG_SVELTE;
- }
-
- /* Sanity Check */
- if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
- flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
- flag |= BOOL_DEFAULT_TRUE;
- }
-
- if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
- __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
- return false;
- }
- if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
- return false;
- }
-
- return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
-}
-
bool __android_logger_valid_buffer_size(unsigned long value) {
- static long pages, pagesize;
- unsigned long maximum;
-
- if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
- return false;
- }
-
- if (!pages) {
- pages = sysconf(_SC_PHYS_PAGES);
- }
- if (pages < 1) {
- return true;
- }
-
- if (!pagesize) {
- pagesize = sysconf(_SC_PAGESIZE);
- if (pagesize <= 1) {
- pagesize = PAGE_SIZE;
- }
- }
-
- /* maximum memory impact a somewhat arbitrary ~3% */
- pages = (pages + 31) / 32;
- maximum = pages * pagesize;
-
- if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
- return true;
- }
-
- return value <= maximum;
+ return LOG_BUFFER_MIN_SIZE <= value && value <= LOG_BUFFER_MAX_SIZE;
}
struct cache2_property_size {
@@ -603,9 +487,12 @@
default_size = do_cache2_property_size(&global);
if (!default_size) {
- default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
- ? LOG_BUFFER_MIN_SIZE /* 64K */
- : LOG_BUFFER_SIZE; /* 256K */
+ char value[PROP_VALUE_MAX] = {};
+ if (__system_property_get("ro.config.low_ram", value) == 0 || strcmp(value, "true") != 0) {
+ default_size = LOG_BUFFER_SIZE;
+ } else {
+ default_size = LOG_BUFFER_MIN_SIZE;
+ }
}
snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
@@ -642,4 +529,4 @@
return 1;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 2a6424b..a17d90c 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -97,6 +97,7 @@
cflags: ["-DNO_PSTORE"],
test_suites: [
"cts",
+ "device-tests",
"vts10",
],
}
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 3e09617..7acd363 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -20,6 +20,7 @@
#include <string>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/log.h> // minimal logging API
#include <gtest/gtest.h>
@@ -29,6 +30,8 @@
// Do not use anything in log/log_time.h despite side effects of the above.
#include <private/android_logger.h>
+using android::base::GetBoolProperty;
+
TEST(liblog, android_logger_get_) {
#ifdef __ANDROID__
// This test assumes the log buffers are filled with noise from
@@ -38,31 +41,27 @@
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
log_id_t id = static_cast<log_id_t>(i);
- const char* name = android_log_id_to_name(id);
- if (id != android_name_to_log_id(name)) {
- continue;
- }
- fprintf(stderr, "log buffer %s\r", name);
+ std::string name = android_log_id_to_name(id);
+ fprintf(stderr, "log buffer %s\r", name.c_str());
struct logger* logger;
EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
EXPECT_EQ(id, android_logger_get_id(logger));
ssize_t get_log_size = android_logger_get_log_size(logger);
/* security buffer is allowed to be denied */
- if (strcmp("security", name)) {
- EXPECT_LT(0, get_log_size);
+ if (name != "security") {
+ EXPECT_GT(get_log_size, 0);
// crash buffer is allowed to be empty, that is actually healthy!
- // kernel buffer is allowed to be empty on "user" builds
- // stats buffer is allowed to be empty TEMPORARILY.
- // TODO: remove stats buffer from here once we start to use it in
- // framework (b/68266385).
- EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
- !!((strcmp("crash", name) != 0) &&
- ((strcmp("kernel", name) != 0) ||
- __android_logger_property_get_bool(
- "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
- BOOL_DEFAULT_FLAG_SVELTE)) &&
- (strcmp("stats", name) != 0)),
- android_logger_get_log_readable_size(logger));
+ // stats buffer is no longer in use.
+ if (name == "crash" || name == "stats") {
+ continue;
+ }
+
+ // kernel buffer is empty if ro.logd.kernel is false
+ if (name == "kernel" && !GetBoolProperty("ro.logd.kernel", false)) {
+ continue;
+ }
+
+ EXPECT_LE(0, android_logger_get_log_readable_size(logger));
} else {
EXPECT_NE(0, get_log_size);
if (get_log_size < 0) {
@@ -71,7 +70,6 @@
EXPECT_LE(0, android_logger_get_log_readable_size(logger));
}
}
- EXPECT_LT(0, android_logger_get_log_version(logger));
}
android_logger_list_close(logger_list);
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 268496f..65371fa 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -23,6 +23,21 @@
export_include_dirs: ["include"],
}
+cc_library_static {
+ name: "libipchecksum",
+
+ srcs: [
+ "checksum.c",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ export_include_dirs: ["include"],
+}
+
cc_binary {
name: "dhcpdbg",
diff --git a/libpixelflinger/include/pixelflinger/format.h b/libpixelflinger/include/pixelflinger/format.h
index 82eeca4..d429477 100644
--- a/libpixelflinger/include/pixelflinger/format.h
+++ b/libpixelflinger/include/pixelflinger/format.h
@@ -82,7 +82,7 @@
GGL_INDEX_CR = 2,
};
-typedef struct {
+typedef struct GGLFormat {
#ifdef __cplusplus
enum {
ALPHA = GGL_INDEX_ALPHA,
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9c1621b..3b6cfd8 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -185,7 +185,6 @@
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
- // Sanity check.
int type = nh->nlmsg_type;
if (type != RTM_NEWADDR && type != RTM_DELADDR) {
SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
@@ -349,7 +348,6 @@
uint8_t type = nh->nlmsg_type;
const char *msgname = rtMessageName(type);
- // Sanity check.
if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
return false;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 19c22c8..8cc780a 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -341,6 +341,37 @@
}
//-------------------------------------------------------------------------
+// Fuzzers
+//-------------------------------------------------------------------------
+cc_defaults {
+ name: "libunwindstack_fuzz_defaults",
+ host_supported: true,
+ defaults: ["libunwindstack_flags"],
+ cflags: [
+ "-Wno-exit-time-destructors",
+ "-g",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "liblzma",
+ "libunwindstack",
+ "libdexfile_support",
+ ],
+}
+
+cc_fuzz {
+ name: "libunwindstack_fuzz_unwinder",
+ defaults: ["libunwindstack_fuzz_defaults"],
+ srcs: [
+ "tests/MemoryFake.cpp",
+ "tests/ElfFake.cpp",
+ "tests/fuzz/UnwinderComponentCreator.cpp",
+ "tests/fuzz/UnwinderFuzz.cpp",
+ ],
+}
+
+//-------------------------------------------------------------------------
// Tools
//-------------------------------------------------------------------------
cc_defaults {
@@ -458,3 +489,4 @@
"tests/GenGnuDebugdata.cpp",
],
}
+
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index c128b9b..c6db209 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -26,7 +26,9 @@
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
+#include <unwindstack/MachineArm64.h>
#include "DwarfCfa.h"
#include "DwarfEncoding.h"
@@ -204,8 +206,12 @@
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
uint64_t* cur_pc) {
const auto* cfa = &DwarfCfaInfo::kTable[op];
- if (cfa->name[0] == '\0') {
- log(indent, "Illegal");
+ if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
+ if (op == 0x2d) {
+ log(indent, "Illegal (Only valid on aarch64)");
+ } else {
+ log(indent, "Illegal");
+ }
log(indent, "Raw Data: 0x%02x", op);
return true;
}
@@ -514,6 +520,24 @@
return true;
}
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_aarch64_negate_ra_state(dwarf_loc_regs_t* loc_regs) {
+ // Only supported on aarch64.
+ if (arch_ != ARCH_ARM64) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ auto cfa_location = loc_regs->find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ if (cfa_location == loc_regs->end()) {
+ (*loc_regs)[Arm64Reg::ARM64_PREG_RA_SIGN_STATE] = {.type = DWARF_LOCATION_PSEUDO_REGISTER,
+ .values = {1}};
+ } else {
+ cfa_location->second.values[0] ^= 1;
+ }
+ return true;
+}
+
const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
{
// 0x00 DW_CFA_nop
@@ -699,7 +723,13 @@
{"", 0, 0, {}, {}}, // 0x2a illegal cfa
{"", 0, 0, {}, {}}, // 0x2b illegal cfa
{"", 0, 0, {}, {}}, // 0x2c illegal cfa
- {"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {
+ "DW_CFA_AARCH64_negate_ra_state", // 0x2d DW_CFA_AARCH64_negate_ra_state
+ 3,
+ 0,
+ {},
+ {},
+ },
{
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
2,
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 569c17c..d627e15 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -31,6 +31,9 @@
namespace unwindstack {
+// Forward declarations.
+enum ArchEnum : uint8_t;
+
// DWARF Standard home: http://dwarfstd.org/
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
@@ -72,7 +75,8 @@
typedef typename std::make_signed<AddressType>::type SignedType;
public:
- DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
+ DwarfCfa(DwarfMemory* memory, const DwarfFde* fde, ArchEnum arch)
+ : memory_(memory), fde_(fde), arch_(arch) {}
virtual ~DwarfCfa() = default;
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -99,6 +103,7 @@
DwarfErrorData last_error_;
DwarfMemory* memory_;
const DwarfFde* fde_;
+ ArchEnum arch_;
AddressType cur_pc_;
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@@ -128,6 +133,7 @@
bool cfa_val_offset_sf(dwarf_loc_regs_t*);
bool cfa_val_expression(dwarf_loc_regs_t*);
bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+ bool cfa_aarch64_negate_ra_state(dwarf_loc_regs_t*);
using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
constexpr static process_func kCallbackTable[64] = {
@@ -221,8 +227,9 @@
nullptr,
// 0x2c illegal cfa
nullptr,
- // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
- nullptr,
+ // 0x2d DW_CFA_AARCH64_negate_ra_state (aarch64 only)
+ // DW_CFA_GNU_window_save on other architectures.
+ &DwarfCfa::cfa_aarch64_negate_ra_state,
// 0x2e DW_CFA_GNU_args_size
&DwarfCfa::cfa_nop,
// 0x2f DW_CFA_GNU_negative_offset_extended
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 18bd490..9e2a3cd 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -21,6 +21,7 @@
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -49,7 +50,7 @@
// Now get the location information for this pc.
dwarf_loc_regs_t loc_regs;
- if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
return false;
}
loc_regs.cie = fde->cie;
@@ -464,6 +465,13 @@
eval_info->return_address_undefined = true;
}
break;
+ case DWARF_LOCATION_PSEUDO_REGISTER: {
+ if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ break;
+ }
default:
break;
}
@@ -491,6 +499,10 @@
// Always set the dex pc to zero when evaluating.
cur_regs->set_dex_pc(0);
+ // Reset necessary pseudo registers before evaluation.
+ // This is needed for ARM64, for example.
+ regs->ResetPseudoRegisters();
+
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
.cie = cie,
.regular_memory = regular_memory,
@@ -527,8 +539,10 @@
AddressType* reg_ptr;
if (reg >= cur_regs->total_regs()) {
- // Skip this unknown register.
- continue;
+ if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
+ // Skip this unknown register.
+ continue;
+ }
}
reg_ptr = eval_info.regs_info.Save(reg);
@@ -554,8 +568,8 @@
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
- dwarf_loc_regs_t* loc_regs) {
- DwarfCfa<AddressType> cfa(&memory_, fde);
+ dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
+ DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Look for the cached copy of the cie data.
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
@@ -576,8 +590,9 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
- DwarfCfa<AddressType> cfa(&memory_, fde);
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
+ ArchEnum arch) {
+ DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Always print the cie information.
const DwarfCie* cie = fde->cie;
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 5b7431a..b496187 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -30,7 +30,10 @@
namespace unwindstack {
RegsArm64::RegsArm64()
- : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+ : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {
+ ResetPseudoRegisters();
+ pac_mask_ = 0;
+}
ArchEnum RegsArm64::Arch() {
return ARCH_ARM64;
@@ -45,6 +48,23 @@
}
void RegsArm64::set_pc(uint64_t pc) {
+ // If the target is aarch64 then the return address may have been
+ // signed using the Armv8.3-A Pointer Authentication extension. The
+ // original return address can be restored by stripping out the
+ // authentication code using a mask or xpaclri. xpaclri is a NOP on
+ // pre-Armv8.3-A architectures.
+ if ((0 != pc) && IsRASigned()) {
+ if (pac_mask_) {
+ pc &= ~pac_mask_;
+#if defined(__aarch64__)
+ } else {
+ register uint64_t x30 __asm("x30") = pc;
+ // This is XPACLRI.
+ asm("hint 0x7" : "+r"(x30));
+ pc = x30;
+#endif
+ }
+ }
regs_[ARM64_REG_PC] = pc;
}
@@ -144,6 +164,37 @@
return true;
}
+void RegsArm64::ResetPseudoRegisters(void) {
+ // DWARF for AArch64 says RA_SIGN_STATE should be initialized to 0.
+ this->SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 0);
+}
+
+bool RegsArm64::SetPseudoRegister(uint16_t id, uint64_t value) {
+ if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
+ pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST] = value;
+ return true;
+ }
+ return false;
+}
+
+bool RegsArm64::GetPseudoRegister(uint16_t id, uint64_t* value) {
+ if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
+ *value = pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST];
+ return true;
+ }
+ return false;
+}
+
+bool RegsArm64::IsRASigned() {
+ uint64_t value;
+ auto result = this->GetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, &value);
+ return (result && (value != 0));
+}
+
+void RegsArm64::SetPACMask(uint64_t mask) {
+ pac_mask_ = mask;
+}
+
Regs* RegsArm64::Clone() {
return new RegsArm64(*this);
}
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 3d50ccf..bf45bc7 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -33,6 +33,7 @@
DWARF_LOCATION_REGISTER,
DWARF_LOCATION_EXPRESSION,
DWARF_LOCATION_VAL_EXPRESSION,
+ DWARF_LOCATION_PSEUDO_REGISTER,
};
struct DwarfLocation {
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index c244749..af823da 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -31,6 +31,7 @@
namespace unwindstack {
// Forward declarations.
+enum ArchEnum : uint8_t;
class Memory;
class Regs;
template <typename AddressType>
@@ -90,13 +91,14 @@
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
- virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
+ virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) = 0;
virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
- virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+ virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
+ ArchEnum arch) = 0;
virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
@@ -140,9 +142,10 @@
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
Regs* regs, bool* finished) override;
- bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+ bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
+ ArchEnum arch) override;
- bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
+ bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) override;
protected:
bool GetNextCieOrFde(const DwarfFde** fde_entry);
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index e953335..358e3d9 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -60,6 +60,13 @@
ARM64_REG_SP = ARM64_REG_R31,
ARM64_REG_LR = ARM64_REG_R30,
+
+ // Pseudo registers. These are not machine registers.
+
+ // AARCH64 Return address signed state pseudo-register
+ ARM64_PREG_RA_SIGN_STATE = 34,
+ ARM64_PREG_FIRST = ARM64_PREG_RA_SIGN_STATE,
+ ARM64_PREG_LAST,
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index a367e6c..5f42565 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -64,6 +64,10 @@
uint64_t dex_pc() { return dex_pc_; }
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
+ virtual void ResetPseudoRegisters() {}
+ virtual bool SetPseudoRegister(uint16_t, uint64_t) { return false; }
+ virtual bool GetPseudoRegister(uint16_t, uint64_t*) { return false; }
+
virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 2b3ddeb..bf7ab15 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -22,6 +22,7 @@
#include <functional>
#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm64.h>
#include <unwindstack/Regs.h>
namespace unwindstack {
@@ -48,11 +49,25 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ void ResetPseudoRegisters() override;
+
+ bool SetPseudoRegister(uint16_t id, uint64_t value) override;
+
+ bool GetPseudoRegister(uint16_t id, uint64_t* value) override;
+
+ bool IsRASigned();
+
+ void SetPACMask(uint64_t mask);
+
Regs* Clone() override final;
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
+
+ protected:
+ uint64_t pseudo_regs_[Arm64Reg::ARM64_PREG_LAST - Arm64Reg::ARM64_PREG_FIRST];
+ uint64_t pac_mask_;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index def4088..2b5a8dc 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -26,6 +26,7 @@
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include "DwarfCfa.h"
@@ -57,7 +58,7 @@
fde_.pc_end = 0x2000;
fde_.pc_end = 0x10000;
fde_.cie = &cie_;
- cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_, ARCH_UNKNOWN));
}
MemoryFake memory_;
@@ -72,8 +73,8 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
for (uint8_t i = 0x17; i < 0x3f; i++) {
- if (i == 0x2e || i == 0x2f) {
- // Skip gnu extension ops.
+ if (i == 0x2d || i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops and aarch64 specialized op.
continue;
}
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
@@ -763,6 +764,26 @@
ASSERT_EQ("", GetFakeLogBuf());
}
+TYPED_TEST_P(DwarfCfaLogTest, cfa_aarch64_negate_ra_state) {
+ // Verify that if the cfa op is handled properly depending on aarch.
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2d});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ std::string expected = "4 unwind Illegal (Only valid on aarch64)\n";
+ expected += "4 unwind Raw Data: 0x2d\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->cfa_.reset(new DwarfCfa<TypeParam>(this->dmem_.get(), &this->fde_, ARCH_ARM64));
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ expected = "4 unwind DW_CFA_AARCH64_negate_ra_state\n";
+ expected += "4 unwind Raw Data: 0x2d\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
@@ -771,7 +792,8 @@
cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
- cfa_gnu_negative_offset_extended, cfa_register_override);
+ cfa_gnu_negative_offset_extended, cfa_register_override,
+ cfa_aarch64_negate_ra_state);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaLogTest, DwarfCfaLogTestTypes);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 9c6ab05..ea7e708 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -25,7 +25,9 @@
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
+#include <unwindstack/MachineArm64.h>
#include "DwarfCfa.h"
@@ -55,7 +57,7 @@
fde_.pc_start = 0x2000;
fde_.cie = &cie_;
- cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_, ARCH_UNKNOWN));
}
MemoryFake memory_;
@@ -70,8 +72,8 @@
TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
for (uint8_t i = 0x17; i < 0x3f; i++) {
- if (i == 0x2e || i == 0x2f) {
- // Skip gnu extension ops.
+ if (i == 0x2d || i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops and aarch64 specialized op.
continue;
}
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
@@ -952,6 +954,57 @@
ASSERT_EQ("", GetFakeLogBuf());
}
+TYPED_TEST_P(DwarfCfaTest, cfa_aarch64_negate_ra_state) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2d});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->LastErrorCode());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->cfa_.reset(new DwarfCfa<TypeParam>(this->dmem_.get(), &this->fde_, ARCH_ARM64));
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ auto location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Verify that the value is set to 0 after another evaluation.
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
+ ASSERT_EQ(0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Verify that the value is set to 1 again after a third op.
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
@@ -960,7 +1013,7 @@
cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
- cfa_register_override);
+ cfa_register_override, cfa_aarch64_negate_ra_state);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaTest, DwarfCfaTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index cac59b7..d57cd33 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -20,6 +20,7 @@
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
#include "DwarfEncoding.h"
@@ -505,7 +506,7 @@
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
dwarf_loc_regs_t loc_regs;
- ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs, ARCH_UNKNOWN));
ASSERT_EQ(2U, loc_regs.size());
auto entry = loc_regs.find(2);
@@ -535,7 +536,7 @@
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
dwarf_loc_regs_t loc_regs;
- ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs, ARCH_UNKNOWN));
ASSERT_EQ(2U, loc_regs.size());
auto entry = loc_regs.find(6);
@@ -560,7 +561,7 @@
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
- ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde, ARCH_UNKNOWN));
ASSERT_EQ(
"4 unwind DW_CFA_nop\n"
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 953dc75..febd6d3 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -20,8 +20,10 @@
#include <gtest/gtest.h>
#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
#include "MemoryFake.h"
+#include "RegsFake.h"
namespace unwindstack {
@@ -35,13 +37,14 @@
MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
(override));
- MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
+ MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*, ArchEnum arch), (override));
MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
- MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
+ MOCK_METHOD(bool, GetCfaLocationInfo,
+ (uint64_t, const DwarfFde*, dwarf_loc_regs_t*, ArchEnum arch), (override));
MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
@@ -56,8 +59,11 @@
MemoryFake memory_;
std::unique_ptr<MockDwarfSection> section_;
+ static RegsFake regs_;
};
+RegsFake DwarfSectionTest::regs_(10);
+
TEST_F(DwarfSectionTest, Step_fail_fde) {
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
@@ -73,7 +79,7 @@
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
bool finished;
- ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+ ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@@ -83,11 +89,11 @@
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Return(false));
bool finished;
- ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+ ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_pass) {
@@ -97,19 +103,19 @@
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Return(true));
MemoryFake process;
- EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, ®s_, ::testing::_))
.WillOnce(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
}
static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
- dwarf_loc_regs_t* loc_regs) {
+ dwarf_loc_regs_t* loc_regs, ArchEnum) {
loc_regs->pc_start = fde->pc_start;
loc_regs->pc_end = fde->pc_end;
return true;
@@ -123,17 +129,17 @@
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
MemoryFake process;
- EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, ®s_, ::testing::_))
.WillRepeatedly(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
- ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1500, ®s_, &process, &finished));
}
TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
@@ -143,26 +149,26 @@
fde0.pc_end = 0x2000;
fde0.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
MemoryFake process;
- EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, ®s_, ::testing::_))
.WillRepeatedly(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
DwarfFde fde1{};
fde1.pc_start = 0x500;
fde1.pc_end = 0x800;
fde1.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
- ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
- ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x600, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x700, ®s_, &process, &finished));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index e4fc6f0..acf72de 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -247,6 +247,14 @@
EXPECT_EQ(0xc200000000U, mips64.pc());
}
+TEST_F(RegsTest, arm64_strip_pac_mask) {
+ RegsArm64 arm64;
+ arm64.SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 1);
+ arm64.SetPACMask(0x007fff8000000000ULL);
+ arm64.set_pc(0x0020007214bb3a04ULL);
+ EXPECT_EQ(0x0000007214bb3a04ULL, arm64.pc());
+}
+
TEST_F(RegsTest, machine_type) {
RegsArm arm_regs;
EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index 6a3e91a..eb2b01d 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -55,7 +55,7 @@
return DWARF_LOCATION_INVALID;
}
dwarf_loc_regs_t regs;
- if (!section->GetCfaLocationInfo(rel_pc, fde, ®s)) {
+ if (!section->GetCfaLocationInfo(rel_pc, fde, ®s, ARCH_UNKNOWN)) {
return DWARF_LOCATION_INVALID;
}
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
new file mode 100644
index 0000000..94f5a73
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnwinderComponentCreator.h"
+
+std::unique_ptr<Regs> GetRegisters(ArchEnum arch) {
+ switch (arch) {
+ case unwindstack::ARCH_ARM: {
+ std::unique_ptr<unwindstack::RegsArm> regs = std::make_unique<unwindstack::RegsArm>();
+ return regs;
+ }
+ case unwindstack::ARCH_ARM64: {
+ std::unique_ptr<unwindstack::RegsArm64> regs = std::make_unique<unwindstack::RegsArm64>();
+ return regs;
+ }
+ case unwindstack::ARCH_X86: {
+ std::unique_ptr<unwindstack::RegsX86> regs = std::make_unique<unwindstack::RegsX86>();
+ return regs;
+ }
+ case unwindstack::ARCH_X86_64: {
+ std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+ return regs;
+ }
+ case unwindstack::ARCH_MIPS: {
+ std::unique_ptr<unwindstack::RegsMips> regs = std::make_unique<unwindstack::RegsMips>();
+ return regs;
+ }
+ case unwindstack::ARCH_MIPS64: {
+ std::unique_ptr<unwindstack::RegsMips64> regs = std::make_unique<unwindstack::RegsMips64>();
+ return regs;
+ }
+ case unwindstack::ARCH_UNKNOWN:
+ default: {
+ std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+ return regs;
+ }
+ }
+}
+
+ArchEnum GetArch(FuzzedDataProvider* data_provider) {
+ uint8_t arch = data_provider->ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+ return static_cast<ArchEnum>(arch);
+}
+
+void ElfAddMapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name, Elf* elf = nullptr) {
+ std::string str_name(name);
+ maps->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+ if (elf != nullptr) {
+ const auto& map_info = *--maps->end();
+ map_info->elf.reset(elf);
+ }
+}
+
+void ElfPushFakeFunctionData(FuzzedDataProvider* data_provider, ElfInterfaceFake* elf) {
+ uint8_t func_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxFuncCount);
+ for (uint8_t i = 0; i < func_count; i++) {
+ std::string func_name = data_provider->ConsumeRandomLengthString(kMaxFuncNameLen);
+ bool global = data_provider->ConsumeBool();
+ if (global) {
+ elf->FakeSetGlobalVariable(func_name, data_provider->ConsumeIntegral<uint64_t>());
+ } else {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i));
+ }
+ }
+}
+void ElfPushFakeStepData(FuzzedDataProvider* data_provider) {
+ uint8_t step_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxStepCount);
+ for (uint8_t i = 0; i < step_count; i++) {
+ uint64_t pc = data_provider->ConsumeIntegral<uint64_t>();
+ uint64_t sp = data_provider->ConsumeIntegral<uint64_t>();
+ bool finished = i + 1 == step_count;
+ ElfInterfaceFake::FakePushStepData(StepData(pc, sp, finished));
+ }
+}
+
+ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) {
+ // This will be passed to a smart pointer in ElfAddMapInfo.
+ ElfFake* elf = new ElfFake(new MemoryFake);
+
+ // This will be handled by a smart pointer within Elf.
+ ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+ std::string build_id = data_provider->ConsumeRandomLengthString(kMaxBuildIdLen);
+ interface_fake->FakeSetBuildID(build_id);
+ std::string so_name = data_provider->ConsumeRandomLengthString(kMaxSoNameLen);
+ interface_fake->FakeSetSoname(so_name.c_str());
+
+ elf->FakeSetArch(GetArch(data_provider));
+ elf->FakeSetLoadBias(data_provider->ConsumeIntegral<uint64_t>());
+
+ ElfPushFakeFunctionData(data_provider, interface_fake);
+ ElfPushFakeStepData(data_provider);
+
+ elf->FakeSetInterface(interface_fake);
+ ElfInterfaceFake::FakeClear();
+ return elf;
+}
+
+std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) {
+ std::unique_ptr<Maps> maps = std::make_unique<Maps>();
+ uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount);
+ for (uint8_t i = 0; i < entry_count; i++) {
+ uint64_t start = data_provider->ConsumeIntegral<uint64_t>();
+ uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX);
+ uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+ std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen);
+ uint8_t flags = PROT_READ | PROT_WRITE;
+
+ bool exec = data_provider->ConsumeBool();
+ if (exec) {
+ flags |= PROT_EXEC;
+ }
+
+ bool shouldAddElf = data_provider->ConsumeBool();
+ if (shouldAddElf) {
+ ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str(),
+ PopulateElfFake(data_provider));
+ } else {
+ ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str());
+ }
+ }
+ maps->Sort();
+ return maps;
+}
+
+// This code (until PutElfFilesInMemory) is pretty much directly copied from JitDebugTest.cpp
+// There's a few minor modifications, most notably, all methods accept a MemoryFake pointer, and
+// PutElfInMemory inserts JIT data when called.
+void WriteDescriptor32(MemoryFake* memory, uint64_t addr, uint32_t entry) {
+ // Format of the 32 bit JITDescriptor structure:
+ // uint32_t version
+ memory->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory->SetData32(addr + 4, 0);
+ // uint32_t relevant_entry
+ memory->SetData32(addr + 8, 0);
+ // uint32_t first_entry
+ memory->SetData32(addr + 12, entry);
+}
+
+void WriteDescriptor64(MemoryFake* memory, uint64_t addr, uint64_t entry) {
+ // Format of the 64 bit JITDescriptor structure:
+ // uint32_t version
+ memory->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory->SetData32(addr + 4, 0);
+ // uint64_t relevant_entry
+ memory->SetData64(addr + 8, 0);
+ // uint64_t first_entry
+ memory->SetData64(addr + 16, entry);
+}
+
+void WriteEntry32Pack(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+ uint32_t elf_addr, uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory->SetData32(addr, next);
+ // uint32_t prev
+ memory->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory->SetData32(addr + 8, elf_addr);
+ // uint64_t symfile_size
+ memory->SetData64(addr + 12, elf_size);
+}
+
+void WriteEntry32Pad(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+ uint32_t elf_addr, uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory->SetData32(addr, next);
+ // uint32_t prev
+ memory->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory->SetData32(addr + 8, elf_addr);
+ // uint32_t pad
+ memory->SetData32(addr + 12, 0);
+ // uint64_t symfile_size
+ memory->SetData64(addr + 16, elf_size);
+}
+
+void WriteEntry64(MemoryFake* memory, uint64_t addr, uint64_t prev, uint64_t next,
+ uint64_t elf_addr, uint64_t elf_size) {
+ // Format of the 64 bit JITCodeEntry structure:
+ // uint64_t next
+ memory->SetData64(addr, next);
+ // uint64_t prev
+ memory->SetData64(addr + 8, prev);
+ // uint64_t symfile_addr
+ memory->SetData64(addr + 16, elf_addr);
+ // uint64_t symfile_size
+ memory->SetData64(addr + 24, elf_size);
+}
+
+template <typename EhdrType, typename ShdrType>
+void PutElfInMemory(MemoryFake* memory, uint64_t offset, uint8_t class_type, uint8_t machine_type,
+ uint32_t pc, uint32_t size) {
+ EhdrType ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ uint64_t sh_offset = sizeof(ehdr);
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_machine = machine_type;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(ShdrType);
+ ehdr.e_shnum = 3;
+ memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+ ShdrType shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+ memory->SetMemory(offset + 0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ // Now add a single cie/fde.
+ uint64_t dwarf_offset = offset + 0x600;
+ if (class_type == ELFCLASS32) {
+ // CIE 32 information.
+ memory->SetData32(dwarf_offset, 0xfc);
+ memory->SetData32(dwarf_offset + 0x4, 0xffffffff);
+ memory->SetData8(dwarf_offset + 0x8, 1);
+ memory->SetData8(dwarf_offset + 0x9, '\0');
+ memory->SetData8(dwarf_offset + 0xa, 0x4);
+ memory->SetData8(dwarf_offset + 0xb, 0x4);
+ memory->SetData8(dwarf_offset + 0xc, 0x1);
+
+ // FDE 32 information.
+ memory->SetData32(dwarf_offset + 0x100, 0xfc);
+ memory->SetData32(dwarf_offset + 0x104, 0);
+ memory->SetData32(dwarf_offset + 0x108, pc);
+ memory->SetData32(dwarf_offset + 0x10c, size);
+ } else {
+ // CIE 64 information.
+ memory->SetData32(dwarf_offset, 0xffffffff);
+ memory->SetData64(dwarf_offset + 4, 0xf4);
+ memory->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+ memory->SetData8(dwarf_offset + 0x14, 1);
+ memory->SetData8(dwarf_offset + 0x15, '\0');
+ memory->SetData8(dwarf_offset + 0x16, 0x4);
+ memory->SetData8(dwarf_offset + 0x17, 0x4);
+ memory->SetData8(dwarf_offset + 0x18, 0x1);
+
+ // FDE 64 information.
+ memory->SetData32(dwarf_offset + 0x100, 0xffffffff);
+ memory->SetData64(dwarf_offset + 0x104, 0xf4);
+ memory->SetData64(dwarf_offset + 0x10c, 0);
+ memory->SetData64(dwarf_offset + 0x114, pc);
+ memory->SetData64(dwarf_offset + 0x11c, size);
+ }
+}
+
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider) {
+ uint8_t elf_file_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxJitElfFiles);
+ int entry_offset = 0;
+ int prev_jit_addr = 0;
+ for (uint8_t i = 0; i < elf_file_count; i++) {
+ uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+ // Technically the max valid value is ELFCLASSNUM - 1 (2), but
+ // we want to test values outside of that range.
+ uint8_t class_type = data_provider->ConsumeIntegral<uint8_t>();
+ // Same here, EM_NUM is 253, max valid machine type is 252
+ uint8_t machine_type = data_provider->ConsumeIntegral<uint8_t>();
+ uint32_t pc = data_provider->ConsumeIntegral<uint32_t>();
+ uint32_t size = data_provider->ConsumeIntegral<uint32_t>();
+ bool sixty_four_bit = data_provider->ConsumeBool();
+ bool write_jit = data_provider->ConsumeBool();
+ if (sixty_four_bit) {
+ PutElfInMemory<Elf64_Ehdr, Elf64_Shdr>(memory, offset, class_type, machine_type, pc, size);
+ } else {
+ PutElfInMemory<Elf32_Ehdr, Elf32_Shdr>(memory, offset, class_type, machine_type, pc, size);
+ }
+ if (write_jit) {
+ bool use_pad = data_provider->ConsumeBool();
+ // It is possible this will overwrite part of the ELF.
+ // This provides an interesting test of how malformed ELF
+ // data is handled.
+ uint64_t cur_descriptor_addr = 0x11800 + entry_offset;
+ uint64_t cur_jit_addr = 0x200000 + entry_offset;
+ uint64_t next_jit_addr = cur_jit_addr + size;
+ if (sixty_four_bit) {
+ WriteDescriptor64(memory, 0x11800, cur_jit_addr);
+ WriteEntry64(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+ } else {
+ // Loop back. Again, this may corrupt data,
+ // but that will allow for testing edge cases with
+ // malformed JIT data.
+ if (cur_jit_addr > UINT32_MAX) {
+ entry_offset = 0;
+ cur_jit_addr = 0x200000;
+ cur_descriptor_addr = 0x11800;
+ next_jit_addr = cur_jit_addr + size;
+ }
+ WriteDescriptor32(memory, cur_descriptor_addr, cur_jit_addr);
+ if (use_pad) {
+ WriteEntry32Pad(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+ } else {
+ WriteEntry32Pack(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+ }
+ }
+ entry_offset += size;
+ prev_jit_addr = cur_jit_addr;
+ }
+ }
+}
+
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+ uint max_strings) {
+ uint str_count = data_provider->ConsumeIntegralInRange<uint>(0, max_strings);
+ std::vector<std::string> strings;
+ for (uint i = 0; i < str_count; i++) {
+ strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len));
+ }
+ return strings;
+}
+
+std::unique_ptr<DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+ std::shared_ptr<Memory> memory, uint max_library_length,
+ uint max_libraries) {
+ std::vector<std::string> search_libs =
+ GetStringList(data_provider, max_library_length, max_libraries);
+ if (search_libs.size() <= 0) {
+ return std::make_unique<DexFiles>(memory);
+ }
+
+ return std::make_unique<DexFiles>(memory, search_libs);
+}
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
new file mode 100644
index 0000000..09b3379
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+#define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+
+#include <elf.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "../ElfFake.h"
+#include "../MemoryFake.h"
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using unwindstack::ArchEnum;
+using unwindstack::DexFiles;
+using unwindstack::Elf;
+using unwindstack::ElfFake;
+using unwindstack::ElfInterfaceFake;
+using unwindstack::FunctionData;
+using unwindstack::Maps;
+using unwindstack::Memory;
+using unwindstack::MemoryFake;
+using unwindstack::Regs;
+using unwindstack::StepData;
+
+static constexpr uint8_t kArchCount = 6;
+
+static constexpr uint8_t kMaxSoNameLen = 150;
+
+static constexpr uint8_t kMaxFuncNameLen = 50;
+static constexpr uint8_t kMaxFuncCount = 100;
+
+static constexpr uint8_t kMaxJitElfFiles = 20;
+static constexpr uint8_t kJitElfPadding = 32;
+
+static constexpr uint8_t kMaxStepCount = 100;
+static constexpr uint8_t kMaxMapEntryCount = 50;
+static constexpr uint8_t kMaxBuildIdLen = 100;
+static constexpr uint8_t kMaxMapInfoNameLen = 150;
+
+std::unique_ptr<unwindstack::Regs> GetRegisters(unwindstack::ArchEnum arch);
+std::unique_ptr<unwindstack::Maps> GetMaps(FuzzedDataProvider* data_provider);
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+ uint max_strings);
+unwindstack::ArchEnum GetArch(FuzzedDataProvider* data_provider);
+
+void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name,
+ Elf* elf = nullptr);
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider);
+
+std::unique_ptr<unwindstack::DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+ std::shared_ptr<unwindstack::Memory> memory,
+ uint max_libraries, uint max_library_length);
+#endif // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
new file mode 100644
index 0000000..2f4986a
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <iostream>
+#include <vector>
+
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Unwinder.h>
+
+#include "../MemoryFake.h"
+#include "UnwinderComponentCreator.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+namespace unwindstack {
+
+static constexpr int kMaxUnwindStringLen = 50;
+static constexpr int kMaxUnwindStrings = 50;
+
+void PerformUnwind(FuzzedDataProvider* data_provider, Unwinder* unwinder) {
+ // 0 = don't set any values
+ // 1 = set initial_map_names_to_skip
+ // 2 = set map_suffixes_to_ignore
+ // 3 = set both
+ uint8_t set_values = data_provider->ConsumeIntegral<uint8_t>() % 4;
+ if (set_values == 0) {
+ unwinder->Unwind();
+ } else if (set_values == 1) {
+ // Only setting initial_map_names_to_skip
+ std::vector<std::string> skip_names =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+ unwinder->Unwind(&skip_names, nullptr);
+ } else if (set_values == 2) {
+ // Only setting map_suffixes_to_ignore
+ std::vector<std::string> ignore_suffixes =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+ unwinder->Unwind(nullptr, &ignore_suffixes);
+ } else if (set_values == 3) {
+ // Setting both values
+ std::vector<std::string> skip_names =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+ std::vector<std::string> ignore_suffixes =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+ unwinder->Unwind(&skip_names, &ignore_suffixes);
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider data_provider(data, size);
+
+ // We need to construct an unwinder.
+ // Generate the Maps:
+ std::unique_ptr<Maps> maps = GetMaps(&data_provider);
+
+ // Generate the Regs:
+ uint8_t arch_val = data_provider.ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+ ArchEnum arch = static_cast<ArchEnum>(arch_val);
+ std::unique_ptr<Regs> regs = GetRegisters(arch);
+
+ // Generate memory:
+ std::shared_ptr<Memory> memory = std::make_shared<MemoryFake>();
+ PutElfFilesInMemory(reinterpret_cast<MemoryFake*>(memory.get()), &data_provider);
+
+ size_t max_frames = data_provider.ConsumeIntegralInRange<size_t>(0, 5000);
+
+ std::unique_ptr<JitDebug> jit_debug_ptr = std::make_unique<JitDebug>(memory);
+
+ // Create instance
+ Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
+ unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+ unwinder.SetResolveNames(data_provider.ConsumeBool());
+ // Call unwind
+ PerformUnwind(&data_provider, &unwinder);
+
+ // Run some additional logic that changes after unwind
+ uint64_t pc = data_provider.ConsumeIntegral<uint64_t>();
+ unwinder.BuildFrameFromPcOnly(pc);
+ unwinder.ConsumeFrames();
+ return 0;
+}
+} // namespace unwindstack
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 7a6d8ba..a5002f2 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -96,7 +96,7 @@
printf(" <%s>", name.c_str());
}
printf("\n");
- if (!section->Log(2, UINT64_MAX, fde)) {
+ if (!section->Log(2, UINT64_MAX, fde, elf->arch())) {
printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
}
}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 0cbcac5..68e0273 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -64,7 +64,8 @@
}
}
-void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
+void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type,
+ ArchEnum arch) {
const DwarfFde* fde = section->GetFdeFromPc(pc);
if (fde == nullptr) {
printf(" No fde found.\n");
@@ -72,7 +73,7 @@
}
dwarf_loc_regs_t regs;
- if (!section->GetCfaLocationInfo(pc, fde, ®s)) {
+ if (!section->GetCfaLocationInfo(pc, fde, ®s, arch)) {
printf(" Cannot get location information.\n");
return;
}
@@ -128,6 +129,11 @@
break;
}
+ case DWARF_LOCATION_PSEUDO_REGISTER: {
+ printf("%" PRId64 " (pseudo)\n", loc->values[0]);
+ break;
+ }
+
case DWARF_LOCATION_UNDEFINED:
printf("undefine\n");
break;
@@ -199,7 +205,7 @@
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
- PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type(), elf.arch());
} else {
printf("\nno eh_frame information\n");
}
@@ -207,7 +213,7 @@
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
- PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type(), elf.arch());
printf("\n");
} else {
printf("\nno debug_frame information\n");
@@ -219,7 +225,8 @@
section = gnu_debugdata_interface->eh_frame();
if (section != nullptr) {
printf("\ngnu_debugdata (eh_frame):\n");
- PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+ PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type(),
+ elf.arch());
printf("\n");
} else {
printf("\nno gnu_debugdata (eh_frame)\n");
@@ -228,7 +235,8 @@
section = gnu_debugdata_interface->debug_frame();
if (section != nullptr) {
printf("\ngnu_debugdata (debug_frame):\n");
- PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+ PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type(),
+ elf.arch());
printf("\n");
} else {
printf("\nno gnu_debugdata (debug_frame)\n");
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 7392806..022dedf 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -78,6 +78,9 @@
"libcutils",
"liblog",
],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
target: {
android: {
@@ -245,6 +248,7 @@
"LruCache_test.cpp",
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
+ "Singleton_test.cpp",
"String8_test.cpp",
"String16_test.cpp",
"StrongPointer_test.cpp",
@@ -281,6 +285,11 @@
},
},
+ data_libs: [
+ "libutils_test_singleton1",
+ "libutils_test_singleton2",
+ ],
+
cflags: [
"-Wall",
"-Wextra",
@@ -291,29 +300,10 @@
test_suites: ["device-tests"],
}
-// TODO: the test infrastructure isn't yet capable of running this,
-// so it's broken out into its own test so that the main libutils_tests
-// can be in presubmit even if this can't.
-
-cc_test {
- name: "libutils_singleton_test",
- srcs: ["Singleton_test.cpp"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: ["libbase"],
-
- required: [
- ":libutils_test_singleton1",
- ":libutils_test_singleton2",
- ],
-}
-
cc_test_library {
name: "libutils_test_singleton1",
host_supported: true,
- relative_install_path: "libutils_test",
+ installable: false,
srcs: ["Singleton_test1.cpp"],
cflags: [
"-Wall",
@@ -324,7 +314,7 @@
cc_test_library {
name: "libutils_test_singleton2",
host_supported: true,
- relative_install_path: "libutils_test",
+ installable: false,
srcs: ["Singleton_test2.cpp"],
cflags: [
"-Wall",
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index d00e39c..c837891 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -424,7 +424,7 @@
char* buf = lockBuffer(len);
buf += start;
while (length > 0) {
- *buf = tolower(*buf);
+ *buf = static_cast<char>(tolower(*buf));
buf++;
length--;
}
@@ -448,7 +448,7 @@
char* buf = lockBuffer(len);
buf += start;
while (length > 0) {
- *buf = toupper(*buf);
+ *buf = static_cast<char>(toupper(*buf));
buf++;
length--;
}
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index b08e061..b6e457b 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -162,9 +162,9 @@
if (index >= src_len) {
return -1;
}
- size_t dummy_index;
+ size_t unused_index;
if (next_index == nullptr) {
- next_index = &dummy_index;
+ next_index = &unused_index;
}
size_t num_read;
int32_t ret = utf32_at_internal(src + index, &num_read);
diff --git a/libutils/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
index 8abfb1a..0fb1a6a 100644
--- a/libutils/include/utils/BitSet.h
+++ b/libutils/include/utils/BitSet.h
@@ -21,9 +21,12 @@
#include <utils/TypeHelpers.h>
/*
- * Contains some bit manipulation helpers.
+ * A class to provide efficient manipulation of bitsets.
*
- * DO NOT USE: std::bitset<32> or std::bitset<64> preferred
+ * Consider using std::bitset<32> or std::bitset<64> if all you want is a class to do basic bit
+ * manipulation (i.e. AND / OR / XOR / flip / etc). These classes are only needed if you want to
+ * efficiently perform operations like finding the first set bit in a bitset and you want to
+ * avoid using the built-in functions (e.g. __builtin_clz) on std::bitset::to_ulong.
*/
namespace android {
@@ -46,7 +49,9 @@
// Returns the number of marked bits in the set.
inline uint32_t count() const { return count(value); }
- static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); }
+ static inline uint32_t count(uint32_t value) {
+ return static_cast<uint32_t>(__builtin_popcountl(value));
+ }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return isEmpty(value); }
@@ -128,7 +133,7 @@
}
static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {
- return __builtin_popcountl(value & ~(0xffffffffUL >> n));
+ return static_cast<uint32_t>(__builtin_popcountl(value & ~(0xffffffffUL >> n)));
}
inline bool operator== (const BitSet32& other) const { return value == other.value; }
@@ -153,17 +158,17 @@
// input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.
static inline uint32_t clz_checked(uint32_t value) {
if (sizeof(unsigned int) == sizeof(uint32_t)) {
- return __builtin_clz(value);
+ return static_cast<uint32_t>(__builtin_clz(value));
} else {
- return __builtin_clzl(value);
+ return static_cast<uint32_t>(__builtin_clzl(value));
}
}
static inline uint32_t ctz_checked(uint32_t value) {
if (sizeof(unsigned int) == sizeof(uint32_t)) {
- return __builtin_ctz(value);
+ return static_cast<uint32_t>(__builtin_ctz(value));
} else {
- return __builtin_ctzl(value);
+ return static_cast<uint32_t>(__builtin_ctzl(value));
}
}
};
@@ -188,7 +193,9 @@
// Returns the number of marked bits in the set.
inline uint32_t count() const { return count(value); }
- static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); }
+ static inline uint32_t count(uint64_t value) {
+ return static_cast<uint32_t>(__builtin_popcountll(value));
+ }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return isEmpty(value); }
@@ -219,26 +226,32 @@
// Result is undefined if all bits are unmarked.
inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
- static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); }
+ static inline uint32_t firstMarkedBit(uint64_t value) {
+ return static_cast<uint32_t>(__builtin_clzll(value));
+ }
// Finds the first unmarked bit in the set.
// Result is undefined if all bits are marked.
inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
- static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); }
+ static inline uint32_t firstUnmarkedBit(uint64_t value) {
+ return static_cast<uint32_t>(__builtin_clzll(~value));
+ }
// Finds the last marked bit in the set.
// Result is undefined if all bits are unmarked.
inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
- static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); }
+ static inline uint32_t lastMarkedBit(uint64_t value) {
+ return static_cast<uint32_t>(63 - __builtin_ctzll(value));
+ }
// Finds the first marked bit in the set and clears it. Returns the bit index.
// Result is undefined if all bits are unmarked.
inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
static inline uint32_t clearFirstMarkedBit(uint64_t& value) {
- uint64_t n = firstMarkedBit(value);
+ uint32_t n = firstMarkedBit(value);
clearBit(value, n);
return n;
}
@@ -248,7 +261,7 @@
inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {
- uint64_t n = firstUnmarkedBit(value);
+ uint32_t n = firstUnmarkedBit(value);
markBit(value, n);
return n;
}
@@ -258,7 +271,7 @@
inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
static inline uint32_t clearLastMarkedBit(uint64_t& value) {
- uint64_t n = lastMarkedBit(value);
+ uint32_t n = lastMarkedBit(value);
clearBit(value, n);
return n;
}
@@ -268,7 +281,7 @@
inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }
static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {
- return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n));
+ return static_cast<uint32_t>(__builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)));
}
inline bool operator== (const BitSet64& other) const { return value == other.value; }
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index ddf71de..be35ea2 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -23,15 +23,13 @@
#include <log/log.h>
#include <utils/TypeHelpers.h>
#include <utils/VectorImpl.h>
-
-/*
- * Used to blacklist some functions from CFI.
- *
- */
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
+/*
+ * Used to exclude some functions from CFI.
+ */
#if __has_attribute(no_sanitize)
#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi")))
#else
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index d15fa2b..c7d1634 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -616,15 +616,15 @@
if (long_options[option_index].name == wrap_str) {
mode |= ANDROID_LOG_WRAP | ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
- size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
+ size_t timeout = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
+ if (optarg && (!ParseUint(optarg, &timeout) || timeout < 1)) {
error(EXIT_FAILURE, 0, "%s %s out of range.",
long_options[option_index].name, optarg);
}
- if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ if (timeout != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
- dummy);
+ timeout);
}
break;
}
diff --git a/logd/Android.bp b/logd/Android.bp
index 036cb7e..265e19e 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -177,6 +177,27 @@
},
test_suites: [
"cts",
+ "device-tests",
"vts10",
],
}
+
+cc_binary {
+ name: "replay_messages",
+ defaults: ["logd_defaults"],
+ host_supported: true,
+
+ srcs: [
+ "ReplayMessages.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "liblogd",
+ "libselinux",
+ "libz",
+ "libzstd",
+ ],
+}
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 0ce9796..0e17476 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -32,7 +32,7 @@
#include <sstream>
#include <android-base/macros.h>
-#include <log/log_properties.h>
+#include <android-base/properties.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -40,6 +40,8 @@
#include "LogUtils.h"
#include "libaudit.h"
+using android::base::GetBoolProperty;
+
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
@@ -48,8 +50,8 @@
: SocketListener(getLogSocket(), false),
logbuf(buf),
fdDmesg(fdDmesg),
- main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
- events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+ main(GetBoolProperty("ro.logd.auditd.main", true)),
+ events(GetBoolProperty("ro.logd.auditd.events", true)),
initialized(false),
stats_(stats) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index dd779f9..26affa8 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -99,6 +99,10 @@
}
LogStatisticsElement LogBufferElement::ToLogStatisticsElement() const {
+ // Estimate the size of this element in the parent std::list<> by adding two void*'s
+ // corresponding to the next/prev pointers and aligning to 64 bit.
+ uint16_t element_in_list_size =
+ (sizeof(*this) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) & -sizeof(uint64_t);
return LogStatisticsElement{
.uid = uid(),
.pid = pid(),
@@ -109,6 +113,7 @@
.msg_len = msg_len(),
.dropped_count = dropped_count(),
.log_id = log_id(),
+ .total_len = static_cast<uint16_t>(element_in_list_size + msg_len()),
};
}
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index 5d57ad1..1fd22c2 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -87,6 +87,6 @@
LogReaderList reader_list_;
LogTags tags_;
PruneList prune_;
- LogStatistics stats_{false};
+ LogStatistics stats_{false, true};
std::unique_ptr<LogBuffer> log_buffer_;
};
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index dbdf7fd..d6c7d25 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -381,8 +381,8 @@
}
if ((i == 9) && (cp[i] == ' ')) {
int pid = 0;
- char dummy;
- if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
+ char placeholder;
+ if (sscanf(cp + 4, "%d%c", &pid, &placeholder) == 2) {
buf = cp + 10; // skip-it-all
return pid;
}
@@ -392,8 +392,8 @@
// Mediatek kernels with modified printk
if (*cp == '[') {
int pid = 0;
- char dummy;
- if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+ char placeholder;
+ if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &placeholder) == 2) {
return pid;
}
break; // Only the first one
@@ -703,7 +703,7 @@
p = " ";
b = 1;
}
- // paranoid sanity check, can not happen ...
+ // This shouldn't happen, but clamp the size if it does.
if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
b = LOGGER_ENTRY_MAX_PAYLOAD;
}
@@ -712,7 +712,7 @@
}
// calculate buffer copy requirements
ssize_t n = 1 + taglen + 1 + b + 1;
- // paranoid sanity check, first two just can not happen ...
+ // Extra checks for likely impossible cases.
if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
return -EINVAL;
}
@@ -722,7 +722,7 @@
// If we malloc'd this buffer, we could get away without n's USHRT_MAX
// test above, but we would then required a max(n, USHRT_MAX) as
// truncating length argument to logbuf->log() below. Gain is protection
- // of stack sanity and speedup, loss is truncated long-line content.
+ // against stack corruption and speedup, loss is truncated long-line content.
char newstr[n];
char* np = newstr;
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 3cd8fde..87069b3 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -26,7 +26,10 @@
#include <unistd.h>
#include <list>
+#include <vector>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <private/android_logger.h>
#include "LogBufferElement.h"
@@ -60,7 +63,9 @@
return std::string(msg, len);
}
-LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
+LogStatistics::LogStatistics(bool enable_statistics, bool track_total_size,
+ std::optional<log_time> start_time)
+ : enable(enable_statistics), track_total_size_(track_total_size) {
log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
mSizes[id] = 0;
@@ -68,8 +73,13 @@
mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
- mOldest[id] = now;
- mNewest[id] = now;
+ if (start_time) {
+ mOldest[id] = *start_time;
+ mNewest[id] = *start_time;
+ } else {
+ mOldest[id] = now;
+ mNewest[id] = now;
+ }
mNewestDropped[id] = now;
}
}
@@ -113,10 +123,15 @@
++mElementsTotal[log_id];
}
-void LogStatistics::Add(const LogStatisticsElement& element) {
+void LogStatistics::Add(LogStatisticsElement element) {
auto lock = std::lock_guard{lock_};
+
+ if (!track_total_size_) {
+ element.total_len = element.msg_len;
+ }
+
log_id_t log_id = element.log_id;
- uint16_t size = element.msg_len;
+ uint16_t size = element.total_len;
mSizes[log_id] += size;
++mElements[log_id];
@@ -184,10 +199,15 @@
}
}
-void LogStatistics::Subtract(const LogStatisticsElement& element) {
+void LogStatistics::Subtract(LogStatisticsElement element) {
auto lock = std::lock_guard{lock_};
+
+ if (!track_total_size_) {
+ element.total_len = element.msg_len;
+ }
+
log_id_t log_id = element.log_id;
- uint16_t size = element.msg_len;
+ uint16_t size = element.total_len;
mSizes[log_id] -= size;
--mElements[log_id];
if (element.dropped_count) {
@@ -230,7 +250,9 @@
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::Drop(const LogStatisticsElement& element) {
+void LogStatistics::Drop(LogStatisticsElement element) {
+ CHECK_EQ(element.dropped_count, 0U);
+
auto lock = std::lock_guard{lock_};
log_id_t log_id = element.log_id;
uint16_t size = element.msg_len;
@@ -265,6 +287,43 @@
tagNameTable.Subtract(TagNameKey(element), element);
}
+void LogStatistics::Erase(LogStatisticsElement element) {
+ CHECK_GT(element.dropped_count, 0U);
+ CHECK_EQ(element.msg_len, 0U);
+
+ auto lock = std::lock_guard{lock_};
+
+ if (!track_total_size_) {
+ element.total_len = 0;
+ }
+
+ log_id_t log_id = element.log_id;
+ --mElements[log_id];
+ --mDroppedElements[log_id];
+ mSizes[log_id] -= element.total_len;
+
+ uidTable[log_id].Erase(element.uid, element);
+ if (element.uid == AID_SYSTEM) {
+ pidSystemTable[log_id].Erase(element.pid, element);
+ }
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.Erase(element.pid, element);
+ tidTable.Erase(element.tid, element);
+
+ uint32_t tag = element.tag;
+ if (tag) {
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.Erase(tag, element);
+ } else {
+ tagTable.Erase(tag, element);
+ }
+ }
+}
+
const char* LogStatistics::UidToName(uid_t uid) const {
auto lock = std::lock_guard{lock_};
return UidToNameLocked(uid);
@@ -733,6 +792,31 @@
return output;
}
+std::string LogStatistics::ReportInteresting() const {
+ auto lock = std::lock_guard{lock_};
+
+ std::vector<std::string> items;
+
+ log_id_for_each(i) { items.emplace_back(std::to_string(mElements[i])); }
+
+ log_id_for_each(i) { items.emplace_back(std::to_string(mSizes[i])); }
+
+ log_id_for_each(i) {
+ items.emplace_back(std::to_string(overhead_[i] ? *overhead_[i] : mSizes[i]));
+ }
+
+ log_id_for_each(i) {
+ uint64_t oldest = mOldest[i].msec() / 1000;
+ uint64_t newest = mNewest[i].msec() / 1000;
+
+ int span = newest - oldest;
+
+ items.emplace_back(std::to_string(span));
+ }
+
+ return android::base::Join(items, ",");
+}
+
std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
auto lock = std::lock_guard{lock_};
@@ -876,12 +960,20 @@
if (els) {
oldLength = output.length();
if (spaces < 0) spaces = 0;
- // estimate the std::list overhead.
- static const size_t overhead =
- ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
- -sizeof(uint64_t)) +
- sizeof(std::list<LogBufferElement*>);
- size_t szs = mSizes[id] + els * overhead;
+ size_t szs = 0;
+ if (overhead_[id]) {
+ szs = *overhead_[id];
+ } else if (track_total_size_) {
+ szs = mSizes[id];
+ } else {
+ // Legacy fallback for Chatty without track_total_size_
+ // Estimate the size of this element in the parent std::list<> by adding two void*'s
+ // corresponding to the next/prev pointers and aligning to 64 bit.
+ static const size_t overhead =
+ (sizeof(LogBufferElement) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) &
+ -sizeof(uint64_t);
+ szs = mSizes[id] + els * overhead;
+ }
totalSize += szs;
output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
spaces -= output.length() - oldLength;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 5f55802..e222d3f 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -57,6 +57,7 @@
uint16_t msg_len;
uint16_t dropped_count;
log_id_t log_id;
+ uint16_t total_len;
};
template <typename TKey, typename TEntry>
@@ -172,6 +173,13 @@
}
}
+ void Erase(const TKey& key, const LogStatisticsElement& element) {
+ iterator it = map.find(key);
+ if (it != map.end()) {
+ it->second.Erase(element);
+ }
+ }
+
iterator begin() { return map.begin(); }
const_iterator begin() const { return map.begin(); }
iterator end() { return map.end(); }
@@ -181,15 +189,17 @@
class EntryBase {
public:
EntryBase() : size_(0) {}
- explicit EntryBase(const LogStatisticsElement& element) : size_(element.msg_len) {}
+ explicit EntryBase(const LogStatisticsElement& element) : size_(element.total_len) {}
size_t getSizes() const { return size_; }
- void Add(const LogStatisticsElement& element) { size_ += element.msg_len; }
+ void Add(const LogStatisticsElement& element) { size_ += element.total_len; }
bool Subtract(const LogStatisticsElement& element) {
- size_ -= element.msg_len;
- return !size_;
+ size_ -= element.total_len;
+ return size_ == 0;
}
+ void Drop(const LogStatisticsElement& element) { size_ -= element.msg_len; }
+ void Erase(const LogStatisticsElement& element) { size_ -= element.total_len; }
static constexpr size_t PRUNED_LEN = 14;
static constexpr size_t TOTAL_LEN = 80;
@@ -229,11 +239,11 @@
}
bool Subtract(const LogStatisticsElement& element) {
dropped_ -= element.dropped_count;
- return EntryBase::Subtract(element) && !dropped_;
+ return EntryBase::Subtract(element) && dropped_ == 0;
}
void Drop(const LogStatisticsElement& element) {
dropped_ += 1;
- EntryBase::Subtract(element);
+ EntryBase::Drop(element);
}
private:
@@ -505,20 +515,24 @@
}
public:
- explicit LogStatistics(bool enable_statistics);
+ LogStatistics(bool enable_statistics, bool track_total_size,
+ std::optional<log_time> start_time = {});
void AddTotal(log_id_t log_id, uint16_t size) EXCLUDES(lock_);
- void Add(const LogStatisticsElement& entry) EXCLUDES(lock_);
- void Subtract(const LogStatisticsElement& entry) EXCLUDES(lock_);
- // entry->setDropped(1) must follow this call
- void Drop(const LogStatisticsElement& entry) EXCLUDES(lock_);
- // Correct for coalescing two entries referencing dropped content
- void Erase(const LogStatisticsElement& element) EXCLUDES(lock_) {
- auto lock = std::lock_guard{lock_};
- log_id_t log_id = element.log_id;
- --mElements[log_id];
- --mDroppedElements[log_id];
- }
+
+ // Add is for adding an element to the log buffer. It may be a chatty element in the case of
+ // log deduplication. Add the total size of the element to statistics.
+ void Add(LogStatisticsElement entry) EXCLUDES(lock_);
+ // Subtract is for removing an element from the log buffer. It may be a chatty element.
+ // Subtract the total size of the element from statistics.
+ void Subtract(LogStatisticsElement entry) EXCLUDES(lock_);
+ // Drop is for converting a normal element into a chatty element. entry->setDropped(1) must
+ // follow this call. Subtract only msg_len from statistics, since a chatty element will remain.
+ void Drop(LogStatisticsElement entry) EXCLUDES(lock_);
+ // Erase is for coalescing two chatty elements into one. Erase() is called on the element that
+ // is removed from the log buffer. Subtract the total size of the element, which is by
+ // definition only the size of the LogBufferElement + list overhead for chatty elements.
+ void Erase(LogStatisticsElement element) EXCLUDES(lock_);
void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
size_t* second_worst_sizes) const EXCLUDES(lock_);
@@ -533,6 +547,9 @@
// Snapshot of the sizes for a given log buffer.
size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
auto lock = std::lock_guard{lock_};
+ if (overhead_[id]) {
+ return *overhead_[id];
+ }
return mSizes[id];
}
// TODO: Get rid of this entirely.
@@ -540,12 +557,18 @@
return SizesTotal;
}
+ std::string ReportInteresting() const EXCLUDES(lock_);
std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
const char* PidToName(pid_t pid) const EXCLUDES(lock_);
uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
const char* UidToName(uid_t uid) const EXCLUDES(lock_);
+ void set_overhead(log_id_t id, size_t size) {
+ auto lock = std::lock_guard{lock_};
+ overhead_[id] = size;
+ }
+
private:
template <typename TKey, typename TEntry>
void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
@@ -559,4 +582,7 @@
const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
mutable std::mutex lock_;
+ bool track_total_size_;
+
+ std::optional<size_t> overhead_[LOG_ID_MAX] GUARDED_BY(lock_);
};
diff --git a/logd/README.property b/logd/README.property
index ab9c4d4..8fd7f48 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -7,8 +7,8 @@
ro.logd.auditd.events bool true selinux audit messages sent to events.
persist.logd.security bool false Enable security buffer.
ro.organization_owned bool false Override persist.logd.security to false
-ro.logd.kernel bool+ svelte+ Enable klogd daemon
-ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
+ro.logd.kernel bool svelte+ Enable klogd daemon
+logd.statistics bool svelte+ Enable logcat -S statistics.
ro.debuggable number if not "1", logd.statistics &
ro.logd.kernel default false.
logd.logpersistd.enable bool auto Safe to start logpersist daemon service
@@ -52,13 +52,13 @@
log.tag.<tag> string persist The <tag> specific logging level.
persist.log.tag.<tag> string build default for log.tag.<tag>
+logd.buffer_type string (empty) Set the log buffer type. Current choices are 'simple',
+ 'chatty', or 'serialized'. Defaults to 'chatty' if empty.
+
NB:
- auto - managed by /init
-- bool+ - "true", "false" and comma separated list of "eng" (forced false if
- ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
- true).
- svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.debuggable for details.
+- svelte+ - If empty, default to true if `ro.config.low_ram == false && ro.debuggable == true`
- ro - <base property> temporary override, ro.<base property> platform default.
- persist - <base property> override, persist.<base property> platform default.
- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/README.replay.md b/logd/README.replay.md
new file mode 100644
index 0000000..5f7ec9e
--- /dev/null
+++ b/logd/README.replay.md
@@ -0,0 +1,46 @@
+logd can record and replay log messages for offline analysis.
+
+Recording Messages
+------------------
+
+logd has a `RecordingLogBuffer` buffer that records messages to /data/misc/logd/recorded-messages.
+It stores messages in memory until that file is accessible, in order to capture all messages since
+the beginning of boot. It is only meant for logging developers to use and must be manually enabled
+in by adding `RecordingLogBuffer.cpp` to `Android.bp` and setting
+`log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);` in `main.cpp`.
+
+Recording messages may delay the Log() function from completing and it is highly recommended to make
+the logd socket in `liblog` blocking, by removing `SOCK_NONBLOCK` from the `socket()` call in
+`liblog/logd_writer.cpp`.
+
+Replaying Messages
+------------------
+
+Recorded messages can be replayed offline with the `replay_messages` tool. It runs on host and
+device and supports the following options:
+
+1. `interesting` - this prints 'interesting' statistics for each of the log buffer types (simple,
+ chatty, serialized). The statistics are:
+ 1. Log Entry Count
+ 2. Size (the uncompressed size of the log messages in bytes)
+ 3. Overhead (the total cost of the log messages in memory in bytes)
+ 4. Range (the range of time that the logs cover in seconds)
+2. `memory_usage BUFFER_TYPE` - this prints the memory usage (sum of private dirty pages of the
+ `replay_messages` process). Note that the input file is mmap()'ed as RO/Shared so it does not
+ appear in these dirty pages, and a baseline is taken before allocating the log buffers, so only
+ their contributions are measured. The tool outputs the memory usage every 100,000 messages.
+3. `latency BUFFER_TYPE` - this prints statistics of the latency of the Log() function for the given
+ buffer type. It specifically prints the 1st, 2nd, and 3rd quartiles; the 95th, 99th, and 99.99th
+ percentiles; and the maximum latency.
+4. `print_logs BUFFER_TYPE [buffers] [print_point]` - this prints the logs as processed by the given
+ buffer_type from the buffers specified by `buffers` starting after the number of logs specified by
+ `print_point` have been logged. This acts as if a user called `logcat` immediately after the
+ specified logs have been logged, which is particularly useful since it will show the chatty
+ pruning messages at that point. It additionally prints the statistics from `logcat -S` after the
+ logs.
+ `buffers` is a comma separated list of the numeric buffer id values from `<android/log.h>`. For
+ example, `0,1,3` represents the main, radio, and system buffers. It can can also be `all`.
+ `print_point` is an positive integer. If it is unspecified, logs are printed after the entire
+ input file is consumed.
+5. `nothing BUFFER_TYPE` - this does nothing other than read the input file and call Log() for the
+ given buffer type. This is used for profiling CPU usage of strictly the log buffer.
diff --git a/logd/RecordedLogMessage.h b/logd/RecordedLogMessage.h
new file mode 100644
index 0000000..f18c422
--- /dev/null
+++ b/logd/RecordedLogMessage.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <log/log_time.h>
+
+struct __attribute__((packed)) RecordedLogMessage {
+ uint32_t uid;
+ uint32_t pid;
+ uint32_t tid;
+ log_time realtime;
+ uint16_t msg_len;
+ uint8_t log_id;
+};
diff --git a/logd/RecordingLogBuffer.cpp b/logd/RecordingLogBuffer.cpp
new file mode 100644
index 0000000..f5991f3
--- /dev/null
+++ b/logd/RecordingLogBuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RecordingLogBuffer.h"
+
+#include <android-base/file.h>
+
+static void WriteLogMessage(int fd, const RecordedLogMessage& meta, const std::string& msg) {
+ android::base::WriteFully(fd, &meta, sizeof(meta));
+ android::base::WriteFully(fd, msg.c_str(), meta.msg_len);
+}
+
+void RecordingLogBuffer::RecordLogMessage(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, const char* msg, uint16_t len) {
+ auto lock = std::lock_guard{lock_};
+ if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ len = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+
+ RecordedLogMessage recorded_log_message = {
+ .uid = uid,
+ .pid = static_cast<uint32_t>(pid),
+ .tid = static_cast<uint32_t>(tid),
+ .realtime = realtime,
+ .msg_len = len,
+ .log_id = static_cast<uint8_t>(log_id),
+ };
+
+ if (!fd_.ok()) {
+ fd_.reset(open("/data/misc/logd/recorded-messages",
+ O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, 0666));
+ if (!fd_.ok()) {
+ since_boot_messages_.emplace_back(recorded_log_message, std::string(msg, len));
+ return;
+ } else {
+ for (const auto& [meta, msg] : since_boot_messages_) {
+ WriteLogMessage(fd_.get(), meta, msg);
+ }
+ }
+ }
+
+ WriteLogMessage(fd_.get(), recorded_log_message, std::string(msg, len));
+}
+
+int RecordingLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) {
+ RecordLogMessage(log_id, realtime, uid, pid, tid, msg, len);
+ return SimpleLogBuffer::Log(log_id, realtime, uid, pid, tid, msg, len);
+}
\ No newline at end of file
diff --git a/logd/RecordingLogBuffer.h b/logd/RecordingLogBuffer.h
new file mode 100644
index 0000000..49a0aba
--- /dev/null
+++ b/logd/RecordingLogBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SimpleLogBuffer.h"
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "RecordedLogMessage.h"
+
+class RecordingLogBuffer : public SimpleLogBuffer {
+ public:
+ RecordingLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats)
+ : SimpleLogBuffer(reader_list, tags, stats) {}
+
+ int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+ uint16_t len) override;
+
+ private:
+ void RecordLogMessage(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len);
+
+ std::vector<std::pair<RecordedLogMessage, std::string>> since_boot_messages_;
+ android::base::unique_fd fd_;
+};
diff --git a/logd/ReplayMessages.cpp b/logd/ReplayMessages.cpp
new file mode 100644
index 0000000..73b0bd0
--- /dev/null
+++ b/logd/ReplayMessages.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include <chrono>
+#include <map>
+
+#include <android-base/file.h>
+#include <android-base/mapped_file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/log.h>
+#include <log/log_time.h>
+#include <log/logprint.h>
+
+#include "ChattyLogBuffer.h"
+#include "LogBuffer.h"
+#include "LogStatistics.h"
+#include "RecordedLogMessage.h"
+#include "SerializedLogBuffer.h"
+#include "SimpleLogBuffer.h"
+
+using android::base::MappedFile;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::Split;
+
+#ifndef __ANDROID__
+// This is hard coded to 1MB on host. On device use persist.logd.size to configure.
+unsigned long __android_logger_get_buffer_size(log_id_t) {
+ return 1 * 1024 * 1024;
+}
+
+bool __android_logger_valid_buffer_size(unsigned long) {
+ return true;
+}
+#endif
+
+char* android::uidToName(uid_t) {
+ return nullptr;
+}
+
+static size_t GetPrivateDirty() {
+ // Allocate once and hope that we don't need to reallocate >40000, to prevent heap fragmentation
+ static std::string smaps(40000, '\0');
+ android::base::ReadFileToString("/proc/self/smaps", &smaps);
+
+ size_t result = 0;
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = smaps.find("Private_Dirty:", base);
+ if (found == smaps.npos) break;
+
+ found += sizeof("Private_Dirty:");
+
+ result += atoi(&smaps[found]);
+
+ base = found + 1;
+ }
+
+ return result;
+}
+
+static AndroidLogFormat* GetLogFormat() {
+ static AndroidLogFormat* format = [] {
+ auto* format = android_log_format_new();
+ android_log_setPrintFormat(format, android_log_formatFromString("threadtime"));
+ android_log_setPrintFormat(format, android_log_formatFromString("uid"));
+ return format;
+ }();
+ return format;
+}
+
+static void PrintMessage(struct log_msg* buf) {
+ bool is_binary =
+ buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
+
+ AndroidLogEntry entry;
+ int err;
+ if (is_binary) {
+ char binaryMsgBuf[1024];
+ err = android_log_processBinaryLogBuffer(&buf->entry, &entry, nullptr, binaryMsgBuf,
+ sizeof(binaryMsgBuf));
+ } else {
+ err = android_log_processLogBuffer(&buf->entry, &entry);
+ }
+ if (err < 0) {
+ fprintf(stderr, "Error parsing log message\n");
+ }
+
+ android_log_printLogLine(GetLogFormat(), STDOUT_FILENO, &entry);
+}
+
+static log_time GetFirstTimeStamp(const MappedFile& recorded_messages) {
+ if (sizeof(RecordedLogMessage) >= recorded_messages.size()) {
+ fprintf(stderr, "At least one log message must be present in the input\n");
+ exit(1);
+ }
+
+ auto* meta = reinterpret_cast<RecordedLogMessage*>(recorded_messages.data());
+ return meta->realtime;
+}
+
+class StdoutWriter : public LogWriter {
+ public:
+ StdoutWriter() : LogWriter(0, true) {}
+ bool Write(const logger_entry& entry, const char* message) override {
+ struct log_msg log_msg;
+ log_msg.entry = entry;
+ if (log_msg.entry.len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ fprintf(stderr, "payload too large %" PRIu16, log_msg.entry.len);
+ exit(1);
+ }
+ memcpy(log_msg.msg(), message, log_msg.entry.len);
+
+ PrintMessage(&log_msg);
+
+ return true;
+ }
+
+ std::string name() const override { return "stdout writer"; }
+};
+
+class Operation {
+ public:
+ virtual ~Operation() {}
+
+ virtual void Begin() {}
+ virtual void Log(const RecordedLogMessage& meta, const char* msg) = 0;
+ virtual void End() {}
+};
+
+class PrintInteresting : public Operation {
+ public:
+ PrintInteresting(log_time first_log_timestamp)
+ : stats_simple_{false, false, first_log_timestamp},
+ stats_chatty_{false, false, first_log_timestamp},
+ stats_serialized_{false, true, first_log_timestamp} {}
+
+ void Begin() override {
+ printf("message_count,simple_main_lines,simple_radio_lines,simple_events_lines,simple_"
+ "system_lines,simple_crash_lines,simple_stats_lines,simple_security_lines,simple_"
+ "kernel_lines,simple_main_size,simple_radio_size,simple_events_size,simple_system_"
+ "size,simple_crash_size,simple_stats_size,simple_security_size,simple_kernel_size,"
+ "simple_main_overhead,simple_radio_overhead,simple_events_overhead,simple_system_"
+ "overhead,simple_crash_overhead,simple_stats_overhead,simple_security_overhead,"
+ "simple_kernel_overhead,simple_main_range,simple_radio_range,simple_events_range,"
+ "simple_system_range,simple_crash_range,simple_stats_range,simple_security_range,"
+ "simple_kernel_range,chatty_main_lines,chatty_radio_lines,chatty_events_lines,"
+ "chatty_system_lines,chatty_crash_lines,chatty_stats_lines,chatty_security_lines,"
+ "chatty_"
+ "kernel_lines,chatty_main_size,chatty_radio_size,chatty_events_size,chatty_system_"
+ "size,chatty_crash_size,chatty_stats_size,chatty_security_size,chatty_kernel_size,"
+ "chatty_main_overhead,chatty_radio_overhead,chatty_events_overhead,chatty_system_"
+ "overhead,chatty_crash_overhead,chatty_stats_overhead,chatty_security_overhead,"
+ "chatty_kernel_overhead,chatty_main_range,chatty_radio_range,chatty_events_range,"
+ "chatty_system_range,chatty_crash_range,chatty_stats_range,chatty_security_range,"
+ "chatty_kernel_range,serialized_main_lines,serialized_radio_lines,serialized_events_"
+ "lines,serialized_"
+ "system_lines,serialized_crash_lines,serialized_stats_lines,serialized_security_"
+ "lines,serialized_"
+ "kernel_lines,serialized_main_size,serialized_radio_size,serialized_events_size,"
+ "serialized_system_"
+ "size,serialized_crash_size,serialized_stats_size,serialized_security_size,"
+ "serialized_kernel_size,"
+ "serialized_main_overhead,serialized_radio_overhead,serialized_events_overhead,"
+ "serialized_system_"
+ "overhead,serialized_crash_overhead,serialized_stats_overhead,serialized_security_"
+ "overhead,"
+ "serialized_kernel_overhead,serialized_main_range,serialized_radio_range,serialized_"
+ "events_range,"
+ "serialized_system_range,serialized_crash_range,serialized_stats_range,serialized_"
+ "security_range,"
+ "serialized_kernel_range\n");
+ }
+
+ void Log(const RecordedLogMessage& meta, const char* msg) override {
+ simple_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
+ meta.pid, meta.tid, msg, meta.msg_len);
+
+ chatty_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
+ meta.pid, meta.tid, msg, meta.msg_len);
+
+ serialized_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
+ meta.pid, meta.tid, msg, meta.msg_len);
+
+ if (num_message_ % 10000 == 0) {
+ printf("%" PRIu64 ",%s,%s,%s\n", num_message_,
+ stats_simple_.ReportInteresting().c_str(),
+ stats_chatty_.ReportInteresting().c_str(),
+ stats_serialized_.ReportInteresting().c_str());
+ }
+
+ num_message_++;
+ }
+
+ private:
+ uint64_t num_message_ = 1;
+
+ LogReaderList reader_list_;
+ LogTags tags_;
+ PruneList prune_list_;
+
+ LogStatistics stats_simple_;
+ SimpleLogBuffer simple_log_buffer_{&reader_list_, &tags_, &stats_simple_};
+
+ LogStatistics stats_chatty_;
+ ChattyLogBuffer chatty_log_buffer_{&reader_list_, &tags_, &prune_list_, &stats_chatty_};
+
+ LogStatistics stats_serialized_;
+ SerializedLogBuffer serialized_log_buffer_{&reader_list_, &tags_, &stats_serialized_};
+};
+
+class SingleBufferOperation : public Operation {
+ public:
+ SingleBufferOperation(log_time first_log_timestamp, const char* buffer) {
+ if (!strcmp(buffer, "simple")) {
+ stats_.reset(new LogStatistics{false, false, first_log_timestamp});
+ log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, stats_.get()));
+ } else if (!strcmp(buffer, "chatty")) {
+ stats_.reset(new LogStatistics{false, false, first_log_timestamp});
+ log_buffer_.reset(
+ new ChattyLogBuffer(&reader_list_, &tags_, &prune_list_, stats_.get()));
+ } else if (!strcmp(buffer, "serialized")) {
+ stats_.reset(new LogStatistics{false, true, first_log_timestamp});
+ log_buffer_.reset(new SerializedLogBuffer(&reader_list_, &tags_, stats_.get()));
+ } else {
+ fprintf(stderr, "invalid log buffer type '%s'\n", buffer);
+ abort();
+ }
+ }
+
+ void Log(const RecordedLogMessage& meta, const char* msg) override {
+ PreOperation();
+ log_buffer_->Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid, meta.pid,
+ meta.tid, msg, meta.msg_len);
+
+ Operation();
+
+ num_message_++;
+ }
+
+ virtual void PreOperation() {}
+ virtual void Operation() {}
+
+ protected:
+ uint64_t num_message_ = 1;
+
+ LogReaderList reader_list_;
+ LogTags tags_;
+ PruneList prune_list_;
+
+ std::unique_ptr<LogStatistics> stats_;
+ std::unique_ptr<LogBuffer> log_buffer_;
+};
+
+class PrintMemory : public SingleBufferOperation {
+ public:
+ PrintMemory(log_time first_log_timestamp, const char* buffer)
+ : SingleBufferOperation(first_log_timestamp, buffer) {}
+
+ void Operation() override {
+ if (num_message_ % 100000 == 0) {
+ printf("%" PRIu64 ",%s\n", num_message_,
+ std::to_string(GetPrivateDirty() - baseline_memory_).c_str());
+ }
+ }
+
+ private:
+ size_t baseline_memory_ = GetPrivateDirty();
+};
+
+class PrintLogs : public SingleBufferOperation {
+ public:
+ PrintLogs(log_time first_log_timestamp, const char* buffer, const char* buffers,
+ const char* print_point)
+ : SingleBufferOperation(first_log_timestamp, buffer) {
+ if (buffers != nullptr) {
+ if (strcmp(buffers, "all") != 0) {
+ std::vector<int> buffer_ids;
+ auto string_ids = Split(buffers, ",");
+ for (const auto& string_id : string_ids) {
+ int result;
+ if (!ParseInt(string_id, &result, 0, 7)) {
+ fprintf(stderr, "Could not parse buffer_id '%s'\n", string_id.c_str());
+ exit(1);
+ }
+ buffer_ids.emplace_back(result);
+ }
+ mask_ = 0;
+ for (const auto& buffer_id : buffer_ids) {
+ mask_ |= 1 << buffer_id;
+ }
+ }
+ }
+ if (print_point != nullptr) {
+ uint64_t result = 0;
+ if (!ParseUint(print_point, &result)) {
+ fprintf(stderr, "Could not parse print point '%s'\n", print_point);
+ exit(1);
+ }
+ print_point_ = result;
+ }
+ }
+
+ void Operation() override {
+ if (print_point_ && num_message_ >= *print_point_) {
+ End();
+ exit(0);
+ }
+ }
+
+ void End() {
+ std::unique_ptr<LogWriter> test_writer(new StdoutWriter());
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, mask_);
+ log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr);
+
+ auto stats_string = stats_->Format(0, 0, mask_);
+ printf("%s\n", stats_string.c_str());
+ }
+
+ private:
+ LogMask mask_ = kLogMaskAll;
+ std::optional<uint64_t> print_point_;
+};
+
+class PrintLatency : public SingleBufferOperation {
+ public:
+ PrintLatency(log_time first_log_timestamp, const char* buffer)
+ : SingleBufferOperation(first_log_timestamp, buffer) {}
+
+ void PreOperation() override { operation_start_ = std::chrono::steady_clock::now(); }
+
+ void Operation() override {
+ auto end = std::chrono::steady_clock::now();
+ auto duration = (end - operation_start_).count();
+ durations_.emplace_back(duration);
+ }
+
+ void End() {
+ std::sort(durations_.begin(), durations_.end());
+ auto q1 = durations_.size() / 4;
+ auto q2 = durations_.size() / 2;
+ auto q3 = 3 * durations_.size() / 4;
+
+ auto p95 = 95 * durations_.size() / 100;
+ auto p99 = 99 * durations_.size() / 100;
+ auto p9999 = 9999 * durations_.size() / 10000;
+
+ printf("q1: %lld q2: %lld q3: %lld p95: %lld p99: %lld p99.99: %lld max: %lld\n",
+ durations_[q1], durations_[q2], durations_[q3], durations_[p95], durations_[p99],
+ durations_[p9999], durations_.back());
+ }
+
+ private:
+ std::chrono::steady_clock::time_point operation_start_;
+ std::vector<long long> durations_;
+};
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s FILE OPERATION [BUFFER] [OPTIONS]\n", argv[0]);
+ return 1;
+ }
+
+ if (strcmp(argv[2], "interesting") != 0 && argc < 4) {
+ fprintf(stderr, "Operations other than 'interesting' require a BUFFER argument\n");
+ return 1;
+ }
+
+ int recorded_messages_fd = open(argv[1], O_RDONLY);
+ if (recorded_messages_fd == -1) {
+ fprintf(stderr, "Couldn't open input file\n");
+ return 1;
+ }
+ struct stat fd_stat;
+ if (fstat(recorded_messages_fd, &fd_stat) != 0) {
+ fprintf(stderr, "Couldn't fstat input file\n");
+ return 1;
+ }
+ auto recorded_messages = MappedFile::FromFd(recorded_messages_fd, 0,
+ static_cast<size_t>(fd_stat.st_size), PROT_READ);
+ if (recorded_messages == nullptr) {
+ fprintf(stderr, "Couldn't mmap input file\n");
+ return 1;
+ }
+
+ // LogStatistics typically uses 'now()' to initialize its log range state, but this doesn't work
+ // when replaying older logs, so we instead give it the timestamp from the first log.
+ log_time first_log_timestamp = GetFirstTimeStamp(*recorded_messages);
+
+ std::unique_ptr<Operation> operation;
+ if (!strcmp(argv[2], "interesting")) {
+ operation.reset(new PrintInteresting(first_log_timestamp));
+ } else if (!strcmp(argv[2], "memory_usage")) {
+ operation.reset(new PrintMemory(first_log_timestamp, argv[3]));
+ } else if (!strcmp(argv[2], "latency")) {
+ operation.reset(new PrintLatency(first_log_timestamp, argv[3]));
+ } else if (!strcmp(argv[2], "print_logs")) {
+ operation.reset(new PrintLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr,
+ argc > 5 ? argv[5] : nullptr));
+ } else if (!strcmp(argv[2], "nothing")) {
+ operation.reset(new SingleBufferOperation(first_log_timestamp, argv[3]));
+ } else {
+ fprintf(stderr, "unknown operation '%s'\n", argv[2]);
+ return 1;
+ }
+
+ // LogBuffer::Log() won't log without this on host.
+ __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
+ // But we still want to suppress messages <= error to not interrupt the rest of the output.
+ __android_log_set_logger([](const struct __android_log_message* log_message) {
+ if (log_message->priority < ANDROID_LOG_ERROR) {
+ return;
+ }
+ __android_log_stderr_logger(log_message);
+ });
+
+ operation->Begin();
+
+ uint64_t read_position = 0;
+ while (read_position + sizeof(RecordedLogMessage) < recorded_messages->size()) {
+ auto* meta =
+ reinterpret_cast<RecordedLogMessage*>(recorded_messages->data() + read_position);
+ if (read_position + sizeof(RecordedLogMessage) + meta->msg_len >=
+ recorded_messages->size()) {
+ break;
+ }
+ char* msg = recorded_messages->data() + read_position + sizeof(RecordedLogMessage);
+ read_position += sizeof(RecordedLogMessage) + meta->msg_len;
+
+ operation->Log(*meta, msg);
+ }
+
+ operation->End();
+
+ return 0;
+}
diff --git a/logd/SerializedFlushToState.cpp b/logd/SerializedFlushToState.cpp
index 2633348..b02ccc3 100644
--- a/logd/SerializedFlushToState.cpp
+++ b/logd/SerializedFlushToState.cpp
@@ -31,7 +31,7 @@
SerializedFlushToState::~SerializedFlushToState() {
log_id_for_each(i) {
if (log_positions_[i]) {
- log_positions_[i]->buffer_it->DecReaderRefCount(true);
+ log_positions_[i]->buffer_it->DecReaderRefCount();
}
}
}
@@ -78,7 +78,7 @@
logs_needed_from_next_position_[log_id] = true;
} else {
// Otherwise, if there is another buffer piece, move to that and do the same check.
- buffer_it->DecReaderRefCount(true);
+ buffer_it->DecReaderRefCount();
++buffer_it;
buffer_it->IncReaderRefCount();
log_positions_[log_id]->read_offset = 0;
@@ -134,7 +134,7 @@
}
// // Decrease the ref count since we're deleting our reference.
- buffer_it->DecReaderRefCount(false);
+ buffer_it->DecReaderRefCount();
// Delete in the reference.
log_positions_[log_id].reset();
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index 0f30794..972a3f3 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -32,12 +32,6 @@
Init();
}
-SerializedLogBuffer::~SerializedLogBuffer() {
- if (deleter_thread_.joinable()) {
- deleter_thread_.join();
- }
-}
-
void SerializedLogBuffer::Init() {
log_id_for_each(i) {
if (SetSize(i, __android_logger_get_buffer_size(i))) {
@@ -109,69 +103,27 @@
}
void SerializedLogBuffer::MaybePrune(log_id_t log_id) {
- auto get_total_size = [](const auto& buffer) {
- size_t total_size = 0;
- for (const auto& chunk : buffer) {
- total_size += chunk.PruneSize();
- }
- return total_size;
- };
- size_t total_size = get_total_size(logs_[log_id]);
+ size_t total_size = GetSizeUsed(log_id);
+ size_t after_size = total_size;
if (total_size > max_size_[log_id]) {
Prune(log_id, total_size - max_size_[log_id], 0);
+ after_size = GetSizeUsed(log_id);
LOG(INFO) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
- << " after size: " << get_total_size(logs_[log_id]);
+ << " after size: " << after_size;
}
+
+ stats_->set_overhead(log_id, after_size);
}
-void SerializedLogBuffer::StartDeleterThread() {
- if (deleter_thread_running_) {
- return;
+void SerializedLogBuffer::RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk) {
+ chunk.IncReaderRefCount();
+ int read_offset = 0;
+ while (read_offset < chunk.write_offset()) {
+ auto* entry = chunk.log_entry(read_offset);
+ stats_->Subtract(entry->ToLogStatisticsElement(log_id));
+ read_offset += entry->total_len();
}
- if (deleter_thread_.joinable()) {
- deleter_thread_.join();
- }
- auto new_thread = std::thread([this] { DeleterThread(); });
- deleter_thread_.swap(new_thread);
- deleter_thread_running_ = true;
-}
-
-// Decompresses the chunks, call LogStatistics::Subtract() on each entry, then delete the chunks and
-// the list. Note that the SerializedLogChunk objects have been removed from logs_ and their
-// references have been deleted from any SerializedFlushToState objects, so this can be safely done
-// without holding lock_. It is done in a separate thread to avoid delaying the writer thread.
-void SerializedLogBuffer::DeleterThread() {
- prctl(PR_SET_NAME, "logd.deleter");
- while (true) {
- std::list<SerializedLogChunk> local_chunks_to_delete;
- log_id_t log_id;
- {
- auto lock = std::lock_guard{lock_};
- log_id_for_each(i) {
- if (!chunks_to_delete_[i].empty()) {
- local_chunks_to_delete = std::move(chunks_to_delete_[i]);
- chunks_to_delete_[i].clear();
- log_id = i;
- break;
- }
- }
- if (local_chunks_to_delete.empty()) {
- deleter_thread_running_ = false;
- return;
- }
- }
-
- for (auto& chunk : local_chunks_to_delete) {
- chunk.IncReaderRefCount();
- int read_offset = 0;
- while (read_offset < chunk.write_offset()) {
- auto* entry = chunk.log_entry(read_offset);
- stats_->Subtract(entry->ToLogStatisticsElement(log_id));
- read_offset += entry->total_len();
- }
- chunk.DecReaderRefCount(false);
- }
- }
+ chunk.DecReaderRefCount();
}
void SerializedLogBuffer::NotifyReadersOfPrune(
@@ -197,8 +149,6 @@
}
}
- StartDeleterThread();
-
auto& log_buffer = logs_[log_id];
auto it = log_buffer.begin();
while (it != log_buffer.end()) {
@@ -206,7 +156,7 @@
break;
}
- // Increment ahead of time since we're going to splice this iterator from the list.
+ // Increment ahead of time since we're going to erase this iterator from the list.
auto it_to_prune = it++;
// The sequence number check ensures that all readers have read all logs in this chunk, but
@@ -222,8 +172,8 @@
}
} else {
size_t buffer_size = it_to_prune->PruneSize();
- chunks_to_delete_[log_id].splice(chunks_to_delete_[log_id].end(), log_buffer,
- it_to_prune);
+ RemoveChunkFromStats(log_id, *it_to_prune);
+ log_buffer.erase(it_to_prune);
if (buffer_size >= bytes_to_free) {
return true;
}
@@ -349,19 +299,22 @@
return Prune(id, ULONG_MAX, uid);
}
+unsigned long SerializedLogBuffer::GetSizeUsed(log_id_t id) {
+ size_t total_size = 0;
+ for (const auto& chunk : logs_[id]) {
+ total_size += chunk.PruneSize();
+ }
+ return total_size;
+}
+
unsigned long SerializedLogBuffer::GetSize(log_id_t id) {
auto lock = std::lock_guard{lock_};
- size_t retval = 2 * max_size_[id] / 3; // See the comment in SetSize().
- return retval;
+ return max_size_[id];
}
// New SerializedLogChunk objects will be allocated according to the new size, but older one are
// unchanged. MaybePrune() is called on the log buffer to reduce it to an appropriate size if the
// new size is lower.
-// ChattyLogBuffer/SimpleLogBuffer don't consider the 'Overhead' of LogBufferElement or the
-// std::list<> overhead as part of the log size. SerializedLogBuffer does by its very nature, so
-// the 'size' metric is not compatible between them. Their actual memory usage is between 1.5x and
-// 2x of what they claim to use, so we conservatively set our internal size as size + size / 2.
int SerializedLogBuffer::SetSize(log_id_t id, unsigned long size) {
// Reasonable limits ...
if (!__android_logger_valid_buffer_size(size)) {
@@ -369,7 +322,7 @@
}
auto lock = std::lock_guard{lock_};
- max_size_[id] = size + size / 2;
+ max_size_[id] = size;
MaybePrune(id);
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
index 421d419..a03050e 100644
--- a/logd/SerializedLogBuffer.h
+++ b/logd/SerializedLogBuffer.h
@@ -37,7 +37,6 @@
class SerializedLogBuffer final : public LogBuffer {
public:
SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats);
- ~SerializedLogBuffer();
void Init() override;
int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
@@ -61,9 +60,8 @@
REQUIRES_SHARED(lock_);
void NotifyReadersOfPrune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk)
REQUIRES(reader_list_->reader_threads_lock());
-
- void StartDeleterThread() REQUIRES(lock_);
- void DeleterThread();
+ void RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk);
+ unsigned long GetSizeUsed(log_id_t id) REQUIRES(lock_);
LogReaderList* reader_list_;
LogTags* tags_;
@@ -73,9 +71,5 @@
std::list<SerializedLogChunk> logs_[LOG_ID_MAX] GUARDED_BY(lock_);
RwLock lock_;
- std::list<SerializedLogChunk> chunks_to_delete_[LOG_ID_MAX] GUARDED_BY(lock_);
- std::thread deleter_thread_ GUARDED_BY(lock_);
- bool deleter_thread_running_ GUARDED_BY(lock_) = false;
-
std::atomic<uint64_t> sequence_ = 1;
};
diff --git a/logd/SerializedLogChunk.cpp b/logd/SerializedLogChunk.cpp
index e444856..de641d6 100644
--- a/logd/SerializedLogChunk.cpp
+++ b/logd/SerializedLogChunk.cpp
@@ -31,7 +31,6 @@
<< " size used: " << write_offset_
<< " compressed size: " << compressed_log_.size();
}
- contents_.Resize(0);
}
// TODO: Develop a better reference counting strategy to guard against the case where the writer is
@@ -44,13 +43,13 @@
CompressionEngine::GetInstance().Decompress(compressed_log_, contents_);
}
-void SerializedLogChunk::DecReaderRefCount(bool compress) {
+void SerializedLogChunk::DecReaderRefCount() {
CHECK_NE(reader_ref_count_, 0U);
if (--reader_ref_count_ != 0) {
return;
}
- if (compress && !writer_active_) {
- Compress();
+ if (!writer_active_) {
+ contents_.Resize(0);
}
}
@@ -83,18 +82,19 @@
}
if (new_write_offset == 0) {
- DecReaderRefCount(false);
+ DecReaderRefCount();
return true;
}
- // Clear the old compressed logs and set write_offset_ appropriately for DecReaderRefCount()
- // to compress the new partially cleared log.
+ // Clear the old compressed logs and set write_offset_ appropriately to compress the new
+ // partially cleared log.
if (new_write_offset != write_offset_) {
compressed_log_.Resize(0);
write_offset_ = new_write_offset;
+ Compress();
}
- DecReaderRefCount(true);
+ DecReaderRefCount();
return false;
}
diff --git a/logd/SerializedLogChunk.h b/logd/SerializedLogChunk.h
index 65a1e86..0991eac 100644
--- a/logd/SerializedLogChunk.h
+++ b/logd/SerializedLogChunk.h
@@ -30,9 +30,7 @@
void Compress();
void IncReaderRefCount();
- // Decrease the reader ref count and compress the log if appropriate. `compress` should only be
- // set to false in the case that the log buffer will be deleted afterwards.
- void DecReaderRefCount(bool compress);
+ void DecReaderRefCount();
// Must have no readers referencing this. Return true if there are no logs left in this chunk.
bool ClearUidLogs(uid_t uid, log_id_t log_id, LogStatistics* stats);
@@ -44,12 +42,15 @@
// If this buffer has been compressed, we only consider its compressed size when accounting for
// memory consumption for pruning. This is since the uncompressed log is only by used by
// readers, and thus not a representation of how much these logs cost to keep in memory.
- size_t PruneSize() const { return compressed_log_.size() ?: contents_.size(); }
+ size_t PruneSize() const {
+ return sizeof(*this) + (compressed_log_.size() ?: contents_.size());
+ }
void FinishWriting() {
writer_active_ = false;
+ Compress();
if (reader_ref_count_ == 0) {
- Compress();
+ contents_.Resize(0);
}
}
diff --git a/logd/SerializedLogChunkTest.cpp b/logd/SerializedLogChunkTest.cpp
index 6572503..f10b9c6 100644
--- a/logd/SerializedLogChunkTest.cpp
+++ b/logd/SerializedLogChunkTest.cpp
@@ -27,7 +27,7 @@
TEST(SerializedLogChunk, smoke) {
size_t chunk_size = 10 * 4096;
auto chunk = SerializedLogChunk{chunk_size};
- EXPECT_EQ(chunk_size, chunk.PruneSize());
+ EXPECT_EQ(chunk_size + sizeof(SerializedLogChunk), chunk.PruneSize());
static const char log_message[] = "log message";
size_t expected_total_len = sizeof(SerializedLogEntry) + sizeof(log_message);
@@ -58,7 +58,7 @@
size_t individual_message_size = sizeof(SerializedLogEntry) + sizeof(log_message);
size_t chunk_size = individual_message_size * 3;
auto chunk = SerializedLogChunk{chunk_size};
- EXPECT_EQ(chunk_size, chunk.PruneSize());
+ EXPECT_EQ(chunk_size + sizeof(SerializedLogChunk), chunk.PruneSize());
ASSERT_TRUE(chunk.CanLog(individual_message_size));
EXPECT_NE(nullptr, chunk.Log(1, log_time(), 1000, 1, 1, log_message, sizeof(log_message)));
@@ -113,8 +113,7 @@
TEST(SerializedLogChunk, catch_DecCompressedRef_CHECK) {
size_t chunk_size = 10 * 4096;
auto chunk = SerializedLogChunk{chunk_size};
- EXPECT_DEATH({ chunk.DecReaderRefCount(true); }, "");
- EXPECT_DEATH({ chunk.DecReaderRefCount(false); }, "");
+ EXPECT_DEATH({ chunk.DecReaderRefCount(); }, "");
}
// Check that the CHECK() in ClearUidLogs() if the ref count is greater than 0 is caught.
@@ -123,7 +122,7 @@
auto chunk = SerializedLogChunk{chunk_size};
chunk.IncReaderRefCount();
EXPECT_DEATH({ chunk.ClearUidLogs(1000, LOG_ID_MAIN, nullptr); }, "");
- chunk.DecReaderRefCount(false);
+ chunk.DecReaderRefCount();
}
class UidClearTest : public testing::TestWithParam<bool> {
@@ -144,7 +143,7 @@
check(chunk_);
if (finish_writing) {
- chunk_.DecReaderRefCount(false);
+ chunk_.DecReaderRefCount();
}
}
diff --git a/logd/SerializedLogEntry.h b/logd/SerializedLogEntry.h
index f599abe..2f2c244 100644
--- a/logd/SerializedLogEntry.h
+++ b/logd/SerializedLogEntry.h
@@ -59,6 +59,7 @@
.msg_len = msg_len(),
.dropped_count = 0,
.log_id = log_id,
+ .total_len = total_len(),
};
}
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 1dc996c..8309f95 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -114,7 +114,7 @@
LogReaderList reader_list;
LogTags tags;
PruneList prune_list;
- LogStatistics stats(true);
+ LogStatistics stats(true, true);
std::unique_ptr<LogBuffer> log_buffer;
#ifdef FUZZ_SERIALIZED
log_buffer.reset(new SerializedLogBuffer(&reader_list, &tags, &stats));
diff --git a/logd/logd_test.cpp b/logd/logd_test.cpp
index ed34ea4..307610e 100644
--- a/logd/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -832,127 +832,3 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-
-#ifdef __ANDROID__
-static inline uint32_t get4LE(const uint8_t* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-static inline uint32_t get4LE(const char* src) {
- return get4LE(reinterpret_cast<const uint8_t*>(src));
-}
-#endif
-
-void __android_log_btwrite_multiple__helper(int count) {
-#ifdef __ANDROID__
- log_time ts(CLOCK_MONOTONIC);
- usleep(100);
- log_time ts1(CLOCK_MONOTONIC);
-
- // We fork to create a unique pid for the submitted log messages
- // so that we do not collide with the other _multiple_ tests.
-
- pid_t pid = fork();
-
- if (pid == 0) {
- // child
- for (int i = count; i; --i) {
- ASSERT_LT(
- 0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
- usleep(100);
- }
- ASSERT_LT(0,
- __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
- usleep(1000000);
-
- _exit(0);
- }
-
- siginfo_t info = {};
- ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
- ASSERT_EQ(0, info.si_status);
-
- struct logger_list* logger_list;
- ASSERT_TRUE(nullptr != (logger_list = android_logger_list_open(LOG_ID_EVENTS,
- ANDROID_LOG_NONBLOCK, 0, pid)));
-
- int expected_count = (count < 2) ? count : 2;
- int expected_chatty_count = (count <= 2) ? 0 : 1;
- int expected_identical_count = (count < 2) ? 0 : (count - 2);
- static const int expected_expire_count = 0;
-
- count = 0;
- int second_count = 0;
- int chatty_count = 0;
- int identical_count = 0;
- int expire_count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
- (log_msg.id() != LOG_ID_EVENTS))
- continue;
-
- char* eventData = log_msg.msg();
- if (!eventData) continue;
-
- uint32_t tag = get4LE(eventData);
-
- if ((eventData[4] == EVENT_TYPE_LONG) &&
- (log_msg.entry.len == (4 + 1 + 8))) {
- if (tag != 0) continue;
-
- log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
- if (ts == *tx) {
- ++count;
- } else if (ts1 == *tx) {
- ++second_count;
- }
- } else if (eventData[4] == EVENT_TYPE_STRING) {
- if (tag != CHATTY_LOG_TAG) continue;
- ++chatty_count;
- // int len = get4LE(eventData + 4 + 1);
- log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
- const char* cp;
- if ((cp = strstr(eventData + 4 + 1 + 4, " identical "))) {
- unsigned val = 0;
- sscanf(cp, " identical %u lines", &val);
- identical_count += val;
- } else if ((cp = strstr(eventData + 4 + 1 + 4, " expire "))) {
- unsigned val = 0;
- sscanf(cp, " expire %u lines", &val);
- expire_count += val;
- }
- }
- }
-
- android_logger_list_close(logger_list);
-
- EXPECT_EQ(expected_count, count);
- EXPECT_EQ(1, second_count);
- EXPECT_EQ(expected_chatty_count, chatty_count);
- EXPECT_EQ(expected_identical_count, identical_count);
- EXPECT_EQ(expected_expire_count, expire_count);
-#else
- count = 0;
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(logd, multiple_test_1) {
- __android_log_btwrite_multiple__helper(1);
-}
-
-TEST(logd, multiple_test_2) {
- __android_log_btwrite_multiple__helper(2);
-}
-
-TEST(logd, multiple_test_3) {
- __android_log_btwrite_multiple__helper(3);
-}
-
-TEST(logd, multiple_test_10) {
- __android_log_btwrite_multiple__helper(10);
-}
diff --git a/logd/main.cpp b/logd/main.cpp
index e1ec52b..93dadf5 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
@@ -61,6 +62,10 @@
#include "SerializedLogBuffer.h"
#include "SimpleLogBuffer.h"
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
+using android::base::SetProperty;
+
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
@@ -79,9 +84,10 @@
PLOG(FATAL) << "failed to set batch scheduler";
}
- if (!__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
- prctl(PR_SET_DUMPABLE, 0) == -1) {
- PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
+ if (!GetBoolProperty("ro.debuggable", false)) {
+ if (prctl(PR_SET_DUMPABLE, 0) == -1) {
+ PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
+ }
}
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
@@ -107,6 +113,14 @@
}
}
+// GetBoolProperty that defaults to true if `ro.debuggable == true && ro.config.low_rawm == false`.
+static bool GetBoolPropertyEngSvelteDefault(const std::string& name) {
+ bool default_value =
+ GetBoolProperty("ro.debuggable", false) && !GetBoolProperty("ro.config.low_ram", false);
+
+ return GetBoolProperty(name, default_value);
+}
+
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -233,10 +247,9 @@
}
int fdPmesg = -1;
- bool klogd = __android_logger_property_get_bool(
- "ro.logd.kernel",
- BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ bool klogd = GetBoolPropertyEngSvelteDefault("ro.logd.kernel");
if (klogd) {
+ SetProperty("ro.logd.kernel", "true");
static const char proc_kmsg[] = "/proc/kmsg";
fdPmesg = android_get_control_file(proc_kmsg);
if (fdPmesg < 0) {
@@ -246,7 +259,7 @@
if (fdPmesg < 0) PLOG(ERROR) << "Failed to open " << proc_kmsg;
}
- bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+ bool auditd = GetBoolProperty("ro.logd.auditd", true);
DropPrivs(klogd, auditd);
// A cache of event log tags
@@ -255,28 +268,31 @@
// Pruning configuration.
PruneList prune_list;
- // Partial (required for chatty) or full logging statistics.
- bool enable_full_log_statistics = __android_logger_property_get_bool(
- "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
- BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
- LogStatistics log_statistics(enable_full_log_statistics);
+ std::string buffer_type = GetProperty("logd.buffer_type", "serialized");
- // Serves the purpose of managing the last logs times read on a
- // socket connection, and as a reader lock on a range of log
- // entries.
+ // Partial (required for chatty) or full logging statistics.
+ LogStatistics log_statistics(GetBoolPropertyEngSvelteDefault("logd.statistics"),
+ buffer_type == "serialized");
+
+ // Serves the purpose of managing the last logs times read on a socket connection, and as a
+ // reader lock on a range of log entries.
LogReaderList reader_list;
// LogBuffer is the object which is responsible for holding all log entries.
- LogBuffer* logBuf;
- if (true) {
- logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+ LogBuffer* log_buffer = nullptr;
+ if (buffer_type == "chatty") {
+ log_buffer = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+ } else if (buffer_type == "serialized") {
+ log_buffer = new SerializedLogBuffer(&reader_list, &log_tags, &log_statistics);
+ } else if (buffer_type == "simple") {
+ log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
} else {
- logBuf = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
+ LOG(FATAL) << "buffer_type must be one of 'chatty', 'serialized', or 'simple'";
}
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
- LogReader* reader = new LogReader(logBuf, &reader_list);
+ LogReader* reader = new LogReader(log_buffer, &reader_list);
if (reader->startListener()) {
return EXIT_FAILURE;
}
@@ -284,14 +300,14 @@
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- LogListener* swl = new LogListener(logBuf);
+ LogListener* swl = new LogListener(log_buffer);
if (!swl->StartListener()) {
return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
- CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
+ CommandListener* cl = new CommandListener(log_buffer, &log_tags, &prune_list, &log_statistics);
if (cl->startListener()) {
return EXIT_FAILURE;
}
@@ -301,15 +317,13 @@
// and LogReader is notified to send updates to connected clients.
LogAudit* al = nullptr;
if (auditd) {
- int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
- ? fdDmesg
- : -1;
- al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
+ int dmesg_fd = GetBoolProperty("ro.logd.auditd.dmesg", true) ? fdDmesg : -1;
+ al = new LogAudit(log_buffer, dmesg_fd, &log_statistics);
}
LogKlog* kl = nullptr;
if (klogd) {
- kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
+ kl = new LogKlog(log_buffer, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
}
readDmesg(al, kl);
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index a643062..3907413 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -139,7 +139,7 @@
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
- // Sanity check
+ // Smoke test
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
index beeccb0..03afb21 100644
--- a/qemu_pipe/qemu_pipe.cpp
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -35,7 +35,6 @@
#endif
int qemu_pipe_open(const char* pipeName) {
- // Sanity check.
if (!pipeName) {
errno = EINVAL;
return -1;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index a9d0ed0..77fa94e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -120,6 +120,27 @@
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
+
+# For /vendor_dlkm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /vendor_dlkm partition. Those symlinks are for
+# devices without /vendor_dlkm partition. For devices with /vendor_dlkm
+# partition, mount vendor_dlkm.img under /vendor_dlkm will hide those symlinks.
+# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
+# via /vendor/lib/modules directly.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
+
+# For /odm_dlkm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /odm_dlkm partition. Those symlinks are for
+# devices without /odm_dlkm partition. For devices with /odm_dlkm
+# partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
+# Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
+# via /odm/lib/modules directly.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
diff --git a/rootdir/init.rc b/rootdir/init.rc
index fb6f1be..6ef3bdc 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -81,6 +81,11 @@
# Mount tracefs
mount tracefs tracefs /sys/kernel/tracing
+ # create sys dirctory
+ mkdir /dev/sys 0755 system system
+ mkdir /dev/sys/fs 0755 system system
+ mkdir /dev/sys/block 0755 system system
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
@@ -867,17 +872,26 @@
chown root system /sys/block/zram0/writeback
chmod 0664 /sys/block/zram0/writeback
+ # to access F2FS sysfs on dm-<num> directly
+ mkdir /dev/sys/fs/by-name 0755 system system
+ symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
+
+ # to access dm-<num> sysfs
+ mkdir /dev/sys/block/by-name 0755 system system
+ symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
+
# F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
# to make it too large, since it may bring userdata loss, if they
# are not aware of using fsync()/sync() to prepare sudden power-cut.
- write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
- write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
+ write /dev/sys/fs/by-name/userdata/cp_interval 200
+ write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
+ write /dev/sys/fs/by-name/userdata/iostat_enable 1
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
# Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
- write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
+ write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
index 682a067..7529cb9 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1 +1 @@
-enh@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index d149664..e416fb2 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -56,9 +56,9 @@
SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
- auto dummy = new uint8_t[vec.size()];
- std::copy(vec.begin(), vec.end(), dummy);
- return {dummy, static_cast<uint32_t>(vec.size())};
+ auto buffer = new uint8_t[vec.size()];
+ std::copy(vec.begin(), vec.end(), buffer);
+ return {buffer, static_cast<uint32_t>(vec.size())};
}
Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index f3ef747..750a9d7 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -173,7 +173,7 @@
}
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
- // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ // Empty buffer to allow ForwardCommand to have something to serialize
Buffer request;
GetHmacSharingParametersResponse response;
ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
new file mode 100644
index 0000000..fd353d1
--- /dev/null
+++ b/trusty/trusty-test.mk
@@ -0,0 +1,16 @@
+# 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.
+
+PRODUCT_PACKAGES += \
+ spiproxyd \
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index af97eba..5de1efa 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -591,7 +591,7 @@
return EXIT_SUCCESS;
}
- open_flags = O_RDWR;
+ open_flags = O_RDWR | O_SYNC;
if (init) {
open_flags |= O_CREAT | O_TRUNC;
}
diff --git a/trusty/utils/spiproxyd/Android.bp b/trusty/utils/spiproxyd/Android.bp
new file mode 100644
index 0000000..c1d0987
--- /dev/null
+++ b/trusty/utils/spiproxyd/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "spiproxyd",
+ vendor: true,
+
+ srcs: [
+ "main.c",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libtrusty",
+ ],
+
+ init_rc: [
+ "proxy.rc",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/trusty/utils/spiproxyd/main.c b/trusty/utils/spiproxyd/main.c
new file mode 100644
index 0000000..c10866b
--- /dev/null
+++ b/trusty/utils/spiproxyd/main.c
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "spiproxyd"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+int handle_msg(int trusty_dev_fd, int spi_dev_fd) {
+ int rc;
+ uint8_t msg_buf[4096];
+ size_t msg_len;
+
+ /* read request from SPI Trusty app */
+ rc = read(trusty_dev_fd, &msg_buf, sizeof(msg_buf));
+ if (rc < 0) {
+ ALOGE("failed (%d) to read request from TA\n", rc);
+ return rc;
+ }
+ msg_len = rc;
+
+ /* forward request to SPI host device */
+ rc = write(spi_dev_fd, &msg_buf, msg_len);
+ if (rc < 0 || (size_t)rc != msg_len) {
+ ALOGE("failed (%d) to forward request to host\n", rc);
+ return rc < 0 ? rc : -1;
+ }
+
+ /* read response from SPI host device */
+ rc = read(spi_dev_fd, &msg_buf, sizeof(msg_buf));
+ if (rc < 0) {
+ ALOGE("failed (%d) to read response from host\n", rc);
+ return rc;
+ }
+ msg_len = rc;
+
+ /* forward response to SPI Trusty app */
+ rc = write(trusty_dev_fd, &msg_buf, msg_len);
+ if (rc < 0 || (size_t)rc != msg_len) {
+ ALOGE("failed (%d) to forward response to TA\n", rc);
+ return rc < 0 ? rc : -1;
+ }
+
+ return 0;
+}
+
+int event_loop(int trusty_dev_fd, int spi_dev_fd) {
+ while (true) {
+ int rc = handle_msg(trusty_dev_fd, spi_dev_fd);
+ if (rc < 0) {
+ ALOGE("exiting event loop\n");
+ return EXIT_FAILURE;
+ }
+ }
+}
+
+static void show_usage() {
+ ALOGE("usage: spiproxyd -t TRUSTY_DEVICE -s SPI_DEVICE -p SPI_PROXY_PORT\n");
+}
+
+static void parse_args(int argc, char* argv[], const char** trusty_dev_name,
+ const char** spi_dev_name, const char** spi_proxy_port) {
+ int opt;
+ while ((opt = getopt(argc, argv, "ht:s:p:")) != -1) {
+ switch (opt) {
+ case 'h':
+ show_usage();
+ exit(EXIT_SUCCESS);
+ break;
+ case 't':
+ *trusty_dev_name = strdup(optarg);
+ break;
+ case 's':
+ *spi_dev_name = strdup(optarg);
+ break;
+ case 'p':
+ *spi_proxy_port = strdup(optarg);
+ break;
+ default:
+ show_usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (!*trusty_dev_name || !*spi_dev_name || !*spi_proxy_port) {
+ show_usage();
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ int rc;
+ const char* trusty_dev_name = NULL;
+ const char* spi_dev_name = NULL;
+ const char* spi_proxy_port = NULL;
+ int trusty_dev_fd;
+ int spi_dev_fd;
+
+ parse_args(argc, argv, &trusty_dev_name, &spi_dev_name, &spi_proxy_port);
+
+ rc = tipc_connect(trusty_dev_name, spi_proxy_port);
+ if (rc < 0) {
+ ALOGE("failed (%d) to connect to SPI proxy port\n", rc);
+ return rc;
+ }
+ trusty_dev_fd = rc;
+
+ rc = open(spi_dev_name, O_RDWR, 0);
+ if (rc < 0) {
+ ALOGE("failed (%d) to open SPI device\n", rc);
+ return rc;
+ }
+ spi_dev_fd = rc;
+
+ return event_loop(trusty_dev_fd, spi_dev_fd);
+}
diff --git a/trusty/utils/spiproxyd/proxy.rc b/trusty/utils/spiproxyd/proxy.rc
new file mode 100644
index 0000000..7d63e6a
--- /dev/null
+++ b/trusty/utils/spiproxyd/proxy.rc
@@ -0,0 +1,20 @@
+# 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.
+
+service spiproxyd /vendor/bin/spiproxyd -t /dev/trusty-ipc-dev0 \
+ -s /dev/vport3p2 -p com.android.trusty.spi.proxy
+ class main
+ user system
+ group system
+ oneshot