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)

Reply via email to