LMDB and other applications create file-backed memory sections with SEC_RESERVE, mapping a view larger than the file and committing pages later with VirtualAlloc(MEM_COMMIT). This pattern fails in Wine because get_mapping_flags() strips SEC_RESERVE from file-backed sections, create_mapping() skips committed-range tracking for them, allocate_virtual_memory() rejects MEM_COMMIT on all SEC_FILE views, and both virtual_map_section() and the server's map_view handler reject oversized views. Fix all of these, and map only the file-backed portion of the view, leaving the excess as anonymous reserved address space. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> --- dlls/ntdll/unix/virtual.c | 13 ++++++++++--- server/mapping.c | 6 ++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 9297a5f..bec60ca 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -3550,7 +3550,8 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P if (*size_ptr) { size = *size_ptr; - if (size > full_size - offset.QuadPart) return STATUS_INVALID_VIEW_SIZE; + if (size > full_size - offset.QuadPart && !(sec_flags & SEC_RESERVE)) + return STATUS_INVALID_VIEW_SIZE; } else { @@ -3576,7 +3577,12 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P if (res) goto done; TRACE( "handle=%p size=%lx offset=%s\n", handle, size, wine_dbgstr_longlong(offset.QuadPart) ); - res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, needs_close ); + { + size_t map_size = size; + if ((sec_flags & SEC_RESERVE) && size > full_size - offset.QuadPart) + map_size = ROUND_SIZE( 0, full_size - offset.QuadPart, page_mask ); + res = map_file_into_view( view, unix_handle, 0, map_size, offset.QuadPart, vprot, needs_close ); + } if (res == STATUS_SUCCESS) { /* file mappings must always be accessible */ @@ -5149,7 +5155,8 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ else /* commit the pages */ { if (!(view = find_view( base, size ))) status = STATUS_NOT_MAPPED_VIEW; - else if (view->protect & SEC_FILE) status = STATUS_ALREADY_COMMITTED; + else if ((view->protect & SEC_FILE) && !(view->protect & SEC_RESERVE)) + status = STATUS_ALREADY_COMMITTED; else if (view->protect & VPROT_FREE_PLACEHOLDER) status = STATUS_CONFLICTING_ADDRESSES; else if (!(status = set_protection( view, base, size, protect )) && (view->protect & SEC_RESERVE)) { diff --git a/server/mapping.c b/server/mapping.c index f9a80bc..72291eb 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -1107,7 +1107,7 @@ static unsigned int get_mapping_flags( obj_handle_t handle, unsigned int flags ) /* fall through */ case SEC_RESERVE: if (flags & SEC_LARGE_PAGES) break; - if (handle) return SEC_FILE | (flags & (SEC_NOCACHE | SEC_WRITECOMBINE)); + if (handle) return SEC_FILE | (flags & (SEC_RESERVE | SEC_NOCACHE | SEC_WRITECOMBINE)); return flags; } set_error( STATUS_INVALID_PARAMETER ); @@ -1193,6 +1193,7 @@ static struct mapping *create_mapping( struct object *root, const struct unicode } if (!grow_file( unix_fd, mapping->size )) goto error; } + if ((flags & SEC_RESERVE) && !(mapping->committed = create_ranges())) goto error; } else /* Anonymous mapping (no associated file) */ { @@ -1706,7 +1707,8 @@ DECL_HANDLER(map_view) if ((mapping->flags & SEC_IMAGE) || req->start >= mapping->size || req->start + req->size < req->start || - req->start + req->size > round_size( mapping->size, page_mask )) + (!(mapping->flags & SEC_RESERVE) && + req->start + req->size > round_size( mapping->size, page_mask ))) { set_error( STATUS_INVALID_PARAMETER ); goto done; -- 2.53.0