Fix NT accept/connect not initializing with SO_UPDATE_*_CONTEXT (#1164)

This commit is contained in:
Gavin Hayes 2024-05-17 05:45:30 -04:00 committed by GitHub
parent 2f3c6e7cc3
commit 624119ea38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 173 additions and 8 deletions

View file

@ -87,10 +87,6 @@ static int sys_accept_nt_start(int64_t handle, struct NtOverlapped *overlap,
if (g_acceptex.lpAcceptEx(args->listensock, handle, args->buffer, 0,
sizeof(args->buffer->local),
sizeof(args->buffer->remote), 0, overlap)) {
// inherit properties of listening socket
unassert(!__imp_setsockopt(args->listensock, SOL_SOCKET,
kNtSoUpdateAcceptContext, &handle,
sizeof(handle)));
return 0;
} else {
return -1;
@ -123,6 +119,13 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
goto Finish;
}
// inherit properties of listening socket
// errors ignored as if f->handle was created before forking
// this fails with WSAENOTSOCK, see
// https://github.com/jart/cosmopolitan/issues/1174
__imp_setsockopt(resources.handle, SOL_SOCKET, kNtSoUpdateAcceptContext,
&f->handle, sizeof(f->handle));
// create file descriptor for new socket
// don't inherit the file open mode bits
int oflags = 0;

View file

@ -35,11 +35,14 @@
#include "libc/sock/syscall_fd.internal.h"
#include "libc/sock/wsaid.internal.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
#include "libc/sock/yoink.inc"
__msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt;
struct ConnectArgs {
const void *addr;
uint32_t addrsize;
@ -113,6 +116,8 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr,
// return ETIMEDOUT if SO_SNDTIMEO elapsed
// note that Linux will return EINPROGRESS
errno = etimedout();
} else if (!rc) {
__imp_setsockopt(f->handle, SOL_SOCKET, kNtSoUpdateConnectContext, 0, 0);
}
return rc;
}
@ -131,7 +136,11 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr,
ok = WSAGetOverlappedResult(f->handle, overlap, &dwBytes, false, &dwFlags);
WSACloseEvent(overlap->hEvent);
free(overlap);
return ok ? 0 : __winsockerr();
if (!ok) {
return __winsockerr();
}
__imp_setsockopt(f->handle, SOL_SOCKET, kNtSoUpdateConnectContext, 0, 0);
return 0;
} else if (WSAGetLastError() == kNtErrorIoPending) {
f->connect_op = overlap;
return einprogress();

View file

@ -68,7 +68,8 @@ o/$(MODE)/test/libc/sock/shutdown_test.runs \
o/$(MODE)/test/libc/sock/setsockopt_test.runs \
o/$(MODE)/test/libc/sock/sendfile_test.runs \
o/$(MODE)/test/libc/sock/poll_test.runs \
o/$(MODE)/test/libc/sock/pollfd_test.runs: \
o/$(MODE)/test/libc/sock/pollfd_test.runs \
o/$(MODE)/test/libc/sock/getpeername_test.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
o/$(MODE)/test/libc/sock/sendrecvmsg_test.runs: \

View file

@ -0,0 +1,154 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Gavin Arthur Hayes
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/sockaddr.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/sock.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
TEST(getpeername, worksAfterAcceptingOnFork) {
// https://github.com/jart/cosmopolitan/issues/1174
if (IsWindows()) {
return;
}
char buf[16] = {0};
uint32_t addrsize = sizeof(struct sockaddr_in);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(0x7f000001),
};
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
SPAWN(fork);
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
struct sockaddr_storage out = {0};
uint32_t out_size = sizeof(out);
ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size));
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
ASSERT_SYS(0, 5, send(4, "hello", 5, 0));
PARENT();
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 5, read(3, buf, 16));
ASSERT_STREQ("hello", buf);
ASSERT_SYS(0, 0, close(3));
WAIT(exit, 0);
}
TEST(getpeername, worksAfterAcceptingOnParent) {
char buf[16] = {0};
uint32_t addrsize = sizeof(struct sockaddr_in);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(0x7f000001),
};
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
SPAWN(fork);
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 5, read(3, buf, 16));
ASSERT_STREQ("hello", buf);
PARENT();
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
struct sockaddr_storage out = {0};
uint32_t out_size = sizeof(out);
ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size));
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
ASSERT_SYS(0, 5, send(4, "hello", 5, 0));
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3));
WAIT(exit, 0);
}
TEST(getpeername, worksAfterConnectingOnFork) {
char buf[16] = {0};
uint32_t addrsize = sizeof(struct sockaddr_in);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(0x7f000001),
};
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
ASSERT_SYS(0, 4, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
SPAWN(fork);
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 0, connect(4, (struct sockaddr *)&addr, sizeof(addr)));
struct sockaddr_storage out = {0};
uint32_t out_size = sizeof(out);
ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size));
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
ASSERT_SYS(0, 5, send(4, "hello", 5, 0));
PARENT();
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
ASSERT_SYS(0, 5, read(4, buf, 16));
ASSERT_STREQ("hello", buf);
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3));
WAIT(exit, 0);
}
TEST(getpeername, worksAfterConnectingOnParent) {
char buf[16] = {0};
uint32_t addrsize = sizeof(struct sockaddr_in);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(0x7f000001),
};
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
SPAWN(fork);
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
ASSERT_SYS(0, 5, read(4, buf, 16));
ASSERT_STREQ("hello", buf);
PARENT();
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
struct sockaddr_storage out = {0};
uint32_t out_size = sizeof(out);
ASSERT_SYS(0, 0, getpeername(3, (struct sockaddr *)&out, &out_size));
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
ASSERT_SYS(0, 5, send(3, "hello", 5, 0));
ASSERT_SYS(0, 0, close(3));
WAIT(exit, 0);
}

View file

@ -152,8 +152,6 @@ __attribute__((__constructor__)) static void StdioPro(int argc, char *argv[]) {
}
TEST(socket, canBeUsedAsExecutedStdio) {
if (IsWindows())
return; // TODO(jart): What broke this?
char buf[16] = {0};
const char *prog;
uint32_t addrsize = sizeof(struct sockaddr_in);