Hello, I recently had to extend QEMU user mode to support PIE enabled binaries compiled for non-native architectures. It was also important to emulate ASLR for the target binary if ASLR was enabled on the host machine. I would like to introduce this as a feature into QEMU mainstream as it seems like a feature others could use as well.
With the patch implementation, ASLR will be enabled if /proc/sys/kernel/randomize_va_space is non-zero on the host machine. An alternative implementation could be passing an argument during runtime to enable or disable ASLR. PIE is automatically enabled as well if the binary has been compiled with this flag. Best regards, Sean Harrington Signed-off-by: Sean Harrington <harring...@battelle.org> --- linux-user/Makefile.objs | 3 +- linux-user/elfload.c | 3 +- linux-user/main.c | 7 +++ linux-user/randomize_va.c | 95 +++++++++++++++++++++++++++++++++++++++ linux-user/randomize_va.h | 23 ++++++++++ 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 linux-user/randomize_va.c create mode 100644 linux-user/randomize_va.h diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 769b8d8336..384944a76d 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,7 +1,8 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ safe-syscall.o $(TARGET_ABI_DIR)/signal.o \ - $(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o + $(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o \ + randomize_va.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5bccd2e243..5add383815 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -7,6 +7,7 @@ #include "qemu.h" #include "disas/disas.h" #include "qemu/path.h" +#include "randomize_va.h" #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -2258,7 +2259,7 @@ static void load_elf_image(const char *image_name, int image_fd, image is pre-linked, LOADDR will be non-zero. Since we do not supply MAP_FIXED here we'll use that address if and only if it remains available. */ - load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE, + load_addr = get_pie_addr(loaddr, hiaddr - loaddr, PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0); if (load_addr == -1) { diff --git a/linux-user/main.c b/linux-user/main.c index 923cbb753a..3b789001dd 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -37,6 +37,7 @@ #include "trace/control.h" #include "target_elf.h" #include "cpu_loop-common.h" +#include "randomize_va.h" char *exec_path; @@ -694,6 +695,12 @@ int main(int argc, char **argv, char **envp) target_environ = envlist_to_environ(envlist, NULL); envlist_free(envlist); + /* + * If host has randomized_va_space enabled, emulate the same for the + * target. + */ + reserved_va = handle_randomized_va(reserved_va, MAX_RESERVED_VA); + /* * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. diff --git a/linux-user/randomize_va.c b/linux-user/randomize_va.c new file mode 100644 index 0000000000..792212a2b8 --- /dev/null +++ b/linux-user/randomize_va.c @@ -0,0 +1,95 @@ +/* + * QEMU user ASLR + PIE emulation + * + * Copyright (c) 2018 Sean Harrington (harring...@battelle.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" + +#include "qemu.h" +#include "qemu-common.h" +#include "randomize_va.h" + +#define PIE_MMAP_ATTEMPTS 10 +#define RANDOMIZATION_RANGE 0x00100000 + +unsigned long handle_randomized_va(unsigned long va, unsigned long max_va_space) { + + int randomize_va_fd = open("/proc/sys/kernel/randomize_va_space", O_RDONLY); + srand(time(NULL)); + + if (randomize_va_fd >= 0) { + int aslr; + if (read(randomize_va_fd, &aslr, sizeof(aslr)) < 0) { + return va; + } + + if (aslr) { + /* At start, mmap_next_state is equal to TASK_UNMAPPED_BASE in + * mmap.c. Randomly choose a new random value within range + * [mmap_next_start - (TASK_UNMAPPED_BASE / 2), + * mmap_next_start - (TASK_UNMAPPED_BASE / 2)] + * Also make sure we are page aligned + */ + + /* If it is possible new va could overflow, prevent it */ + if ((va + (RANDOMIZATION_RANGE / 2)) > max_va_space) + { + va = max_va_space - (RANDOMIZATION_RANGE / 2); + } + /* If it is possible new va could underflow, prevent it */ + else if ((va - (RANDOMIZATION_RANGE / 2)) > max_va_space) + va = RANDOMIZATION_RANGE / 2; + + /* Perform randomization */ + abi_ulong offset = va - (RANDOMIZATION_RANGE / 2); + + abi_ulong range = RANDOMIZATION_RANGE; + abi_ulong random_val = rand(); + va = (random_val % range) + offset; + + /* Page align result */ + va &= qemu_host_page_mask; + } + } + + return va; +} + +abi_ulong get_pie_addr(abi_ulong addr, abi_ulong size, int prot_opts, + int map_opts, int fd, off_t offset) { + abi_ulong orig_addr = addr; + abi_ulong newloaddr = 0; + + /* New seed for upcoming rand. */ + srand(time(NULL)); + + /* If addr is 0, kernel handles finding a valid address. If non-zero + * we should honor the request for the specified mmap address. + */ + if (orig_addr == 0) { + while (newloaddr < size) + newloaddr = rand() & TARGET_PAGE_MASK; + } + else + newloaddr = addr; + + addr = target_mmap(newloaddr, size, prot_opts, map_opts, fd, + offset); + + return addr; +} diff --git a/linux-user/randomize_va.h b/linux-user/randomize_va.h new file mode 100644 index 0000000000..43b1a1a1a3 --- /dev/null +++ b/linux-user/randomize_va.h @@ -0,0 +1,23 @@ +/* + * QEMU user ASLR + PIE emulation + * + * Copyright (c) 2018 Sean Harrington (harring...@battelle.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +unsigned long handle_randomized_va(unsigned long va, unsigned long max_va_space); + +abi_ulong get_pie_addr(abi_ulong addr, abi_ulong size, int prot_opts, + int map_opts, int fd, off_t offset); -- 2.17.2 (Apple Git-113)