[PATCH] ws2_32: Don't post completion packet if receiving fails with error immediately (v3).

Stefan Dösinger stefan at codeweavers.com
Mon May 15 12:06:32 CDT 2017


This fixes bug 38980.

Signed-off-by: Stefan Dösinger <stefan at codeweavers.com>

---

Version 3: Add a test triggering WSAENOTCONN to have a non-todo test
that uses the changed codepath.

Version 2: Use select() instead of Sleep() to ensure data is ready to
read. Extend some failure messages to print the last error and remove a
few redundant SetLastError calls.

This patch is the equivalent to 1bbe92e7 for receiving operations.
Application in bug 38980 uses libtorrent, which in turn uses boost's
async IO classes. Boost starts IO operations and waits for their results
via IOCP. If the operation fails right away it enqueues a completion
status itself. By failing and enquing a status we broke Boost's
receiving logic - after the first status was read the internal data was
reused elsewhere (though the pointer was not freed). The second IOCP
result caused boost to call libtorrent's callback function with bad
parameters, ultimately causing a segfault or assertion violation in
libtorrent.
---
 dlls/ws2_32/socket.c     |   2 -
 dlls/ws2_32/tests/sock.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 162 insertions(+), 2 deletions(-)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index f6db83206a..e464b58e3f 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -7744,9 +7744,7 @@ static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
 
             if (errno != EAGAIN)
             {
-                int loc_errno = errno;
                 err = wsaErrno();
-                if (cvalue) WS_AddCompletion( s, cvalue, sock_get_ntstatus(loc_errno), 0 );
                 goto error;
             }
         }
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 023eb423b8..57455c0df4 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -9184,6 +9184,7 @@ static void test_completion_port(void)
     struct sockaddr_in bindAddress;
     GUID acceptExGuid = WSAID_ACCEPTEX;
     LPFN_ACCEPTEX pAcceptEx = NULL;
+    fd_set fds_recv;
 
     memset(buf, 0, sizeof(buf));
     previous_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
@@ -9299,6 +9300,167 @@ static void test_completion_port(void)
     if (dest != INVALID_SOCKET)
         closesocket(dest);
 
+    /* Test IOCP response on successful immediate read. */
+    tcp_socketpair(&src, &dest);
+    if (src == INVALID_SOCKET || dest == INVALID_SOCKET)
+    {
+        skip("failed to create sockets\n");
+        goto end;
+    }
+
+    bufs.len = sizeof(buf);
+    bufs.buf = buf;
+    flags = 0;
+    SetLastError(0xdeadbeef);
+
+    iret = WSASend(src, &bufs, 1, &num_bytes, 0, &ov, NULL);
+    ok(!iret, "WSASend failed - %d, last error %u\n", iret, GetLastError());
+    ok(num_bytes == sizeof(buf), "Managed to send %d\n", num_bytes);
+
+    io_port = CreateIoCompletionPort((HANDLE)dest, previous_port, 125, 0);
+    ok(io_port != NULL, "failed to create completion port %u\n", GetLastError());
+    set_blocking(dest, FALSE);
+
+    FD_ZERO(&fds_recv);
+    FD_SET(dest, &fds_recv);
+    select(dest + 1, &fds_recv, NULL, NULL, NULL);
+
+    num_bytes = 0xdeadbeef;
+    flags = 0;
+
+    iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL);
+    ok(!iret, "WSARecv failed - %d, last error %u\n", iret, GetLastError());
+    ok(num_bytes == sizeof(buf), "Managed to read %d\n", num_bytes);
+
+    SetLastError(0xdeadbeef);
+    key = 0xdeadbeef;
+    num_bytes = 0xdeadbeef;
+    olp = (WSAOVERLAPPED *)0xdeadbeef;
+
+    bret = GetQueuedCompletionStatus( io_port, &num_bytes, &key, &olp, 200 );
+    ok(bret == TRUE, "failed to get completion status %u\n", bret);
+    ok(GetLastError() == 0xdeadbeef, "Last error was %d\n", GetLastError());
+    ok(key == 125, "Key is %lu\n", key);
+    ok(num_bytes == sizeof(buf), "Number of bytes transferred is %u\n", num_bytes);
+    ok(olp == &ov, "Overlapped structure is at %p\n", olp);
+
+    /* Test IOCP response on graceful shutdown. */
+    closesocket(src);
+
+    FD_ZERO(&fds_recv);
+    FD_SET(dest, &fds_recv);
+    select(dest + 1, &fds_recv, NULL, NULL, NULL);
+
+    num_bytes = 0xdeadbeef;
+    flags = 0;
+    memset(&ov, 0, sizeof(ov));
+
+    iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL);
+    ok(!iret, "WSARecv failed - %d, last error %u\n", iret, GetLastError());
+    ok(!num_bytes, "Managed to read %d\n", num_bytes);
+
+    SetLastError(0xdeadbeef);
+    key = 0xdeadbeef;
+    num_bytes = 0xdeadbeef;
+    olp = (WSAOVERLAPPED *)0xdeadbeef;
+
+    bret = GetQueuedCompletionStatus( io_port, &num_bytes, &key, &olp, 200 );
+    ok(bret == TRUE, "failed to get completion status %u\n", bret);
+    ok(GetLastError() == 0xdeadbeef, "Last error was %d\n", GetLastError());
+    ok(key == 125, "Key is %lu\n", key);
+    ok(!num_bytes, "Number of bytes transferred is %u\n", num_bytes);
+    ok(olp == &ov, "Overlapped structure is at %p\n", olp);
+
+    closesocket(src);
+    src = INVALID_SOCKET;
+    closesocket(dest);
+    dest = INVALID_SOCKET;
+
+    /* Test IOCP response on hard shutdown. This was the condition that triggered
+     * a crash in an actual app (bug 38980). */
+    tcp_socketpair(&src, &dest);
+    if (src == INVALID_SOCKET || dest == INVALID_SOCKET)
+    {
+        skip("failed to create sockets\n");
+        goto end;
+    }
+
+    bufs.len = sizeof(buf);
+    bufs.buf = buf;
+    flags = 0;
+    memset(&ov, 0, sizeof(ov));
+
+    ling.l_onoff = 1;
+    ling.l_linger = 0;
+    iret = setsockopt (src, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
+    ok(!iret, "Failed to set linger %d\n", GetLastError());
+
+    io_port = CreateIoCompletionPort((HANDLE)dest, previous_port, 125, 0);
+    ok(io_port != NULL, "failed to create completion port %u\n", GetLastError());
+    set_blocking(dest, FALSE);
+
+    closesocket(src);
+    src = INVALID_SOCKET;
+
+    FD_ZERO(&fds_recv);
+    FD_SET(dest, &fds_recv);
+    select(dest + 1, &fds_recv, NULL, NULL, NULL);
+
+    num_bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+
+    /* Somehow a hard shutdown doesn't work on my Linux box. It seems SO_LINGER is ignored. */
+    iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL);
+    todo_wine ok(iret == SOCKET_ERROR, "WSARecv failed - %d\n", iret);
+    todo_wine ok(GetLastError() == WSAECONNRESET, "Last error was %d\n", GetLastError());
+    todo_wine ok(num_bytes == 0xdeadbeef, "Managed to read %d\n", num_bytes);
+
+    SetLastError(0xdeadbeef);
+    key = 0xdeadbeef;
+    num_bytes = 0xdeadbeef;
+    olp = (WSAOVERLAPPED *)0xdeadbeef;
+
+    bret = GetQueuedCompletionStatus( io_port, &num_bytes, &key, &olp, 200 );
+    todo_wine ok(bret == FALSE, "GetQueuedCompletionStatus returned %u\n", bret );
+    todo_wine ok(GetLastError() == WAIT_TIMEOUT, "Last error was %d\n", GetLastError());
+    todo_wine ok(key == 0xdeadbeef, "Key is %lu\n", key);
+    todo_wine ok(num_bytes == 0xdeadbeef, "Number of bytes transferred is %u\n", num_bytes);
+    todo_wine ok(!olp, "Overlapped structure is at %p\n", olp);
+
+    closesocket(dest);
+
+    /* Test reading from a non-connected socket, mostly because the above test is marked todo. */
+    dest = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(dest != INVALID_SOCKET, "socket() failed\n");
+
+    io_port = CreateIoCompletionPort((HANDLE)dest, previous_port, 125, 0);
+    ok(io_port != NULL, "failed to create completion port %u\n", GetLastError());
+    set_blocking(dest, FALSE);
+
+    num_bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    memset(&ov, 0, sizeof(ov));
+
+    iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL);
+    ok(iret == SOCKET_ERROR, "WSARecv failed - %d\n", iret);
+    ok(GetLastError() == WSAENOTCONN, "Last error was %d\n", GetLastError());
+    ok(num_bytes == 0xdeadbeef, "Managed to read %d\n", num_bytes);
+
+    SetLastError(0xdeadbeef);
+    key = 0xdeadbeef;
+    num_bytes = 0xdeadbeef;
+    olp = (WSAOVERLAPPED *)0xdeadbeef;
+
+    bret = GetQueuedCompletionStatus( io_port, &num_bytes, &key, &olp, 200 );
+    ok(bret == FALSE, "GetQueuedCompletionStatus returned %u\n", bret );
+    ok(GetLastError() == WAIT_TIMEOUT, "Last error was %d\n", GetLastError());
+    ok(key == 0xdeadbeef, "Key is %lu\n", key);
+    ok(num_bytes == 0xdeadbeef, "Number of bytes transferred is %u\n", num_bytes);
+    ok(!olp, "Overlapped structure is at %p\n", olp);
+
+    num_bytes = 0xdeadbeef;
+    closesocket(dest);
+
     dest = socket(AF_INET, SOCK_STREAM, 0);
     if (dest == INVALID_SOCKET)
     {
-- 
2.13.0




More information about the wine-patches mailing list