Patchwork linux-user: Fix 32-on-64 mmap for x86_64

login
register
mail settings
Submitter Alexander Graf
Date Nov. 24, 2011, 11:32 p.m.
Message ID <1322177573-5269-1-git-send-email-agraf@suse.de>
Download mbox | patch
Permalink /patch/127627/
State New
Headers show

Comments

Alexander Graf - Nov. 24, 2011, 11:32 p.m.
When running a 32 bit guest on a 64 bit host, we can run into trouble while
calling the host's mmap() because it could potentially give us a 64 bit
return value which the guest can't interpret.

There are 2 ways of dealing with this:

  1) Only do MAP_FIXED mmap calls and implement our own vm management in QEMU
  2) Tell the kernel that we only want mappings in the lower 32 bits

Way 1 is very involved and hard to do. It's been advocated forever now but
nobody sat down to actually implement it.

Way 2 is easy. It's what this patch does. However, it only works on x86_64
because that's the only platform implementing the MAP_32BIT flag. Since most
people are on x86_64 though, I think it's a good enough compromise for now
though

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 linux-user/mmap.c |   35 ++++++++++++++++++++++++++---------
 1 files changed, 26 insertions(+), 9 deletions(-)

Patch

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 994c02b..7d846f3 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -33,6 +33,22 @@ 
 
 //#define DEBUG_MMAP
 
+/*
+ * On x86_64 we can tell mmap that we only want to map within the first 32
+ * bits to not get pointers that potentially exceed the return size. Without
+ * this flag set mmap will eventually break for users when running 32-on-64.
+ *
+ * However, Linux doesn't implement this for non-x86_64 systems. So we have
+ * to safeguard the bit with an empty flag which will be ignore on other
+ * architectures. At least we fixed the "common case" this way :).
+ *
+ * - agraf
+ */
+#if !defined(MAP_32BIT) || !defined(__x86_64__) || (TARGET_LONG_BITS != 32)
+#undef MAP_32BIT
+#define MAP_32BIT 0
+#endif
+
 #if defined(CONFIG_USE_NPTL)
 static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
 static __thread int mmap_lock_count;
@@ -169,7 +185,7 @@  static int mmap_frag(abi_ulong real_start,
     if (prot1 == 0) {
         /* no page was there, so we allocate one */
         void *p = mmap(host_start, qemu_host_page_size, prot,
-                       flags | MAP_ANONYMOUS, -1, 0);
+                       flags | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
         if (p == MAP_FAILED)
             return -1;
         prot1 = prot;
@@ -292,7 +308,7 @@  abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
          *  - shmat() with SHM_REMAP flag
          */
         ptr = mmap(g2h(addr), size, PROT_NONE,
-                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
+                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE|MAP_32BIT, -1, 0);
 
         /* ENOMEM, if host address space has no memory */
         if (ptr == MAP_FAILED) {
@@ -454,14 +470,15 @@  abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
            especially important if qemu_host_page_size >
            qemu_real_host_page_size */
         p = mmap(g2h(mmap_start),
-                 host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
+                 host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS | MAP_32BIT,
+                 -1, 0);
         if (p == MAP_FAILED)
             goto fail;
         /* update start so that it points to the file position at 'offset' */
         host_start = (unsigned long)p;
         if (!(flags & MAP_ANONYMOUS)) {
             p = mmap(g2h(mmap_start), len, prot, 
-                     flags | MAP_FIXED, fd, host_offset);
+                     flags | MAP_FIXED | MAP_32BIT, fd, host_offset);
             host_start += offset - host_offset;
         }
         start = h2g(host_start);
@@ -495,8 +512,8 @@  abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
                 goto fail;
             }
             retaddr = target_mmap(start, len, prot | PROT_WRITE,
-                                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
-                                  -1, 0);
+                                  MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS |
+                                  MAP_32BIT, -1, 0);
             if (retaddr == -1)
                 goto fail;
             if (pread(fd, g2h(start), len, offset) == -1)
@@ -547,7 +564,7 @@  abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
             else
                 offset1 = offset + real_start - start;
             p = mmap(g2h(real_start), real_end - real_start,
-                     prot, flags, fd, offset1);
+                     prot, flags | MAP_32BIT, fd, offset1);
             if (p == MAP_FAILED)
                 goto fail;
         }
@@ -603,8 +620,8 @@  static void mmap_reserve(abi_ulong start, abi_ulong size)
     }
     if (real_start != real_end) {
         mmap(g2h(real_start), real_end - real_start, PROT_NONE,
-                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
-                 -1, 0);
+                 MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE |
+                 MAP_32BIT, -1, 0);
     }
 }