[newlib-cygwin] Cygwin: map beyond EOF on 64 bit and WOW64 as well

Corinna Vinschen corinna@sourceware.org
Wed Jun 5 18:09:00 GMT 2019


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=605bdcd410384dda6db66b9b8cd19e863702e1bb

commit 605bdcd410384dda6db66b9b8cd19e863702e1bb
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Wed Jun 5 20:08:34 2019 +0200

    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 <corinna@vinschen.de>

Diff:
---
 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 8b1aedc..7973737 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;



More information about the Cygwin-cvs mailing list