[PATCH] ntdll: Add a shortcut to NtReadVirtualMemory for the local process

Andrew Wesie awesie at gmail.com
Tue Oct 29 15:42:15 CDT 2019


On Tue, Oct 29, 2019 at 9:04 AM Martin Storsjo <martin at martin.st> wrote:
> ...
> Therefore, exception handling in executables with libunwind on i686
> doesn't work when run with wine within docker.

I personally don't think this is a great rationale for changing the
behavior of wine. It sounds like a rationale for docker to allow
SYS_PTRACE. But I am not a wine maintainer, so my only real concern is
that the correctness of NtReadVirtualMemory is not affected as it is
used by anti-cheat and other programs.

> ...
> +            server_enter_uninterrupted_section( &csVirtual, &sigset );
> +            if (virtual_check_buffer_for_read( addr, size ))
> +            {
> +                memcpy(buffer, addr, size);
> +                status = STATUS_SUCCESS;
> +            }

This does not have the same semantics as the original code. The
original code used kernel system calls (e.g. pread) which will not
cause a page fault. virtual_check_buffer_for_read explicitly reads
each page to trigger a fault. At a minimum, this causes the behavior
to diverge from Windows for guard pages. It may have other differences
as well.

A potential fix may be to implement something similar to
virtual_uninterrupted_read_memory. Or use kernel system calls (e.g.
read, write, pread, etc.) to access the memory, which will not cause a
page fault. In any case, I think more testing is needed.

Here is a sample program using VPROT_GUARD that triggers divergent behavior:

#include <windows.h>
#include <stdio.h>

typedef NTSTATUS WINAPI (*pNtReadVirtualMemory)(
        HANDLE, const void *, void *, SIZE_T, SIZE_T *);

int main()
{
    char tmp[64];
    char *p;
    HANDLE ntdll;
    NTSTATUS status;
    SIZE_T bytes_read;

    ntdll = GetModuleHandle("ntdll");
    pNtReadVirtualMemory NtReadVirtualMemory =
        (pNtReadVirtualMemory)GetProcAddress(ntdll, "NtReadVirtualMemory");

    p = (char *)VirtualAlloc(NULL, 0x10000, MEM_COMMIT | MEM_RESERVE,
        PAGE_GUARD | PAGE_READWRITE);
    printf("p = %p\n", p);

    status = NtReadVirtualMemory(GetCurrentProcess(), p, tmp, sizeof(tmp),
        &bytes_read);
    printf("status = %x\n", status);

    status = NtReadVirtualMemory(GetCurrentProcess(), p, tmp, sizeof(tmp),
        &bytes_read);
    printf("status = %x\n", status);
    return 0;
}

-Andrew



More information about the wine-devel mailing list