From 605bdcd410384dda6db66b9b8cd19e863702e1bb Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Wed, 5 Jun 2019 20:08:34 +0200 Subject: [PATCH] Cygwin: map beyond EOF on 64 bit and WOW64 as well 32 bit Cygwin performs a POSIX-compatible mapping after EOF which is not supported in this form on Windows. The 64 bit Windows kernel never supported the AT_ROUND_TO_PAGE mapping flag, so we couldn't page-aligned map the space right after the file's EOF. So mapping beyond EOF was disabled in 64 bit Windows and WOW64. However, if mmap works, a matching munmap should work as well, *and* it should not accidentally unmap unrelated memory. Therefore we enable mapping beyond EOF on 64 bit as well. Since that mapping is always 64K aligned, the are between the last file page and the next 64K allocation boundary will be unallocated. There's no way around that. Signed-off-by: Corinna Vinschen --- winsup/cygwin/mmap.cc | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc index 8b1aedc48..797373742 100644 --- a/winsup/cygwin/mmap.cc +++ b/winsup/cygwin/mmap.cc @@ -834,7 +834,7 @@ public: { /* If it points to a free area, big enough to fulfill the request, return the address. */ - if (VirtualQuery (in_addr, &mbi, sizeof mbi) + if (VirtualQuery (in_addr, &mbi, sizeof mbi) && mbi.State == MEM_FREE && mbi.RegionSize >= size) return in_addr; @@ -1075,14 +1075,17 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, off_t off) } fsiz -= off; /* We're creating the pages beyond EOF as reserved, anonymous pages. - Note that this isn't done in 64 bit environments since apparently - 64 bit systems don't support the AT_ROUND_TO_PAGE flag, which is - required to get this right. Too bad. */ + Note that 64 bit environments don't support the AT_ROUND_TO_PAGE + flag, which is required to get this right for the remainder of + the first 64K block the file ends in. We perform the workaround + nevertheless to support expectations that the range mapped beyond + EOF can be safely munmap'ed instead of being taken by another, + totally unrelated mapping. */ + if ((off_t) len > fsiz && !autogrow (flags)) + orig_len = len; #ifdef __i386__ - if (!wincap.is_wow64 () - && (((off_t) len > fsiz && !autogrow (flags)) - || roundup2 (len, wincap.page_size ()) - < roundup2 (len, pagesize))) + else if (!wincap.is_wow64 () && roundup2 (len, wincap.page_size ()) + < roundup2 (len, pagesize)) orig_len = len; #endif if ((off_t) len > fsiz) @@ -1185,12 +1188,22 @@ go_ahead: raise a SIGBUS when trying to access them. AT_ROUND_TO_PAGE and page protection on shared pages is only supported by the 32 bit environment, so don't even try on 64 bit or even WOW64. - This is accomplished by not setting orig_len on 64 bit above. */ - len = roundup2 (len, wincap.page_size ()); + This results in an allocation gap in the first 64K block the file + ends in, but there's nothing at all we can do about that. */ +#ifdef __x86_64__ + len = roundup2 (len, wincap.allocation_granularity ()); +#else + len = roundup2 (len, wincap.is_wow64 () ? wincap.allocation_granularity () + : wincap.page_size ()); +#endif if (orig_len - len) { orig_len -= len; - size_t valid_page_len = orig_len % pagesize; + size_t valid_page_len = 0; +#ifndef __x86_64__ + if (!wincap.is_wow64 ()) + valid_page_len = orig_len % pagesize; +#endif size_t sigbus_page_len = orig_len - valid_page_len; caddr_t at_base = base + len;