From 52a4e3af8ca37d895bcff2ede1073ebb2cb2dd29 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 19 Nov 2011 22:57:55 +0100 Subject: [PATCH 12/32] linux-user: Fix 32-on-64 mmap for x86_64 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 --- linux-user/mmap.c | 35 ++++++++++++++++++++++++++--------- 1 files changed, 26 insertions(+), 9 deletions(-) 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); } } -- 1.6.0.2