[PATCH 0/2] MR127: ws2_32: Make wait in select alertable

Jinoh Kang jinoh.kang.kr at gmail.com
Wed May 25 10:46:52 CDT 2022


On 5/25/22 22:30, Alexandre Julliard wrote:
>> We can wrap the IOSB writes in `set_async_iosb` inside a `__TRY` block from `wine/unixlib.h`.
> 
> That would only be hiding the bug.
> 

Handling the exception does not necessarily mean silencing the bug; we can flag the error condition via ERR().
Also, letting the IOSB leak won't make debugging much more pleasurable either IMHO:

1. There's no way to detect pulling the rug out under a pending I/O operation.
2. It causes hard-to-trace memory leak.

---
As a side note, Windows does ignore invalid memory accesses when writing IOSB.  On Windows, running the test program below results in the following:

    Test 1. Valid Status, Valid Information
    iosb->Status      = 0
    iosb->Information = 0x1
    
    Test 2. Valid Status, Faulting Information
    iosb->Status      = 0xcccccccc
    iosb->Information = 0xcccccccccccccccc
    
    Test 3. Faulting Status, Valid Information
    iosb->Status      = 0xcccccccc
    iosb->Information = 0x1
    
    Test 4. Faulting Status, Faulting Information
    iosb->Status      = 0xcccccccc
    iosb->Information = 0xcccccccccccccccc
    
>From the output above, we can infer two things:

1. Windows gives up writing IOSB and let the program continue as usual when it encounters invalid memory accesses.
2. Even if the Status field is invalid, Windows still successfully writes the Information field.

---
#define WIN32_LEAN_AND_MEAN
#define _UNICODE
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include <stdlib.h>

NTSYSAPI NTSTATUS  WINAPI NtReadFile(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,PVOID,ULONG,PLARGE_INTEGER,PULONG);;

void die_(int line, unsigned int errcode)
{
    fprintf(stderr, "error @ %d: %u (%#x)\n", line, errcode, errcode);
    exit(EXIT_FAILURE);
}
#define die() die_(__LINE__, GetLastError())

static void subtest(HANDLE read_pipe, HANDLE write_pipe, IO_STATUS_BLOCK *iosb, void *fault_page);

int wmain(void)
{
    HANDLE read_pipe, write_pipe;
    void *page_0, *page_1, *fault_page;
    const WCHAR pipe_name[] = L"\\\\.\\PIPE\\LOCAL\\faulty-iosb-test";
    IO_STATUS_BLOCK *iosb;

    read_pipe = CreateNamedPipeW(
        pipe_name,
        PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_ACCEPT_REMOTE_CLIENTS,
        1,
        4096,
        4096,
        0,
        NULL
    );
    if (read_pipe == INVALID_HANDLE_VALUE) die();

    write_pipe = CreateFile(
        pipe_name,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );
    if (write_pipe == INVALID_HANDLE_VALUE) die();

    page_0 = VirtualAlloc(NULL, 4096 * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (!page_0) die();
    page_1 = (char *)page_0 + 4096;

    puts("Test 1. Valid Status, Valid Information");
    iosb = (IO_STATUS_BLOCK *)page_0;
    fault_page = page_1;
    subtest(read_pipe, write_pipe, iosb, fault_page);

    puts("Test 2. Valid Status, Faulting Information");
    iosb = CONTAINING_RECORD((ULONG_PTR *)page_1, IO_STATUS_BLOCK, Information);
    fault_page = page_1;
    subtest(read_pipe, write_pipe, iosb, fault_page);

    puts("Test 3. Faulting Status, Valid Information");
    iosb = CONTAINING_RECORD((ULONG_PTR *)page_1, IO_STATUS_BLOCK, Information);
    fault_page = page_0;
    subtest(read_pipe, write_pipe, iosb, fault_page);

    puts("Test 4. Faulting Status, Faulting Information");
    iosb = (IO_STATUS_BLOCK *)page_0;
    fault_page = page_0;
    subtest(read_pipe, write_pipe, iosb, fault_page);

    return 0;
}

static void subtest(HANDLE read_pipe, HANDLE write_pipe, IO_STATUS_BLOCK *iosb, void *fault_page)
{
    DWORD old_prot;
    DWORD result;
    NTSTATUS status;
    char buffer[1];

    memset(iosb, 0xcc, sizeof(*iosb));
    status = NtReadFile(read_pipe, NULL, NULL, NULL, iosb, buffer, sizeof(buffer), NULL, NULL);
    if (status != STATUS_PENDING) die_(__LINE__, status);

    if (!VirtualProtect(fault_page, 4096, PAGE_NOACCESS, &old_prot)) die();

    if (!WriteFile(write_pipe, "", 1, &result, NULL)) die();
    if (WaitForSingleObject(read_pipe, INFINITE) != WAIT_OBJECT_0) die();

    if (!VirtualProtect(fault_page, 4096, old_prot, &old_prot)) die();

    printf("iosb->Status      = %#lx\n", (unsigned long)iosb->Status);
    printf("iosb->Information = %#Ix\n", iosb->Information);
    puts("");
}


-- 
Sincerely,
Jinoh Kang



More information about the wine-devel mailing list