We used this function to find free areas outside of the reserved range, and it's obviously incorrect as there can be some system or external memory mapping we don't know about.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/virtual.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index cb869feff02..ded00e3eb7d 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -496,14 +496,14 @@ static struct file_view *find_view_range( const void *addr, size_t size ) return NULL; }
- /*********************************************************************** - * find_free_area + * find_reserved_free_area * * Find a free area between views inside the specified range. * The csVirtual section must be held by caller. + * The range must be inside the preloader reserved range. */ -static void *find_free_area( void *base, void *end, size_t size, size_t mask, int top_down ) +static void *find_reserved_free_area( void *base, void *end, size_t size, size_t mask, int top_down ) { struct wine_rb_entry *first = NULL, *ptr = views_tree.root; void *start; @@ -1053,14 +1053,14 @@ static int alloc_reserved_area_callback( void *start, size_t size, void *arg ) else { /* 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 ))) + if ((alloc->result = find_reserved_free_area( start, preload_reserve_start, alloc->size, + alloc->mask, alloc->top_down ))) 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_reserved_free_area( start, end, alloc->size, alloc->mask, alloc->top_down ))) return 1;
return 0; @@ -1167,7 +1167,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, { if (!zero_bits_64) ptr = NULL; - else if (!(ptr = find_free_area( (void*)0, alloc.limit, view_size, mask, top_down ))) + else if (!(ptr = find_reserved_free_area( (void*)0, alloc.limit, view_size, mask, top_down ))) return STATUS_NO_MEMORY;
if ((ptr = wine_anon_mmap( ptr, view_size, VIRTUAL_GetUnixProt(vprot), ptr ? MAP_FIXED : 0 )) == (void *)-1)
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/virtual.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index ded00e3eb7d..505c84cfaad 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -496,17 +496,17 @@ static struct file_view *find_view_range( const void *addr, size_t size ) return NULL; }
+ /*********************************************************************** - * find_reserved_free_area + * find_view_inside_range * - * Find a free area between views inside the specified range. + * Find first (resp. last, if top_down) view inside a range. * The csVirtual section 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, size_t mask, int top_down ) +static void *find_view_inside_range( void **base_ptr, void **end_ptr, int top_down ) { struct wine_rb_entry *first = NULL, *ptr = views_tree.root; - void *start; + void *base = *base_ptr, *end = *end_ptr;
/* find the first (resp. last) view inside the range */ while (ptr) @@ -529,6 +529,24 @@ static void *find_reserved_free_area( void *base, void *end, size_t size, size_t } }
+ *base_ptr = base; + *end_ptr = end; + return first; +} + + +/*********************************************************************** + * find_reserved_free_area + * + * Find a free area between views inside the specified range. + * The csVirtual section 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, size_t mask, int top_down ) +{ + struct wine_rb_entry *first = find_view_inside_range( &base, &end, top_down ); + void *start; + if (top_down) { start = ROUND_ADDR( (char *)end - size, mask );
This introduces map_free_area function which tries mapping the expected free areas until it finds one that succeeds. It now also works for memory regions outside of the reserved region, but could probably be improved.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/virtual.c | 97 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index 505c84cfaad..382b4174795 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -535,6 +535,90 @@ static void *find_view_inside_range( void **base_ptr, void **end_ptr, int top_do }
+/*********************************************************************** + * try_map_free_area + * + * Try mmaping some expected free memory region, eventually stepping and + * retrying inside it, and return where it actually succeeded, or NULL. + */ +static void* try_map_free_area( void *base, void *end, ptrdiff_t step, + void *start, size_t size, int unix_prot ) +{ + void *ptr; + + while (start && base <= start && (char*)start + size <= (char*)end) + { + if ((ptr = wine_anon_mmap( start, size, unix_prot, 0 )) == start) + return start; + TRACE( "Found free area is already mapped, start %p.\n", start ); + + if (ptr != (void *)-1) + munmap( ptr, size ); + + if ((step > 0 && (char *)end - (char *)start < step) || + (step < 0 && (char *)start - (char *)base < -step) || + step == 0) + break; + start = (char *)start + step; + } + + return NULL; +} + + +/*********************************************************************** + * map_free_area + * + * Find a free area between views inside the specified range and map it. + * The csVirtual section must be held by caller. + */ +static void *map_free_area( void *base, void *end, size_t size, size_t mask, int top_down, + int unix_prot ) +{ + struct wine_rb_entry *first = find_view_inside_range( &base, &end, top_down ); + ptrdiff_t step = top_down ? -(mask + 1) : (mask + 1); + void *start; + + if (top_down) + { + start = ROUND_ADDR( (char *)end - size, mask ); + if (start >= end || start < base) return NULL; + + while (first) + { + 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, mask ); + /* stop if remaining space is not large enough */ + if (!start || start >= end || start < base) return NULL; + first = wine_rb_prev( first ); + } + } + else + { + start = ROUND_ADDR( (char *)base + mask, mask ); + if (!start || start >= end || (char *)end - (char *)start < size) return NULL; + + while (first) + { + 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 + 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 (!first) + return try_map_free_area( base, end, step, start, size, unix_prot ); + + return start; +} + + /*********************************************************************** * find_reserved_free_area * @@ -1181,14 +1265,17 @@ 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_reserved_free_area( (void*)0, alloc.limit, view_size, mask, top_down ))) + if (!(ptr = map_free_area( (void*)0, alloc.limit, size, mask, top_down, VIRTUAL_GetUnixProt(vprot) ))) return STATUS_NO_MEMORY; + TRACE( "got mem with map_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;
The search was initiated with base == 0, which returns NULL immediately if MEM_TOP_DOWN is not used. Use address_space_start instead.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47974 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/virtual.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index 382b4174795..a177adc886a 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -1267,7 +1267,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
if (zero_bits_64) { - if (!(ptr = map_free_area( (void*)0, alloc.limit, size, mask, top_down, VIRTUAL_GetUnixProt(vprot) ))) + if (!(ptr = map_free_area( address_space_start, alloc.limit, size, mask, top_down, VIRTUAL_GetUnixProt(vprot) ))) return STATUS_NO_MEMORY; TRACE( "got mem with map_free_area %p-%p\n", ptr, (char *)ptr + size ); goto done;