diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp index 224cd41f3..1c6c845b5 100644 --- a/libc/bionic/ndk_cruft.cpp +++ b/libc/bionic/ndk_cruft.cpp @@ -45,6 +45,7 @@ #include #include +#include "private/bionic_macros.h" #include "private/libc_logging.h" extern "C" { diff --git a/libc/private/bionic_macros.h b/libc/private/bionic_macros.h index 4969bd95f..d5c5b9c6f 100644 --- a/libc/private/bionic_macros.h +++ b/libc/private/bionic_macros.h @@ -17,6 +17,8 @@ #ifndef _BIONIC_MACROS_H_ #define _BIONIC_MACROS_H_ +#include + // Frameworks OpenGL code currently leaks this header and allows // collisions with other declarations, e.g., from libnativehelper. // TODO: Remove once cleaned up. b/18334516 @@ -46,4 +48,22 @@ ? (1UL << (64 - __builtin_clzl(static_cast(value)))) \ : (1UL << (32 - __builtin_clz(static_cast(value))))) +static inline uintptr_t align_down(uintptr_t p, size_t align) { + return p & ~(align - 1); +} + +static inline uintptr_t align_up(uintptr_t p, size_t align) { + return (p + align - 1) & ~(align - 1); +} + +template +static inline T* align_down(T* p, size_t align) { + return reinterpret_cast(align_down(reinterpret_cast(p), align)); +} + +template +static inline T* align_up(T* p, size_t align) { + return reinterpret_cast(align_up(reinterpret_cast(p), align)); +} + #endif // _BIONIC_MACROS_H_ diff --git a/linker/linker.h b/linker/linker.h index ea77920ef..fd437aace 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -503,4 +503,7 @@ android_namespace_t* create_namespace(const void* caller_addr, const char* permitted_when_isolated_path, android_namespace_t* parent_namespace); +constexpr unsigned kLibraryAlignmentBits = 18; +constexpr size_t kLibraryAlignment = 1UL << kLibraryAlignmentBits; + #endif diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index a2ae2a25c..a7af82fb8 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -449,6 +449,40 @@ size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, return max_vaddr - min_vaddr; } +// Reserve a virtual address range such that if it's limits were extended to the next 2**align +// boundary, it would not overlap with any existing mappings. +static void* ReserveAligned(void* hint, size_t size, size_t align) { + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; + // Address hint is only used in Art for the image mapping, and it is pretty important. Don't mess + // with it. + // FIXME: try an aligned allocation and fall back to plain mmap() if the former does not provide a + // mapping at the requested address? + if (align == PAGE_SIZE || hint != nullptr) { + void* mmap_ptr = mmap(hint, size, PROT_NONE, mmap_flags, -1, 0); + if (mmap_ptr == MAP_FAILED) { + return nullptr; + } + return mmap_ptr; + } + + // Allocate enough space so that the end of the desired region aligned up is still inside the + // mapping. + size_t mmap_size = align_up(size, align) + align - PAGE_SIZE; + uint8_t* mmap_ptr = + reinterpret_cast(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0)); + if (mmap_ptr == MAP_FAILED) { + return nullptr; + } + + uint8_t* first = align_up(mmap_ptr, align); + uint8_t* last = align_down(mmap_ptr + mmap_size, align) - size; + size_t n = arc4random_uniform((last - first) / PAGE_SIZE + 1); + uint8_t* start = first + n * PAGE_SIZE; + munmap(mmap_ptr, start - mmap_ptr); + munmap(start + size, mmap_ptr + mmap_size - (start + size)); + return start; +} + // Reserve a virtual address range big enough to hold all loadable // segments of a program header table. This is done by creating a // private anonymous mmap() with PROT_NONE. @@ -490,9 +524,8 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { reserved_size - load_size_, load_size_, name_.c_str()); 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) { + start = ReserveAligned(mmap_hint, load_size_, kLibraryAlignment); + if (start == nullptr) { DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str()); return false; }