From patchwork Thu Feb 11 23:29:21 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 45173 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 5ADFEB7C8E for ; Fri, 12 Feb 2010 12:54:24 +1100 (EST) Received: from localhost ([127.0.0.1]:46369 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Nfkeb-0002DP-37 for incoming@patchwork.ozlabs.org; Thu, 11 Feb 2010 20:49:09 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NfkbI-0001XZ-OT for qemu-devel@nongnu.org; Thu, 11 Feb 2010 20:45:44 -0500 Received: from [199.232.76.173] (port=58966 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NfkbI-0001XA-8Y for qemu-devel@nongnu.org; Thu, 11 Feb 2010 20:45:44 -0500 Received: from Debian-exim by monty-python.gnu.org with spam-scanned (Exim 4.60) (envelope-from ) id 1NfkbG-0002jX-A2 for qemu-devel@nongnu.org; Thu, 11 Feb 2010 20:45:44 -0500 Received: from mx20.gnu.org ([199.232.41.8]:52911) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1NfkbF-0002jA-TI for qemu-devel@nongnu.org; Thu, 11 Feb 2010 20:45:42 -0500 Received: from are.twiddle.net ([75.149.56.221]) by mx20.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1Nfk0W-00021b-Gf for qemu-devel@nongnu.org; Thu, 11 Feb 2010 20:07:44 -0500 Received: by are.twiddle.net (Postfix, from userid 5000) id 47F42C6F; Thu, 11 Feb 2010 16:54:14 -0800 (PST) Message-Id: <771ff642955919fb5dcab208a22fb5179ff88764.1265933757.git.rth@twiddle.net> In-Reply-To: References: From: Richard Henderson Date: Thu, 11 Feb 2010 15:29:21 -0800 To: qemu-devel@nongnu.org X-detected-operating-system: by mx20.gnu.org: GNU/Linux 2.6 (newer, 2) X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) Subject: [Qemu-devel] [PATCH 6/6] linux-user: Fix mmap_find_vma returning invalid addresses. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Don't return addresses that aren't properly aligned for the guest, e.g. when the guest has a larger page size than the host. Don't return addresses that are outside the virtual address space for the target, by paying proper attention to the h2g/g2h macros. At the same time, place the default mapping base for 64-bit guests (on 64-bit hosts) outside the low 4G. Consistently interpret mmap_next_start in the guest address space. --- linux-user/main.c | 7 +--- linux-user/mmap.c | 102 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index a0d8ce7..7db9fc3 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2725,12 +2725,9 @@ int main(int argc, char **argv, char **envp) /* * Read in mmap_min_addr kernel parameter. This value is used * When loading the ELF image to determine whether guest_base - * is needed. - * - * When user has explicitly set the quest base, we skip this - * test. + * is needed. It is also used in mmap_find_vma. */ - if (!have_guest_base) { + { FILE *fp; if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) { diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 65fdc33..ad00b6f 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -264,12 +264,15 @@ static int mmap_frag(abi_ulong real_start, return 0; } -#if defined(__CYGWIN__) +#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 +# define TASK_UNMAPPED_BASE (1ul << 38) +#elif defined(__CYGWIN__) /* Cygwin doesn't have a whole lot of address space. */ -static abi_ulong mmap_next_start = 0x18000000; +# define TASK_UNMAPPED_BASE 0x18000000 #else -static abi_ulong mmap_next_start = 0x40000000; +# define TASK_UNMAPPED_BASE 0x40000000 #endif +static abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; unsigned long last_brk; @@ -281,19 +284,24 @@ unsigned long last_brk; */ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) { - void *ptr; + void *ptr, *prev; abi_ulong addr; - - size = HOST_PAGE_ALIGN(size); - start &= qemu_host_page_mask; + int wrapped, repeat; /* If 'start' == 0, then a default start address is used. */ - if (start == 0) + if (start == 0) { start = mmap_next_start; + } else { + start &= qemu_host_page_mask; + } + + size = HOST_PAGE_ALIGN(size); addr = start; + wrapped = repeat = 0; + prev = 0; - for(;;) { + for (;; prev = ptr) { /* * Reserve needed memory area to avoid a race. * It should be discarded using: @@ -301,31 +309,77 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) * - mremap() with MREMAP_FIXED flag * - shmat() with SHM_REMAP flag */ - ptr = mmap((void *)(unsigned long)addr, size, PROT_NONE, + ptr = mmap(g2h(addr), size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); /* ENOMEM, if host address space has no memory */ - if (ptr == MAP_FAILED) + if (ptr == MAP_FAILED) { return (abi_ulong)-1; + } - /* If address fits target address space we've found what we need */ - if ((unsigned long)ptr + size - 1 <= (abi_ulong)-1) - break; + /* Count the number of sequential returns of the same address. + This is used to modify the search algorithm below. */ + repeat = (ptr == prev ? repeat + 1 : 0); - /* Unmap and try again with new page */ + if (h2g_valid(ptr + size - 1)) { + addr = h2g(ptr); + + if ((addr & ~TARGET_PAGE_MASK) == 0) { + /* Success. */ + if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) { + mmap_next_start = addr + size; + } + return addr; + } + + /* The address is not properly aligned for the target. */ + switch (repeat) { + case 0: + /* Assume the result that the kernel gave us is the + first with enough free space, so start again at the + next higher target page. */ + addr = TARGET_PAGE_ALIGN(addr); + break; + case 1: + /* Sometimes the kernel decides to perform the allocation + at the top end of memory instead. */ + addr &= TARGET_PAGE_MASK; + break; + case 2: + /* Start over at low memory. */ + addr = 0; + break; + default: + /* Fail. This unaligned block must the last. */ + addr = -1; + break; + } + } else { + /* Since the result the kernel gave didn't fit, start + again at low memory. If any repetition, fail. */ + addr = (repeat ? -1 : 0); + } + + /* Unmap and try again. */ munmap(ptr, size); - addr += qemu_host_page_size; - /* ENOMEM if we check whole of target address space */ - if (addr == start) + /* ENOMEM if we checked the whole of the target address space. */ + if (addr == -1ul) { return (abi_ulong)-1; + } else if (addr == 0) { + if (wrapped) { + return (abi_ulong)-1; + } + wrapped = 1; + /* Don't actually use 0 when wrapping, instead indicate + that we'd truely like an allocation in low memory. */ + addr = (mmap_min_addr > TARGET_PAGE_SIZE + ? TARGET_PAGE_ALIGN(mmap_min_addr) + : TARGET_PAGE_SIZE); + } else if (wrapped && addr >= start) { + return (abi_ulong)-1; + } } - - /* Update default start address */ - if (start == mmap_next_start) - mmap_next_start = (unsigned long)ptr + size; - - return h2g(ptr); } /* NOTE: all the constants are the HOST ones */