From df1a3c6d21702e3e96cfcddadee4a50bfac82110 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Fri, 17 Jul 2015 12:13:27 -0400 Subject: [PATCH] make vdso function pointers read-only at runtime Global, writable function pointers are low-hanging fruit for hijacking control flow with an overflow from a global buffer or an arbitrary write vulnerability. This moves the function pointer table into a dedicated page and makes it read-only at runtime, similar to RELRO. This increases the memory usage of the library by just under one page. This could be avoided by having the linker load the vdso by replacing weak symbols. It's not significant within the Zygote spawning model though because it's read-only after early init. Change-Id: Id7a49c96c1b15c2e1926528304b3c54a81e78caf --- libc/bionic/vdso.cpp | 45 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/libc/bionic/vdso.cpp b/libc/bionic/vdso.cpp index a2406634d..d4aea986b 100644 --- a/libc/bionic/vdso.cpp +++ b/libc/bionic/vdso.cpp @@ -30,8 +30,14 @@ #define VDSO_GETTIMEOFDAY_SYMBOL "__vdso_gettimeofday" #endif +#include +#include +#include #include +#include "private/bionic_prctl.h" +#include "private/libc_logging.h" + extern "C" int __clock_gettime(int, timespec*); extern "C" int __gettimeofday(timeval*, struct timezone*); @@ -46,28 +52,33 @@ enum { VDSO_END }; -static vdso_entry vdso_entries[] = { +static const vdso_entry vdso_entries_template[] = { [VDSO_CLOCK_GETTIME] = { VDSO_CLOCK_GETTIME_SYMBOL, reinterpret_cast(__clock_gettime) }, [VDSO_GETTIMEOFDAY] = { VDSO_GETTIMEOFDAY_SYMBOL, reinterpret_cast(__gettimeofday) }, }; +static vdso_entry* vdso_entries; + int clock_gettime(int clock_id, timespec* tp) { - static int (*vdso_clock_gettime)(int, timespec*) = + int (*vdso_clock_gettime)(int, timespec*) = reinterpret_cast(vdso_entries[VDSO_CLOCK_GETTIME].fn); return vdso_clock_gettime(clock_id, tp); } int gettimeofday(timeval* tv, struct timezone* tz) { - static int (*vdso_gettimeofday)(timeval*, struct timezone*) = + int (*vdso_gettimeofday)(timeval*, struct timezone*) = reinterpret_cast(vdso_entries[VDSO_GETTIMEOFDAY].fn); return vdso_gettimeofday(tv, tz); } -void __libc_init_vdso() { +static void __libc_init_vdso_entries() { + // Set up the defaults in case we don't have a vdso or can't find everything we're looking for. + memcpy(vdso_entries, vdso_entries_template, sizeof(vdso_entries_template)); + // Do we have a vdso? uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR); ElfW(Ehdr)* vdso_ehdr = reinterpret_cast(vdso_ehdr_addr); - if (vdso_ehdr == NULL) { + if (vdso_ehdr == nullptr) { return; } @@ -85,7 +96,7 @@ void __libc_init_vdso() { // Where's the dynamic table? ElfW(Addr) vdso_addr = 0; - ElfW(Dyn)* vdso_dyn = NULL; + ElfW(Dyn)* vdso_dyn = nullptr; ElfW(Phdr)* vdso_phdr = reinterpret_cast(vdso_ehdr_addr + vdso_ehdr->e_phoff); for (size_t i = 0; i < vdso_ehdr->e_phnum; ++i) { if (vdso_phdr[i].p_type == PT_DYNAMIC) { @@ -94,13 +105,13 @@ void __libc_init_vdso() { vdso_addr = vdso_ehdr_addr + vdso_phdr[i].p_offset - vdso_phdr[i].p_vaddr; } } - if (vdso_addr == 0 || vdso_dyn == NULL) { + if (vdso_addr == 0 || vdso_dyn == nullptr) { return; } // Where are the string and symbol tables? - const char* strtab = NULL; - ElfW(Sym)* symtab = NULL; + const char* strtab = nullptr; + ElfW(Sym)* symtab = nullptr; for (ElfW(Dyn)* d = vdso_dyn; d->d_tag != DT_NULL; ++d) { if (d->d_tag == DT_STRTAB) { strtab = reinterpret_cast(vdso_addr + d->d_un.d_ptr); @@ -108,7 +119,7 @@ void __libc_init_vdso() { symtab = reinterpret_cast(vdso_addr + d->d_un.d_ptr); } } - if (strtab == NULL || symtab == NULL) { + if (strtab == nullptr || symtab == nullptr) { return; } @@ -122,6 +133,20 @@ void __libc_init_vdso() { } } +void __libc_init_vdso() { + static_assert(PAGE_SIZE >= sizeof(vdso_entries_template), "vdso_entries_template too large"); + vdso_entries = reinterpret_cast(mmap(nullptr, sizeof(vdso_entries_template), PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)); + if (vdso_entries == MAP_FAILED) { + __libc_fatal("failed to allocate vdso function pointer table: %s", strerror(errno)); + } + __libc_init_vdso_entries(); + if (mprotect(vdso_entries, sizeof(vdso_entries_template), PROT_READ) == -1) { + __libc_fatal("failed to mprotect PROT_READ vdso function pointer table: %s", strerror(errno)); + } + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, vdso_entries, sizeof(vdso_entries_template), "vdso function pointer table"); +} + #else void __libc_init_vdso() {