Re-land Implement deterministic MTE globals for dlext RELRO sharing
This patch makes MTE globals deterministic for dlext loading that uses
either ANDROID_DLEXT_WRITE_RELRO or ANDROID_DLEXT_USE_RELRO. Without
this patch, every load of a dynamic library ends up with completely
random tags. This of course, kills the page sharing 15/16-th of the
time.
So, instead, for this specific RELRO loading, we use deterministic
global tags. This provides a small amount of protection without
killing the sharing and eating up more ram.
As a nice side effect, this fixes up one of the dlext tests that ensures
that the RELRO pages do get shared.
Bug: 315182011
Test: On both an MTE-enabled and non-MTE-enabled device:
Test: atest libprocinfo_test bionic-unit-tests bionic-unit-tests-static
CtsGwpAsanTestCases gwp_asan_unittest debuggerd_test
Change-Id: I8e5d1c466633fa3610298844f080bd0478245479
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 9cd660c..8f78915 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1698,11 +1698,19 @@
}
}
+ // The WebView loader uses RELRO sharing in order to promote page sharing of the large RELRO
+ // segment, as it's full of C++ vtables. Because MTE globals, by default, applies random tags to
+ // each global variable, the RELRO segment is polluted and unique for each process. In order to
+ // allow sharing, but still provide some protection, we use deterministic global tagging schemes
+ // for DSOs that are loaded through android_dlopen_ext, such as those loaded by WebView.
+ bool dlext_use_relro =
+ extinfo && extinfo->flags & (ANDROID_DLEXT_WRITE_RELRO | ANDROID_DLEXT_USE_RELRO);
+
// Step 3: pre-link all DT_NEEDED libraries in breadth first order.
bool any_memtag_stack = false;
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
- if (!si->is_linked() && !si->prelink_image()) {
+ if (!si->is_linked() && !si->prelink_image(dlext_use_relro)) {
return false;
}
// si->memtag_stack() needs to be called after si->prelink_image() which populates
@@ -2847,7 +2855,7 @@
// An empty list of soinfos
static soinfo_list_t g_empty_list;
-bool soinfo::prelink_image() {
+bool soinfo::prelink_image(bool dlext_use_relro) {
if (flags_ & FLAG_PRELINKED) return true;
/* Extract dynamic section */
ElfW(Word) dynamic_flags = 0;
@@ -3344,7 +3352,7 @@
// pages is unnecessary on non-MTE devices (where we might still run MTE-globals enabled code).
if (should_tag_memtag_globals() &&
remap_memtag_globals_segments(phdr, phnum, base) == 0) {
- tag_globals();
+ tag_globals(dlext_use_relro);
protect_memtag_globals_ro_segments(phdr, phnum, base);
}
@@ -3466,7 +3474,7 @@
}
// https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#global-variable-tagging
-void soinfo::tag_globals() {
+void soinfo::tag_globals(bool dlext_use_relro) {
if (is_linked()) return;
if (flags_ & FLAG_GLOBALS_TAGGED) return;
flags_ |= FLAG_GLOBALS_TAGGED;
@@ -3483,6 +3491,7 @@
// Don't ever generate tag zero, to easily distinguish between tagged and
// untagged globals in register/tag dumps.
uint64_t last_tag_mask = 1;
+ uint64_t last_tag = 1;
constexpr uint64_t kDistanceReservedBits = 3;
while (decoder.has_bytes()) {
@@ -3495,9 +3504,14 @@
addr += distance;
void* tagged_addr;
- tagged_addr = insert_random_tag(reinterpret_cast<void*>(addr), last_tag_mask);
- uint64_t tag = (reinterpret_cast<uint64_t>(tagged_addr) >> 56) & 0x0f;
- last_tag_mask = 1 | (1 << tag);
+ if (dlext_use_relro) {
+ tagged_addr = reinterpret_cast<void*>(addr | (last_tag++ << 56));
+ if (last_tag > (1 << kTagGranuleSize)) last_tag = 1;
+ } else {
+ tagged_addr = insert_random_tag(reinterpret_cast<void*>(addr), last_tag_mask);
+ uint64_t tag = (reinterpret_cast<uint64_t>(tagged_addr) >> 56) & 0x0f;
+ last_tag_mask = 1 | (1 << tag);
+ }
for (size_t k = 0; k < ngranules; k++) {
auto* granule = static_cast<uint8_t*>(tagged_addr) + k * kTagGranuleSize;