Hello!

I am a developer at Arm working on improving Cygwin compatibility on
the AArch64 architecture. I will soon be posting a series of bugfix
patches that allow additional unit tests to pass. Some (but not all) of
these patches are based on earlier work posted by Thirumalai Nagalingam,
itself authored by multiple contributors.

One of my patches is related to changing the memory layout that is
described in the file winsup/cygwin/local_includes/memory_layout.h,
and I am writing to ask for feedback on this change.

* Background/observed issue
---------------------------

Currently, the range from 0x800000000 to 0xa00000000 is reserved for
the cygheap. However, on Windows on Arm (AArch64), where mandatory
userspace ASLR is enforced, I have observed this region occasionally
being occupied by the PEB, TEB, and stack.

This can be reproduced with reasonably high probability using the
ltp/fork11.exe test. Below is an excerpt from a WinDbg !address
dump showing such a case:

        BaseAddress      EndAddress+1        RegionSize     Type       State    
             Protect             Usage
--------------------------------------------------------------------------------------------------------------------------
+        0`00000000        0`7ffe0000        0`7ffe0000             MEM_FREE    
PAGE_NOACCESS                      Free
+        0`7ffe0000        0`7ffe1000        0`00001000 MEM_PRIVATE MEM_COMMIT  
PAGE_READONLY                      Other      [User Shared Data]
+        0`7ffe1000        1`a0000000        1`2001f000             MEM_FREE    
PAGE_NOACCESS                      Free
+        1`a0000000        1`a000f000        0`0000f000 MEM_MAPPED  MEM_COMMIT  
PAGE_READWRITE                     MappedFile "PageFile"
+        1`a000f000        1`a0010000        0`00001000             MEM_FREE    
PAGE_NOACCESS                      Free
+        1`a0010000        1`a0019000        0`00009000 MEM_MAPPED  MEM_COMMIT  
PAGE_READWRITE                     MappedFile "PageFile"
+        1`a0019000        1`a0020000        0`00007000             MEM_FREE    
PAGE_NOACCESS                      Free
+        1`a0020000        1`a0030000        0`00010000 MEM_MAPPED  MEM_COMMIT  
PAGE_READWRITE                     MappedFile "PageFile"
+        1`a0030000        1`a0031000        0`00001000 MEM_MAPPED  MEM_COMMIT  
PAGE_READWRITE                     MappedFile "PageFile"
+        1`a0031000        7`ffe00000        6`5fdcf000             MEM_FREE    
PAGE_NOACCESS                      Free
+        7`ffe00000        7`ffff5000        0`001f5000 MEM_PRIVATE MEM_RESERVE 
                                   Stack      [~0; 2658.41b8]
         7`ffff5000        7`ffff8000        0`00003000 MEM_PRIVATE MEM_COMMIT  
PAGE_READWRITE | PAGE_GUARD        Stack      [~0; 2658.41b8]
         7`ffff8000        8`00000000        0`00008000 MEM_PRIVATE MEM_COMMIT  
PAGE_READWRITE                     Stack      [~0; 2658.41b8]
+        8`00000000        8`00300000        0`00300000 MEM_PRIVATE MEM_COMMIT  
PAGE_READWRITE                     <unknown>  [................]
         8`00300000        a`00000000        1`ffd00000 MEM_PRIVATE MEM_RESERVE 
                                   <unknown>
+        a`00000000        a`00030000        0`00030000 MEM_PRIVATE MEM_COMMIT  
PAGE_READWRITE                     <unknown>  [................]
         a`00030000        a`20000000        0`1ffd0000 MEM_PRIVATE MEM_RESERVE 
                                   <unknown>
+        a`20000000       78`3ee00000       6e`1ee00000             MEM_FREE    
PAGE_NOACCESS                      Free
[...]
+      186`d9c60000     7ff4`ee570000     7e6e`14910000             MEM_FREE    
PAGE_NOACCESS                      Free

* Proposed change
-----------------

My proposal is to shift cygheap, user heap and mmap_storage to
the end of the largest FREE block of memory.

Based on empirical testing, this appears to work consistently and
reliably, at least on Windows 11 23H2, on both AArch64 (with
mandatory ASLR) and x86_64. Here is an example of this block on
x86_64 with ASLR disabled:

+        1`0041e000     7ff4`fdec0000     7ff3`fdaa2000             MEM_FREE    
PAGE_NOACCESS                      Free

Please see attachments for my proposed changes.

* Questions
-----------

I have not been able to find any authoritative documentation that
would describe the exact mapping of these ranges whether on x86_64
or AArch64, on Microsoft's website or elsewhere.

I would therefore appreciate any guidance from the community on:
* whether this relocation has potential downsides I may be missing,
* whether there are known constraints on placing cygheap/user heap
  in this region,
* or whether there is a more appropriate approach to handling this
  under mandatory ASLR.

With regard to shared-memory interactions, my understanding is that
this change should not introduce new issues. The Cygwin FAQ already
notes that mixing executables from different Cygwin installations is
not a supported configuration
(https://cygwin.com/faq.html#faq.using.multiple-copies), and the
proposed change does not alter that assumption.

I look forward to any feedback or suggestions.

Best regards,
Igor Podgainoi
Arm
IMPORTANT NOTICE: The contents of this email and any attachments are 
confidential and may also be privileged. If you are not the intended recipient, 
please notify the sender immediately and do not disclose the contents to any 
other person, use it for any purpose, or store or copy the information in any 
medium. Thank you.
diff --git a/winsup/cygwin/local_includes/memory_layout.h b/winsup/cygwin/local_includes/memory_layout.h
index f8ec80bb5..9cbcc4339 100644
--- a/winsup/cygwin/local_includes/memory_layout.h
+++ b/winsup/cygwin/local_includes/memory_layout.h
@@ -36,20 +36,20 @@ details. */
 
 /* That's where the cygheap is located. CYGHEAP_STORAGE_INITIAL defines the
    end of the initially committed heap area. */
-#define CYGHEAP_STORAGE_LOW		0x800000000UL
-#define CYGHEAP_STORAGE_INITIAL		0x800300000UL
-#define CYGHEAP_STORAGE_HIGH		0xa00000000UL
+#define CYGHEAP_STORAGE_LOW		0x0ff700000000UL
+#define CYGHEAP_STORAGE_INITIAL		0x0ff700300000UL
+#define CYGHEAP_STORAGE_HIGH		0x0ff900000000UL
 
 /* This is where the user heap starts.  There's no defined end address.
    The user heap pontentially grows into the mmap arena.  However,
    the user heap grows upwards and the mmap arena grows downwards,
    so there's not much chance to meet unluckily. */
-#define USERHEAP_START			0xa00000000UL
+#define USERHEAP_START			0x0ff900000000UL
 
 /* The memory region used for memory maps.  Mmaps grow downwards.
    Set the lowest address to leave ~32 Gigs for heap. */
-#define MMAP_STORAGE_LOW		0x001000000000UL
-#define MMAP_STORAGE_HIGH		0x700000000000UL
+#define MMAP_STORAGE_LOW		0x0fff00000000UL
+#define MMAP_STORAGE_HIGH		0x7fef00000000UL
 
 /* Default number of pages used as thread stack guard pages. */
 #define DEFAULT_GUARD_PAGE_COUNT	3

Reply via email to