diff --git a/patches/ntdll-BitmaskAllocAreaSearch/0001-ntdll-Fix-free-area-search-outside-of-reserved-area-.patch b/patches/ntdll-BitmaskAllocAreaSearch/0001-ntdll-Fix-free-area-search-outside-of-reserved-area-.patch new file mode 100644 index 00000000..900eba3d --- /dev/null +++ b/patches/ntdll-BitmaskAllocAreaSearch/0001-ntdll-Fix-free-area-search-outside-of-reserved-area-.patch @@ -0,0 +1,165 @@ +From 19635dea9206b1606ad0ac53c99205d48bc2ee41 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 24 Oct 2019 20:17:36 +0300 +Subject: [PATCH] ntdll: Fix free area search outside of reserved area in + map_view() for non-zero bitmask. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Patch includes logic changes suggested and edited by RĂ©mi Bernon. +--- + dlls/ntdll/virtual.c | 90 ++++++++++++++++++++++++++++++++------------ + 1 file changed, 66 insertions(+), 24 deletions(-) + +diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c +index 72ee2cade3..25fbe3b0b1 100644 +--- a/dlls/ntdll/virtual.c ++++ b/dlls/ntdll/virtual.c +@@ -543,6 +543,24 @@ static struct file_view *find_view_range( const void *addr, size_t size ) + return NULL; + } + ++/*********************************************************************** ++ * try_map_free_area ++ * ++ * Try mmaping some expected free memory region, and return whether it ++ * succeeded as expected. ++ */ ++static BOOL try_map_free_area( void *start, size_t size, int unix_prot ) ++{ ++ void *map_ptr; ++ ++ if ((map_ptr = wine_anon_mmap(start, size, unix_prot, 0)) == start) ++ return TRUE; ++ ++ if (map_ptr != (void *)-1) ++ munmap(map_ptr, size); ++ ++ return FALSE; ++} + + /*********************************************************************** + * find_free_area +@@ -550,7 +568,7 @@ static struct file_view *find_view_range( const void *addr, size_t size ) + * Find a free area between views inside the specified range. + * The csVirtual section must be held by caller. + */ +-static void *find_free_area( void *base, void *end, size_t size, size_t mask, int top_down ) ++static void *find_free_area( void *base, void *end, size_t size, size_t mask, int top_down, BOOL map, int unix_prot ) + { + struct wine_rb_entry *first = NULL, *ptr = views_tree.root; + void *start; +@@ -579,33 +597,53 @@ static void *find_free_area( void *base, void *end, size_t size, size_t mask, in + if (top_down) + { + start = ROUND_ADDR( (char *)end - size, mask ); +- if (start >= end || start < base) return NULL; + +- while (first) ++ for (;;) + { +- struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); ++ if (start >= end || start < base) return NULL; ++ ++ while (first) ++ { ++ struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); ++ ++ if ((char *)view->base + view->size <= (char *)start) break; ++ start = ROUND_ADDR( (char *)view->base - size, mask ); ++ /* stop if remaining space is not large enough */ ++ if (!start || start >= end || start < base) return NULL; ++ first = wine_rb_prev( first ); ++ } + +- if ((char *)view->base + view->size <= (char *)start) break; +- start = ROUND_ADDR( (char *)view->base - size, mask ); +- /* stop if remaining space is not large enough */ +- if (!start || start >= end || start < base) return NULL; +- first = wine_rb_prev( first ); ++ if (!map || try_map_free_area(start, size, unix_prot)) ++ break; ++ ++ TRACE("Found free area is already mapped, start %p.\n", start); ++ start = ROUND_ADDR((char *)start - 1, mask); + } + } + else + { + start = ROUND_ADDR( (char *)base + mask, mask ); +- if (!start || start >= end || (char *)end - (char *)start < size) return NULL; + +- while (first) ++ for (;;) + { +- struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); +- +- if ((char *)view->base >= (char *)start + size) break; +- start = ROUND_ADDR( (char *)view->base + view->size + mask, mask ); +- /* stop if remaining space is not large enough */ + if (!start || start >= end || (char *)end - (char *)start < size) return NULL; +- first = wine_rb_next( first ); ++ ++ while (first) ++ { ++ struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); ++ ++ if ((char *)view->base >= (char *)start + size) break; ++ start = ROUND_ADDR( (char *)view->base + view->size + mask, mask ); ++ /* stop if remaining space is not large enough */ ++ if (!start || start >= end || (char *)end - (char *)start < size) return NULL; ++ first = wine_rb_next( first ); ++ } ++ ++ if (!map || try_map_free_area(start, size, unix_prot)) ++ break; ++ ++ TRACE("Found free area is already mapped, start %p.\n", start); ++ start = (char *)start + mask + 1; + } + } + return start; +@@ -1101,13 +1139,13 @@ static int alloc_reserved_area_callback( void *start, size_t size, void *arg ) + { + /* range is split in two by the preloader reservation, try first part */ + if ((alloc->result = find_free_area( start, preload_reserve_start, alloc->size, +- alloc->mask, alloc->top_down ))) ++ alloc->mask, alloc->top_down, FALSE, 0 ))) + return 1; + /* then fall through to try second part */ + start = preload_reserve_end; + } + } +- if ((alloc->result = find_free_area( start, end, alloc->size, alloc->mask, alloc->top_down ))) ++ if ((alloc->result = find_free_area( start, end, alloc->size, alloc->mask, alloc->top_down, FALSE, 0 ))) + return 1; + + return 0; +@@ -1210,14 +1248,18 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, + goto done; + } + +- for (;;) ++ if (zero_bits_64) + { +- if (!zero_bits_64) +- ptr = NULL; +- else if (!(ptr = find_free_area( (void*)0, alloc.limit, view_size, mask, top_down ))) ++ if (!(ptr = find_free_area(address_space_start, alloc.limit, size, ++ mask, top_down, TRUE, VIRTUAL_GetUnixProt(vprot)))) + return STATUS_NO_MEMORY; ++ TRACE( "got mem with find_free_area %p-%p\n", ptr, (char *)ptr + size ); ++ goto done; ++ } + +- if ((ptr = wine_anon_mmap( ptr, view_size, VIRTUAL_GetUnixProt(vprot), ptr ? MAP_FIXED : 0 )) == (void *)-1) ++ for (;;) ++ { ++ if ((ptr = wine_anon_mmap(NULL, view_size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1) + { + if (errno == ENOMEM) return STATUS_NO_MEMORY; + return STATUS_INVALID_PARAMETER; +-- +2.23.0 + diff --git a/patches/ntdll-BitmaskAllocAreaSearch/definition b/patches/ntdll-BitmaskAllocAreaSearch/definition new file mode 100644 index 00000000..7d619a5b --- /dev/null +++ b/patches/ntdll-BitmaskAllocAreaSearch/definition @@ -0,0 +1,2 @@ +Fixes: [47974] X Rebirth: NtVirtualAlloc() does not find available memory with nonzero bitmask + diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 3808f41a..71770765 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -179,6 +179,7 @@ patch_enable_all () enable_ntdll_APC_Performance="$1" enable_ntdll_Activation_Context="$1" enable_ntdll_ApiSetMap="$1" + enable_ntdll_BitmaskAllocAreaSearch="$1" enable_ntdll_Builtin_Prot="$1" enable_ntdll_CriticalSection="$1" enable_ntdll_DOS_Attributes="$1" @@ -660,6 +661,9 @@ patch_enable () ntdll-ApiSetMap) enable_ntdll_ApiSetMap="$2" ;; + ntdll-BitmaskAllocAreaSearch) + enable_ntdll_BitmaskAllocAreaSearch="$2" + ;; ntdll-Builtin_Prot) enable_ntdll_Builtin_Prot="$2" ;; @@ -4521,6 +4525,21 @@ if test "$enable_ntdll_ApiSetMap" -eq 1; then ) >> "$patchlist" fi +# Patchset ntdll-BitmaskAllocAreaSearch +# | +# | This patchset fixes the following Wine bugs: +# | * [#47974] X Rebirth: NtVirtualAlloc() does not find available memory with nonzero bitmask +# | +# | Modified files: +# | * dlls/ntdll/virtual.c +# | +if test "$enable_ntdll_BitmaskAllocAreaSearch" -eq 1; then + patch_apply ntdll-BitmaskAllocAreaSearch/0001-ntdll-Fix-free-area-search-outside-of-reserved-area-.patch + ( + printf '%s\n' '+ { "Paul Gofman", "ntdll: Fix free area search outside of reserved area in map_view() for non-zero bitmask.", 1 },'; + ) >> "$patchlist" +fi + # Patchset ntdll-Builtin_Prot # | # | This patchset has the following (direct or indirect) dependencies: