Merge "Revert "Make libstatssocket a shared_lib""
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 2b6aa7c..c9993b7 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -144,7 +144,7 @@
return initialized_;
}
- virtual ~AsyncServiceRef() {
+ void DestroyServiceRef() {
if (!initialized_) {
return;
}
@@ -152,9 +152,13 @@
// Order matters here! Must destroy the fdevent first since it has a
// reference to |sdRef_|.
fdevent_destroy(fde_);
+ D("DNSServiceRefDeallocate(sdRef=%p)", sdRef_);
DNSServiceRefDeallocate(sdRef_);
+ initialized_ = false;
}
+ virtual ~AsyncServiceRef() { DestroyServiceRef(); }
+
protected:
DNSServiceRef sdRef_;
@@ -203,6 +207,7 @@
if (ret != kDNSServiceErr_NoError) {
D("Got %d from DNSServiceGetAddrInfo.", ret);
} else {
+ D("DNSServiceGetAddrInfo(sdRef=%p, hosttarget=%s)", sdRef_, hosttarget);
Initialize();
}
@@ -223,7 +228,7 @@
return true;
}
- void Connect(const sockaddr* address) {
+ bool AddToServiceRegistry(const sockaddr* address) {
sa_family_ = address->sa_family;
if (sa_family_ == AF_INET) {
@@ -234,13 +239,13 @@
addr_format_ = "[%s]:%hu";
} else { // Should be impossible
D("mDNS resolved non-IP address.");
- return;
+ return false;
}
// Winsock version requires the const cast Because Microsoft.
if (!inet_ntop(sa_family_, const_cast<void*>(ip_addr_data_), ip_addr_, sizeof(ip_addr_))) {
D("Could not convert IP address to string.");
- return;
+ return false;
}
// adb secure service needs to do something different from just
@@ -264,19 +269,32 @@
}
int adbSecureServiceType = serviceIndex();
+ ServiceRegistry* services = nullptr;
switch (adbSecureServiceType) {
case kADBTransportServiceRefIndex:
- sAdbTransportServices->push_back(this);
+ services = sAdbTransportServices;
break;
case kADBSecurePairingServiceRefIndex:
- sAdbSecurePairingServices->push_back(this);
+ services = sAdbSecurePairingServices;
break;
case kADBSecureConnectServiceRefIndex:
- sAdbSecureConnectServices->push_back(this);
+ services = sAdbSecureConnectServices;
break;
default:
- break;
+ LOG(WARNING) << "No registry available for reg_type=[" << regType_ << "]";
+ 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));
+
+ return true;
}
int serviceIndex() const { return adb_DNSServiceIndexByName(regType_.c_str()); }
@@ -291,7 +309,7 @@
uint16_t port() const { return port_; }
- using ServiceRegistry = std::vector<ResolvedService*>;
+ using ServiceRegistry = std::vector<std::unique_ptr<ResolvedService>>;
// unencrypted tcp connections
static ServiceRegistry* sAdbTransportServices;
@@ -321,13 +339,13 @@
};
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbTransportServices = NULL;
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecurePairingServices = NULL;
// static
-std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
+ResolvedService::ServiceRegistry* ResolvedService::sAdbSecureConnectServices = NULL;
// static
void ResolvedService::initAdbServiceRegistries() {
@@ -348,7 +366,7 @@
adb_secure_foreach_service_callback cb) {
initAdbServiceRegistries();
- for (auto service : services) {
+ for (const auto& service : services) {
auto service_name = service->serviceName();
auto reg_type = service->regType();
auto ip = service->ipAddress();
@@ -366,7 +384,7 @@
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
const std::string& service_name) {
initAdbServiceRegistries();
- for (auto service : services) {
+ for (const auto& service : services) {
if (service_name == service->serviceName()) {
D("Got service_name match [%s]", service->serviceName().c_str());
return service->ConnectSecureWifiDevice();
@@ -393,23 +411,28 @@
service_name);
}
-static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
- DNSServiceFlags /*flags*/,
+static void DNSSD_API register_service_ip(DNSServiceRef sdRef, DNSServiceFlags flags,
uint32_t /*interfaceIndex*/,
- DNSServiceErrorType /*errorCode*/,
- const char* /*hostname*/,
- const sockaddr* address,
- uint32_t /*ttl*/,
- void* context) {
- D("Got IP for service.");
+ DNSServiceErrorType errorCode, const char* hostname,
+ const sockaddr* address, uint32_t ttl, void* context) {
+ D("%s: sdRef=%p flags=0x%08x errorCode=%u ttl=%u", __func__, sdRef, flags, errorCode, ttl);
std::unique_ptr<ResolvedService> data(
reinterpret_cast<ResolvedService*>(context));
- data->Connect(address);
+ // Only resolve the address once. If the address or port changes, we'll just get another
+ // registration.
+ data->DestroyServiceRef();
- // For ADB Secure services, keep those ResolvedService's around
- // for later processing with secure connection establishment.
- if (data->serviceIndex() != kADBTransportServiceRefIndex) {
- data.release();
+ if (errorCode != kDNSServiceErr_NoError) {
+ D("Got error while looking up ipaddr [%u]", errorCode);
+ return;
+ }
+
+ if (flags & kDNSServiceFlagsAdd) {
+ D("Resolved IP address for [%s]. Adding to service registry.", hostname);
+ auto* ptr = data.release();
+ if (!ptr->AddToServiceRegistry(address)) {
+ data.reset(ptr);
+ }
}
}
@@ -459,6 +482,7 @@
};
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) {
@@ -475,10 +499,15 @@
return;
}
+ if (services->empty()) {
+ return;
+ }
+
std::string sName(serviceName);
- services->erase(std::remove_if(
- services->begin(), services->end(),
- [&sName](ResolvedService* service) { return (sName == service->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,
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 03bdcbd..9912f11 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -659,14 +659,14 @@
print(f"Registering {serv_instance}.{serv_type} ...")
with zeroconf_register_service(zc, service_info) as info:
"""Give adb some time to register the service"""
- time.sleep(0.25)
+ time.sleep(1)
print(f"services={_mdns_services(server_port)}")
self.assertTrue(any((serv_instance in line and serv_type in line)
for line in _mdns_services(server_port)))
"""Give adb some time to unregister the service"""
print("Unregistering mdns service...")
- time.sleep(0.25)
+ time.sleep(1)
print(f"services={_mdns_services(server_port)}")
self.assertFalse(any((serv_instance in line and serv_type in line)
for line in _mdns_services(server_port)))
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d67b522..31c2d5d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -169,6 +169,7 @@
"libdebuggerd/backtrace.cpp",
"libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
+ "libdebuggerd/scudo.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
],
@@ -176,8 +177,13 @@
local_include_dirs: ["libdebuggerd/include"],
export_include_dirs: ["libdebuggerd/include"],
- // Needed for private/bionic_fdsan.h
- include_dirs: ["bionic/libc"],
+ include_dirs: [
+ // Needed for private/bionic_fdsan.h
+ "bionic/libc",
+
+ // Needed for scudo/interface.h
+ "external/scudo/standalone/include",
+ ],
header_libs: [
"bionic_libc_platform_headers",
"gwp_asan_headers",
@@ -192,7 +198,10 @@
"liblog",
],
- whole_static_libs: ["gwp_asan_crash_handler"],
+ whole_static_libs: [
+ "gwp_asan_crash_handler",
+ "libscudo",
+ ],
target: {
recovery: {
@@ -206,6 +215,9 @@
debuggable: {
cflags: ["-DROOT_POSSIBLE"],
},
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
},
}
@@ -256,6 +268,10 @@
"gwp_asan_headers",
],
+ include_dirs: [
+ "external/scudo/standalone/include",
+ ],
+
local_include_dirs: [
"libdebuggerd",
],
@@ -271,6 +287,12 @@
},
test_suites: ["device-tests"],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
cc_benchmark {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 0cd2350..d7cb972 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -289,6 +289,8 @@
process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address;
process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
+ process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
+ process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
FALLTHROUGH_INTENDED;
case 1:
case 2:
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 054f836..25417a9 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -31,6 +31,9 @@
#include <android/fdsan.h>
#include <android/set_abort_message.h>
+#include <bionic/malloc.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <android-base/cmsg.h>
@@ -331,6 +334,173 @@
R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
}
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+static void SetTagCheckingLevelSync() {
+ int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (tagged_addr_ctrl < 0) {
+ abort();
+ }
+
+ tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_SYNC;
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) != 0) {
+ abort();
+ }
+
+ HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
+ if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
+ abort();
+ }
+}
+#endif
+
+TEST_F(CrasherTest, mte_uaf) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ free((void *)p);
+ p[0] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a 16-byte allocation)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_overflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ p[4] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_underflow) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(16);
+ p[-1] = 42;
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a 16-byte allocation)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
+TEST_F(CrasherTest, mte_multiple_causes) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ SetTagCheckingLevelSync();
+
+ // Make two allocations with the same tag and close to one another. Check for both properties
+ // with a bounds check -- this relies on the fact that only if the allocations have the same tag
+ // would they be measured as closer than 128 bytes to each other. Otherwise they would be about
+ // (some non-zero value << 56) apart.
+ //
+ // The out-of-bounds access will be considered either an overflow of one or an underflow of the
+ // other.
+ std::set<uintptr_t> allocs;
+ for (int i = 0; i != 4096; ++i) {
+ uintptr_t alloc = reinterpret_cast<uintptr_t>(malloc(16));
+ auto it = allocs.insert(alloc).first;
+ if (it != allocs.begin() && *std::prev(it) + 128 > alloc) {
+ *reinterpret_cast<int*>(*std::prev(it) + 16) = 42;
+ }
+ if (std::next(it) != allocs.end() && alloc + 128 > *std::next(it)) {
+ *reinterpret_cast<int*>(alloc + 16) = 42;
+ }
+ }
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+ ASSERT_MATCH(
+ result,
+ R"(Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.)");
+
+ // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
+ // overflows), so we can't match explicitly for an underflow message.
+ ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+#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/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 6650294..254ed4f 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -40,6 +40,8 @@
void* fdsan_table;
const gwp_asan::AllocatorState* gwp_asan_state;
const gwp_asan::AllocationMetadata* gwp_asan_metadata;
+ const char* scudo_stack_depot;
+ const char* scudo_region_info;
};
// These callbacks are called in a signal handler, and thus must be async signal safe.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
new file mode 100644
index 0000000..4d00ece
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "types.h"
+#include "utility.h"
+
+#include <memory.h>
+
+#include "scudo/interface.h"
+
+class ScudoCrashData {
+ public:
+ ScudoCrashData() = delete;
+ ~ScudoCrashData() = default;
+ ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+
+ bool CrashIsMine() const;
+
+ void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ private:
+ scudo_error_info error_info_ = {};
+ uintptr_t untagged_fault_addr_;
+
+ void DumpReport(const scudo_error_report* report, log_t* log,
+ unwindstack::Unwinder* unwinder) const;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 35c3fd6..04c4b5c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -41,6 +41,8 @@
uintptr_t fdsan_table_address = 0;
uintptr_t gwp_asan_state = 0;
uintptr_t gwp_asan_metadata = 0;
+ uintptr_t scudo_stack_depot = 0;
+ uintptr_t scudo_region_info = 0;
bool has_fault_address = false;
uintptr_t fault_address = 0;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
new file mode 100644
index 0000000..f8bfe07
--- /dev/null
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "libdebuggerd/scudo.h"
+#include "libdebuggerd/gwp_asan.h"
+
+#include "unwindstack/Memory.h"
+#include "unwindstack/Unwinder.h"
+
+#include <bionic/macros.h>
+
+std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
+ size_t size) {
+ auto buf = std::make_unique<char[]>(size);
+ if (!process_memory->ReadFully(addr, buf.get(), size)) {
+ return std::unique_ptr<char[]>();
+ }
+ return buf;
+}
+
+static const uintptr_t kTagGranuleSize = 16;
+
+ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
+ const ProcessInfo& process_info) {
+ if (!process_info.has_fault_address) {
+ return;
+ }
+
+ auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+ __scudo_get_stack_depot_size());
+ auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
+ __scudo_get_region_info_size());
+
+ untagged_fault_addr_ = untag_address(process_info.fault_address);
+ uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+
+ uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+ if (memory_begin > fault_page) {
+ return;
+ }
+
+ uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+ if (memory_end < fault_page) {
+ return;
+ }
+
+ auto memory = std::make_unique<char[]>(memory_end - memory_begin);
+ for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
+ process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+ }
+
+ auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
+ for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
+ memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
+ }
+
+ __scudo_get_error_info(&error_info_, process_info.fault_address, stack_depot.get(),
+ region_info.get(), memory.get(), memory_tags.get(), memory_begin,
+ memory_end - memory_begin);
+}
+
+bool ScudoCrashData::CrashIsMine() const {
+ return error_info_.reports[0].error_type != UNKNOWN;
+}
+
+void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
+ if (error_info_.reports[1].error_type != UNKNOWN) {
+ _LOG(log, logtype::HEADER,
+ "\nNote: multiple potential causes for this crash were detected, listing them in "
+ "decreasing order of probability.\n");
+ }
+
+ size_t report_num = 0;
+ while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
+ error_info_.reports[report_num].error_type != UNKNOWN) {
+ DumpReport(&error_info_.reports[report_num++], log, unwinder);
+ }
+}
+
+void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
+ unwindstack::Unwinder* unwinder) const {
+ const char *error_type_str;
+ switch (report->error_type) {
+ case USE_AFTER_FREE:
+ error_type_str = "Use After Free";
+ break;
+ case BUFFER_OVERFLOW:
+ error_type_str = "Buffer Overflow";
+ break;
+ case BUFFER_UNDERFLOW:
+ error_type_str = "Buffer Underflow";
+ break;
+ default:
+ error_type_str = "Unknown";
+ break;
+ }
+
+ uintptr_t diff;
+ const char* location_str;
+
+ if (untagged_fault_addr_ < report->allocation_address) {
+ // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+ location_str = "left of";
+ diff = report->allocation_address - untagged_fault_addr_;
+ } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
+ // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+ location_str = "into";
+ diff = untagged_fault_addr_ - report->allocation_address;
+ } else {
+ // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
+ location_str = "right of";
+ diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
+ }
+
+ // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+ const char* byte_suffix = "s";
+ if (diff == 1) {
+ byte_suffix = "";
+ }
+ _LOG(log, logtype::HEADER,
+ "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_type_str, diff, byte_suffix, location_str, report->allocation_size,
+ report->allocation_address);
+
+ if (report->allocation_trace[0]) {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < 64 && report->allocation_trace[i]; ++i) {
+ unwindstack::FrameData frame_data =
+ unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
+ frame_data.num = i;
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+ }
+
+ if (report->deallocation_trace[0]) {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < 64 && report->deallocation_trace[i]; ++i) {
+ unwindstack::FrameData frame_data =
+ unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
+ frame_data.num = i;
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+ }
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d6b2e25..ab65dd1 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -56,6 +56,7 @@
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
#include "gwp_asan/common.h"
@@ -389,14 +390,17 @@
}
std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
+ std::unique_ptr<ScudoCrashData> scudo_crash_data;
if (primary_thread) {
gwp_asan_crash_data = std::make_unique<GwpAsanCrashData>(unwinder->GetProcessMemory().get(),
process_info, thread_info);
+ scudo_crash_data =
+ std::make_unique<ScudoCrashData>(unwinder->GetProcessMemory().get(), process_info);
}
if (primary_thread && gwp_asan_crash_data->CrashIsMine()) {
gwp_asan_crash_data->DumpCause(log);
- } else if (thread_info.siginfo) {
+ } else if (thread_info.siginfo && !(primary_thread && scudo_crash_data->CrashIsMine())) {
dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
thread_info.registers.get());
}
@@ -427,6 +431,8 @@
gwp_asan_crash_data->DumpAllocationTrace(log, unwinder);
}
+ scudo_crash_data->DumpCause(log, unwinder);
+
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 3bf28b6..c8a3431 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,6 +35,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
@@ -374,6 +375,12 @@
return "SEGV_ADIDERR";
case SEGV_ADIPERR:
return "SEGV_ADIPERR";
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+ case SEGV_MTEAERR:
+ return "SEGV_MTEAERR";
+ case SEGV_MTESERR:
+ return "SEGV_MTESERR";
+#endif
}
static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
break;
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index e85660c..53a76ea 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,6 +95,8 @@
uintptr_t fdsan_table_address;
uintptr_t gwp_asan_state;
uintptr_t gwp_asan_metadata;
+ uintptr_t scudo_stack_depot;
+ uintptr_t scudo_region_info;
};
struct __attribute__((__packed__)) CrashInfo {
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b8eee4a..2553353 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -625,7 +625,7 @@
if (!sm) {
return device->WriteFail("Unable to create SnapshotManager");
}
- if (!sm->HandleImminentDataWipe()) {
+ if (!sm->FinishMergeInRecovery()) {
return device->WriteFail("Unable to finish snapshot merge");
}
} else {
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index bde9d0a..cae43e6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -91,6 +91,7 @@
cc_test {
name: "fiemap_image_test",
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -99,7 +100,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
@@ -118,6 +118,7 @@
"-DSKIP_TEST_IN_PRESUBMIT",
],
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -126,7 +127,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 5388b44..6663391 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -131,132 +131,6 @@
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
- public:
- ImageTest() : dm_(DeviceMapper::Instance()) {}
-
- void SetUp() override {
- manager_ = ImageManager::Open(kMetadataPath, gDataPath);
- ASSERT_NE(manager_, nullptr);
-
- manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
- ASSERT_NE(submanager_, nullptr);
-
- submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- // Ensure that metadata is cleared in between runs.
- submanager_->RemoveAllImages();
- manager_->RemoveAllImages();
-
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- base_name_ = tinfo->name();
- test_image_name_ = base_name_ + "-base";
- wrapper_device_name_ = base_name_ + "-wrapper";
-
- ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
- ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
- }
-
- void TearDown() override {
- submanager_->UnmapImageDevice(test_image_name_);
- umount(gDataMountPath.c_str());
- dm_.DeleteDeviceIfExists(wrapper_device_name_);
- manager_->UnmapImageDevice(base_name_);
- manager_->DeleteBackingImage(base_name_);
- }
-
- protected:
- bool DoFormat(const std::string& device) {
- // clang-format off
- std::vector<std::string> mkfs_args = {
- "/system/bin/mke2fs",
- "-F",
- "-b 4096",
- "-t ext4",
- "-m 0",
- "-O has_journal",
- device,
- ">/dev/null",
- "2>/dev/null",
- "</dev/null",
- };
- // clang-format on
- auto command = android::base::Join(mkfs_args, " ");
- return system(command.c_str()) == 0;
- }
-
- std::unique_ptr<ImageManager> manager_;
- std::unique_ptr<ImageManager> submanager_;
-
- DeviceMapper& dm_;
- std::string base_name_;
- std::string base_device_;
- std::string test_image_name_;
- std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
- ASSERT_TRUE(DoFormat(base_device_));
- ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148874852";
-#endif
- // Create a simple wrapper around the base device that we'll mount from
- // instead. This will simulate the code paths for dm-crypt/default-key/bow
- // and force us to use device-mapper rather than loop devices.
- uint64_t device_size = 0;
- {
- unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- device_size = get_block_device_size(fd);
- ASSERT_EQ(device_size, kTestImageSize * 16);
- }
- uint64_t num_sectors = device_size / 512;
-
- auto& dm = DeviceMapper::Instance();
-
- DmTable table;
- table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
- ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
- // Format and mount.
- std::string wrapper_device;
- ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
- ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
- ASSERT_TRUE(DoFormat(wrapper_device));
- ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::set<std::string> backing_devices;
- auto init = [&](std::set<std::string> devices) -> bool {
- backing_devices = std::move(devices);
- return true;
- };
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
- ASSERT_TRUE(submanager_->MapAllImages(init));
- ASSERT_FALSE(backing_devices.empty());
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-}
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index dc3b985..c37d70e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,6 +19,7 @@
#include <string.h>
#include <algorithm>
+#include <limits>
#include <android-base/unique_fd.h>
@@ -369,7 +370,10 @@
}
// Align the metadata size up to the nearest sector.
- metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+ if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
+ LERROR << "Max metadata size " << metadata_max_size << " is too large.";
+ return false;
+ }
// Validate and build the block device list.
uint32_t logical_block_size = 0;
@@ -401,10 +405,15 @@
// untouched to be compatible code that looks for an MBR. Thus we
// start counting free sectors at sector 1, not 0.
uint64_t free_area_start = LP_SECTOR_SIZE;
+ bool ok;
if (out.alignment) {
- free_area_start = AlignTo(free_area_start, out.alignment);
+ ok = AlignTo(free_area_start, out.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -441,10 +450,15 @@
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
+ bool ok;
if (super.alignment || super.alignment_offset) {
- free_area_start = AlignTo(free_area_start, super.alignment);
+ ok = AlignTo(free_area_start, super.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -544,7 +558,11 @@
const Interval& current = extents[i];
DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
+ uint64_t aligned;
+ if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
+ LERROR << "Sector " << previous.end << " caused integer overflow.";
+ continue;
+ }
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -730,7 +748,10 @@
// Choose an aligned sector for the midpoint. This could lead to one half
// being slightly larger than the other, but this will not restrict the
// size of partitions (it might lead to one extra extent if "B" overflows).
- midpoint = AlignSector(super, midpoint);
+ if (!AlignSector(super, midpoint, &midpoint)) {
+ LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
+ return free_list;
+ }
std::vector<Interval> first_half;
std::vector<Interval> second_half;
@@ -768,7 +789,11 @@
// If the sector ends where the next aligned chunk begins, then there's
// no missing gap to try and allocate.
const auto& block_device = block_devices_[extent->device_index()];
- uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+ uint64_t next_aligned_sector;
+ if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
+ LERROR << "Integer overflow aligning sector " << extent->end_sector();
+ return nullptr;
+ }
if (extent->end_sector() == next_aligned_sector) {
return nullptr;
}
@@ -925,13 +950,19 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
- uint64_t sector) const {
+bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
+ uint64_t* out) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, block_device.alignment);
- return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+ if (!AlignTo(lba, block_device.alignment, out)) {
+ return false;
+ }
+ if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
+ return false;
+ }
+ *out /= LP_SECTOR_SIZE;
+ return true;
}
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
@@ -1005,7 +1036,12 @@
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
const std::vector<Interval>& free_region_hint) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
+ uint64_t aligned_size;
+ if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
+ LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
+ << " bytes; integer overflow.";
+ return false;
+ }
uint64_t old_size = partition->size();
if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 52a3217..1a3250a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -228,8 +228,9 @@
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(extent.num_sectors, 80);
+ uint64_t aligned_lba;
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
- uint64_t aligned_lba = AlignTo(lba, device_info.alignment);
+ ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
EXPECT_EQ(lba, aligned_lba);
}
@@ -1051,3 +1052,17 @@
EXPECT_EQ(e2->physical_sector(), 3072);
EXPECT_EQ(e2->end_sector(), 4197368);
}
+
+TEST_F(BuilderTest, ResizeOverflow) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ Partition* p = builder->AddPartition("system", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index bd39150..732dbea 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -359,7 +359,7 @@
bool GrowPartition(Partition* partition, uint64_t aligned_size,
const std::vector<Interval>& free_region_hint);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+ bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index f210eaf..c4fe3ed 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <limits>
#include <string>
#include <string_view>
@@ -66,16 +67,26 @@
void SHA256(const void* data, size_t length, uint8_t out[32]);
// Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+// have to be a power of two. Return false on overflow.
+template <typename T>
+bool AlignTo(T base, uint32_t alignment, T* out) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
if (!alignment) {
- return base;
+ *out = base;
+ return true;
}
- uint64_t remainder = base % alignment;
+ T remainder = base % alignment;
if (remainder == 0) {
- return base;
+ *out = base;
+ return true;
}
- return base + (alignment - remainder);
+ T to_add = alignment - remainder;
+ if (to_add > std::numeric_limits<T>::max() - base) {
+ return false;
+ }
+ *out = base + to_add;
+ return true;
}
// Update names from C++ strings.
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index b64861d..fc90872 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <optional>
+
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -58,15 +60,28 @@
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
}
+std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {
+ uint64_t r;
+ if (!AlignTo(base, alignment, &r)) {
+ return {};
+ }
+ return {r};
+}
+
TEST(liblp, AlignTo) {
- EXPECT_EQ(AlignTo(37, 0), 37);
- EXPECT_EQ(AlignTo(1024, 1024), 1024);
- EXPECT_EQ(AlignTo(555, 1024), 1024);
- EXPECT_EQ(AlignTo(555, 1000), 1000);
- EXPECT_EQ(AlignTo(0, 1024), 0);
- EXPECT_EQ(AlignTo(54, 32), 64);
- EXPECT_EQ(AlignTo(32, 32), 32);
- EXPECT_EQ(AlignTo(17, 32), 32);
+ EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37));
+ EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));
+ EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));
+ EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));
+ EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));
+ EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));
+
+ auto u32limit = std::numeric_limits<uint32_t>::max();
+ auto u64limit = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});
+ EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});
}
TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 758d66c..cf0b085 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -43,6 +43,7 @@
(const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
(override));
MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
+ MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
(const std::unique_ptr<AutoDevice>& metadata_device), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 4658fb4..2d6071f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -202,6 +202,10 @@
// optional callback fires periodically to query progress via GetUpdateState.
virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
+ // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
+ // but does not expect a data wipe after.
+ virtual bool FinishMergeInRecovery() = 0;
+
// This method is only allowed in recovery and is used as a helper to
// initialize the snapshot devices as a requirement to mount a snapshotted
// /system in recovery.
@@ -290,6 +294,7 @@
const std::string& super_device,
const std::chrono::milliseconds& timeout_ms = {}) override;
bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
CreateResult RecoveryCreateSnapshotDevices() override;
CreateResult RecoveryCreateSnapshotDevices(
const std::unique_ptr<AutoDevice>& metadata_device) override;
@@ -575,11 +580,16 @@
bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
const std::function<bool()>& callback);
+ // Return device string of a mapped image, or if it is not available, the mapped image path.
+ bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
+ std::string* device_string_or_mapped_path);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
bool has_local_image_manager_ = false;
+ bool in_factory_data_reset_ = false;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 9b2590c..9c82906 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -41,6 +41,7 @@
const std::string& super_device,
const std::chrono::milliseconds& timeout_ms = {}) override;
bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
CreateResult RecoveryCreateSnapshotDevices() override;
CreateResult RecoveryCreateSnapshotDevices(
const std::unique_ptr<AutoDevice>& metadata_device) override;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 526f874..adfb975 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -46,20 +46,20 @@
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
- constexpr uint64_t initial_size = 1_MiB;
- constexpr uint64_t final_size = 40_KiB;
+ constexpr uint64_t super_size = 1_MiB;
+ constexpr uint64_t partition_size = 40_KiB;
- auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
- auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
PartitionCowCreator creator{.target_metadata = builder_b.get(),
.target_suffix = "_b",
@@ -68,8 +68,8 @@
.current_suffix = "_a"};
auto ret = creator.Run();
ASSERT_TRUE(ret.has_value());
- ASSERT_EQ(final_size, ret->snapshot_status.device_size());
- ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(partition_size, ret->snapshot_status.device_size());
+ ASSERT_EQ(partition_size, ret->snapshot_status.snapshot_size());
}
TEST_F(PartitionCowCreatorTest, Holes) {
@@ -118,20 +118,20 @@
using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
using Extent = chromeos_update_engine::Extent;
- constexpr uint64_t initial_size = 50_MiB;
- constexpr uint64_t final_size = 40_MiB;
+ constexpr uint64_t super_size = 50_MiB;
+ constexpr uint64_t partition_size = 40_MiB;
- auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
- auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
const uint64_t block_size = builder_b->logical_block_size();
const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
@@ -197,6 +197,31 @@
ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
}
+TEST_F(PartitionCowCreatorTest, Zero) {
+ constexpr uint64_t super_size = 1_MiB;
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+
+ PartitionCowCreator creator{.target_metadata = builder_b.get(),
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a.get(),
+ .current_suffix = "_a",
+ .operations = nullptr};
+
+ auto ret = creator.Run();
+
+ ASSERT_EQ(0u, ret->snapshot_status.device_size());
+ ASSERT_EQ(0u, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(0u, ret->snapshot_status.cow_file_size());
+ ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
+}
+
TEST(DmSnapshotInternals, CowSizeCalculator) {
DmSnapCowSizeCalculator cc(512, 8);
unsigned long int b;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index eafeea2..488009a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -577,8 +577,16 @@
return false;
}
+ auto other_suffix = device_->GetOtherSlotSuffix();
+
auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
+ if (android::base::EndsWith(snapshot, other_suffix)) {
+ // Allow the merge to continue, but log this unexpected case.
+ LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
+ continue;
+ }
+
// The device has to be mapped, since everything should be merged at
// the same time. This is a fairly serious error. We could forcefully
// map everything here, but it should have been mapped during first-
@@ -1008,6 +1016,15 @@
}
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+ // It's not possible to remove update state in recovery, so write an
+ // indicator that cleanup is needed on reboot. If a factory data reset
+ // was requested, it doesn't matter, everything will get wiped anyway.
+ // To make testing easier we consider a /data wipe as cleaned up.
+ if (device_->IsRecovery() && !in_factory_data_reset_) {
+ WriteUpdateState(lock, UpdateState::MergeCompleted);
+ return;
+ }
+
RemoveAllUpdateState(lock);
}
@@ -1674,7 +1691,7 @@
return false;
}
std::string cow_device;
- if (!dm.GetDeviceString(cow_name, &cow_device)) {
+ if (!GetMappedImageDeviceStringOrPath(cow_name, &cow_device)) {
LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
return false;
}
@@ -1771,7 +1788,7 @@
// If the COW image exists, append it as the last extent.
if (snapshot_status.cow_file_size() > 0) {
std::string cow_image_device;
- if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
+ if (!GetMappedImageDeviceStringOrPath(cow_image_name, &cow_image_device)) {
LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
return false;
}
@@ -2347,7 +2364,6 @@
const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
CHECK(lock);
- auto& dm = DeviceMapper::Instance();
CreateLogicalPartitionParams cow_params{
.block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
.metadata = exported_target_metadata,
@@ -2372,7 +2388,7 @@
}
std::string cow_path;
- if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
+ if (!images_->GetMappedImageDevice(cow_name, &cow_path)) {
LOG(ERROR) << "Cannot determine path for " << cow_name;
return Return::Error();
}
@@ -2528,7 +2544,43 @@
}
return true;
};
- if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {
+
+ in_factory_data_reset_ = true;
+ bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+ in_factory_data_reset_ = false;
+
+ if (!ok) {
+ return false;
+ }
+
+ // Nothing should be depending on partitions now, so unmap them all.
+ if (!UnmapAllPartitions()) {
+ LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+ }
+ return true;
+}
+
+bool SnapshotManager::FinishMergeInRecovery() {
+ if (!device_->IsRecovery()) {
+ LOG(ERROR) << "Data wipes are only allowed in recovery.";
+ return false;
+ }
+
+ auto mount = EnsureMetadataMounted();
+ if (!mount || !mount->HasDevice()) {
+ return false;
+ }
+
+ auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_path = device_->GetSuperDevice(slot_number);
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+ LOG(ERROR) << "Unable to map partitions to complete merge.";
+ return false;
+ }
+
+ UpdateState state = ProcessUpdateState();
+ if (state != UpdateState::MergeCompleted) {
+ LOG(ERROR) << "Merge returned unexpected status: " << state;
return false;
}
@@ -2689,5 +2741,24 @@
return SnapshotMergeStats::GetInstance(*this);
}
+bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,
+ std::string* device_string_or_mapped_path) {
+ auto& dm = DeviceMapper::Instance();
+ // Try getting the device string if it is a device mapper device.
+ if (dm.GetState(device_name) != DmDeviceState::INVALID) {
+ return dm.GetDeviceString(device_name, device_string_or_mapped_path);
+ }
+
+ // Otherwise, get path from IImageManager.
+ if (!images_->GetMappedImageDevice(device_name, device_string_or_mapped_path)) {
+ return false;
+ }
+
+ LOG(WARNING) << "Calling GetMappedImageDevice with local image manager; device "
+ << (device_string_or_mapped_path ? *device_string_or_mapped_path : "(nullptr)")
+ << "may not be available in first stage init! ";
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 2747b03..2aaa78c 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -89,6 +89,11 @@
return false;
}
+bool SnapshotManagerStub::FinishMergeInRecovery() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
LOG(ERROR) << __FUNCTION__ << " should never be called.";
return CreateResult::ERROR;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 53a37be..2bd0135 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1459,6 +1459,52 @@
ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
}
+// Test that a merge does not clear the snapshot state in fastboot.
+TEST_F(SnapshotUpdateTest, MergeInFastboot) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ // Initiate the merge and then immediately stop it to simulate a reboot.
+ auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ test_device->set_recovery(true);
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+ ASSERT_TRUE(new_sm->FinishMergeInRecovery());
+
+ auto mount = new_sm->EnsureMetadataMounted();
+ ASSERT_TRUE(mount && mount->HasDevice());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+
+ // Finish the merge in a normal boot.
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ init = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
+}
+
// Test that after an OTA, before a merge, we can wipe data in recovery.
TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
// Execute the first update.
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 175b2b7..d7c83a2 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -33,6 +33,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -59,6 +60,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
export_include_dirs: ["include"],
@@ -142,6 +144,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
srcs: [
"config_utils.cpp",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..33ae13d 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -26,4 +26,5 @@
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
+ min_sdk_version: "29",
}
diff --git a/liblog/README.md b/liblog/README.md
index f64f376..74a2cd7 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -60,8 +60,6 @@
LOG_EVENT_INT(tag, value)
LOG_EVENT_LONG(tag, value)
- clockid_t android_log_clockid()
-
log_id_t android_logger_get_id(struct logger *logger)
int android_logger_clear(struct logger *logger)
int android_logger_get_log_size(struct logger *logger)
@@ -119,7 +117,7 @@
multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
`android_logger_open()` for each log id. Each entry can be retrieved with
`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`.
-`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
+`ANDROID_LOG_NONBLOCK` mode will report when the log reading is done with an `EAGAIN` error return
code, otherwise the `android_logger_list_read()` call will block for new entries.
The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 820b7cb..d7e9b7d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -133,12 +133,6 @@
(void)__android_log_bswrite(_tag, _value);
#endif
-#ifdef __linux__
-
-clockid_t android_log_clockid(void);
-
-#endif /* __linux__ */
-
/* --------------------------------------------------------------------- */
/*
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 3a75fa3..22c7eca 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -192,7 +192,7 @@
return -EINVAL;
}
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
if (log_id == LOG_ID_SECURITY) {
if (vec[0].iov_len < 4) {
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 9e8d277..238431f 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -216,11 +216,7 @@
p_ret->year_output = false;
p_ret->zone_output = false;
p_ret->epoch_output = false;
-#ifdef __ANDROID__
- p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
-#else
p_ret->monotonic_output = false;
-#endif
p_ret->uid_output = false;
p_ret->descriptive_output = false;
descriptive_output = false;
@@ -1465,13 +1461,10 @@
nsec = entry->tv_nsec;
#if __ANDROID__
if (p_format->monotonic_output) {
- /* prevent convertMonotonic from being called if logd is monotonic */
- if (android_log_clockid() != CLOCK_MONOTONIC) {
- struct timespec time;
- convertMonotonic(&time, entry);
- now = time.tv_sec;
- nsec = time.tv_nsec;
- }
+ struct timespec time;
+ convertMonotonic(&time, entry);
+ now = time.tv_sec;
+ nsec = time.tv_nsec;
}
#endif
if (now < 0) {
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 0751e2c..8e676bd 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -188,7 +188,7 @@
return -EINVAL;
}
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
cp = strdup(filename);
if (!cp) {
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 37670ec..f5e060c 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -365,29 +365,6 @@
return c;
}
-static unsigned char evaluate_persist_ro(const struct cache2_char* self) {
- unsigned char c = self->cache_persist.c;
-
- if (c) {
- return c;
- }
-
- return self->cache_ro.c;
-}
-
-/*
- * Timestamp state generally remains constant, but can change at any time
- * to handle developer requirements.
- */
-clockid_t android_log_clockid() {
- static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
- "persist.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
- "ro.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
- evaluate_persist_ro};
-
- return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
-}
-
/*
* Security state generally remains constant, but the DO must be able
* to turn off logging should it become spammy after an attack is detected.
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index f4734b9..a4e4def 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -184,7 +184,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
android_pmsg_log_header_t pmsg_header;
pmsg_header.magic = LOGGER_MAGIC;
@@ -260,7 +260,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -335,7 +335,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -410,7 +410,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -483,7 +483,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
struct packet {
android_pmsg_log_header_t pmsg_header;
@@ -792,16 +792,6 @@
BENCHMARK(BM_is_loggable);
/*
- * Measure the time it takes for android_log_clockid.
- */
-static void BM_clockid(benchmark::State& state) {
- while (state.KeepRunning()) {
- android_log_clockid();
- }
-}
-BENCHMARK(BM_clockid);
-
-/*
* Measure the time it takes for __android_log_security.
*/
static void BM_security(benchmark::State& state) {
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index d3d8e91..bbc985a 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -329,8 +329,6 @@
#ifdef __ANDROID__
pid_t pid = getpid();
- log_time ts(android_log_clockid());
-
size_t num_lines = 1, size = 0, length = 0, total = 0;
const char* cp = message;
while (*cp) {
@@ -432,7 +430,6 @@
pid_t pid = getpid();
static const char tag[] = "TEST__android_log_buf_write";
- log_time ts(android_log_clockid());
auto write_function = [&] {
EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 2c1b255..bda11e9 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -17,6 +17,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
}
cc_library {
@@ -60,4 +61,5 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
}
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index f01b092..286febc 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -124,6 +124,12 @@
return false;
}
+ if (arch() == ARCH_ARM64) {
+ // Tagged pointer after Android R would lead top byte to have random values
+ // https://source.android.com/devices/tech/debug/tagged-pointers
+ vaddr &= (1ULL << 56) - 1;
+ }
+
// Check the .data section.
uint64_t vaddr_start = interface_->data_vaddr_start();
if (vaddr >= vaddr_start && vaddr < interface_->data_vaddr_end()) {
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index fc90dab..3b6cb80 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -55,6 +55,8 @@
void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+ void FakeSetArch(ArchEnum arch) { arch_ = arch; }
+
void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
gnu_debugdata_interface_.reset(interface);
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1f3ed81..f0852a4 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -438,6 +438,48 @@
EXPECT_EQ(0xc080U, offset);
}
+TEST_F(ElfTest, get_global_vaddr_with_tagged_pointer) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetArch(ARCH_ARM64);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x500);
+ interface->MockSetDataVaddrEnd(0x600);
+ interface->MockSetDataOffset(0xa000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+ ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0xa080U, offset);
+}
+
+TEST_F(ElfTest, get_global_vaddr_without_tagged_pointer) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetArch(ARCH_X86_64);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ interface->MockSetDataVaddrStart(0x8800000000000500);
+ interface->MockSetDataVaddrEnd(0x8800000000000600);
+ interface->MockSetDataOffset(0x880000000000a000);
+
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, ::testing::_))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x8800000000000580),
+ ::testing::Return(true)));
+
+ uint64_t offset;
+ ASSERT_TRUE(elf.GetGlobalVariableOffset(global, &offset));
+ EXPECT_EQ(0x880000000000a080U, offset);
+}
+
TEST_F(ElfTest, is_valid_pc_elf_invalid) {
ElfFake elf(memory_);
elf.FakeSetValid(false);
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 1202c15..1d899ab 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -195,7 +195,7 @@
int prot = PROT_READ;
if (!readOnly) prot |= PROT_WRITE;
- void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+ void* ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset);
if (ptr == MAP_FAILED) {
if (errno == EINVAL && length == 0) {
ptr = nullptr;
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
index 576d89b..9f7ce85 100644
--- a/libutils/FileMap_test.cpp
+++ b/libutils/FileMap_test.cpp
@@ -32,3 +32,23 @@
ASSERT_EQ(0u, m.getDataLength());
ASSERT_EQ(4096, m.getDataOffset());
}
+
+TEST(FileMap, large_offset) {
+ // Make sure that an offset > INT32_MAX will not fail the create
+ // function. See http://b/155662887.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ off64_t offset = INT32_MAX + 1024LL;
+
+ // Make the temporary file large enough to pass the mmap.
+ ASSERT_EQ(offset, lseek64(tf.fd, offset, SEEK_SET));
+ char value = 0;
+ ASSERT_EQ(1, write(tf.fd, &value, 1));
+
+ android::FileMap m;
+ ASSERT_TRUE(m.create("test", tf.fd, offset, 0, true));
+ ASSERT_STREQ("test", m.getFileName());
+ ASSERT_EQ(0u, m.getDataLength());
+ ASSERT_EQ(offset, m.getDataOffset());
+}
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 8185f01..13023f2 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -459,7 +459,7 @@
closedir);
if (!dir.get()) return retval;
- log_time now(android_log_clockid());
+ log_time now(CLOCK_REALTIME);
size_t len = strlen(file);
log_time modulo(0, NS_PER_SEC);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index b32b437..3a55c4e 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -174,11 +174,6 @@
}
TEST(logcat, year) {
- if (android_log_clockid() == CLOCK_MONOTONIC) {
- fprintf(stderr, "Skipping test, logd is monotonic time\n");
- return;
- }
-
int count;
int tries = 3; // in case run too soon after system start or buffer clear
@@ -249,11 +244,6 @@
}
TEST(logcat, tz) {
- if (android_log_clockid() == CLOCK_MONOTONIC) {
- fprintf(stderr, "Skipping test, logd is monotonic time\n");
- return;
- }
-
int tries = 4; // in case run too soon after system start or buffer clear
int count;
diff --git a/logd/Android.bp b/logd/Android.bp
index b337b7c..ea1054d 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -36,10 +36,9 @@
"CommandListener.cpp",
"LogListener.cpp",
"LogReader.cpp",
- "FlushCommand.cpp",
+ "LogReaderThread.cpp",
"LogBuffer.cpp",
"LogBufferElement.cpp",
- "LogTimes.cpp",
"LogStatistics.cpp",
"LogWhiteBlackList.cpp",
"libaudit.c",
@@ -53,7 +52,9 @@
export_include_dirs: ["."],
- cflags: ["-Werror"] + event_flag,
+ cflags: [
+ "-Wextra",
+ ] + event_flag,
}
cc_binary {
@@ -76,7 +77,9 @@
"libcap",
],
- cflags: ["-Werror"],
+ cflags: [
+ "-Wextra",
+ ],
}
cc_binary {
@@ -91,10 +94,8 @@
shared_libs: ["libbase"],
cflags: [
- "-Wall",
+ "-Wconversion",
"-Wextra",
- "-Werror",
- "-Wconversion"
],
}
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 694b5fa..4044dc9 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -31,6 +31,7 @@
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
@@ -38,37 +39,20 @@
#include "LogCommand.h"
#include "LogUtils.h"
-CommandListener::CommandListener(LogBuffer* buf, LogReader* /*reader*/,
- LogListener* /*swl*/)
- : FrameworkListener(getLogSocket()) {
- // registerCmd(new ShutdownCmd(buf, writer, swl));
- registerCmd(new ClearCmd(buf));
- registerCmd(new GetBufSizeCmd(buf));
- registerCmd(new SetBufSizeCmd(buf));
- registerCmd(new GetBufSizeUsedCmd(buf));
- registerCmd(new GetStatisticsCmd(buf));
- registerCmd(new SetPruneListCmd(buf));
- registerCmd(new GetPruneListCmd(buf));
- registerCmd(new GetEventTagCmd(buf));
- registerCmd(new ReinitCmd());
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
+ : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+ registerCmd(new ClearCmd(this));
+ registerCmd(new GetBufSizeCmd(this));
+ registerCmd(new SetBufSizeCmd(this));
+ registerCmd(new GetBufSizeUsedCmd(this));
+ registerCmd(new GetStatisticsCmd(this));
+ registerCmd(new SetPruneListCmd(this));
+ registerCmd(new GetPruneListCmd(this));
+ registerCmd(new GetEventTagCmd(this));
+ registerCmd(new ReinitCmd(this));
registerCmd(new ExitCmd(this));
}
-CommandListener::ShutdownCmd::ShutdownCmd(LogReader* reader, LogListener* swl)
- : LogCommand("shutdown"), mReader(*reader), mSwl(*swl) {
-}
-
-int CommandListener::ShutdownCmd::runCommand(SocketClient* /*cli*/,
- int /*argc*/, char** /*argv*/) {
- mSwl.stopListener();
- mReader.stopListener();
- exit(0);
-}
-
-CommandListener::ClearCmd::ClearCmd(LogBuffer* buf)
- : LogCommand("clear"), mBuf(*buf) {
-}
-
static void setname() {
static bool name_set;
if (!name_set) {
@@ -96,14 +80,10 @@
return 0;
}
- cli->sendMsg(mBuf.clear((log_id_t)id, uid) ? "busy" : "success");
+ cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
return 0;
}
-CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer* buf)
- : LogCommand("getLogSize"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -118,17 +98,13 @@
return 0;
}
- unsigned long size = mBuf.getSize((log_id_t)id);
+ unsigned long size = buf()->getSize((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer* buf)
- : LogCommand("setLogSize"), mBuf(*buf) {
-}
-
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -149,7 +125,7 @@
}
unsigned long size = atol(argv[2]);
- if (mBuf.setSize((log_id_t)id, size)) {
+ if (buf()->setSize((log_id_t)id, size)) {
cli->sendMsg("Range Error");
return 0;
}
@@ -158,10 +134,6 @@
return 0;
}
-CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer* buf)
- : LogCommand("getLogSizeUsed"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -176,17 +148,13 @@
return 0;
}
- unsigned long size = mBuf.getSizeUsed((log_id_t)id);
+ unsigned long size = buf()->getSizeUsed((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer* buf)
- : LogCommand("getStatistics"), mBuf(*buf) {
-}
-
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
// prefix includes the length of the prefix itself.
static std::string PackageString(const std::string& str) {
@@ -241,25 +209,17 @@
}
}
- cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+ cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
return 0;
}
-CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer* buf)
- : LogCommand("getPruneList"), mBuf(*buf) {
-}
-
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
int /*argc*/, char** /*argv*/) {
setname();
- cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
+ cli->sendMsg(PackageString(prune()->format()).c_str());
return 0;
}
-CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer* buf)
- : LogCommand("setPruneList"), mBuf(*buf) {
-}
-
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -276,7 +236,7 @@
str += argv[i];
}
- int ret = mBuf.initPrune(str.c_str());
+ int ret = prune()->init(str.c_str());
if (ret) {
cli->sendMsg("Invalid");
@@ -288,10 +248,6 @@
return 0;
}
-CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer* buf)
- : LogCommand("getEventTag"), mBuf(*buf) {
-}
-
int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -328,39 +284,45 @@
cli->sendMsg("can not mix id= with either format= or name=");
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
+ cli->sendMsg(PackageString(tags()->formatEntry(atoi(id), uid)).c_str());
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
+ cli->sendMsg(PackageString(tags()->formatGetEventTag(uid, name, format)).c_str());
return 0;
}
-CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
-}
-
int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
- reinit_signal_handler(SIGHUP);
+ android::prdebug("logd reinit");
+ buf()->init();
+ prune()->init(nullptr);
+
+ // This only works on userdebug and eng devices to re-read the
+ // /data/misc/logd/event-log-tags file right after /data is mounted.
+ // The operation is near to boot and should only happen once. There
+ // are races associated with its use since it can trigger a Rebuild
+ // of the file, but that is a can-not-happen since the file was not
+ // read yet. More dangerous if called later, but if all is well it
+ // should just skip over everything and not write any new entries.
+ if (__android_log_is_debuggable()) {
+ tags()->ReadFileEventLogTags(tags()->debug_event_log_tags);
+ }
cli->sendMsg("success");
return 0;
}
-CommandListener::ExitCmd::ExitCmd(CommandListener* parent)
- : LogCommand("EXIT"), mParent(*parent) {
-}
-
int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
cli->sendMsg("success");
- release(cli);
+ parent_->release(cli);
return 0;
}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index ed99419..c90c247 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -14,85 +14,53 @@
* limitations under the License.
*/
-#ifndef _COMMANDLISTENER_H__
-#define _COMMANDLISTENER_H__
+#pragma once
#include <sysutils/FrameworkListener.h>
+
#include "LogBuffer.h"
#include "LogCommand.h"
#include "LogListener.h"
#include "LogReader.h"
-
-// See main.cpp for implementation
-void reinit_signal_handler(int /*signal*/);
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
class CommandListener : public FrameworkListener {
- public:
- CommandListener(LogBuffer* buf, LogReader* reader, LogListener* swl);
- virtual ~CommandListener() {
- }
+ public:
+ CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+ virtual ~CommandListener() {}
- private:
+ private:
static int getLogSocket();
- class ShutdownCmd : public LogCommand {
- LogReader& mReader;
- LogListener& mSwl;
+ LogBuffer* buf_;
+ LogTags* tags_;
+ PruneList* prune_;
- public:
- ShutdownCmd(LogReader* reader, LogListener* swl);
- virtual ~ShutdownCmd() {
- }
- int runCommand(SocketClient* c, int argc, char** argv);
- };
-
-#define LogBufferCmd(name) \
+#define LogCmd(name, command_string) \
class name##Cmd : public LogCommand { \
- LogBuffer& mBuf; \
+ public: \
+ explicit name##Cmd(CommandListener* parent) \
+ : LogCommand(#command_string), parent_(parent) {} \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient* c, int argc, char** argv); \
\
- public: \
- explicit name##Cmd(LogBuffer* buf); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
+ private: \
+ LogBuffer* buf() const { return parent_->buf_; } \
+ LogTags* tags() const { return parent_->tags_; } \
+ PruneList* prune() const { return parent_->prune_; } \
+ CommandListener* parent_; \
}
- LogBufferCmd(Clear);
- LogBufferCmd(GetBufSize);
- LogBufferCmd(SetBufSize);
- LogBufferCmd(GetBufSizeUsed);
- LogBufferCmd(GetStatistics);
- LogBufferCmd(GetPruneList);
- LogBufferCmd(SetPruneList);
- LogBufferCmd(GetEventTag);
-
-#define LogCmd(name) \
- class name##Cmd : public LogCommand { \
- public: \
- name##Cmd(); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- }
-
- LogCmd(Reinit);
-
-#define LogParentCmd(name) \
- class name##Cmd : public LogCommand { \
- CommandListener& mParent; \
- \
- public: \
- name##Cmd(); \
- explicit name##Cmd(CommandListener* parent); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- void release(SocketClient* c) { \
- mParent.release(c); \
- } \
- }
-
- LogParentCmd(Exit);
+ LogCmd(Clear, clear);
+ LogCmd(GetBufSize, getLogSize);
+ LogCmd(SetBufSize, setLogSize);
+ LogCmd(GetBufSizeUsed, getLogSizeUsed);
+ LogCmd(GetStatistics, getStatistics);
+ LogCmd(GetPruneList, getPruneList);
+ LogCmd(SetPruneList, setPruneList);
+ LogCmd(GetEventTag, getEventTag);
+ LogCmd(Reinit, reinit);
+ LogCmd(Exit, EXIT);
+#undef LogCmd
};
-
-#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
deleted file mode 100644
index 0845504..0000000
--- a/logd/FlushCommand.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-#include "LogUtils.h"
-
-// runSocketCommand is called once for every open client on the
-// log reader socket. Here we manage and associated the reader
-// client tracking and log region locks LastLogTimes list of
-// LogTimeEntrys, and spawn a transitory per-client thread to
-// work at filing data to the socket.
-//
-// global LogTimeEntry::wrlock() is used to protect access,
-// reference counts are used to ensure that individual
-// LogTimeEntry lifetime is managed when not protected.
-void FlushCommand::runSocketCommand(SocketClient* client) {
- LogTimeEntry* entry = nullptr;
- LastLogTimes& times = mReader.logbuf().mTimes;
-
- LogTimeEntry::wrlock();
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- entry = it->get();
- if (entry->mClient == client) {
- if (!entry->isWatchingMultiple(mLogMask)) {
- LogTimeEntry::unlock();
- return;
- }
- if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
- LogTimeEntry::unlock();
- return;
- }
- entry->triggerReader_Locked();
- LogTimeEntry::unlock();
- return;
- }
- it++;
- }
-
- LogTimeEntry::unlock();
-}
-
-bool FlushCommand::hasReadLogs(SocketClient* client) {
- return clientHasLogCredentials(client);
-}
-
-static bool clientHasSecurityCredentials(SocketClient* client) {
- return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
-}
-
-bool FlushCommand::hasSecurityLogs(SocketClient* client) {
- return clientHasSecurityCredentials(client);
-}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
deleted file mode 100644
index a69d439..0000000
--- a/logd/FlushCommand.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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 _FLUSH_COMMAND_H
-#define _FLUSH_COMMAND_H
-
-#include <android/log.h>
-#include <sysutils/SocketClientCommand.h>
-
-class LogBufferElement;
-
-#include "LogTimes.h"
-
-class LogReader;
-
-class FlushCommand : public SocketClientCommand {
- LogReader& mReader;
- log_mask_t mLogMask;
-
- public:
- explicit FlushCommand(LogReader& reader, log_mask_t logMask)
- : mReader(reader), mLogMask(logMask) {
- }
-
- virtual void runSocketCommand(SocketClient* client);
-
- static bool hasReadLogs(SocketClient* client);
- static bool hasSecurityLogs(SocketClient* client);
-};
-
-#endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..bcc187d 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -247,22 +247,10 @@
static const char audit_str[] = " audit(";
char* timeptr = strstr(str, audit_str);
- if (timeptr &&
- ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
+ if (timeptr && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q"))) &&
(*cp == ':')) {
memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
- if (!isMonotonic()) {
- if (android::isMonotonic(now)) {
- LogKlog::convertMonotonicToReal(now);
- }
- } else {
- if (!android::isMonotonic(now)) {
- LogKlog::convertRealToMonotonic(now);
- }
- }
- } else if (isMonotonic()) {
- now = log_time(CLOCK_MONOTONIC);
} else {
now = log_time(CLOCK_REALTIME);
}
@@ -277,7 +265,7 @@
: LOGGER_ENTRY_MAX_PAYLOAD;
size_t message_len = str_len + sizeof(android_log_event_string_t);
- log_mask_t notify = 0;
+ unsigned int notify = 0;
if (events) { // begin scope for event buffer
uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index c3d7a3e..7df0a5d 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -36,9 +36,6 @@
public:
LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
int log(char* buf, size_t len);
- bool isMonotonic() {
- return logbuf->isMonotonic();
- }
protected:
virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 36273de..2cb0c5e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -50,67 +50,21 @@
setSize(i, LOG_BUFFER_MIN_SIZE);
}
}
- bool lastMonotonic = monotonic;
- monotonic = android_log_clockid() == CLOCK_MONOTONIC;
- if (lastMonotonic != monotonic) {
- //
- // Fixup all timestamps, may not be 100% accurate, but better than
- // throwing what we have away when we get 'surprised' by a change.
- // In-place element fixup so no need to check reader-lock. Entries
- // should already be in timestamp order, but we could end up with a
- // few out-of-order entries if new monotonics come in before we
- // are notified of the reinit change in status. A Typical example would
- // be:
- // --------- beginning of system
- // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data
- // --------- beginning of kernel
- // 0.000000 0 0 I : Initializing cgroup subsys
- // as the act of mounting /data would trigger persist.logd.timestamp to
- // be corrected. 1/30 corner case YMMV.
- //
- rdlock();
- LogBufferElementCollection::iterator it = mLogElements.begin();
- while ((it != mLogElements.end())) {
- LogBufferElement* e = *it;
- if (monotonic) {
- if (!android::isMonotonic(e->mRealTime)) {
- LogKlog::convertRealToMonotonic(e->mRealTime);
- if ((e->mRealTime.tv_nsec % 1000) == 0) {
- e->mRealTime.tv_nsec++;
- }
- }
- } else {
- if (android::isMonotonic(e->mRealTime)) {
- LogKlog::convertMonotonicToReal(e->mRealTime);
- if ((e->mRealTime.tv_nsec % 1000) == 0) {
- e->mRealTime.tv_nsec++;
- }
- }
- }
- ++it;
- }
- unlock();
- }
-
- // We may have been triggered by a SIGHUP. Release any sleeping reader
- // threads to dump their current content.
- //
- // NB: this is _not_ performed in the context of a SIGHUP, it is
- // performed during startup, and in context of reinit administrative thread
- LogTimeEntry::wrlock();
+ // Release any sleeping reader threads to dump their current content.
+ LogReaderThread::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
+ LogReaderThread* entry = times->get();
entry->triggerReader_Locked();
times++;
}
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
}
-LogBuffer::LogBuffer(LastLogTimes* times)
- : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
+ : mTimes(*times), tags_(tags), prune_(prune) {
pthread_rwlock_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
@@ -130,14 +84,14 @@
LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
auto it = mLogElements.begin();
- if (mOldest[log_id]) {
- it = *mOldest[log_id];
+ if (oldest_[log_id]) {
+ it = *oldest_[log_id];
}
while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
it++;
}
if (it != mLogElements.end()) {
- mOldest[log_id] = it;
+ oldest_[log_id] = it;
}
return it;
}
@@ -232,7 +186,7 @@
const char* tag = nullptr;
size_t tag_len = 0;
if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
- tag = tagToName(elem->getTag());
+ tag = tags_->tagToName(elem->getTag());
if (tag) {
tag_len = strlen(tag);
}
@@ -461,7 +415,7 @@
bool setLast[LOG_ID_MAX];
bool doSetLast = false;
- log_id_for_each(i) { doSetLast |= setLast[i] = mOldest[i] && it == *mOldest[i]; }
+ log_id_for_each(i) { doSetLast |= setLast[i] = oldest_[i] && it == *oldest_[i]; }
#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
LogBufferElementCollection::iterator bad = it;
int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
@@ -473,9 +427,9 @@
log_id_for_each(i) {
if (setLast[i]) {
if (__predict_false(it == mLogElements.end())) {
- mOldest[i] = std::nullopt;
+ oldest_[i] = std::nullopt;
} else {
- mOldest[i] = it; // Store the next iterator even if it does not correspond to
+ oldest_[i] = it; // Store the next iterator even if it does not correspond to
// the same log_id, as a starting point for GetOldest().
}
}
@@ -580,14 +534,14 @@
// If the selected reader is blocking our pruning progress, decide on
// what kind of mitigation is necessary to unblock the situation.
-void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
+void LogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
// A misbehaving or slow reader has its connection
// dropped if we hit too much memory pressure.
android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
- me->mClient->getPid());
+ me->client()->getPid());
me->release_Locked();
- } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
+ } else if (me->timeout().tv_sec || me->timeout().tv_nsec) {
// Allow a blocked WRAP timeout reader to
// trigger and start reporting the log data.
me->triggerReader_Locked();
@@ -595,7 +549,7 @@
// tell slow reader to skip entries to catch up
android::prdebug(
"Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
- pruneRows, me->mClient->getPid());
+ pruneRows, me->client()->getPid());
me->triggerSkip_Locked(id, pruneRows);
}
}
@@ -648,20 +602,19 @@
// LogBuffer::wrlock() must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- LogTimeEntry* oldest = nullptr;
+ LogReaderThread* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
- LogTimeEntry::rdlock();
+ LogReaderThread::rdlock();
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
- if (entry->isWatching(id) &&
- (!oldest || (oldest->mStart > entry->mStart) ||
- ((oldest->mStart == entry->mStart) &&
- (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
+ LogReaderThread* entry = times->get();
+ if (entry->IsWatching(id) && (!oldest || oldest->start() > entry->start() ||
+ (oldest->start() == entry->start() &&
+ (entry->timeout().tv_sec || entry->timeout().tv_nsec)))) {
oldest = entry;
}
times++;
@@ -682,7 +635,7 @@
continue;
}
- if (oldest && oldest->mStart <= element->getSequence()) {
+ if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
break;
@@ -693,12 +646,12 @@
break;
}
}
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
return busy;
}
// prune by worst offenders; by blacklist, UID, and by PID of system UID
- bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
int worst = -1; // not valid for getUid() or getKey()
@@ -706,7 +659,7 @@
size_t second_worst_sizes = 0;
pid_t worstPid = 0; // POSIX guarantees PID != 0
- if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+ if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
// Calculate threshold as 12.5% of available storage
size_t threshold = log_buffer_size(id) / 8;
@@ -720,7 +673,7 @@
.findWorst(worst, worst_sizes, second_worst_sizes,
threshold);
- if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+ if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
stats.sortPids(worst, (pid_t)0, 2, id)
.findWorst(worstPid, worst_sizes, second_worst_sizes);
}
@@ -773,7 +726,7 @@
while (it != mLogElements.end()) {
LogBufferElement* element = *it;
- if (oldest && oldest->mStart <= element->getSequence()) {
+ if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
// Do not let chatty eliding trigger any reader mitigation
break;
@@ -802,7 +755,7 @@
? element->getTag()
: element->getUid();
- if (hasBlacklist && mPrune.naughty(element)) {
+ if (hasBlacklist && prune_->naughty(element)) {
last.clear(element);
it = erase(it);
if (dropped) {
@@ -899,13 +852,13 @@
}
last.clear();
- if (!kick || !mPrune.worstUidEnabled()) {
+ if (!kick || !prune_->worstUidEnabled()) {
break; // the following loop will ask bad clients to skip/drop
}
}
bool whitelist = false;
- bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
it = GetOldest(id);
while ((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement* element = *it;
@@ -915,13 +868,13 @@
continue;
}
- if (oldest && oldest->mStart <= element->getSequence()) {
+ if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
if (!whitelist) kickMe(oldest, id, pruneRows);
break;
}
- if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+ if (hasWhitelist && !element->getDropped() && prune_->nice(element)) {
// WhiteListed
whitelist = true;
it++;
@@ -943,7 +896,7 @@
continue;
}
- if (oldest && oldest->mStart <= element->getSequence()) {
+ if (oldest && oldest->start() <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
break;
@@ -954,7 +907,7 @@
}
}
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
return (pruneRows > 0) && busy;
}
@@ -977,20 +930,20 @@
// readers and let the clear run (below) deal with determining
// if we are still blocked and return an error code to caller.
if (busy) {
- LogTimeEntry::wrlock();
+ LogReaderThread::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
- LogTimeEntry* entry = times->get();
+ LogReaderThread* entry = times->get();
// Killer punch
- if (entry->isWatching(id)) {
+ if (entry->IsWatching(id)) {
android::prdebug(
"Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
- entry->mClient->getPid());
+ entry->client()->getPid());
entry->release_Locked();
}
times++;
}
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
}
}
wrlock();
@@ -1034,7 +987,7 @@
uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid, bool privileged,
bool security,
- int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+ const std::function<int(const LogBufferElement* element)>& filter) {
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
@@ -1072,7 +1025,7 @@
// NB: calling out to another object with wrlock() held (safe)
if (filter) {
- int ret = (*filter)(element, arg);
+ int ret = filter(element);
if (ret == false) {
continue;
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 458fbbb..3c45667 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_BUFFER_H__
-#define _LOGD_LOG_BUFFER_H__
+#pragma once
#include <sys/types.h>
@@ -28,51 +27,11 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
+#include "LogReaderThread.h"
#include "LogStatistics.h"
#include "LogTags.h"
-#include "LogTimes.h"
#include "LogWhiteBlackList.h"
-//
-// We are either in 1970ish (MONOTONIC) or 2016+ish (REALTIME) so to
-// differentiate without prejudice, we use 1972 to delineate, earlier
-// is likely monotonic, later is real. Otherwise we start using a
-// dividing line between monotonic and realtime if more than a minute
-// difference between them.
-//
-namespace android {
-
-static bool isMonotonic(const log_time& mono) {
- static const uint32_t EPOCH_PLUS_2_YEARS = 2 * 24 * 60 * 60 * 1461 / 4;
- static const uint32_t EPOCH_PLUS_MINUTE = 60;
-
- if (mono.tv_sec >= EPOCH_PLUS_2_YEARS) {
- return false;
- }
-
- log_time now(CLOCK_REALTIME);
-
- /* Timezone and ntp time setup? */
- if (now.tv_sec >= EPOCH_PLUS_2_YEARS) {
- return true;
- }
-
- /* no way to differentiate realtime from monotonic time */
- if (now.tv_sec < EPOCH_PLUS_MINUTE) {
- return false;
- }
-
- log_time cpu(CLOCK_MONOTONIC);
- /* too close to call to differentiate monotonic times from realtime */
- if ((cpu.tv_sec + EPOCH_PLUS_MINUTE) >= now.tv_sec) {
- return false;
- }
-
- /* dividing line half way between monotonic and realtime */
- return mono.tv_sec < ((cpu.tv_sec + now.tv_sec) / 2);
-}
-}
-
typedef std::list<LogBufferElement*> LogBufferElementCollection;
class LogBuffer {
@@ -81,10 +40,6 @@
LogStatistics stats;
- PruneList mPrune;
- // Keeps track of the iterator to the oldest log message of a given log type, as an
- // optimization when pruning logs. Use GetOldest() to retrieve.
- std::optional<LogBufferElementCollection::iterator> mOldest[LOG_ID_MAX];
// watermark of any worst/chatty uid processing
typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
LogBufferIteratorMap;
@@ -96,10 +51,6 @@
unsigned long mMaxSize[LOG_ID_MAX];
- bool monotonic;
-
- LogTags tags;
-
LogBufferElement* lastLoggedElements[LOG_ID_MAX];
LogBufferElement* droppedElements[LOG_ID_MAX];
void log(LogBufferElement* elem);
@@ -107,12 +58,9 @@
public:
LastLogTimes& mTimes;
- explicit LogBuffer(LastLogTimes* times);
+ LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
~LogBuffer();
void init();
- bool isMonotonic() {
- return monotonic;
- }
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);
@@ -122,8 +70,7 @@
uint64_t flushTo(SocketClient* writer, uint64_t start,
pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
bool privileged, bool security,
- int (*filter)(const LogBufferElement* element, void* arg) = nullptr,
- void* arg = nullptr);
+ const std::function<int(const LogBufferElement* element)>& filter);
bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
@@ -136,24 +83,6 @@
stats.enableStatistics();
}
- int initPrune(const char* cp) {
- return mPrune.init(cp);
- }
- std::string formatPrune() {
- return mPrune.format();
- }
-
- std::string formatGetEventTag(uid_t uid, const char* name,
- const char* format) {
- return tags.formatGetEventTag(uid, name, format);
- }
- std::string formatEntry(uint32_t tag, uid_t uid) {
- return tags.formatEntry(tag, uid);
- }
- const char* tagToName(uint32_t tag) {
- return tags.tagToName(tag);
- }
-
// helper must be protected directly or implicitly by wrlock()/unlock()
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
@@ -177,7 +106,7 @@
static constexpr size_t maxPrune = 256;
void maybePrune(log_id_t id);
- void kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows);
+ void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
@@ -186,6 +115,11 @@
// Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
// there are no logs for the given log type. Requires mLogElementsLock to be held.
LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
-};
-#endif // _LOGD_LOG_BUFFER_H__
+ LogTags* tags_;
+ PruneList* prune_;
+
+ // Keeps track of the iterator to the oldest log message of a given log type, as an
+ // optimization when pruning logs. Use GetOldest() to retrieve.
+ std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
+};
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index edd326a..c308073 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -309,8 +309,6 @@
}
buf = cp;
- if (isMonotonic()) return now;
-
const char* b;
if (((b = android::strnstr(cp, len, suspendStr))) &&
(((b += strlen(suspendStr)) - cp) < len)) {
@@ -356,11 +354,7 @@
convertMonotonicToReal(now);
} else {
- if (isMonotonic()) {
- now = log_time(CLOCK_MONOTONIC);
- } else {
- now = log_time(CLOCK_REALTIME);
- }
+ now = log_time(CLOCK_REALTIME);
}
return now;
}
@@ -431,45 +425,6 @@
return pri;
}
-// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
-// compensated start time.
-void LogKlog::synchronize(const char* buf, ssize_t len) {
- const char* cp = android::strnstr(buf, len, suspendStr);
- if (!cp) {
- cp = android::strnstr(buf, len, resumeStr);
- if (!cp) return;
- } else {
- const char* rp = android::strnstr(buf, len, resumeStr);
- if (rp && (rp < cp)) cp = rp;
- }
-
- do {
- --cp;
- } while ((cp > buf) && (*cp != '\n'));
- if (*cp == '\n') {
- ++cp;
- }
- parseKernelPrio(cp, len - (cp - buf));
-
- log_time now = sniffTime(cp, len - (cp - buf), true);
-
- const char* suspended = android::strnstr(buf, len, suspendedStr);
- if (!suspended || (suspended > cp)) {
- return;
- }
- cp = suspended;
-
- do {
- --cp;
- } while ((cp > buf) && (*cp != '\n'));
- if (*cp == '\n') {
- ++cp;
- }
- parseKernelPrio(cp, len - (cp - buf));
-
- sniffTime(cp, len - (cp - buf), true);
-}
-
// Convert kernel log priority number into an Android Logger priority number
static int convertKernelPrioToAndroidPrio(int pri) {
switch (pri & LOG_PRIMASK) {
@@ -789,7 +744,7 @@
memcpy(np, p, b);
np[b] = '\0';
- if (!isMonotonic()) {
+ {
// Watch out for singular race conditions with timezone causing near
// integer quarter-hour jumps in the time and compensate accordingly.
// Entries will be temporal within near_seconds * 2. b/21868540
@@ -819,7 +774,7 @@
// notify readers
if (rc > 0) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
+ reader->notifyNewLog(static_cast<unsigned int>(1 << LOG_ID_KERNEL));
}
return rc;
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 6bfd6a8..4c09751 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -42,17 +42,10 @@
LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
bool auditd);
int log(const char* buf, ssize_t len);
- void synchronize(const char* buf, ssize_t len);
- bool isMonotonic() {
- return logbuf->isMonotonic();
- }
static void convertMonotonicToReal(log_time& real) {
real += correction;
}
- static void convertRealToMonotonic(log_time& real) {
- real -= correction;
- }
protected:
log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index ba61042..138ab28 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -108,7 +108,7 @@
int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
if (res > 0) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
+ reader->notifyNewLog(static_cast<unsigned int>(1 << logId));
}
return true;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index f79d39c..a590cef 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -24,21 +24,35 @@
#include <cutils/sockets.h>
#include <private/android_logger.h>
-#include "FlushCommand.h"
#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogReader.h"
#include "LogUtils.h"
+static bool CanReadSecurityLogs(SocketClient* client) {
+ return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
+}
+
LogReader::LogReader(LogBuffer* logbuf)
: SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
}
// When we are notified a new log entry is available, inform
// listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(log_mask_t logMask) {
- FlushCommand command(*this, logMask);
- runOnEachSocket(&command);
+void LogReader::notifyNewLog(unsigned int log_mask) {
+ LastLogTimes& times = mLogbuf.mTimes;
+
+ LogReaderThread::wrlock();
+ for (const auto& entry : times) {
+ if (!entry->IsWatchingMultiple(log_mask)) {
+ continue;
+ }
+ if (entry->timeout().tv_sec || entry->timeout().tv_nsec) {
+ continue;
+ }
+ entry->triggerReader_Locked();
+ }
+ LogReaderThread::unlock();
}
// Note returning false will release the SocketClient instance.
@@ -60,15 +74,15 @@
// Clients are only allowed to send one command, disconnect them if they
// send another.
- LogTimeEntry::wrlock();
+ LogReaderThread::wrlock();
for (const auto& entry : mLogbuf.mTimes) {
- if (entry->mClient == cli) {
+ if (entry->client() == cli) {
entry->release_Locked();
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
return false;
}
}
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
unsigned long tail = 0;
static const char _tail[] = " tail=";
@@ -123,66 +137,46 @@
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
- LogTimeEntry::wrlock();
- LogTimeEntry::unlock();
+ LogReaderThread::wrlock();
+ LogReaderThread::unlock();
sched_yield();
nonBlock = true;
}
+ bool privileged = clientHasLogCredentials(cli);
+ bool can_read_security = CanReadSecurityLogs(cli);
+
uint64_t sequence = 1;
// Convert realtime to sequence number
if (start != log_time::EPOCH) {
- class LogFindStart {
- const pid_t mPid;
- const unsigned mLogMask;
- bool startTimeSet;
- const log_time start;
- uint64_t& sequence;
- uint64_t last;
- bool isMonotonic;
-
- public:
- LogFindStart(unsigned logMask, pid_t pid, log_time start, uint64_t& sequence,
- bool isMonotonic)
- : mPid(pid),
- mLogMask(logMask),
- startTimeSet(false),
- start(start),
- sequence(sequence),
- last(sequence),
- isMonotonic(isMonotonic) {}
-
- static int callback(const LogBufferElement* element, void* obj) {
- LogFindStart* me = reinterpret_cast<LogFindStart*>(obj);
- if ((!me->mPid || (me->mPid == element->getPid())) &&
- (me->mLogMask & (1 << element->getLogId()))) {
- if (me->start == element->getRealTime()) {
- me->sequence = element->getSequence();
- me->startTimeSet = true;
- return -1;
- } else if (!me->isMonotonic || android::isMonotonic(element->getRealTime())) {
- if (me->start < element->getRealTime()) {
- me->sequence = me->last;
- me->startTimeSet = true;
- return -1;
- }
- me->last = element->getSequence();
- } else {
- me->last = element->getSequence();
- }
- }
- return false;
+ bool start_time_set = false;
+ uint64_t last = sequence;
+ auto log_find_start = [pid, logMask, start, &sequence, &start_time_set,
+ &last](const LogBufferElement* element) -> int {
+ if (pid && pid != element->getPid()) {
+ return 0;
}
+ if ((logMask & (1 << element->getLogId())) == 0) {
+ return 0;
+ }
+ if (start == element->getRealTime()) {
+ sequence = element->getSequence();
+ start_time_set = true;
+ return -1;
+ } else {
+ if (start < element->getRealTime()) {
+ sequence = last;
+ start_time_set = true;
+ return -1;
+ }
+ last = element->getSequence();
+ }
+ return 0;
+ };
- bool found() { return startTimeSet; }
- } logFindStart(logMask, pid, start, sequence,
- logbuf().isMonotonic() && android::isMonotonic(start));
+ logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security, log_find_start);
- logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
- FlushCommand::hasSecurityLogs(cli),
- logFindStart.callback, &logFindStart);
-
- if (!logFindStart.found()) {
+ if (!start_time_set) {
if (nonBlock) {
doSocketDelete(cli);
return false;
@@ -201,11 +195,12 @@
timeout = 0;
}
- LogTimeEntry::wrlock();
- auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
- sequence, timeout);
+ LogReaderThread::wrlock();
+ auto entry =
+ std::make_unique<LogReaderThread>(*this, cli, nonBlock, tail, logMask, pid, start,
+ sequence, timeout, privileged, can_read_security);
if (!entry->startReader_Locked()) {
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
return false;
}
@@ -218,24 +213,24 @@
setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t,
sizeof(t));
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
return true;
}
void LogReader::doSocketDelete(SocketClient* cli) {
LastLogTimes& times = mLogbuf.mTimes;
- LogTimeEntry::wrlock();
+ LogReaderThread::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
- LogTimeEntry* entry = it->get();
- if (entry->mClient == cli) {
+ LogReaderThread* entry = it->get();
+ if (entry->client() == cli) {
entry->release_Locked();
break;
}
it++;
}
- LogTimeEntry::unlock();
+ LogReaderThread::unlock();
}
int LogReader::getLogSocket() {
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b5312b6..f00cc21 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_WRITER_H__
-#define _LOGD_LOG_WRITER_H__
+#pragma once
#include <sysutils/SocketListener.h>
-#include "LogTimes.h"
+#include "LogReaderThread.h"
#define LOGD_SNDTIMEO 32
@@ -30,7 +29,7 @@
public:
explicit LogReader(LogBuffer* logbuf);
- void notifyNewLog(log_mask_t logMask);
+ void notifyNewLog(unsigned int logMask);
LogBuffer& logbuf(void) const {
return mLogbuf;
@@ -44,5 +43,3 @@
void doSocketDelete(SocketClient* cli);
};
-
-#endif
diff --git a/logd/LogReaderThread.cpp b/logd/LogReaderThread.cpp
new file mode 100644
index 0000000..5413c4d
--- /dev/null
+++ b/logd/LogReaderThread.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2014 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 "LogReaderThread.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include <thread>
+
+#include "LogBuffer.h"
+#include "LogReader.h"
+
+using namespace std::placeholders;
+
+pthread_mutex_t LogReaderThread::timesLock = PTHREAD_MUTEX_INITIALIZER;
+
+LogReaderThread::LogReaderThread(LogReader& reader, SocketClient* client, bool non_block,
+ unsigned long tail, unsigned int log_mask, pid_t pid,
+ log_time start_time, uint64_t start, uint64_t timeout,
+ bool privileged, bool can_read_security_logs)
+ : leading_dropped_(false),
+ reader_(reader),
+ log_mask_(log_mask),
+ pid_(pid),
+ tail_(tail),
+ count_(0),
+ index_(0),
+ client_(client),
+ start_time_(start_time),
+ start_(start),
+ non_block_(non_block),
+ privileged_(privileged),
+ can_read_security_logs_(can_read_security_logs) {
+ timeout_.tv_sec = timeout / NS_PER_SEC;
+ timeout_.tv_nsec = timeout % NS_PER_SEC;
+ memset(last_tid_, 0, sizeof(last_tid_));
+ pthread_cond_init(&thread_triggered_condition_, nullptr);
+ cleanSkip_Locked();
+}
+
+bool LogReaderThread::startReader_Locked() {
+ auto thread = std::thread{&LogReaderThread::ThreadFunction, this};
+ thread.detach();
+ return true;
+}
+
+void LogReaderThread::ThreadFunction() {
+ prctl(PR_SET_NAME, "logd.reader.per");
+
+ SocketClient* client = client_;
+
+ LogBuffer& logbuf = reader_.logbuf();
+
+ leading_dropped_ = true;
+
+ wrlock();
+
+ uint64_t start = start_;
+
+ while (!release_) {
+ if (timeout_.tv_sec || timeout_.tv_nsec) {
+ if (pthread_cond_clockwait(&thread_triggered_condition_, ×Lock, CLOCK_MONOTONIC,
+ &timeout_) == ETIMEDOUT) {
+ timeout_.tv_sec = 0;
+ timeout_.tv_nsec = 0;
+ }
+ if (release_) {
+ break;
+ }
+ }
+
+ unlock();
+
+ if (tail_) {
+ logbuf.flushTo(client, start, nullptr, privileged_, can_read_security_logs_,
+ std::bind(&LogReaderThread::FilterFirstPass, this, _1));
+ leading_dropped_ =
+ true; // TODO: Likely a bug, if leading_dropped_ was not true before calling
+ // flushTo(), then it should not be reset to true after.
+ }
+ start = logbuf.flushTo(client, start, last_tid_, privileged_, can_read_security_logs_,
+ std::bind(&LogReaderThread::FilterSecondPass, this, _1));
+
+ // We only ignore entries before the original start time for the first flushTo(), if we
+ // get entries after this first flush before the original start time, then the client
+ // wouldn't have seen them.
+ // Note: this is still racy and may skip out of order events that came in since the last
+ // time the client disconnected and then reconnected with the new start time. The long term
+ // solution here is that clients must request events since a specific sequence number.
+ start_time_.tv_sec = 0;
+ start_time_.tv_nsec = 0;
+
+ wrlock();
+
+ if (start == LogBufferElement::FLUSH_ERROR) {
+ break;
+ }
+
+ start_ = start + 1;
+
+ if (non_block_ || release_) {
+ break;
+ }
+
+ cleanSkip_Locked();
+
+ if (!timeout_.tv_sec && !timeout_.tv_nsec) {
+ pthread_cond_wait(&thread_triggered_condition_, ×Lock);
+ }
+ }
+
+ LogReader& reader = reader_;
+ reader.release(client);
+
+ client->decRef();
+
+ LastLogTimes& times = reader.logbuf().mTimes;
+ auto it = std::find_if(times.begin(), times.end(),
+ [this](const auto& other) { return other.get() == this; });
+
+ if (it != times.end()) {
+ times.erase(it);
+ }
+
+ unlock();
+}
+
+// A first pass to count the number of elements
+int LogReaderThread::FilterFirstPass(const LogBufferElement* element) {
+ LogReaderThread::wrlock();
+
+ if (leading_dropped_) {
+ if (element->getDropped()) {
+ LogReaderThread::unlock();
+ return false;
+ }
+ leading_dropped_ = false;
+ }
+
+ if (count_ == 0) {
+ start_ = element->getSequence();
+ }
+
+ if ((!pid_ || pid_ == element->getPid()) && IsWatching(element->getLogId()) &&
+ (start_time_ == log_time::EPOCH || start_time_ <= element->getRealTime())) {
+ ++count_;
+ }
+
+ LogReaderThread::unlock();
+
+ return false;
+}
+
+// A second pass to send the selected elements
+int LogReaderThread::FilterSecondPass(const LogBufferElement* element) {
+ LogReaderThread::wrlock();
+
+ start_ = element->getSequence();
+
+ if (skip_ahead_[element->getLogId()]) {
+ skip_ahead_[element->getLogId()]--;
+ goto skip;
+ }
+
+ if (leading_dropped_) {
+ if (element->getDropped()) {
+ goto skip;
+ }
+ leading_dropped_ = false;
+ }
+
+ // Truncate to close race between first and second pass
+ if (non_block_ && tail_ && index_ >= count_) {
+ goto stop;
+ }
+
+ if (!IsWatching(element->getLogId())) {
+ goto skip;
+ }
+
+ if (pid_ && pid_ != element->getPid()) {
+ goto skip;
+ }
+
+ if (start_time_ != log_time::EPOCH && element->getRealTime() <= start_time_) {
+ goto skip;
+ }
+
+ if (release_) {
+ goto stop;
+ }
+
+ if (!tail_) {
+ goto ok;
+ }
+
+ ++index_;
+
+ if (count_ > tail_ && index_ <= (count_ - tail_)) {
+ goto skip;
+ }
+
+ if (!non_block_) {
+ tail_ = 0;
+ }
+
+ok:
+ if (!skip_ahead_[element->getLogId()]) {
+ LogReaderThread::unlock();
+ return true;
+ }
+ // FALLTHRU
+
+skip:
+ LogReaderThread::unlock();
+ return false;
+
+stop:
+ LogReaderThread::unlock();
+ return -1;
+}
+
+void LogReaderThread::cleanSkip_Locked(void) {
+ memset(skip_ahead_, 0, sizeof(skip_ahead_));
+}
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
new file mode 100644
index 0000000..39a8b63
--- /dev/null
+++ b/logd/LogReaderThread.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012-2013 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 <pthread.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <list>
+#include <memory>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+
+class LogReader;
+class LogBufferElement;
+
+class LogReaderThread {
+ static pthread_mutex_t timesLock;
+
+ public:
+ LogReaderThread(LogReader& reader, SocketClient* client, bool non_block, unsigned long tail,
+ unsigned int log_mask, pid_t pid, log_time start_time, uint64_t sequence,
+ uint64_t timeout, bool privileged, bool can_read_security_logs);
+
+ // Protect List manipulations
+ static void wrlock() { pthread_mutex_lock(×Lock); }
+ static void rdlock() { pthread_mutex_lock(×Lock); }
+ static void unlock() { pthread_mutex_unlock(×Lock); }
+
+ bool startReader_Locked();
+
+ void triggerReader_Locked() { pthread_cond_signal(&thread_triggered_condition_); }
+
+ void triggerSkip_Locked(log_id_t id, unsigned int skip) { skip_ahead_[id] = skip; }
+ void cleanSkip_Locked();
+
+ void release_Locked() {
+ // gracefully shut down the socket.
+ shutdown(client_->getSocket(), SHUT_RDWR);
+ release_ = true;
+ pthread_cond_signal(&thread_triggered_condition_);
+ }
+
+ bool IsWatching(log_id_t id) const { return log_mask_ & (1 << id); }
+ bool IsWatchingMultiple(unsigned int log_mask) const { return log_mask_ & log_mask; }
+
+ const SocketClient* client() const { return client_; }
+ uint64_t start() const { return start_; }
+ const timespec& timeout() const { return timeout_; }
+
+ private:
+ void ThreadFunction();
+ // flushTo filter callbacks
+ int FilterFirstPass(const LogBufferElement* element);
+ int FilterSecondPass(const LogBufferElement* element);
+
+ // Set to true to cause the thread to end and the LogReaderThread to delete itself.
+ bool release_ = false;
+ // Indicates whether or not 'leading' (first logs seen starting from start_) 'dropped' (chatty)
+ // messages should be ignored.
+ bool leading_dropped_;
+
+ // Condition variable for waking the reader thread if there are messages pending for its client.
+ pthread_cond_t thread_triggered_condition_;
+
+ // Reference to the parent thread that manages log reader sockets.
+ LogReader& reader_;
+ // A mask of the logs buffers that are read by this reader.
+ const unsigned int log_mask_;
+ // If set to non-zero, only pids equal to this are read by the reader.
+ const pid_t pid_;
+ // When a reader is referencing (via start_) old elements in the log buffer, and the log
+ // buffer's size grows past its memory limit, the log buffer may request the reader to skip
+ // ahead a specified number of logs.
+ unsigned int skip_ahead_[LOG_ID_MAX];
+ // Used for distinguishing 'dropped' messages for duplicate logs vs chatty drops
+ pid_t last_tid_[LOG_ID_MAX];
+
+ // These next three variables are used for reading only the most recent lines aka `adb logcat
+ // -t` / `adb logcat -T`.
+ // tail_ is the number of most recent lines to print.
+ unsigned long tail_;
+ // count_ is the result of a first pass through the log buffer to determine how many total
+ // messages there are.
+ unsigned long count_;
+ // index_ is used along with count_ to only start sending lines once index_ > (count_ - tail_)
+ // and to disconnect the reader (if it is dumpAndClose, `adb logcat -t`), when index_ >= count_.
+ unsigned long index_;
+
+ // A pointer to the socket for this reader.
+ SocketClient* client_;
+ // When a reader requests logs starting from a given timestamp, its stored here for the first
+ // pass, such that logs before this time stamp that are accumulated in the buffer are ignored.
+ log_time start_time_;
+ // The point from which the reader will read logs once awoken.
+ uint64_t start_;
+ // CLOCK_MONOTONIC based timeout used for log wrapping. If this timeout expires before logs
+ // wrap, then wake up and send the logs to the reader anyway.
+ timespec timeout_;
+ // If this reader is 'dumpAndClose' and will disconnect once it has read its intended logs.
+ const bool non_block_;
+
+ // Whether or not this reader can read logs from all UIDs or only its own UID. See
+ // clientHasLogCredentials().
+ bool privileged_;
+ // Whether or not this reader can read security logs. See CanReadSecurityLogs().
+ bool can_read_security_logs_;
+};
+
+typedef std::list<std::unique_ptr<LogReaderThread>> LastLogTimes;
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index e45cc8a..082e4a1 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -99,8 +99,7 @@
struct tm tm;
localtime_r(&now, &tm);
char timebuf[20];
- size_t len =
- strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
+ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);
android::base::WriteStringToFd(
android::base::StringPrintf(
"# Rebuilt %.20s, content owned by logd\n", timebuf),
@@ -189,7 +188,6 @@
// Read the event log tags file, and build up our internal database
void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
bool etc = !strcmp(filename, system_event_log_tags);
- bool debug = !etc && !strcmp(filename, debug_event_log_tags);
if (!etc) {
RebuildFileEventLogTags(filename, warn);
@@ -391,23 +389,6 @@
return me->tagToName(tag);
}
-// Prototype in LogUtils.h allowing external access to our database.
-//
-// This only works on userdebug and eng devices to re-read the
-// /data/misc/logd/event-log-tags file right after /data is mounted.
-// The operation is near to boot and should only happen once. There
-// are races associated with its use since it can trigger a Rebuild
-// of the file, but that is a can-not-happen since the file was not
-// read yet. More dangerous if called later, but if all is well it
-// should just skip over everything and not write any new entries.
-void android::ReReadEventLogTags() {
- LogTags* me = logtags;
-
- if (me && __android_log_is_debuggable()) {
- me->ReadFileEventLogTags(me->debug_event_log_tags);
- }
-}
-
// converts an event tag into a format
const char* LogTags::tagToFormat(uint32_t tag) const {
tag2format_const_iterator iform;
@@ -565,7 +546,7 @@
*/
struct timespec ts;
- clock_gettime(android_log_clockid(), &ts);
+ clock_gettime(CLOCK_REALTIME, &ts);
android_log_header_t header = {
.id = LOG_ID_EVENTS,
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
deleted file mode 100644
index ed8d2f5..0000000
--- a/logd/LogTimes.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2014 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 <errno.h>
-#include <string.h>
-#include <sys/prctl.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-
-pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
-
-LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
- unsigned long tail, log_mask_t logMask, pid_t pid, log_time start_time,
- uint64_t start, uint64_t timeout)
- : leadingDropped(false),
- mReader(reader),
- mLogMask(logMask),
- mPid(pid),
- mCount(0),
- mTail(tail),
- mIndex(0),
- mClient(client),
- mStartTime(start_time),
- mStart(start),
- mNonBlock(nonBlock) {
- mTimeout.tv_sec = timeout / NS_PER_SEC;
- mTimeout.tv_nsec = timeout % NS_PER_SEC;
- memset(mLastTid, 0, sizeof(mLastTid));
- pthread_cond_init(&threadTriggeredCondition, nullptr);
- cleanSkip_Locked();
-}
-
-bool LogTimeEntry::startReader_Locked() {
- pthread_attr_t attr;
-
- if (!pthread_attr_init(&attr)) {
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- if (!pthread_create(&mThread, &attr, LogTimeEntry::threadStart,
- this)) {
- pthread_attr_destroy(&attr);
- return true;
- }
- }
- pthread_attr_destroy(&attr);
- }
-
- return false;
-}
-
-void* LogTimeEntry::threadStart(void* obj) {
- prctl(PR_SET_NAME, "logd.reader.per");
-
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- SocketClient* client = me->mClient;
-
- LogBuffer& logbuf = me->mReader.logbuf();
-
- bool privileged = FlushCommand::hasReadLogs(client);
- bool security = FlushCommand::hasSecurityLogs(client);
-
- me->leadingDropped = true;
-
- wrlock();
-
- uint64_t start = me->mStart;
-
- while (!me->mRelease) {
- if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
- if (pthread_cond_clockwait(&me->threadTriggeredCondition, ×Lock, CLOCK_MONOTONIC,
- &me->mTimeout) == ETIMEDOUT) {
- me->mTimeout.tv_sec = 0;
- me->mTimeout.tv_nsec = 0;
- }
- if (me->mRelease) {
- break;
- }
- }
-
- unlock();
-
- if (me->mTail) {
- logbuf.flushTo(client, start, nullptr, privileged, security,
- FilterFirstPass, me);
- me->leadingDropped = true;
- }
- start = logbuf.flushTo(client, start, me->mLastTid, privileged,
- security, FilterSecondPass, me);
-
- // We only ignore entries before the original start time for the first flushTo(), if we
- // get entries after this first flush before the original start time, then the client
- // wouldn't have seen them.
- // Note: this is still racy and may skip out of order events that came in since the last
- // time the client disconnected and then reconnected with the new start time. The long term
- // solution here is that clients must request events since a specific sequence number.
- me->mStartTime.tv_sec = 0;
- me->mStartTime.tv_nsec = 0;
-
- wrlock();
-
- if (start == LogBufferElement::FLUSH_ERROR) {
- break;
- }
-
- me->mStart = start + 1;
-
- if (me->mNonBlock || me->mRelease) {
- break;
- }
-
- me->cleanSkip_Locked();
-
- if (!me->mTimeout.tv_sec && !me->mTimeout.tv_nsec) {
- pthread_cond_wait(&me->threadTriggeredCondition, ×Lock);
- }
- }
-
- LogReader& reader = me->mReader;
- reader.release(client);
-
- client->decRef();
-
- LastLogTimes& times = reader.logbuf().mTimes;
- auto it =
- std::find_if(times.begin(), times.end(),
- [&me](const auto& other) { return other.get() == me; });
-
- if (it != times.end()) {
- times.erase(it);
- }
-
- unlock();
-
- return nullptr;
-}
-
-// A first pass to count the number of elements
-int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- LogTimeEntry::wrlock();
-
- if (me->leadingDropped) {
- if (element->getDropped()) {
- LogTimeEntry::unlock();
- return false;
- }
- me->leadingDropped = false;
- }
-
- if (me->mCount == 0) {
- me->mStart = element->getSequence();
- }
-
- if ((!me->mPid || me->mPid == element->getPid()) && me->isWatching(element->getLogId()) &&
- (me->mStartTime == log_time::EPOCH || me->mStartTime <= element->getRealTime())) {
- ++me->mCount;
- }
-
- LogTimeEntry::unlock();
-
- return false;
-}
-
-// A second pass to send the selected elements
-int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
- LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
-
- LogTimeEntry::wrlock();
-
- me->mStart = element->getSequence();
-
- if (me->skipAhead[element->getLogId()]) {
- me->skipAhead[element->getLogId()]--;
- goto skip;
- }
-
- if (me->leadingDropped) {
- if (element->getDropped()) {
- goto skip;
- }
- me->leadingDropped = false;
- }
-
- // Truncate to close race between first and second pass
- if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
- goto stop;
- }
-
- if (!me->isWatching(element->getLogId())) {
- goto skip;
- }
-
- if (me->mPid && (me->mPid != element->getPid())) {
- goto skip;
- }
-
- if (me->mStartTime != log_time::EPOCH && element->getRealTime() <= me->mStartTime) {
- goto skip;
- }
-
- if (me->mRelease) {
- goto stop;
- }
-
- if (!me->mTail) {
- goto ok;
- }
-
- ++me->mIndex;
-
- if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
- goto skip;
- }
-
- if (!me->mNonBlock) {
- me->mTail = 0;
- }
-
-ok:
- if (!me->skipAhead[element->getLogId()]) {
- LogTimeEntry::unlock();
- return true;
- }
-// FALLTHRU
-
-skip:
- LogTimeEntry::unlock();
- return false;
-
-stop:
- LogTimeEntry::unlock();
- return -1;
-}
-
-void LogTimeEntry::cleanSkip_Locked(void) {
- memset(skipAhead, 0, sizeof(skipAhead));
-}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
deleted file mode 100644
index a99c73b..0000000
--- a/logd/LogTimes.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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 _LOGD_LOG_TIMES_H__
-#define _LOGD_LOG_TIMES_H__
-
-#include <pthread.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <list>
-#include <memory>
-
-#include <log/log.h>
-#include <sysutils/SocketClient.h>
-
-typedef unsigned int log_mask_t;
-
-class LogReader;
-class LogBufferElement;
-
-class LogTimeEntry {
- static pthread_mutex_t timesLock;
- bool mRelease = false;
- bool leadingDropped;
- pthread_cond_t threadTriggeredCondition;
- pthread_t mThread;
- LogReader& mReader;
- static void* threadStart(void* me);
- const log_mask_t mLogMask;
- const pid_t mPid;
- unsigned int skipAhead[LOG_ID_MAX];
- pid_t mLastTid[LOG_ID_MAX];
- unsigned long mCount;
- unsigned long mTail;
- unsigned long mIndex;
-
- public:
- LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock, unsigned long tail,
- log_mask_t logMask, pid_t pid, log_time start_time, uint64_t sequence,
- uint64_t timeout);
-
- SocketClient* mClient;
- log_time mStartTime;
- uint64_t mStart;
- struct timespec mTimeout; // CLOCK_MONOTONIC based timeout used for log wrapping.
- const bool mNonBlock;
-
- // Protect List manipulations
- static void wrlock(void) {
- pthread_mutex_lock(×Lock);
- }
- static void rdlock(void) {
- pthread_mutex_lock(×Lock);
- }
- static void unlock(void) {
- pthread_mutex_unlock(×Lock);
- }
-
- bool startReader_Locked();
-
- void triggerReader_Locked(void) {
- pthread_cond_signal(&threadTriggeredCondition);
- }
-
- void triggerSkip_Locked(log_id_t id, unsigned int skip) {
- skipAhead[id] = skip;
- }
- void cleanSkip_Locked(void);
-
- void release_Locked(void) {
- // gracefully shut down the socket.
- shutdown(mClient->getSocket(), SHUT_RDWR);
- mRelease = true;
- pthread_cond_signal(&threadTriggeredCondition);
- }
-
- bool isWatching(log_id_t id) const {
- return mLogMask & (1 << id);
- }
- bool isWatchingMultiple(log_mask_t logMask) const {
- return mLogMask & logMask;
- }
- // flushTo filter callbacks
- static int FilterFirstPass(const LogBufferElement* element, void* me);
- static int FilterSecondPass(const LogBufferElement* element, void* me);
-};
-
-typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
-
-#endif // _LOGD_LOG_TIMES_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..f9cd42d 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_UTILS_H__
-#define _LOGD_LOG_UTILS_H__
+#pragma once
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -41,7 +40,6 @@
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
-void ReReadEventLogTags();
// Furnished by LogKlog.cpp
char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
@@ -72,5 +70,3 @@
return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
(id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
}
-
-#endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/README.property b/logd/README.property
index 1b7e165..6a9369a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -44,10 +44,6 @@
oldest entries of chattiest UID, and
the chattiest PID of system
(1000, or AID_SYSTEM).
-persist.logd.timestamp string ro The recording timestamp source.
- "m[onotonic]" is the only supported
- key character, otherwise realtime.
-ro.logd.timestamp string realtime default for persist.logd.timestamp
log.tag string persist The global logging level, VERBOSE,
DEBUG, INFO, WARN, ERROR, ASSERT or
SILENT. Only the first character is
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 4d1589b..8156612 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -16,7 +16,7 @@
#include <string>
#include "../LogBuffer.h"
-#include "../LogTimes.h"
+#include "../LogReaderThread.h"
// We don't want to waste a lot of entropy on messages
#define MAX_MSG_LENGTH 5
@@ -94,12 +94,14 @@
}
LastLogTimes times;
- LogBuffer log_buffer(×);
+ LogTags tags;
+ PruneList prune_list;
+ LogBuffer log_buffer(×, &tags, &prune_list);
size_t data_left = size;
const uint8_t** pdata = &data;
log_buffer.enableStatistics();
- log_buffer.initPrune(nullptr);
+ prune_list.init(nullptr);
// We want to get pruning code to get called.
log_id_for_each(i) { log_buffer.setSize(i, 10000); }
diff --git a/logd/main.cpp b/logd/main.cpp
index 23bbf86..d9dd249 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -52,6 +52,7 @@
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogListener.h"
+#include "LogTags.h"
#include "LogUtils.h"
#define KMSG_PRIORITY(PRI) \
@@ -108,21 +109,6 @@
return 0;
}
-// 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);
-}
-
static int fdDmesg = -1;
void android::prdebug(const char* fmt, ...) {
if (fdDmesg < 0) {
@@ -150,50 +136,6 @@
}
}
-static sem_t reinit;
-static bool reinit_running = false;
-static LogBuffer* logBuf = nullptr;
-
-static void* reinit_thread_start(void* /*obj*/) {
- prctl(PR_SET_NAME, "logd.daemon");
-
- while (reinit_running && !sem_wait(&reinit) && reinit_running) {
- if (fdDmesg >= 0) {
- static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
- 'l',
- 'o',
- 'g',
- 'd',
- '.',
- 'd',
- 'a',
- 'e',
- 'm',
- 'o',
- 'n',
- ':',
- ' ',
- 'r',
- 'e',
- 'i',
- 'n',
- 'i',
- 't',
- '\n' };
- write(fdDmesg, reinit_message, sizeof(reinit_message));
- }
-
- // Anything that reads persist.<property>
- if (logBuf) {
- logBuf->init();
- logBuf->initPrune(nullptr);
- }
- android::ReReadEventLogTags();
- }
-
- return nullptr;
-}
-
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -220,12 +162,6 @@
return userdata.name;
}
-// Serves as a global method to trigger reinitialization
-// and as a function that can be provided to signal().
-void reinit_signal_handler(int /*signal*/) {
- sem_post(&reinit);
-}
-
static void readDmesg(LogAudit* al, LogKlog* kl) {
if (!al && !kl) {
return;
@@ -250,10 +186,6 @@
}
buf[--len] = '\0';
- if (kl && kl->isMonotonic()) {
- kl->synchronize(buf.get(), len);
- }
-
ssize_t sublen;
for (char *ptr = nullptr, *tok = buf.get();
(rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
@@ -336,24 +268,10 @@
return EXIT_FAILURE;
}
- // Reinit Thread
- sem_init(&reinit, 0, 0);
- pthread_attr_t attr;
- if (!pthread_attr_init(&attr)) {
- struct sched_param param;
-
- memset(¶m, 0, sizeof(param));
- pthread_attr_setschedparam(&attr, ¶m);
- pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- pthread_t thread;
- reinit_running = true;
- if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
- reinit_running = false;
- }
- }
- pthread_attr_destroy(&attr);
- }
+ // A cache of event log tags
+ LogTags log_tags;
+ // Pruning configuration.
+ PruneList prune_list;
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
@@ -364,9 +282,7 @@
// LogBuffer is the object which is responsible for holding all
// log entries.
- logBuf = new LogBuffer(times);
-
- signal(SIGHUP, reinit_signal_handler);
+ LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
if (__android_logger_property_get_bool(
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
@@ -396,7 +312,7 @@
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
- CommandListener* cl = new CommandListener(logBuf, reader, swl);
+ CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
if (cl->startListener()) {
return EXIT_FAILURE;
}
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 1dd5c86..55737e9 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -606,7 +606,7 @@
// A few tries to get it right just in case wrap kicks in due to
// content providers being active during the test.
int i = 5;
- log_time start(android_log_clockid());
+ log_time start(CLOCK_REALTIME);
start.tv_sec -= 30; // reach back a moderate period of time
while (--i) {
@@ -682,7 +682,7 @@
if (msg > start) {
start = msg;
start.tv_sec += 30;
- log_time now = log_time(android_log_clockid());
+ log_time now = log_time(CLOCK_REALTIME);
if (start > now) {
start = now;
--start.tv_sec;
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 405f5a9..5de422f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so nopreload
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index b565340..77f8bb8 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -17,6 +17,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 7cbda08..82196e4 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so