Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thestinger/fc55e3a0194c1a1b1979983b03b271b6 to your computer and use it in GitHub Desktop.
Save thestinger/fc55e3a0194c1a1b1979983b03b271b6 to your computer and use it in GitHub Desktop.
From 02f8db1d34c6979c72a8e5c9992398d7a948d36f Mon Sep 17 00:00:00 2001
From: Daniel Micay <danielmicay@gmail.com>
Date: Sun, 13 Sep 2015 20:40:56 -0400
Subject: [PATCH] add an isolated region for dynamic libraries
---
linker/linker.cpp | 134 ++++++++++++++++++++++++++++++++++++++++++++++++-
linker/linker.h | 2 +
linker/linker_phdr.cpp | 13 +++--
3 files changed, 145 insertions(+), 4 deletions(-)
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d3ac1d000..beffca093 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -45,6 +45,8 @@
#include <vector>
// Private C library headers.
+#include "private/bionic_macros.h"
+#include "private/bionic_prctl.h"
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
#include "private/ScopedPthreadMutexLocker.h"
@@ -226,6 +228,133 @@ void SoinfoListAllocator::free(LinkedListEntry<soinfo>* entry) {
g_soinfo_links_allocator.free(entry);
}
+struct region_span {
+ struct region_span* prev;
+ struct region_span* next;
+ char* ptr;
+ size_t size;
+};
+
+static region_span region_spans;
+static LinkerTypeAllocator<region_span> region_span_allocator;
+
+static const void* library_region_start;
+static const void* library_region_end;
+
+static void remove_span(region_span* span) {
+ span->prev->next = span->next;
+ if (span->next != nullptr) {
+ span->next->prev = span->prev;
+ }
+ region_span_allocator.free(span);
+}
+
+static void library_region_init() {
+#ifdef __LP64__
+ size_t size = 1024 * 1024 * 1024;
+#else
+ size_t size = 128 * 1024 * 1024;
+#endif
+ size_t gap_size = BIONIC_ALIGN_DOWN(arc4random_uniform(size), PAGE_SIZE);
+
+ char* ptr = static_cast<char*>(mmap(nullptr, gap_size + size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (ptr == MAP_FAILED) {
+ return;
+ }
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, gap_size, "library region random gap");
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr + gap_size, size, "library region");
+
+ region_span* span = region_span_allocator.alloc();
+ span->prev = &region_spans;
+ span->next = nullptr;
+ span->ptr = ptr + gap_size;
+ span->size = size;
+
+ region_spans.next = span;
+
+ library_region_start = span->ptr;
+ library_region_end = span->ptr + size;
+}
+
+void* get_library_mapping(size_t size) {
+ if (size % PAGE_SIZE != 0) {
+ __libc_format_log(ANDROID_LOG_WARN, "libc", "not a multiple of the page size");
+ }
+
+ region_span* span = nullptr;
+ for (region_span* s = region_spans.next; s != nullptr; s = s->next) {
+ if (s->size < size) {
+ continue;
+ }
+ if (span == nullptr || s->size < span->size || (s->size == span->size && s->ptr < span->ptr)) {
+ span = s;
+ }
+ }
+
+ if (span == nullptr) {
+ __libc_format_log(ANDROID_LOG_WARN, "libc", "library isolation region exhausted");
+ void* ptr = mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (ptr == MAP_FAILED) {
+ return nullptr;
+ }
+ return ptr;
+ }
+
+ void* ptr = span->ptr;
+ if (size == span->size) {
+ remove_span(span);
+ } else {
+ span->ptr += size;
+ span->size -= size;
+ }
+ return ptr;
+}
+
+static void release_library_mapping(void* ptr, size_t size) {
+ if (ptr >= library_region_start && ptr < library_region_end) {
+ mmap(ptr, size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, "library region");
+
+ region_span* span = nullptr;
+ for (region_span* s = region_spans.next; s != nullptr; s = s->next) {
+ if (s->ptr + s->size == ptr) {
+ ptr = s->ptr;
+ size += s->size;
+ span = s;
+ break;
+ }
+ }
+
+ for (region_span* s = region_spans.next; s != nullptr; s = s->next) {
+ if (s->ptr == static_cast<char*>(ptr) + size) {
+ size += s->size;
+ if (span == nullptr) {
+ span = s;
+ } else {
+ remove_span(s);
+ }
+ break;
+ }
+ }
+
+ if (span == nullptr) {
+ span = region_span_allocator.alloc();
+ span->prev = &region_spans;
+ span->next = region_spans.next;
+ if (region_spans.next != nullptr) {
+ region_spans.next->prev = span;
+ }
+ region_spans.next = span;
+ }
+
+ span->ptr = static_cast<char*>(ptr);
+ span->size = size;
+ } else {
+ munmap(ptr, size);
+ }
+}
+
static soinfo* soinfo_alloc(const char* name, struct stat* file_stat,
off64_t file_offset, uint32_t rtld_flags) {
if (strlen(name) >= PATH_MAX) {
@@ -248,7 +377,7 @@ static void soinfo_free(soinfo* si) {
}
if (si->base != 0 && si->size != 0) {
- munmap(reinterpret_cast<void*>(si->base), si->size);
+ release_library_mapping(reinterpret_cast<void*>(si->base), si->size);
}
soinfo *prev = nullptr, *trav;
@@ -809,6 +938,7 @@ class ProtectedDataGuard {
void protect_data(int protection) {
g_soinfo_allocator.protect_all(protection);
g_soinfo_links_allocator.protect_all(protection);
+ region_span_allocator.protect_all(protection);
}
static size_t ref_count_;
@@ -3152,6 +3282,8 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
// Initialize system properties
__system_properties_init(); // may use 'environ'
+ library_region_init();
+
debuggerd_init();
// Get a few environment variables.
diff --git a/linker/linker.h b/linker/linker.h
index 023b6721f..27bd3a3f5 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -438,4 +438,6 @@ size_t linker_get_error_buffer_size();
void set_application_target_sdk_version(uint32_t target);
uint32_t get_application_target_sdk_version();
+__LIBC_HIDDEN__ void* get_library_mapping(size_t size);
+
#endif
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 30118e363..bc5b0ab78 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -338,9 +338,16 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) {
reserved_size - load_size_, load_size_, name_);
return false;
}
- int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
- start = mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -1, 0);
- if (start == MAP_FAILED) {
+ if (mmap_hint) {
+ int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ start = mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -1, 0);
+ if (start == MAP_FAILED) {
+ start = nullptr;
+ }
+ } else {
+ start = get_library_mapping(load_size_);
+ }
+ if (!start) {
DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
return false;
}
--
2.12.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment