Skip to content

Commit 3c58ecd

Browse files
committed
Fix bug with send() on Windows in O_NONBLOCK mode
There is a bug in WIN32 where using CancelIoEx() on an overlapped i/o op initiated by WSASend() will cause WSAGetOverlappedResult() to report the operation failed when it actually succeeded. We now work around that, by having send and sendto initially consult WSAPoll() on O_NONBLOCK sockets
1 parent 5aa970b commit 3c58ecd

File tree

4 files changed

+22
-5
lines changed

4 files changed

+22
-5
lines changed

libc/sock/internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *,
6565

6666
size_t __iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *, size_t);
6767

68-
ssize_t __winsock_block(int64_t, uint32_t, bool, uint32_t, uint64_t,
68+
ssize_t __winsock_block(int64_t, uint32_t, int, uint32_t, uint64_t,
6969
int (*)(int64_t, struct NtOverlapped *, uint32_t *,
7070
void *),
7171
void *);

libc/sock/send-nt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
5656
sigset_t m = __sig_block();
5757
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
5858
flags &= ~_MSG_DONTWAIT;
59-
rc = __winsock_block(f->handle, flags, nonblock, f->sndtimeo, m,
59+
rc = __winsock_block(f->handle, flags, -nonblock, f->sndtimeo, m,
6060
sys_send_nt_start, &(struct SendArgs){iov, iovlen});
6161
__sig_unblock(m);
6262
return rc;

libc/sock/sendto-nt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
6060
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
6161
flags &= ~_MSG_DONTWAIT;
6262
rc = __winsock_block(
63-
f->handle, flags, nonblock, f->sndtimeo, m, sys_sendto_nt_start,
63+
f->handle, flags, -nonblock, f->sndtimeo, m, sys_sendto_nt_start,
6464
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
6565
__sig_unblock(m);
6666
return rc;

libc/sock/winsockblock.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,19 @@
3030
#include "libc/nt/events.h"
3131
#include "libc/nt/runtime.h"
3232
#include "libc/nt/struct/overlapped.h"
33+
#include "libc/nt/struct/pollfd.h"
3334
#include "libc/nt/synchronization.h"
3435
#include "libc/nt/thread.h"
3536
#include "libc/nt/winsock.h"
3637
#include "libc/sock/internal.h"
38+
#include "libc/sysv/consts/poll.h"
3739
#include "libc/sysv/consts/sicode.h"
3840
#include "libc/sysv/errfuns.h"
3941
#include "libc/thread/posixthread.internal.h"
4042
#ifdef __x86_64__
4143

4244
textwindows ssize_t
43-
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
45+
__winsock_block(int64_t handle, uint32_t flags, int nonblock,
4446
uint32_t srwtimeout, sigset_t waitmask,
4547
int StartSocketOp(int64_t handle, struct NtOverlapped *overlap,
4648
uint32_t *flags, void *arg),
@@ -61,6 +63,21 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock,
6163
bool got_eagain = false;
6264
uint32_t other_error = 0;
6365

66+
// send() and sendto() provide O_NONBLOCK as a negative number
67+
// because winsock has a bug that causes CancelIoEx() to cause
68+
// WSAGetOverlappedResult() to report errors when it succeeded
69+
if (nonblock < 0) {
70+
struct sys_pollfd_nt fds[1] = {{handle, POLLOUT}};
71+
switch (WSAPoll(fds, 1, 0)) {
72+
case -1:
73+
return __winsockerr();
74+
case 0:
75+
return eagain();
76+
default:
77+
break;
78+
}
79+
}
80+
6481
// create event handle for overlapped i/o
6582
intptr_t event;
6683
if (!(event = WSACreateEvent()))
@@ -69,7 +86,7 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock,
6986
struct NtOverlapped overlap = {.hEvent = event};
7087
bool32 ok = !StartSocketOp(handle, &overlap, &flags, arg);
7188
if (!ok && WSAGetLastError() == kNtErrorIoPending) {
72-
if (nonblock) {
89+
if (nonblock > 0) {
7390
CancelIoEx(handle, &overlap);
7491
got_eagain = true;
7592
} else {

0 commit comments

Comments
 (0)