The two attached patches fix the bugs reported in

 https://cygwin.com/pipermail/cygwin/2024-December/256911.html

and

  https://cygwin.com/pipermail/cygwin/2024-December/256913.html

The second one is still under discussion on the cygwin list and may turn out to be wrong.

Ken
From a8ce46ae4353988827a2d646b63d14a6abb8bc90 Mon Sep 17 00:00:00 2001
From: Ken Brown <kbr...@cornell.edu>
Date: Wed, 18 Dec 2024 11:39:31 -0500
Subject: [PATCH 1/2] Cygwin: mmap: fix protection when unused pages are
 recycled

Previously, when unused pages from an mmap_record were recycled, they
were given the protection of the mmap_record rather than the
protection requested in the mmap call.  Fix this by adding a "prot"
parameter to mmap_list::try_map and mmap_record::map_pages to keep
track of the requested protection.  Then use prot in the call to
VirtualProtect.

Addresses: https://cygwin.com/pipermail/cygwin/2024-December/256911.html
Fixes: ??
Signed-off-by: Ken Brown <kbr...@cornell.edu>
---
 winsup/cygwin/mm/mmap.cc    | 28 +++++++++++++++-------------
 winsup/cygwin/release/3.5.5 |  3 +++
 2 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/winsup/cygwin/mm/mmap.cc b/winsup/cygwin/mm/mmap.cc
index 332c015a72d9..a2041ce63d45 100644
--- a/winsup/cygwin/mm/mmap.cc
+++ b/winsup/cygwin/mm/mmap.cc
@@ -339,8 +339,8 @@ class mmap_record
 
     SIZE_T find_unused_pages (SIZE_T pages) const;
     bool match (caddr_t addr, SIZE_T len, caddr_t &m_addr, SIZE_T &m_len);
-    off_t map_pages (SIZE_T len);
-    bool map_pages (caddr_t addr, SIZE_T len);
+    off_t map_pages (SIZE_T len, int prot);
+    bool map_pages (caddr_t addr, SIZE_T len, int prot);
     bool unmap_pages (caddr_t addr, SIZE_T len);
     int access (caddr_t address);
 
@@ -373,7 +373,7 @@ class mmap_list
     void set (int nfd, struct stat *st);
     mmap_record *add_record (mmap_record &r);
     bool del_record (mmap_record *rec);
-    caddr_t try_map (void *addr, size_t len, int flags, off_t off);
+    caddr_t try_map (void *addr, size_t len, int prot, int flags, off_t off);
 };
 
 class mmap_areas
@@ -455,14 +455,14 @@ mmap_record::init_page_map (mmap_record &r)
 }
 
 off_t
-mmap_record::map_pages (SIZE_T len)
+mmap_record::map_pages (SIZE_T len, int prot)
 {
   /* Used ONLY if this mapping matches into the chunk of another already
      performed mapping in a special case of MAP_ANON|MAP_PRIVATE.
 
      Otherwise it's job is now done by init_page_map(). */
   DWORD old_prot;
-  debug_printf ("map_pages (fd=%d, len=%lu)", get_fd (), len);
+  debug_printf ("map_pages (fd=%d, len=%lu, prot=%y)", get_fd (), len, prot);
   len = PAGE_CNT (len);
 
   off_t off = find_unused_pages (len);
@@ -470,7 +470,8 @@ mmap_record::map_pages (SIZE_T len)
     return (off_t) 0;
   if (!noreserve ()
       && !VirtualProtect (get_address () + off * wincap.page_size (),
-                         len * wincap.page_size (), gen_protect (),
+                         len * wincap.page_size (),
+                         ::gen_protect (prot, get_flags ()),
                          &old_prot))
     {
       __seterrno ();
@@ -483,9 +484,9 @@ mmap_record::map_pages (SIZE_T len)
 }
 
 bool
-mmap_record::map_pages (caddr_t addr, SIZE_T len)
+mmap_record::map_pages (caddr_t addr, SIZE_T len, int prot)
 {
-  debug_printf ("map_pages (addr=%p, len=%lu)", addr, len);
+  debug_printf ("map_pages (addr=%p, len=%lu, prot=%y)", addr, len, prot);
   DWORD old_prot;
   off_t off = addr - get_address ();
   off /= wincap.page_size ();
@@ -499,7 +500,8 @@ mmap_record::map_pages (caddr_t addr, SIZE_T len)
       }
   if (!noreserve ()
       && !VirtualProtect (get_address () + off * wincap.page_size (),
-                         len * wincap.page_size (), gen_protect (),
+                         len * wincap.page_size (),
+                         ::gen_protect (prot, get_flags ()),
                          &old_prot))
     {
       __seterrno ();
@@ -614,7 +616,7 @@ mmap_list::del_record (mmap_record *rec)
 }
 
 caddr_t
-mmap_list::try_map (void *addr, size_t len, int flags, off_t off)
+mmap_list::try_map (void *addr, size_t len, int prot, int flags, off_t off)
 {
   mmap_record *rec;
 
@@ -628,7 +630,7 @@ mmap_list::try_map (void *addr, size_t len, int flags, 
off_t off)
          break;
       if (rec && rec->compatible_flags (flags))
        {
-         if ((off = rec->map_pages (len)) == (off_t) -1)
+         if ((off = rec->map_pages (len, prot)) == (off_t) -1)
            return (caddr_t) MAP_FAILED;
          return (caddr_t) rec->get_address () + off;
        }
@@ -655,7 +657,7 @@ mmap_list::try_map (void *addr, size_t len, int flags, 
off_t off)
              set_errno (EINVAL);
              return (caddr_t) MAP_FAILED;
            }
-         if (!rec->map_pages ((caddr_t) addr, len))
+         if (!rec->map_pages ((caddr_t) addr, len, prot))
            return (caddr_t) MAP_FAILED;
          return (caddr_t) addr;
        }
@@ -1051,7 +1053,7 @@ go_ahead:
   /* Test if an existing anonymous mapping can be recycled. */
   if (map_list && anonymous (flags))
     {
-      caddr_t tried = map_list->try_map (addr, len, flags, off);
+      caddr_t tried = map_list->try_map (addr, len, prot, flags, off);
       /* try_map returns NULL if no map matched, otherwise it returns
         a valid address, or MAP_FAILED in case of a fatal error. */
       if (tried)
diff --git a/winsup/cygwin/release/3.5.5 b/winsup/cygwin/release/3.5.5
index e99739241c18..eb97c3d4aafe 100644
--- a/winsup/cygwin/release/3.5.5
+++ b/winsup/cygwin/release/3.5.5
@@ -61,3 +61,6 @@ Fixes:
 - Fix several problems triggered when a lot of SIGSTOP/SIGCONT signals
   are received rapidly.
   Addresses: https://cygwin.com/pipermail/cygwin/2024-November/256744.html
+
+- Fix the protection when mmap(2) recycles unused pages.
+  Addresses: https://cygwin.com/pipermail/cygwin/2024-December/256911.html
-- 
2.45.1

From c09bc8669696aeb6fd75e2e715d7859f94af6ad2 Mon Sep 17 00:00:00 2001
From: Ken Brown <kbr...@cornell.edu>
Date: Wed, 18 Dec 2024 11:43:09 -0500
Subject: [PATCH 2/2] Cygwin: mmap_list::try_map: fix a condition in a test of
 an mmap request

In testing whether the requested area is contained in an existing
mapped region, an incorrect condition was used due to a
misinterpretation of the u_addr and u_len variables.

Addresses: https://cygwin.com/pipermail/cygwin/2024-December/256913.html
Fixes: ?
Signed-off-by: Ken Brown <kbr...@cornell.edu>
---
 winsup/cygwin/mm/mmap.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/winsup/cygwin/mm/mmap.cc b/winsup/cygwin/mm/mmap.cc
index a2041ce63d45..8dd93230e69c 100644
--- a/winsup/cygwin/mm/mmap.cc
+++ b/winsup/cygwin/mm/mmap.cc
@@ -648,7 +648,7 @@ mmap_list::try_map (void *addr, size_t len, int prot, int 
flags, off_t off)
          break;
       if (rec)
        {
-         if (u_addr > (caddr_t) addr || u_addr + len < (caddr_t) addr + len
+         if (u_addr > (caddr_t) addr || u_len < len
              || !rec->compatible_flags (flags))
            {
              /* Partial match only, or access mode doesn't match. */
-- 
2.45.1

Reply via email to