From 31acabe792dc90d9aa67fd4e1f9e84a8e9056dcc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Sat, 12 Nov 2022 18:30:04 -0600 Subject: [PATCH] Updated and re-enabled ntdll-ForceBottomUpAlloc patchset. --- ...tep-after-failed-map-attempt-in-try_.patch | 8 +- ...ree-ranges-view-block-size-on-64-bit.patch | 8 +- ...orce-virtual-memory-allocation-order.patch | 317 +++++++++++------- ...tively-mapped-areas-from-free-areas-.patch | 113 +++---- patches/ntdll-ForceBottomUpAlloc/definition | 2 +- patches/patchinstall.sh | 83 +++-- 6 files changed, 295 insertions(+), 236 deletions(-) diff --git a/patches/ntdll-ForceBottomUpAlloc/0001-ntdll-Increase-step-after-failed-map-attempt-in-try_.patch b/patches/ntdll-ForceBottomUpAlloc/0001-ntdll-Increase-step-after-failed-map-attempt-in-try_.patch index b1a4064e..1d03a31e 100644 --- a/patches/ntdll-ForceBottomUpAlloc/0001-ntdll-Increase-step-after-failed-map-attempt-in-try_.patch +++ b/patches/ntdll-ForceBottomUpAlloc/0001-ntdll-Increase-step-after-failed-map-attempt-in-try_.patch @@ -1,4 +1,4 @@ -From d28a26a59e9837191cc48854d5dad16eba532f04 Mon Sep 17 00:00:00 2001 +From 5814a4b9c7d4dec027b0c2dd29822664aa56ada1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 14 Jan 2020 21:39:23 +0300 Subject: [PATCH] ntdll: Increase step after failed map attempt in @@ -9,10 +9,10 @@ Subject: [PATCH] ntdll: Increase step after failed map attempt in 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index d79e3de662e..f8f317a6483 100644 +index 84a4e331ad7..a63882023e6 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c -@@ -1073,6 +1073,7 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step, +@@ -1327,6 +1327,7 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step, step == 0) break; start = (char *)start + step; @@ -21,5 +21,5 @@ index d79e3de662e..f8f317a6483 100644 return NULL; -- -2.26.2 +2.38.1 diff --git a/patches/ntdll-ForceBottomUpAlloc/0002-ntdll-Increase-free-ranges-view-block-size-on-64-bit.patch b/patches/ntdll-ForceBottomUpAlloc/0002-ntdll-Increase-free-ranges-view-block-size-on-64-bit.patch index f32fecb3..da2bdc2c 100644 --- a/patches/ntdll-ForceBottomUpAlloc/0002-ntdll-Increase-free-ranges-view-block-size-on-64-bit.patch +++ b/patches/ntdll-ForceBottomUpAlloc/0002-ntdll-Increase-free-ranges-view-block-size-on-64-bit.patch @@ -1,4 +1,4 @@ -From 266f5082387ec92cb79a2ec3ce71d956cf05190d Mon Sep 17 00:00:00 2001 +From b1e6d32e7dc3bac93419f3a573f509ee6e1177b2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 23 Jul 2020 18:40:39 +0300 Subject: [PATCH] ntdll: Increase free ranges view block size on 64 bit. @@ -8,10 +8,10 @@ Subject: [PATCH] ntdll: Increase free ranges view block size on 64 bit. 1 file changed, 4 insertions(+) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index f8f317a6483..4ca033b0e19 100644 +index a63882023e6..83909f57d4a 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c -@@ -183,7 +183,11 @@ static BYTE *pages_vprot; +@@ -205,7 +205,11 @@ static BYTE *pages_vprot; #endif static struct file_view *view_block_start, *view_block_end, *next_free_view; @@ -24,5 +24,5 @@ index f8f317a6483..4ca033b0e19 100644 static void *preload_reserve_end; static BOOL force_exec_prot; /* whether to force PROT_EXEC on all PROT_READ mmaps */ -- -2.26.2 +2.38.1 diff --git a/patches/ntdll-ForceBottomUpAlloc/0003-ntdll-Force-virtual-memory-allocation-order.patch b/patches/ntdll-ForceBottomUpAlloc/0003-ntdll-Force-virtual-memory-allocation-order.patch index c2a9a1fa..dcfa9088 100644 --- a/patches/ntdll-ForceBottomUpAlloc/0003-ntdll-Force-virtual-memory-allocation-order.patch +++ b/patches/ntdll-ForceBottomUpAlloc/0003-ntdll-Force-virtual-memory-allocation-order.patch @@ -1,28 +1,29 @@ -From 0cb1d81c14522afe45b1bb5a8194efd2470efe27 Mon Sep 17 00:00:00 2001 +From c3252fd68ecdcc0186b4d284df1b36ea11ca7d7f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Nov 2019 12:19:20 +0300 Subject: [PATCH] ntdll: Force virtual memory allocation order. Windows allocates virtual memory strictly bottom up or -top down depending on the requested flags. Modern Linux -VM allocator always allocates memory top down. Some +top down depending on the requested flags (when ASLR is disabled). +Modern Linux VM allocator always allocates memory top down. Some applications break if the allocated memory addresses are from higher memory than they expect. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48175 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46568 --- - dlls/ntdll/unix/virtual.c | 417 ++++++++++++++++++-------------------- - 1 file changed, 202 insertions(+), 215 deletions(-) + dlls/ntdll/unix/virtual.c | 450 +++++++++++++++++++------------------- + 1 file changed, 227 insertions(+), 223 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index 7654055f59b..e971e0523ba 100644 +index 83909f57d4a..4bce8f2f806 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c -@@ -1202,44 +1202,6 @@ static struct file_view *find_view_range( const void *addr, size_t size ) +@@ -1266,44 +1266,15 @@ static struct file_view *find_view_range( const void *addr, size_t size ) + return NULL; } - +- -/*********************************************************************** - * find_view_inside_range - * @@ -30,7 +31,8 @@ index 7654055f59b..e971e0523ba 100644 - * virtual_mutex must be held by caller. - */ -static struct wine_rb_entry *find_view_inside_range( void **base_ptr, void **end_ptr, int top_down ) --{ ++struct alloc_area + { - struct wine_rb_entry *first = NULL, *ptr = views_tree.root; - void *base = *base_ptr, *end = *end_ptr; - @@ -60,11 +62,17 @@ index 7654055f59b..e971e0523ba 100644 - return first; -} - -- ++ char *map_area_start, *map_area_end, *result; ++ size_t size; ++ ptrdiff_t step; ++ int unix_prot; ++ BOOL top_down; ++ UINT_PTR align_mask; ++}; + /*********************************************************************** * try_map_free_area - * -@@ -1272,110 +1234,6 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step, +@@ -1337,110 +1308,6 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step, return NULL; } @@ -75,15 +83,15 @@ index 7654055f59b..e971e0523ba 100644 - * Find a free area between views inside the specified range and map it. - * virtual_mutex must be held by caller. - */ --static void *map_free_area( void *base, void *end, size_t size, int top_down, int unix_prot ) +-static void *map_free_area( void *base, void *end, size_t size, int top_down, int unix_prot, size_t align_mask ) -{ - struct wine_rb_entry *first = find_view_inside_range( &base, &end, top_down ); -- ptrdiff_t step = top_down ? -(granularity_mask + 1) : (granularity_mask + 1); +- ptrdiff_t step = top_down ? -(align_mask + 1) : (align_mask + 1); - void *start; - - if (top_down) - { -- start = ROUND_ADDR( (char *)end - size, granularity_mask ); +- start = ROUND_ADDR( (char *)end - size, align_mask ); - if (start >= end || start < base) return NULL; - - while (first) @@ -91,7 +99,7 @@ index 7654055f59b..e971e0523ba 100644 - struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); - if ((start = try_map_free_area( (char *)view->base + view->size, (char *)start + size, step, - start, size, unix_prot ))) break; -- start = ROUND_ADDR( (char *)view->base - size, granularity_mask ); +- start = ROUND_ADDR( (char *)view->base - size, align_mask ); - /* stop if remaining space is not large enough */ - if (!start || start >= end || start < base) return NULL; - first = rb_prev( first ); @@ -99,7 +107,7 @@ index 7654055f59b..e971e0523ba 100644 - } - else - { -- start = ROUND_ADDR( (char *)base + granularity_mask, granularity_mask ); +- start = ROUND_ADDR( (char *)base + align_mask, align_mask ); - if (!start || start >= end || (char *)end - (char *)start < size) return NULL; - - while (first) @@ -107,7 +115,7 @@ index 7654055f59b..e971e0523ba 100644 - struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); - if ((start = try_map_free_area( start, view->base, step, - start, size, unix_prot ))) break; -- start = ROUND_ADDR( (char *)view->base + view->size + granularity_mask, granularity_mask ); +- start = ROUND_ADDR( (char *)view->base + view->size + align_mask, align_mask ); - /* stop if remaining space is not large enough */ - if (!start || start >= end || (char *)end - (char *)start < size) return NULL; - first = rb_next( first ); @@ -128,13 +136,13 @@ index 7654055f59b..e971e0523ba 100644 - * virtual_mutex must be held by caller. - * The range must be inside the preloader reserved range. - */ --static void *find_reserved_free_area( void *base, void *end, size_t size, int top_down ) +-static void *find_reserved_free_area( void *base, void *end, size_t size, int top_down, size_t align_mask ) -{ - struct range_entry *range; - void *start; - -- base = ROUND_ADDR( (char *)base + granularity_mask, granularity_mask ); -- end = (char *)ROUND_ADDR( (char *)end - size, granularity_mask ) + size; +- base = ROUND_ADDR( (char *)base + align_mask, align_mask ); +- end = (char *)ROUND_ADDR( (char *)end - size, align_mask ) + size; - - if (top_down) - { @@ -142,13 +150,13 @@ index 7654055f59b..e971e0523ba 100644 - range = free_ranges_lower_bound( start ); - assert(range != free_ranges_end && range->end >= start); - -- if ((char *)range->end - (char *)start < size) start = ROUND_ADDR( (char *)range->end - size, granularity_mask ); +- if ((char *)range->end - (char *)start < size) start = ROUND_ADDR( (char *)range->end - size, align_mask ); - do - { - if (start >= end || start < base || (char *)end - (char *)start < size) return NULL; - if (start < range->end && start >= range->base && (char *)range->end - (char *)start >= size) break; - if (--range < free_ranges) return NULL; -- start = ROUND_ADDR( (char *)range->end - size, granularity_mask ); +- start = ROUND_ADDR( (char *)range->end - size, align_mask ); - } - while (1); - } @@ -158,13 +166,13 @@ index 7654055f59b..e971e0523ba 100644 - range = free_ranges_lower_bound( start ); - assert(range != free_ranges_end && range->end >= start); - -- if (start < range->base) start = ROUND_ADDR( (char *)range->base + granularity_mask, granularity_mask ); +- if (start < range->base) start = ROUND_ADDR( (char *)range->base + align_mask, align_mask ); - do - { - if (start >= end || start < base || (char *)end - (char *)start < size) return NULL; - if (start < range->end && start >= range->base && (char *)range->end - (char *)start >= size) break; - if (++range == free_ranges_end) return NULL; -- start = ROUND_ADDR( (char *)range->base + granularity_mask, granularity_mask ); +- start = ROUND_ADDR( (char *)range->base + align_mask, align_mask ); - } - while (1); - } @@ -175,39 +183,45 @@ index 7654055f59b..e971e0523ba 100644 /*********************************************************************** * add_reserved_area * -@@ -1533,8 +1391,7 @@ static void delete_view( struct file_view *view ) /* [in] View */ +@@ -1608,8 +1475,7 @@ static void free_view( struct file_view *view ) + */ + static void unregister_view( struct file_view *view ) { - if (!(view->protect & VPROT_SYSTEM)) unmap_area( view->base, view->size ); - set_page_vprot( view->base, view->size, 0 ); - if (mmap_is_in_reserved_area( view->base, view->size )) - free_ranges_remove_view( view ); + free_ranges_remove_view( view ); wine_rb_remove( &views_tree, &view->entry ); - *(struct file_view **)view = next_free_view; - next_free_view = view; -@@ -1582,8 +1439,7 @@ static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t siz - set_page_vprot( base, size, vprot ); + } +@@ -1636,8 +1502,7 @@ static void delete_view( struct file_view *view ) /* [in] View */ + static void register_view( struct file_view *view ) + { wine_rb_put( &views_tree, view->base, &view->entry ); - if (mmap_is_in_reserved_area( view->base, view->size )) - free_ranges_insert_view( view ); + free_ranges_insert_view( view ); + } - *view_ret = view; -@@ -1797,51 +1653,218 @@ static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t want +@@ -1906,55 +1771,229 @@ static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t want + return ptr; + } - struct alloc_area +- +-struct alloc_area ++static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, void *arg ) { -+ char *map_area_start, *map_area_end, *result; - size_t size; +- size_t size; - int top_down; - void *limit; - void *result; -+ ptrdiff_t step; -+ int unix_prot; -+ BOOL top_down; - }; +- size_t align_mask; +-}; ++ char *intersect_start, *intersect_end; ++ char *end = (char *)start + size; ++ struct alloc_area *area = arg; ++ UINT_PTR align_mask; ++ char *alloc_start; -/*********************************************************************** - * alloc_reserved_area_callback @@ -215,43 +229,77 @@ index 7654055f59b..e971e0523ba 100644 - * Try to map some space inside a reserved area. Callback for mmap_enum_reserved_areas. - */ -static int alloc_reserved_area_callback( void *start, SIZE_T size, void *arg ) -+static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, void *arg ) - { +-{ - struct alloc_area *alloc = arg; - void *end = (char *)start + size; -- -- if (start < address_space_start) start = address_space_start; -- if (is_beyond_limit( start, size, alloc->limit )) end = alloc->limit; -- if (start >= end) return 0; -+ char *intersect_start, *intersect_end; -+ char *end = (char *)start + size; -+ struct alloc_area *area = arg; -+ char *alloc_start; - -- /* make sure we don't touch the preloader reserved range */ -- if (preload_reserve_end >= start) ++ align_mask = area->align_mask; ++ + if (area->top_down) - { -- if (preload_reserve_end >= end) ++ { + if (area->map_area_start >= end) + return 1; + + if (area->map_area_end <= (char *)start) + return 0; + ++ if ((ULONG_PTR)area->map_area_end < area->size) ++ return 1; ++ + intersect_start = max((char *)start, area->map_area_start); + intersect_end = min((char *)end, area->map_area_end); -+ -+ assert(ROUND_ADDR(intersect_start, granularity_mask) == intersect_start); -+ assert(ROUND_ADDR(intersect_end + granularity_mask, granularity_mask) == intersect_end); -+ assert(ROUND_ADDR(area->map_area_end, granularity_mask) == area->map_area_end); -+ ++ assert(intersect_start <= intersect_end); + if (area->map_area_end - intersect_end >= area->size) ++ { ++ alloc_start = ROUND_ADDR( (char *)area->map_area_end - size, align_mask ); ++ if ((area->result = try_map_free_area( intersect_end, alloc_start + size, area->step, ++ alloc_start, area->size, area->unix_prot ))) ++ return 1; ++ } + +- if (start < address_space_start) start = address_space_start; +- if (is_beyond_limit( start, size, alloc->limit )) end = alloc->limit; +- if (start >= end) return 0; ++ if (intersect_end - intersect_start >= area->size) ++ { ++ alloc_start = ROUND_ADDR( intersect_end - area->size, align_mask ); ++ if (alloc_start >= intersect_start) ++ { ++ if ((area->result = anon_mmap_fixed( alloc_start, area->size, ++ area->unix_prot, 0 )) != alloc_start) ++ ERR("Could not map in reserved area, alloc_start %p, size %p.\n", ++ alloc_start, (void *)area->size); ++ return 1; ++ } ++ } + +- /* make sure we don't touch the preloader reserved range */ +- if (preload_reserve_end >= start) ++ area->map_area_end = intersect_start; ++ if (area->map_area_end - area->map_area_start < area->size) ++ return 1; ++ } ++ else + { +- if (preload_reserve_end >= end) ++ if (area->map_area_end <= (char *)start) ++ return 1; ++ ++ if (area->map_area_start >= (char *)end) ++ return 0; ++ ++ if (area->map_area_start + align_mask < area->map_area_start) ++ return 1; ++ ++ intersect_start = max((char *)start, area->map_area_start); ++ intersect_end = min((char *)end, area->map_area_end); ++ assert(intersect_start <= intersect_end); ++ ++ if (intersect_start - area->map_area_start >= area->size) { - if (preload_reserve_start <= start) return 0; /* no space in that area */ - if (preload_reserve_start < end) end = preload_reserve_start; -+ alloc_start = ROUND_ADDR( (char *)area->map_area_end - size, granularity_mask ); -+ if ((area->result = try_map_free_area( intersect_end, alloc_start + size, area->step, ++ alloc_start = ROUND_ADDR( area->map_area_start + align_mask, align_mask ); ++ if ((area->result = try_map_free_area( area->map_area_start, intersect_start, area->step, + alloc_start, area->size, area->unix_prot ))) + return 1; } @@ -262,55 +310,22 @@ index 7654055f59b..e971e0523ba 100644 { - /* range is split in two by the preloader reservation, try first part */ - if ((alloc->result = find_reserved_free_area( start, preload_reserve_start, alloc->size, -- alloc->top_down ))) -+ alloc_start = ROUND_ADDR( intersect_end - area->size, granularity_mask ); -+ if ((area->result = anon_mmap_fixed( alloc_start, area->size, -+ area->unix_prot, 0 )) != alloc_start) -+ ERR("Could not map in reserved area, alloc_start %p, size %p.\n", -+ alloc_start, (void *)area->size); -+ return 1; -+ } -+ -+ area->map_area_end = intersect_start; -+ if (area->map_area_end - area->map_area_start < area->size) -+ return 1; -+ } -+ else -+ { -+ if (area->map_area_end <= (char *)start) -+ return 1; -+ -+ if (area->map_area_start >= (char *)end) -+ return 0; -+ -+ intersect_start = max((char *)start, area->map_area_start); -+ intersect_end = min((char *)end, area->map_area_end); -+ -+ assert(ROUND_ADDR(intersect_start, granularity_mask) == intersect_start); -+ assert(ROUND_ADDR(intersect_end + granularity_mask, granularity_mask) == intersect_end); -+ assert(ROUND_ADDR(area->map_area_start, granularity_mask) == area->map_area_start); -+ -+ if (intersect_start - area->map_area_start >= area->size) -+ { -+ if ((area->result = try_map_free_area( area->map_area_start, intersect_start, area->step, -+ area->map_area_start, area->size, area->unix_prot ))) +- alloc->top_down, alloc->align_mask ))) ++ alloc_start = ROUND_ADDR( intersect_start + align_mask, align_mask ); ++ if (alloc_start + area->size <= intersect_end) ++ { ++ if ((area->result = anon_mmap_fixed( alloc_start, area->size, area->unix_prot, 0 )) != alloc_start) ++ ERR("Could not map in reserved area, alloc_start %p, size %p.\n", alloc_start, (void *)area->size); return 1; - /* then fall through to try second part */ - start = preload_reserve_end; ++ } } -+ -+ if (intersect_end - intersect_start >= area->size) -+ { -+ if ((area->result = anon_mmap_fixed( intersect_start, area->size, area->unix_prot, 0 )) -+ != intersect_start) -+ ERR("Could not map in reserved area.\n"); -+ return 1; -+ } + area->map_area_start = intersect_end; + if (area->map_area_end - area->map_area_start < area->size) + return 1; } -- if ((alloc->result = find_reserved_free_area( start, end, alloc->size, alloc->top_down ))) +- if ((alloc->result = find_reserved_free_area( start, end, alloc->size, alloc->top_down, alloc->align_mask ))) - return 1; return 0; @@ -318,6 +333,7 @@ index 7654055f59b..e971e0523ba 100644 +static void *alloc_free_area_in_range( struct alloc_area *area, char *base, char *end ) +{ ++ UINT_PTR align_mask = area->align_mask; + char *start; + + TRACE("range %p-%p.\n", base, end); @@ -330,13 +346,14 @@ index 7654055f59b..e971e0523ba 100644 + + if (area->top_down) + { -+ start = ROUND_ADDR( end - area->size, granularity_mask ); -+ if (start >= end || start < base) -+ return NULL; ++ if ((ULONG_PTR)end < area->size) return NULL; ++ start = ROUND_ADDR( end - area->size, align_mask ); ++ if (start >= end || start < base) return NULL; + } + else + { -+ start = ROUND_ADDR( base + granularity_mask, granularity_mask ); ++ if (base + align_mask < base) return NULL; ++ start = ROUND_ADDR( base + align_mask, align_mask ); + if (!start || start >= end || (char *)end - (char *)start < area->size) + return NULL; + } @@ -348,7 +365,8 @@ index 7654055f59b..e971e0523ba 100644 + + if (area->top_down) + { -+ start = ROUND_ADDR( area->map_area_end - area->size, granularity_mask ); ++ if ((ULONG_PTR)area->map_area_end < area->size) return NULL; ++ start = ROUND_ADDR( area->map_area_end - area->size, align_mask ); + if (start >= area->map_area_end || start < area->map_area_start) + return NULL; + @@ -357,7 +375,8 @@ index 7654055f59b..e971e0523ba 100644 + } + else + { -+ start = ROUND_ADDR( area->map_area_start + granularity_mask, granularity_mask ); ++ if (area->map_area_start + align_mask < area->map_area_start) return NULL; ++ start = ROUND_ADDR( area->map_area_start + align_mask, align_mask ); + if (!start || start >= area->map_area_end + || area->map_area_end - start < area->size) + return NULL; @@ -367,7 +386,7 @@ index 7654055f59b..e971e0523ba 100644 + } +} + -+static void *alloc_free_area( void *limit, size_t size, BOOL top_down, int unix_prot ) ++static void *alloc_free_area( void *limit, size_t size, BOOL top_down, int unix_prot, UINT_PTR align_mask ) +{ + struct range_entry *range, *ranges_start, *ranges_end; + char *reserve_start, *reserve_end; @@ -391,13 +410,14 @@ index 7654055f59b..e971e0523ba 100644 + } + + memset( &area, 0, sizeof(area) ); -+ area.step = top_down ? -(granularity_mask + 1) : (granularity_mask + 1); ++ area.step = top_down ? -(align_mask + 1) : (align_mask + 1); + area.size = size; + area.top_down = top_down; + area.unix_prot = unix_prot; ++ area.align_mask = align_mask; + -+ reserve_start = ROUND_ADDR( (char *)preload_reserve_start, granularity_mask ); -+ reserve_end = ROUND_ADDR( (char *)preload_reserve_end + granularity_mask, granularity_mask ); ++ reserve_start = preload_reserve_start; ++ reserve_end = preload_reserve_end; + + for (range = ranges_start; range != ranges_end; range += ranges_inc) + { @@ -406,10 +426,13 @@ index 7654055f59b..e971e0523ba 100644 + + TRACE("range %p-%p.\n", base, end); + ++ if (base >= (char *)limit) ++ continue; ++ + if (base < (char *)address_space_start) + base = (char *)address_space_start; -+ if (end > (char *)ROUND_ADDR( limit, granularity_mask )) -+ end = ROUND_ADDR( limit, granularity_mask ); ++ if (end > (char *)limit) ++ end = (char *)limit; + + if (reserve_end >= base) + { @@ -417,6 +440,7 @@ index 7654055f59b..e971e0523ba 100644 + { + if (reserve_start <= base) + continue; /* no space in that area */ ++ + if (reserve_start < end) + end = reserve_start; + } @@ -428,14 +452,14 @@ index 7654055f59b..e971e0523ba 100644 + { + /* range is split in two by the preloader reservation, try first part. */ + if ((area.result = alloc_free_area_in_range( &area, base, reserve_start ))) -+ return area.result; ++ break; + /* then fall through to try second part. */ + base = reserve_end; + } + } + + if ((area.result = alloc_free_area_in_range( &area, base, end ))) -+ return area.result; ++ break; + } + return NULL; +} @@ -443,20 +467,41 @@ index 7654055f59b..e971e0523ba 100644 /*********************************************************************** * map_fixed_area * -@@ -1913,48 +1936,11 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, +@@ -2019,6 +2058,8 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, + void *ptr; + NTSTATUS status; + ++ limit = limit ? min( limit + 1, (UINT_PTR)user_space_limit) : (UINT_PTR)user_space_limit; ++ + if (alloc_type & MEM_REPLACE_PLACEHOLDER) + { + if ((*view_ret = find_view( base, 0 ))) +@@ -2044,6 +2085,8 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, + return STATUS_INVALID_PARAMETER; + } + ++ if (!align_mask) align_mask = granularity_mask; ++ + if (base) + { + if (is_beyond_limit( base, size, address_space_limit )) +@@ -2052,52 +2095,10 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, if (status != STATUS_SUCCESS) return status; ptr = base; } - else -+ else if (!(ptr = alloc_free_area( (void*)(get_zero_bits_mask( zero_bits ) -+ & (UINT_PTR)user_space_limit), size, top_down, get_unix_prot( vprot ) ))) ++ else if (!(ptr = alloc_free_area( (void *)limit, size, top_down, get_unix_prot( vprot ), align_mask ))) { -- size_t view_size = size + granularity_mask + 1; - struct alloc_area alloc; +- size_t view_size; +- +- if (!align_mask) align_mask = granularity_mask; +- view_size = size + align_mask + 1; - - alloc.size = size; - alloc.top_down = top_down; -- alloc.limit = (void*)(get_zero_bits_mask( zero_bits ) & (UINT_PTR)user_space_limit); +- alloc.limit = limit ? min( (void *)(limit + 1), user_space_limit ) : user_space_limit; +- alloc.align_mask = align_mask; - - if (mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down )) - { @@ -467,10 +512,10 @@ index 7654055f59b..e971e0523ba 100644 - goto done; - } - -- if (zero_bits) +- if (limit) - { - if (!(ptr = map_free_area( address_space_start, alloc.limit, size, -- top_down, get_unix_prot(vprot) ))) +- top_down, get_unix_prot(vprot), align_mask ))) - return STATUS_NO_MEMORY; - TRACE( "got mem with map_free_area %p-%p\n", ptr, (char *)ptr + size ); - goto done; @@ -488,14 +533,22 @@ index 7654055f59b..e971e0523ba 100644 - if (is_beyond_limit( ptr, view_size, user_space_limit )) add_reserved_area( ptr, view_size ); - else break; - } -- ptr = unmap_extra_space( ptr, view_size, size ); +- ptr = unmap_extra_space( ptr, view_size, size, align_mask ); + return STATUS_NO_MEMORY; } -done: status = create_view( view_ret, ptr, size, vprot ); if (status != STATUS_SUCCESS) unmap_area( ptr, size ); return status; -@@ -2680,6 +2666,7 @@ void virtual_init(void) +@@ -2758,6 +2759,7 @@ static NTSTATUS virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_PTR z + done: + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + if (needs_close) close( unix_handle ); ++ TRACE("status %#x.\n", res); + return res; + } + +@@ -2826,6 +2828,7 @@ void virtual_init(void) if (preload_reserve_start) address_space_start = min( address_space_start, preload_reserve_start ); } @@ -503,6 +556,14 @@ index 7654055f59b..e971e0523ba 100644 } /* try to find space in a reserved area for the views and pages protection table */ +@@ -5579,6 +5582,7 @@ NTSTATUS WINAPI NtWow64AllocateVirtualMemory64( HANDLE process, ULONG64 *ret, UL + *ret = (ULONG_PTR)base; + *size_ptr = size; + } ++ TRACE("status %#x.\n", status); + return status; + } + -- -2.34.1 +2.38.1 diff --git a/patches/ntdll-ForceBottomUpAlloc/0004-ntdll-Exclude-natively-mapped-areas-from-free-areas-.patch b/patches/ntdll-ForceBottomUpAlloc/0004-ntdll-Exclude-natively-mapped-areas-from-free-areas-.patch index 2f830cc9..99b32e67 100644 --- a/patches/ntdll-ForceBottomUpAlloc/0004-ntdll-Exclude-natively-mapped-areas-from-free-areas-.patch +++ b/patches/ntdll-ForceBottomUpAlloc/0004-ntdll-Exclude-natively-mapped-areas-from-free-areas-.patch @@ -1,25 +1,25 @@ -From bcc882f5d1980cbf353891d1e1686e97c068f8c7 Mon Sep 17 00:00:00 2001 +From d29a79d8cca2220c838fbbea6ea81b9f73070ba5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 2 Jun 2020 21:06:33 +0300 Subject: [PATCH] ntdll: Exclude natively mapped areas from free areas list. --- - dlls/ntdll/unix/virtual.c | 137 ++++++++++++++++++++++++++++++-------- - 1 file changed, 109 insertions(+), 28 deletions(-) + dlls/ntdll/unix/virtual.c | 118 ++++++++++++++++++++++++++++++++------ + 1 file changed, 100 insertions(+), 18 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index 4899fc7b443..cee739bd942 100644 +index 4bce8f2f806..3c1bf6edc30 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c -@@ -143,6 +143,7 @@ struct file_view - #define VPROT_WRITEWATCH 0x40 - /* per-mapping protection flags */ +@@ -125,6 +125,7 @@ struct file_view #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */ -+#define VPROT_NATIVE 0x0400 + #define VPROT_PLACEHOLDER 0x0400 + #define VPROT_FROMPLACEHOLDER 0x0800 ++#define VPROT_NATIVE 0x1000 /* Conversion from VPROT_* to Win32 flags */ static const BYTE VIRTUAL_Win32Flags[16] = -@@ -1081,7 +1082,9 @@ static void dump_view( struct file_view *view ) +@@ -1119,7 +1120,9 @@ static void dump_view( struct file_view *view ) BYTE prot = get_page_vprot( addr ); TRACE( "View: %p - %p", addr, addr + view->size - 1 ); @@ -28,26 +28,18 @@ index 4899fc7b443..cee739bd942 100644 + TRACE(" (native)\n"); + else if (view->protect & VPROT_SYSTEM) TRACE( " (builtin image)\n" ); - else if (view->protect & SEC_IMAGE) - TRACE( " (image)\n" ); -@@ -1216,6 +1219,16 @@ static struct file_view *find_view_range( const void *addr, size_t size ) - return NULL; - } - -+struct alloc_area -+{ -+ char *map_area_start, *map_area_end, *result; -+ size_t size; -+ ptrdiff_t step; -+ int unix_prot; -+ BOOL top_down; + else if (view->protect & VPROT_PLACEHOLDER) + TRACE( " (placeholder)\n" ); +@@ -1274,6 +1277,8 @@ struct alloc_area + int unix_prot; + BOOL top_down; + UINT_PTR align_mask; + char *native_mapped; + size_t native_mapped_size; -+}; + }; /*********************************************************************** - * try_map_free_area -@@ -1223,21 +1236,27 @@ static struct file_view *find_view_range( const void *addr, size_t size ) +@@ -1282,21 +1287,28 @@ struct alloc_area * Try mmaping some expected free memory region, eventually stepping and * retrying inside it, and return where it actually succeeded, or NULL. */ @@ -56,6 +48,7 @@ index 4899fc7b443..cee739bd942 100644 +static void* try_map_free_area( struct alloc_area *area, void *base, void *end, void *start ) { + ptrdiff_t step = area->step; ++ UINT_PTR abs_step = step > 0 ? step : -step; void *ptr; - while (start && base <= start && (char*)start + size <= (char*)end) @@ -71,54 +64,40 @@ index 4899fc7b443..cee739bd942 100644 + strerror(errno), start, (char *)start + area->size, area->unix_prot ); return NULL; } -+ if (!area->native_mapped && step) ++ if (!area->native_mapped && step && abs_step < (granularity_mask + 1) * 2) + { + area->native_mapped = start; -+ area->native_mapped_size = step > 0 ? step : -step; ++ area->native_mapped_size = abs_step; + area->native_mapped_size = min(area->native_mapped_size, (char *)end - (char *)start); + } if ((step > 0 && (char *)end - (char *)start < step) || (step < 0 && (char *)start - (char *)base < -step) || step == 0) -@@ -1666,15 +1685,6 @@ static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t want - } - - --struct alloc_area --{ -- char *map_area_start, *map_area_end, *result; -- size_t size; -- ptrdiff_t step; -- int unix_prot; -- BOOL top_down; --}; -- - static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, void *arg ) - { - char *intersect_start, *intersect_end; -@@ -1700,8 +1710,8 @@ static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, +@@ -1797,9 +1809,9 @@ static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, + assert(intersect_start <= intersect_end); if (area->map_area_end - intersect_end >= area->size) { - alloc_start = ROUND_ADDR( (char *)area->map_area_end - size, granularity_mask ); +- alloc_start = ROUND_ADDR( (char *)area->map_area_end - size, align_mask ); - if ((area->result = try_map_free_area( intersect_end, alloc_start + size, area->step, - alloc_start, area->size, area->unix_prot ))) ++ alloc_start = ROUND_ADDR( (char *)area->map_area_end - area->size, align_mask ); + if ((area->result = try_map_free_area( area, intersect_end, + alloc_start + size, alloc_start ))) return 1; } -@@ -1736,8 +1746,8 @@ static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, - +@@ -1838,8 +1850,8 @@ static int alloc_area_in_reserved_or_between_callback( void *start, SIZE_T size, if (intersect_start - area->map_area_start >= area->size) { + alloc_start = ROUND_ADDR( area->map_area_start + align_mask, align_mask ); - if ((area->result = try_map_free_area( area->map_area_start, intersect_start, area->step, -- area->map_area_start, area->size, area->unix_prot ))) +- alloc_start, area->size, area->unix_prot ))) + if ((area->result = try_map_free_area( area, area->map_area_start, -+ intersect_start, area->map_area_start ))) ++ intersect_start, alloc_start ))) return 1; } -@@ -1792,8 +1802,7 @@ static void *alloc_free_area_in_range( struct alloc_area *area, char *base, char +@@ -1900,8 +1912,7 @@ static void *alloc_free_area_in_range( struct alloc_area *area, char *base, char if (start >= area->map_area_end || start < area->map_area_start) return NULL; @@ -128,7 +107,7 @@ index 4899fc7b443..cee739bd942 100644 } else { -@@ -1802,8 +1811,7 @@ static void *alloc_free_area_in_range( struct alloc_area *area, char *base, char +@@ -1911,8 +1922,7 @@ static void *alloc_free_area_in_range( struct alloc_area *area, char *base, char || area->map_area_end - start < area->size) return NULL; @@ -138,7 +117,7 @@ index 4899fc7b443..cee739bd942 100644 } } -@@ -1813,6 +1821,7 @@ static void *alloc_free_area( void *limit, size_t size, BOOL top_down, int unix_ +@@ -1922,6 +1932,7 @@ static void *alloc_free_area( void *limit, size_t size, BOOL top_down, int unix_ char *reserve_start, *reserve_end; struct alloc_area area; char *base, *end; @@ -146,20 +125,9 @@ index 4899fc7b443..cee739bd942 100644 int ranges_inc; TRACE("limit %p, size %p, top_down %#x.\n", limit, (void *)size, top_down); -@@ -1868,16 +1877,67 @@ static void *alloc_free_area( void *limit, size_t size, BOOL top_down, int unix_ - { - /* range is split in two by the preloader reservation, try first part. */ - if ((area.result = alloc_free_area_in_range( &area, base, reserve_start ))) -- return area.result; -+ break; - /* then fall through to try second part. */ - base = reserve_end; - } - } - +@@ -1991,7 +2002,58 @@ static void *alloc_free_area( void *limit, size_t size, BOOL top_down, int unix_ if ((area.result = alloc_free_area_in_range( &area, base, end ))) -- return area.result; -+ break; + break; } - return NULL; + @@ -217,7 +185,7 @@ index 4899fc7b443..cee739bd942 100644 } /*********************************************************************** -@@ -1931,6 +1991,17 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot ) +@@ -2045,6 +2107,17 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot ) return STATUS_SUCCESS; } @@ -235,21 +203,20 @@ index 4899fc7b443..cee739bd942 100644 /*********************************************************************** * map_view * -@@ -1954,7 +2025,12 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, - else if (!(ptr = alloc_free_area( (void*)(get_zero_bits_mask( zero_bits ) - & (UINT_PTR)user_space_limit), size, top_down, get_unix_prot( vprot ) ))) +@@ -2097,7 +2170,11 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, + } + else if (!(ptr = alloc_free_area( (void *)limit, size, top_down, get_unix_prot( vprot ), align_mask ))) { - return STATUS_NO_MEMORY; + WARN("Allocation failed, clearing native views.\n"); + + clear_native_views(); -+ if (!(ptr = alloc_free_area( (void*)(get_zero_bits_mask( zero_bits ) -+ & (UINT_PTR)user_space_limit), size, top_down, get_unix_prot( vprot ) ))) ++ if (!(ptr = alloc_free_area( (void *)limit, size, top_down, get_unix_prot( vprot ), align_mask ))) + return STATUS_NO_MEMORY; } status = create_view( view_ret, ptr, size, vprot ); if (status != STATUS_SUCCESS) unmap_area( ptr, size ); -@@ -3646,7 +3722,12 @@ void virtual_set_force_exec( BOOL enable ) +@@ -3835,7 +3912,12 @@ void virtual_set_force_exec( BOOL enable ) WINE_RB_FOR_EACH_ENTRY( view, &views_tree, struct file_view, entry ) { /* file mappings are always accessible */ @@ -264,5 +231,5 @@ index 4899fc7b443..cee739bd942 100644 mprotect_range( view->base, view->size, commit, 0 ); } -- -2.30.2 +2.38.1 diff --git a/patches/ntdll-ForceBottomUpAlloc/definition b/patches/ntdll-ForceBottomUpAlloc/definition index db90605f..92b63d54 100644 --- a/patches/ntdll-ForceBottomUpAlloc/definition +++ b/patches/ntdll-ForceBottomUpAlloc/definition @@ -1,3 +1,3 @@ Fixes: [48175] AION (64 bit) - crashes in crysystem.dll.CryFree() due to high memory pointers allocated Fixes: [46568] 64-bit msxml6.dll from Microsoft Core XML Services 6.0 redist package fails to load (Wine doesn't respect 44-bit user-mode VA limitation from Windows < 8.1) -Disabled: True +Depends: ntdll-Placeholders diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index e028f409..c62db07d 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -133,6 +133,7 @@ patch_enable_all () enable_ntdll_Builtin_Prot="$1" enable_ntdll_CriticalSection="$1" enable_ntdll_Exception="$1" + enable_ntdll_ForceBottomUpAlloc="$1" enable_ntdll_HashLinks="$1" enable_ntdll_Hide_Wine_Exports="$1" enable_ntdll_Junction_Points="$1" @@ -413,6 +414,9 @@ patch_enable () ntdll-Exception) enable_ntdll_Exception="$2" ;; + ntdll-ForceBottomUpAlloc) + enable_ntdll_ForceBottomUpAlloc="$2" + ;; ntdll-HashLinks) enable_ntdll_HashLinks="$2" ;; @@ -1225,6 +1229,13 @@ if test "$enable_nvapi_Stub_DLL" -eq 1; then enable_nvcuda_CUDA_Support=1 fi +if test "$enable_ntdll_ForceBottomUpAlloc" -eq 1; then + if test "$enable_ntdll_Placeholders" -gt 1; then + abort "Patchset ntdll-Placeholders disabled, but ntdll-ForceBottomUpAlloc depends on that." + fi + enable_ntdll_Placeholders=1 +fi + if test "$enable_ntdll_Builtin_Prot" -eq 1; then if test "$enable_ntdll_WRITECOPY" -gt 1; then abort "Patchset ntdll-WRITECOPY disabled, but ntdll-Builtin_Prot depends on that." @@ -2142,6 +2153,52 @@ if test "$enable_ntdll_Exception" -eq 1; then patch_apply ntdll-Exception/0002-ntdll-OutputDebugString-should-throw-the-exception-a.patch fi +# Patchset ntdll-Placeholders +# | +# | Modified files: +# | * dlls/kernelbase/memory.c, dlls/kernelbase/tests/process.c, dlls/ntdll/tests/virtual.c, dlls/ntdll/unix/server.c, +# | dlls/ntdll/unix/virtual.c, server/protocol.def +# | +if test "$enable_ntdll_Placeholders" -eq 1; then + patch_apply ntdll-Placeholders/0001-ntdll-tests-Add-tests-for-freeing-a-part-of-view.patch + patch_apply ntdll-Placeholders/0002-kernelbase-Validate-nonzero-size-for-MEM_RELEASE-in-.patch + patch_apply ntdll-Placeholders/0003-ntdll-Fix-size-validation-in-NtFreeVirtualMemory.patch + patch_apply ntdll-Placeholders/0004-ntdll-Fully-support-unaligned-views-in-free-ranges-m.patch + patch_apply ntdll-Placeholders/0005-ntdll-Factor-out-some-view-manipulation-functions.patch + patch_apply ntdll-Placeholders/0006-ntdll-Support-partial-view-release-in-NtFreeVirtualM.patch + patch_apply ntdll-Placeholders/0007-ntdll-Add-logging-for-free-ranges.patch + patch_apply ntdll-Placeholders/0008-ntdll-Handle-NULL-process-handle-in-MapViewOfFile3.patch + patch_apply ntdll-Placeholders/0009-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtFreeVirt.patch + patch_apply ntdll-Placeholders/0010-ntdll-Pass-allocation-type-to-map_view.patch + patch_apply ntdll-Placeholders/0011-ntdll-Support-MEM_RESERVE_PLACEHOLDER-in-NtAllocateV.patch + patch_apply ntdll-Placeholders/0012-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-NtAllocateV.patch + patch_apply ntdll-Placeholders/0013-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-virtual_map.patch + patch_apply ntdll-Placeholders/0014-ntdll-tests-Add-more-tests-for-placeholders.patch + patch_apply ntdll-Placeholders/0015-ntdll-Support-MEM_COALESCE_PLACEHOLDERS-in-NtFreeVir.patch + patch_apply ntdll-Placeholders/0016-ntdll-Factor-out-unmap_view_of_section-function.patch + patch_apply ntdll-Placeholders/0017-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtUnmapVie.patch +fi + +# Patchset ntdll-ForceBottomUpAlloc +# | +# | This patchset has the following (direct or indirect) dependencies: +# | * ntdll-Placeholders +# | +# | This patchset fixes the following Wine bugs: +# | * [#48175] AION (64 bit) - crashes in crysystem.dll.CryFree() due to high memory pointers allocated +# | * [#46568] 64-bit msxml6.dll from Microsoft Core XML Services 6.0 redist package fails to load (Wine doesn't respect +# | 44-bit user-mode VA limitation from Windows < 8.1) +# | +# | Modified files: +# | * dlls/ntdll/unix/virtual.c +# | +if test "$enable_ntdll_ForceBottomUpAlloc" -eq 1; then + patch_apply ntdll-ForceBottomUpAlloc/0001-ntdll-Increase-step-after-failed-map-attempt-in-try_.patch + patch_apply ntdll-ForceBottomUpAlloc/0002-ntdll-Increase-free-ranges-view-block-size-on-64-bit.patch + patch_apply ntdll-ForceBottomUpAlloc/0003-ntdll-Force-virtual-memory-allocation-order.patch + patch_apply ntdll-ForceBottomUpAlloc/0004-ntdll-Exclude-natively-mapped-areas-from-free-areas-.patch +fi + # Patchset ntdll-HashLinks # | # | Modified files: @@ -2195,32 +2252,6 @@ if test "$enable_ntdll_NtSetLdtEntries" -eq 1; then patch_apply ntdll-NtSetLdtEntries/0002-libs-wine-Allow-to-modify-reserved-LDT-entries.patch fi -# Patchset ntdll-Placeholders -# | -# | Modified files: -# | * dlls/kernelbase/memory.c, dlls/kernelbase/tests/process.c, dlls/ntdll/tests/virtual.c, dlls/ntdll/unix/server.c, -# | dlls/ntdll/unix/virtual.c, server/protocol.def -# | -if test "$enable_ntdll_Placeholders" -eq 1; then - patch_apply ntdll-Placeholders/0001-ntdll-tests-Add-tests-for-freeing-a-part-of-view.patch - patch_apply ntdll-Placeholders/0002-kernelbase-Validate-nonzero-size-for-MEM_RELEASE-in-.patch - patch_apply ntdll-Placeholders/0003-ntdll-Fix-size-validation-in-NtFreeVirtualMemory.patch - patch_apply ntdll-Placeholders/0004-ntdll-Fully-support-unaligned-views-in-free-ranges-m.patch - patch_apply ntdll-Placeholders/0005-ntdll-Factor-out-some-view-manipulation-functions.patch - patch_apply ntdll-Placeholders/0006-ntdll-Support-partial-view-release-in-NtFreeVirtualM.patch - patch_apply ntdll-Placeholders/0007-ntdll-Add-logging-for-free-ranges.patch - patch_apply ntdll-Placeholders/0008-ntdll-Handle-NULL-process-handle-in-MapViewOfFile3.patch - patch_apply ntdll-Placeholders/0009-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtFreeVirt.patch - patch_apply ntdll-Placeholders/0010-ntdll-Pass-allocation-type-to-map_view.patch - patch_apply ntdll-Placeholders/0011-ntdll-Support-MEM_RESERVE_PLACEHOLDER-in-NtAllocateV.patch - patch_apply ntdll-Placeholders/0012-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-NtAllocateV.patch - patch_apply ntdll-Placeholders/0013-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-virtual_map.patch - patch_apply ntdll-Placeholders/0014-ntdll-tests-Add-more-tests-for-placeholders.patch - patch_apply ntdll-Placeholders/0015-ntdll-Support-MEM_COALESCE_PLACEHOLDERS-in-NtFreeVir.patch - patch_apply ntdll-Placeholders/0016-ntdll-Factor-out-unmap_view_of_section-function.patch - patch_apply ntdll-Placeholders/0017-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtUnmapVie.patch -fi - # Patchset ntdll-ProcessQuotaLimits # | # | This patchset fixes the following Wine bugs: