Patch attached. This version is much simpler and more efficient than the previous one because it doesn't require keeping an mmap_list in a specific order.

With this patch installed, the two test cases in

  https://cygwin.com/pipermail/cygwin-developers/2024-December/012725.html

both succeed.

I have a question about the "Fixes" line. Since the commit in question was in the old CVS style, it doesn't have a good one-line summary. I tried to choose the next-best thing, but I'm not sure about it.

Ken
From 6501824000b4e21bec4d932a6d848bd89dd87ca3 Mon Sep 17 00:00:00 2001
From: Ken Brown <kbr...@cornell.edu>
Date: Fri, 27 Dec 2024 10:10:06 -0500
Subject: [PATCH v2] Cygwin: mmap: fix mmap_is_attached_or_noreserve

This commit fixes two problems.  The first is that
mmap_is_attached_or_noreserve would sometimes call VirtualAlloc with
MEM_COMMIT on address ranges that were not known to have MEM_RESERVE
status.  These calls could fail, causing SIGBUS to be raised
incorrectly.  See

  https://cygwin.com/pipermail/cygwin-developers/2024-December/012725.html

for details.  Fix this by calling VirtualAlloc only on the part of the
address range that's contained in the current mmap_record.

The second problem is that the code would sometimes break out of the
main loop without knowing whether attached mappings still occur later
in the mmap_list.  This could cause SIGBUS to not be raised when it
should be.  Fix this by using "continue" rather than "break".  For
efficiency, introduce a boolean variable "nocover" that's set to true
if we discover that the address range cannot be covered by noreserve
mmap regions.

Addresses:
https://cygwin.com/pipermail/cygwin-developers/2024-December/012725.html
Fixes: 70e476d27be8 ("* include/cygwin/version.h: Bump DLL version to
1.7.0")
Signed-off-by: Ken Brown <kbr...@cornell.edu>
---
 winsup/cygwin/mm/mmap.cc | 52 +++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/winsup/cygwin/mm/mmap.cc b/winsup/cygwin/mm/mmap.cc
index 13418d782baf..fc126a87072a 100644
--- a/winsup/cygwin/mm/mmap.cc
+++ b/winsup/cygwin/mm/mmap.cc
@@ -738,18 +738,18 @@ is_mmapped_region (caddr_t start_addr, caddr_t 
end_address)
 
 /* This function is called from exception_handler when a segmentation
    violation has occurred.  It should also be called from all Cygwin
-   functions that want to support passing noreserve mmap page addresses
-   to Windows system calls.  In that case, it should be called only after
-   a system call indicates that the application buffer passed had an
-   invalid virtual address to avoid any performance impact in non-noreserve
-   cases.
+   functions that want to support passing noreserve (anonymous) mmap
+   page addresses to Windows system calls.  In that case, it should be
+   called only after a system call indicates that the application
+   buffer passed had an invalid virtual address to avoid any
+   performance impact in non-noreserve cases.
 
    Check if the address range is all within noreserve mmap regions.  If so,
    call VirtualAlloc to commit the pages and return MMAP_NORESERVE_COMMITED
-   on success.  If the page has __PROT_ATTACH (SUSv3 memory protection
-   extension), or if VirtualAlloc fails, return MMAP_RAISE_SIGBUS.
-   Otherwise, return MMAP_NONE if the address range is not covered by an
-   attached or noreserve map.
+   on success.  If some page in the address range has __PROT_ATTACH
+   (SUSv3 memory protection extension), or if VirtualAlloc fails,
+   return MMAP_RAISE_SIGBUS.  Otherwise, return MMAP_NONE if the
+   address range is not covered by noreserve maps.
 
    On MAP_NORESERVE_COMMITED, the exeception handler should return 0 to
    allow the application to retry the memory access, or the calling Cygwin
@@ -768,12 +768,16 @@ mmap_is_attached_or_noreserve (void *addr, size_t len)
   len += ((caddr_t) addr - start_addr);
   len = roundup2 (len, pagesize);
 
-  if (map_list == NULL)
-    goto out;
-
   mmap_record *rec;
   caddr_t u_addr;
   SIZE_T u_len;
+  /* nocover is set to true if we discover that our address range
+     cannot be covered by noreserve mmap regions. */
+  bool nocover = false;
+  size_t remaining = len;
+
+  if (map_list == NULL)
+    goto out;
 
   LIST_FOREACH (rec, &map_list->recs, mr_next)
     {
@@ -784,23 +788,27 @@ mmap_is_attached_or_noreserve (void *addr, size_t len)
          ret = MMAP_RAISE_SIGBUS;
          break;
        }
-      if (!rec->noreserve ())
-       break;
+      if (nocover)
+       /* We need to continue in case we encounter an attached mmap
+          later in the list. */
+       continue;
 
-      size_t commit_len = u_len - (start_addr - u_addr);
-      if (commit_len > len)
-       commit_len = len;
+      if (!rec->noreserve ())
+       {
+         nocover = true;
+         continue;
+       }
 
-      if (!VirtualAlloc (start_addr, commit_len, MEM_COMMIT,
-                        rec->gen_protect ()))
+      /* The interval determined by u_addr and u_len is the part of
+        our address range contained in the mmap region of rec.  */
+      if (!VirtualAlloc (u_addr, u_len, MEM_COMMIT, rec->gen_protect ()))
        {
          ret = MMAP_RAISE_SIGBUS;
          break;
        }
 
-      start_addr += commit_len;
-      len -= commit_len;
-      if (!len)
+      remaining -= u_len;
+      if (!remaining)
        {
          ret = MMAP_NORESERVE_COMMITED;
          break;
-- 
2.45.1

Reply via email to