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

Stefan Dösinger stefan at codeweavers.com
Mon May 8 14:41:00 CDT 2017


This fixes bug 38980.

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.

Signed-off-by: Stefan Dösinger <stefan at codeweavers.com>
---
 dlls/ws2_32/socket.c     |   2 -
 dlls/ws2_32/tests/sock.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+), 2 deletions(-)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 9d27fab..0950754 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 023eb42..7fefff5 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -9299,6 +9299,133 @@ 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;
+
+    iret = WSASend(src, &bufs, 1, &num_bytes, 0, &ov, NULL);
+    ok(!iret, "WSASend failed - %d\n", iret);
+    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);
+
+    Sleep(100);
+
+    num_bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    flags = 0;
+
+    iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL);
+    ok(!iret, "WSARecv failed - %d\n", iret);
+    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);
+    Sleep(100);
+
+    num_bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    flags = 0;
+    memset(&ov, 0, sizeof(ov));
+
+    iret = WSARecv(dest, &bufs, 1, &num_bytes, &flags, &ov, NULL);
+    ok(!iret, "WSARecv failed - %d\n", iret);
+    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. */
+    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;
+
+    Sleep(100);
+
+    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 send %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);
+
+    num_bytes = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+
+    closesocket(dest);
+
     dest = socket(AF_INET, SOCK_STREAM, 0);
     if (dest == INVALID_SOCKET)
     {
-- 
2.10.2




More information about the wine-patches mailing list