Skip to content

Commit 624119e

Browse files
authored
Fix NT accept/connect not initializing with SO_UPDATE_*_CONTEXT (#1164)
1 parent 2f3c6e7 commit 624119e

File tree

5 files changed

+173
-8
lines changed

5 files changed

+173
-8
lines changed

libc/sock/accept-nt.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ static int sys_accept_nt_start(int64_t handle, struct NtOverlapped *overlap,
8787
if (g_acceptex.lpAcceptEx(args->listensock, handle, args->buffer, 0,
8888
sizeof(args->buffer->local),
8989
sizeof(args->buffer->remote), 0, overlap)) {
90-
// inherit properties of listening socket
91-
unassert(!__imp_setsockopt(args->listensock, SOL_SOCKET,
92-
kNtSoUpdateAcceptContext, &handle,
93-
sizeof(handle)));
9490
return 0;
9591
} else {
9692
return -1;
@@ -123,6 +119,13 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
123119
goto Finish;
124120
}
125121

122+
// inherit properties of listening socket
123+
// errors ignored as if f->handle was created before forking
124+
// this fails with WSAENOTSOCK, see
125+
// https://github.com/jart/cosmopolitan/issues/1174
126+
__imp_setsockopt(resources.handle, SOL_SOCKET, kNtSoUpdateAcceptContext,
127+
&f->handle, sizeof(f->handle));
128+
126129
// create file descriptor for new socket
127130
// don't inherit the file open mode bits
128131
int oflags = 0;

libc/sock/connect-nt.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@
3535
#include "libc/sock/syscall_fd.internal.h"
3636
#include "libc/sock/wsaid.internal.h"
3737
#include "libc/sysv/consts/o.h"
38+
#include "libc/sysv/consts/sol.h"
3839
#include "libc/sysv/errfuns.h"
3940

4041
#ifdef __x86_64__
4142
#include "libc/sock/yoink.inc"
4243

44+
__msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt;
45+
4346
struct ConnectArgs {
4447
const void *addr;
4548
uint32_t addrsize;
@@ -113,6 +116,8 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr,
113116
// return ETIMEDOUT if SO_SNDTIMEO elapsed
114117
// note that Linux will return EINPROGRESS
115118
errno = etimedout();
119+
} else if (!rc) {
120+
__imp_setsockopt(f->handle, SOL_SOCKET, kNtSoUpdateConnectContext, 0, 0);
116121
}
117122
return rc;
118123
}
@@ -131,7 +136,11 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr,
131136
ok = WSAGetOverlappedResult(f->handle, overlap, &dwBytes, false, &dwFlags);
132137
WSACloseEvent(overlap->hEvent);
133138
free(overlap);
134-
return ok ? 0 : __winsockerr();
139+
if (!ok) {
140+
return __winsockerr();
141+
}
142+
__imp_setsockopt(f->handle, SOL_SOCKET, kNtSoUpdateConnectContext, 0, 0);
143+
return 0;
135144
} else if (WSAGetLastError() == kNtErrorIoPending) {
136145
f->connect_op = overlap;
137146
return einprogress();

test/libc/sock/BUILD.mk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ o/$(MODE)/test/libc/sock/shutdown_test.runs \
6868
o/$(MODE)/test/libc/sock/setsockopt_test.runs \
6969
o/$(MODE)/test/libc/sock/sendfile_test.runs \
7070
o/$(MODE)/test/libc/sock/poll_test.runs \
71-
o/$(MODE)/test/libc/sock/pollfd_test.runs: \
71+
o/$(MODE)/test/libc/sock/pollfd_test.runs \
72+
o/$(MODE)/test/libc/sock/getpeername_test.runs: \
7273
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
7374

7475
o/$(MODE)/test/libc/sock/sendrecvmsg_test.runs: \

test/libc/sock/getpeername_test.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2024 Gavin Arthur Hayes │
5+
│ │
6+
│ Permission to use, copy, modify, and/or distribute this software for │
7+
│ any purpose with or without fee is hereby granted, provided that the │
8+
│ above copyright notice and this permission notice appear in all copies. │
9+
│ │
10+
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
11+
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
12+
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
13+
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
14+
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
15+
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
16+
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17+
│ PERFORMANCE OF THIS SOFTWARE. │
18+
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/dce.h"
20+
#include "libc/sock/sock.h"
21+
#include "libc/sock/struct/sockaddr.h"
22+
#include "libc/sysv/consts/af.h"
23+
#include "libc/sysv/consts/ipproto.h"
24+
#include "libc/sysv/consts/limits.h"
25+
#include "libc/sysv/consts/sock.h"
26+
#include "libc/testlib/subprocess.h"
27+
#include "libc/testlib/testlib.h"
28+
29+
TEST(getpeername, worksAfterAcceptingOnFork) {
30+
// https://github.com/jart/cosmopolitan/issues/1174
31+
if (IsWindows()) {
32+
return;
33+
}
34+
char buf[16] = {0};
35+
uint32_t addrsize = sizeof(struct sockaddr_in);
36+
struct sockaddr_in addr = {
37+
.sin_family = AF_INET,
38+
.sin_addr.s_addr = htonl(0x7f000001),
39+
};
40+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
41+
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
42+
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
43+
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
44+
SPAWN(fork);
45+
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
46+
struct sockaddr_storage out = {0};
47+
uint32_t out_size = sizeof(out);
48+
ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size));
49+
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
50+
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
51+
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
52+
ASSERT_SYS(0, 5, send(4, "hello", 5, 0));
53+
PARENT();
54+
ASSERT_SYS(0, 0, close(3));
55+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
56+
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
57+
ASSERT_SYS(0, 5, read(3, buf, 16));
58+
ASSERT_STREQ("hello", buf);
59+
ASSERT_SYS(0, 0, close(3));
60+
WAIT(exit, 0);
61+
}
62+
63+
TEST(getpeername, worksAfterAcceptingOnParent) {
64+
char buf[16] = {0};
65+
uint32_t addrsize = sizeof(struct sockaddr_in);
66+
struct sockaddr_in addr = {
67+
.sin_family = AF_INET,
68+
.sin_addr.s_addr = htonl(0x7f000001),
69+
};
70+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
71+
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
72+
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
73+
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
74+
SPAWN(fork);
75+
ASSERT_SYS(0, 0, close(3));
76+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
77+
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
78+
ASSERT_SYS(0, 5, read(3, buf, 16));
79+
ASSERT_STREQ("hello", buf);
80+
PARENT();
81+
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
82+
struct sockaddr_storage out = {0};
83+
uint32_t out_size = sizeof(out);
84+
ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size));
85+
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
86+
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
87+
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
88+
ASSERT_SYS(0, 5, send(4, "hello", 5, 0));
89+
ASSERT_SYS(0, 0, close(4));
90+
ASSERT_SYS(0, 0, close(3));
91+
WAIT(exit, 0);
92+
}
93+
94+
TEST(getpeername, worksAfterConnectingOnFork) {
95+
char buf[16] = {0};
96+
uint32_t addrsize = sizeof(struct sockaddr_in);
97+
struct sockaddr_in addr = {
98+
.sin_family = AF_INET,
99+
.sin_addr.s_addr = htonl(0x7f000001),
100+
};
101+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
102+
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
103+
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
104+
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
105+
ASSERT_SYS(0, 4, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
106+
SPAWN(fork);
107+
ASSERT_SYS(0, 0, close(3));
108+
ASSERT_SYS(0, 0, connect(4, (struct sockaddr *)&addr, sizeof(addr)));
109+
struct sockaddr_storage out = {0};
110+
uint32_t out_size = sizeof(out);
111+
ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size));
112+
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
113+
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
114+
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
115+
ASSERT_SYS(0, 5, send(4, "hello", 5, 0));
116+
PARENT();
117+
ASSERT_SYS(0, 0, close(4));
118+
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
119+
ASSERT_SYS(0, 5, read(4, buf, 16));
120+
ASSERT_STREQ("hello", buf);
121+
ASSERT_SYS(0, 0, close(4));
122+
ASSERT_SYS(0, 0, close(3));
123+
WAIT(exit, 0);
124+
}
125+
126+
TEST(getpeername, worksAfterConnectingOnParent) {
127+
char buf[16] = {0};
128+
uint32_t addrsize = sizeof(struct sockaddr_in);
129+
struct sockaddr_in addr = {
130+
.sin_family = AF_INET,
131+
.sin_addr.s_addr = htonl(0x7f000001),
132+
};
133+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
134+
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
135+
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
136+
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
137+
SPAWN(fork);
138+
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
139+
ASSERT_SYS(0, 5, read(4, buf, 16));
140+
ASSERT_STREQ("hello", buf);
141+
PARENT();
142+
ASSERT_SYS(0, 0, close(3));
143+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
144+
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
145+
struct sockaddr_storage out = {0};
146+
uint32_t out_size = sizeof(out);
147+
ASSERT_SYS(0, 0, getpeername(3, (struct sockaddr *)&out, &out_size));
148+
EXPECT_GE(sizeof(struct sockaddr_in), out_size);
149+
EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family);
150+
EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr);
151+
ASSERT_SYS(0, 5, send(3, "hello", 5, 0));
152+
ASSERT_SYS(0, 0, close(3));
153+
WAIT(exit, 0);
154+
}

test/libc/sock/socket_test.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,6 @@ __attribute__((__constructor__)) static void StdioPro(int argc, char *argv[]) {
152152
}
153153

154154
TEST(socket, canBeUsedAsExecutedStdio) {
155-
if (IsWindows())
156-
return; // TODO(jart): What broke this?
157155
char buf[16] = {0};
158156
const char *prog;
159157
uint32_t addrsize = sizeof(struct sockaddr_in);

0 commit comments

Comments
 (0)