From f395d952bf1b1546532a3ec79510302ff9c8c970 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Thu, 7 May 2026 01:01:56 +0200 Subject: [PATCH 01/23] tool_doswin: don't use `TerminateThread` in stdin relay It is a dangerous function. Remove usage by only starting the thread after setup completed successfully. Ref: https://github.com/curl/curl/pull/21467#discussion_r3173013432 --- src/tool_doswin.c | 289 +++++++++++++++++++++++++++------------------- 1 file changed, 173 insertions(+), 116 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index a64665bcd9..cf009e4d50 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -30,6 +30,7 @@ #ifdef _WIN32 # include "curlx/winapi.h" /* for curlx_win32_random() */ +# include "curlx/nonblock.h" /* for curlx_nonblock() */ # include #elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ # define CURL_USE_LFN(f) 0 /* long filenames never available */ @@ -707,59 +708,15 @@ struct win_thread_data { /* This is a copy of the true stdin file handle before any redirection. It is read by the thread. */ HANDLE stdin_handle; - /* This is the listen socket for the thread. It is closed after the first - connection. */ - curl_socket_t socket_l; - /* This is the random number which the background thread will use to verify - * the peer. */ - uint64_t expected_auth_val; + /* this is the socket the thread will forward stdin to. It is connected to + * the socket which replaces the stdin handle. */ + curl_socket_t socket_w; }; static DWORD WINAPI win_stdin_thread_func(void *thread_data) { struct win_thread_data *tdata = (struct win_thread_data *)thread_data; - struct sockaddr_in clientAddr; - int clientAddrLen = sizeof(clientAddr); - size_t nread = 0; - uint64_t auth_val = 0; - curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, - (struct sockaddr *)&clientAddr, - &clientAddrLen); - - if(socket_w == CURL_SOCKET_BAD) { - errorf("accept error: %d", SOCKERRNO); - goto ThreadCleanup; - } - - sclose(tdata->socket_l); - tdata->socket_l = CURL_SOCKET_BAD; - - do { - ssize_t ret = sread(socket_w, ((unsigned char *)&auth_val) + nread, - sizeof(auth_val) - nread); - if(ret <= 0) { - if(!ret) { - errorf("relay peer disconnected"); - } - else { - errorf("read error: %d", SOCKERRNO); - } - - goto ThreadCleanup; - } - nread += ret; - } while(nread < sizeof(auth_val)); - - if(auth_val != tdata->expected_auth_val) { - errorf("relay peer auth failed"); - goto ThreadCleanup; - } - - if(shutdown(socket_w, SHUT_RD)) { - errorf("shutdown error: %d", SOCKERRNO); - goto ThreadCleanup; - } for(;;) { DWORD n; ssize_t nwritten; @@ -769,34 +726,105 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) break; if(n == 0) break; - nwritten = swrite(socket_w, buffer, n); + nwritten = swrite(tdata->socket_w, buffer, n); if(nwritten == -1) break; if((DWORD)nwritten != n) break; } -ThreadCleanup: - if(tdata->socket_l != CURL_SOCKET_BAD) { - sclose(tdata->socket_l); - tdata->socket_l = CURL_SOCKET_BAD; + + if(tdata->socket_w != CURL_SOCKET_BAD) { + sclose(tdata->socket_w); + tdata->socket_w = CURL_SOCKET_BAD; + } + + if(tdata->stdin_handle != NULL) { + CloseHandle(tdata->stdin_handle); + tdata->stdin_handle = NULL; } - if(socket_w != CURL_SOCKET_BAD) - sclose(socket_w); curlx_free(tdata); return 0; } +static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, + unsigned char* data, + size_t len) +{ + fd_set fdwrite; + fd_set fdexcep; + size_t nwritten = 0; + + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + do { + ssize_t ret; + FD_SET(nonblock_sock, &fdwrite); + FD_SET(nonblock_sock, &fdexcep); + + if(select(0, NULL, &fdwrite, &fdexcep, NULL) <= 0) { + errorf("select error: %d", SOCKERRNO); + return -1; + } + + if(FD_ISSET(nonblock_sock, &fdexcep)) { + int sock_err = 0; + int sock_err_size = sizeof(sock_err); + getsockopt(nonblock_sock, SOL_SOCKET, SO_ERROR, + (char *)&sock_err, &sock_err_size); + errorf("connect failure: %d", sock_err); + return -1; + } + + ret = swrite(nonblock_sock, data + nwritten, + len - nwritten); + + if(ret <= 0) { + errorf("socket write error: %d", SOCKERRNO); + return -1; + } + + nwritten += ret; + } while(nwritten < len); + + return 0; +} + +static int read_auth_val(curl_socket_t sock, + uint64_t* buf) +{ + size_t nread = 0; + + do { + ssize_t ret = sread(sock, buf + nread, + sizeof(*buf) - nread); + if(ret <= 0) { + if(!ret) { + errorf("stdin relay peer disconnected"); + } + else { + errorf("read error: %d", SOCKERRNO); + } + + return -1; + } + nread += ret; + } while(nread < sizeof(*buf)); + + return 0; +} + /* The background thread that reads and buffers the true stdin. */ curl_socket_t win32_stdin_read_thread(void) { int rc = 0; struct win_thread_data *tdata = NULL; - HANDLE stdin_handle = NULL; - uint64_t auth_rnd; - size_t nwritten = 0; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; + curl_socket_t socket_l = CURL_SOCKET_BAD; + uint64_t auth_rnd = 0; + uint64_t recvd_val = 1; if(socket_r != CURL_SOCKET_BAD) { assert(stdin_thread); @@ -815,10 +843,10 @@ curl_socket_t win32_stdin_read_thread(void) errorf("curlx_calloc() error"); break; } - /* Create the listening socket for the thread. When it starts, it accepts - * our connection and begin writing STDIN data to the connection. */ - tdata->socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(tdata->socket_l == CURL_SOCKET_BAD) { + /* Create the listening socket. It is used to create the writing socket by + * accepting a connection from the reading socket. */ + socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(socket_l == CURL_SOCKET_BAD) { errorf("socket() error: %d", SOCKERRNO); break; } @@ -828,36 +856,103 @@ curl_socket_t win32_stdin_read_thread(void) selfaddr.sin_family = AF_INET; selfaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Bind to any available loopback port */ - if(bind(tdata->socket_l, (const struct sockaddr *)&selfaddr, socksize)) { + if(bind(socket_l, (const struct sockaddr *)&selfaddr, socksize)) { errorf("bind error: %d", SOCKERRNO); break; } /* Retrieve the assigned loopback port/address */ - if(getsockname(tdata->socket_l, (struct sockaddr *)&selfaddr, &socksize)) { + if(getsockname(socket_l, (struct sockaddr *)&selfaddr, &socksize)) { errorf("getsockname error: %d", SOCKERRNO); break; } - if(listen(tdata->socket_l, 1)) { + if(listen(socket_l, 1)) { errorf("listen error: %d", SOCKERRNO); break; } + /* Create the reading socket */ + socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(socket_r == CURL_SOCKET_BAD) { + errorf("socket error: %d", SOCKERRNO); + break; + } + + /* Hard close the socket on closesocket() */ + setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); + + /* Make the reading socket nonblocking */ + if(curlx_nonblock(socket_r, TRUE)) { + errorf("curlx_nonblock() error"); + break; + } + + /* Connect to the listening socket */ + if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { + ssize_t sockerrno = SOCKERRNO; + if(sockerrno != WSAEWOULDBLOCK) { + errorf("connect error: %d", sockerrno); + break; + } + } + + /* Accept the connection on the other end, creating the writing socket + * which will be given to the background thread */ + tdata->socket_w = CURL_ACCEPT(socket_l, NULL, NULL); + + if(tdata->socket_w == CURL_SOCKET_BAD) { + errorf("accept error: %d", SOCKERRNO); + break; + } + + /* We don't need the listening socket anymore */ + sclose(socket_l); + socket_l = CURL_SOCKET_BAD; + + /* Authenticate the reading socket to the writing socket to make sure we don't + * leak information.*/ if(curlx_win32_random((unsigned char *)&auth_rnd, sizeof(auth_rnd))) { errorf("curlx_win32_random() error"); break; } - tdata->expected_auth_val = auth_rnd; + + if(swrite_blocking_on_nonblock(socket_r, (unsigned char *)&auth_rnd, + sizeof(auth_rnd))) + break; + + if(read_auth_val(tdata->socket_w, &recvd_val)) + break; + + if(recvd_val != auth_rnd) { + errorf("relay peer auth failed"); + break; + } + + if(shutdown(tdata->socket_w, SHUT_RD)) { + errorf("shutdown error: %d", SOCKERRNO); + break; + } + + if(shutdown(socket_r, SHUT_WR)) { + errorf("shutdown error: %d", SOCKERRNO); + break; + } + /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), - GetCurrentProcess(), &stdin_handle, + GetCurrentProcess(), &tdata->stdin_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { errorf("DuplicateHandle error: 0x%08lx", GetLastError()); break; } - tdata->stdin_handle = stdin_handle; + + /* Set the stdin handle to read from the socket. */ + if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) { + errorf("SetStdHandle error: 0x%08lx", GetLastError()); + break; + } /* Start up the thread. We do not bother keeping a reference to it because it runs until program termination. From here on out all reads @@ -871,72 +966,34 @@ curl_socket_t win32_stdin_read_thread(void) } tdata = NULL; /* win_stdin_thread_func owns it now */ - /* Connect to the thread and rearrange our own STDIN handles */ - socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(socket_r == CURL_SOCKET_BAD) { - errorf("socket error: %d", SOCKERRNO); - break; - } - - /* Hard close the socket on closesocket() */ - setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); - - if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { - errorf("connect error: %d", SOCKERRNO); - break; - } - - do { - ssize_t ret = swrite(socket_r, ((unsigned char *)&auth_rnd) + nwritten, - sizeof(auth_rnd) - nwritten); - - if(ret <= 0) { - errorf("socket write error: %d", SOCKERRNO); - goto err; - } - - nwritten += ret; - } while(nwritten < sizeof(auth_rnd)); - - if(shutdown(socket_r, SHUT_WR)) { - errorf("shutdown error: %d", SOCKERRNO); - break; - } - - /* Set the stdin handle to read from the socket. */ - if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) { - errorf("SetStdHandle error: 0x%08lx", GetLastError()); - break; - } + /* Starting the thread is the last thing we do, since there aren't any + * reliable ways to close it in case of subsequent errors. */ rc = 1; } while(0); -err: if(rc != 1) { - if(stdin_thread) { - TerminateThread(stdin_thread, 1); - CloseHandle(stdin_thread); - stdin_thread = NULL; - } - if(socket_r != CURL_SOCKET_BAD) { if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r && - stdin_handle) { + tdata && tdata->stdin_handle) { /* restore STDIN */ - SetStdHandle(STD_INPUT_HANDLE, stdin_handle); - stdin_handle = NULL; + SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle); + tdata->stdin_handle = NULL; } sclose(socket_r); socket_r = CURL_SOCKET_BAD; } + + if(socket_l != CURL_SOCKET_BAD) + sclose(socket_l); + if(tdata) { if(tdata->stdin_handle) CloseHandle(tdata->stdin_handle); - if(tdata->socket_l != CURL_SOCKET_BAD) - sclose(tdata->socket_l); + if(tdata->socket_w != CURL_SOCKET_BAD) + sclose(tdata->socket_w); curlx_free(tdata); } From 2bfa796c31ce54c16d373b2390107863593f1d40 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:38:25 +0200 Subject: [PATCH 02/23] fix code style --- src/tool_doswin.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index cf009e4d50..ad72db29d0 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -738,7 +738,7 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) tdata->socket_w = CURL_SOCKET_BAD; } - if(tdata->stdin_handle != NULL) { + if(tdata->stdin_handle) { CloseHandle(tdata->stdin_handle); tdata->stdin_handle = NULL; } @@ -748,7 +748,7 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) } static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, - unsigned char* data, + unsigned char *data, size_t len) { fd_set fdwrite; @@ -910,8 +910,8 @@ curl_socket_t win32_stdin_read_thread(void) sclose(socket_l); socket_l = CURL_SOCKET_BAD; - /* Authenticate the reading socket to the writing socket to make sure we don't - * leak information.*/ + /* Authenticate the reading socket to the writing socket to make sure + * we don't leak information.*/ if(curlx_win32_random((unsigned char *)&auth_rnd, sizeof(auth_rnd))) { errorf("curlx_win32_random() error"); break; From 77265b595d5c3f6b16c48911ce253148f2435538 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:08:01 +0200 Subject: [PATCH 03/23] fix format string --- src/tool_doswin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index ad72db29d0..8d0ff84b02 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -892,7 +892,7 @@ curl_socket_t win32_stdin_read_thread(void) if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { ssize_t sockerrno = SOCKERRNO; if(sockerrno != WSAEWOULDBLOCK) { - errorf("connect error: %d", sockerrno); + errorf("connect error: %lld", sockerrno); break; } } From c728ac5ed4e6d4a52731f6b6b2482c26f2a5d97e Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:55:12 +0200 Subject: [PATCH 04/23] address comments --- src/tool_doswin.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 8d0ff84b02..fa6308f46c 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -890,9 +890,9 @@ curl_socket_t win32_stdin_read_thread(void) /* Connect to the listening socket */ if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { - ssize_t sockerrno = SOCKERRNO; - if(sockerrno != WSAEWOULDBLOCK) { - errorf("connect error: %lld", sockerrno); + int sockerr = SOCKERRNO; + if(!SOCK_EAGAIN(sockerr)) { + errorf("connect error: %d", sockerr); break; } } @@ -939,7 +939,6 @@ curl_socket_t win32_stdin_read_thread(void) break; } - /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &tdata->stdin_handle, @@ -985,7 +984,6 @@ curl_socket_t win32_stdin_read_thread(void) socket_r = CURL_SOCKET_BAD; } - if(socket_l != CURL_SOCKET_BAD) sclose(socket_l); From e555532403095972dde0f43df983542cac388aff Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:42:13 +0200 Subject: [PATCH 05/23] test --- tests/http/test_07_upload.py | 15 +++++++++++++++ tests/http/testenv/curl.py | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 6cb4d943e1..697fb6b11e 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -713,6 +713,21 @@ class TestUpload: # we might send less before the server handshakes. assert 0 < earlydata[1] <= (upload_size + 1024), f'{earlydata}\n{r.dump_logs()}' + @pytest.mark.parametrize("proto", Env.http_protos()) + @pytest.mark.parametrize("indata", [ + '', '1', '123\n456andsomething\n\n' + ]) + def test_07_71_upload_async_stdin(self, env: Env, httpd, nghttpx, proto, indata): + count = 1 + curl = CurlClient(env=env) + url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]' + r = curl.http_put(urls=[url], data=indata, alpn_proto=proto, async_stdin=True) + r.check_stats(count=count, http_status=200, exitcode=0) + for i in range(count): + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() + assert respdata == [f'{len(indata)}'] + def check_downloads(self, client, r, source: List[str], count: int, complete: bool = True): for i in range(count): diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 2abbbbe49c..5c9f12eeaf 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -811,13 +811,14 @@ class CurlClient: with_headers: bool = False, with_profile: bool = False, suppress_cl: bool = False, + async_stdin: bool = False, extra_args: Optional[List[str]] = None): if extra_args is None: extra_args = [] if fdata is not None: extra_args.extend(['-T', fdata]) elif data is not None: - extra_args.extend(['-T', '-']) + extra_args.extend(['-T', '.' if async_stdin else '-']) extra_args.extend([ '-o', 'download_#1.data', ]) From 481e91f8934001979de1727c93fa261d6bdd0716 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:42:13 +0200 Subject: [PATCH 06/23] add test --- tests/http/test_07_upload.py | 15 +++++++++++++++ tests/http/testenv/curl.py | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 6cb4d943e1..697fb6b11e 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -713,6 +713,21 @@ class TestUpload: # we might send less before the server handshakes. assert 0 < earlydata[1] <= (upload_size + 1024), f'{earlydata}\n{r.dump_logs()}' + @pytest.mark.parametrize("proto", Env.http_protos()) + @pytest.mark.parametrize("indata", [ + '', '1', '123\n456andsomething\n\n' + ]) + def test_07_71_upload_async_stdin(self, env: Env, httpd, nghttpx, proto, indata): + count = 1 + curl = CurlClient(env=env) + url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]' + r = curl.http_put(urls=[url], data=indata, alpn_proto=proto, async_stdin=True) + r.check_stats(count=count, http_status=200, exitcode=0) + for i in range(count): + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() + assert respdata == [f'{len(indata)}'] + def check_downloads(self, client, r, source: List[str], count: int, complete: bool = True): for i in range(count): diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 2abbbbe49c..5c9f12eeaf 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -811,13 +811,14 @@ class CurlClient: with_headers: bool = False, with_profile: bool = False, suppress_cl: bool = False, + async_stdin: bool = False, extra_args: Optional[List[str]] = None): if extra_args is None: extra_args = [] if fdata is not None: extra_args.extend(['-T', fdata]) elif data is not None: - extra_args.extend(['-T', '-']) + extra_args.extend(['-T', '.' if async_stdin else '-']) extra_args.extend([ '-o', 'download_#1.data', ]) From 1e91bf6e6e449283089b2d46ac174dd78a0c8ff2 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Sun, 26 Jul 2026 13:12:05 +0200 Subject: [PATCH 07/23] more style fixes --- src/tool_doswin.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index fa6308f46c..6c062ea60b 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -777,8 +777,7 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, return -1; } - ret = swrite(nonblock_sock, data + nwritten, - len - nwritten); + ret = swrite(nonblock_sock, data + nwritten, len - nwritten); if(ret <= 0) { errorf("socket write error: %d", SOCKERRNO); @@ -791,21 +790,17 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, return 0; } -static int read_auth_val(curl_socket_t sock, - uint64_t* buf) +static int read_auth_val(curl_socket_t sock, uint64_t* buf) { size_t nread = 0; do { - ssize_t ret = sread(sock, buf + nread, - sizeof(*buf) - nread); + ssize_t ret = sread(sock, buf + nread, sizeof(*buf) - nread); if(ret <= 0) { - if(!ret) { + if(!ret) errorf("stdin relay peer disconnected"); - } - else { + else errorf("read error: %d", SOCKERRNO); - } return -1; } From 971b83e389329a6cfa5c65904d21f673a6eb29c8 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Mon, 27 Jul 2026 02:42:28 +0200 Subject: [PATCH 08/23] remove useless pytest + unblock test 1498 for windows --- tests/data/test1498 | 1 - tests/http/test_07_upload.py | 15 --------------- tests/http/testenv/curl.py | 3 +-- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/tests/data/test1498 b/tests/data/test1498 index 67c3f8409d..b6aa35d813 100644 --- a/tests/data/test1498 +++ b/tests/data/test1498 @@ -27,7 +27,6 @@ blablabla http -!win32 HTTP PUT from stdin using period diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 697fb6b11e..6cb4d943e1 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -713,21 +713,6 @@ class TestUpload: # we might send less before the server handshakes. assert 0 < earlydata[1] <= (upload_size + 1024), f'{earlydata}\n{r.dump_logs()}' - @pytest.mark.parametrize("proto", Env.http_protos()) - @pytest.mark.parametrize("indata", [ - '', '1', '123\n456andsomething\n\n' - ]) - def test_07_71_upload_async_stdin(self, env: Env, httpd, nghttpx, proto, indata): - count = 1 - curl = CurlClient(env=env) - url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]' - r = curl.http_put(urls=[url], data=indata, alpn_proto=proto, async_stdin=True) - r.check_stats(count=count, http_status=200, exitcode=0) - for i in range(count): - with open(curl.response_file(i)) as fr: - respdata = fr.readlines() - assert respdata == [f'{len(indata)}'] - def check_downloads(self, client, r, source: List[str], count: int, complete: bool = True): for i in range(count): diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 5c9f12eeaf..2abbbbe49c 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -811,14 +811,13 @@ class CurlClient: with_headers: bool = False, with_profile: bool = False, suppress_cl: bool = False, - async_stdin: bool = False, extra_args: Optional[List[str]] = None): if extra_args is None: extra_args = [] if fdata is not None: extra_args.extend(['-T', fdata]) elif data is not None: - extra_args.extend(['-T', '.' if async_stdin else '-']) + extra_args.extend(['-T', '-']) extra_args.extend([ '-o', 'download_#1.data', ]) From f5378361eb3aff2a53a60e275f1c1ba4b1f95dca Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Mon, 27 Jul 2026 13:43:42 +0200 Subject: [PATCH 09/23] remove features section + unlbock test 2300 --- .github/workflows/windows.yml | 1 - tests/data/test1498 | 2 -- 2 files changed, 3 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 267a2a108a..d397cb9948 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -513,7 +513,6 @@ jobs: export CURL_TEST_SSH_ENABLE_KEX=diffie-hellman-group-exchange-sha256 fi if [[ "${TFLAGS}" = *'-t'* ]]; then - TFLAGS+=' !2300' # Leaks memory and file handle via tool_doswin.c / win32_stdin_read_thread() export CURL_TEST_NO_TASKKILL=1 # experiment to see if it reduces flaky failures fi if [[ "${MATRIX_INSTALL} " = *'-libssh '* && \ diff --git a/tests/data/test1498 b/tests/data/test1498 index b6aa35d813..362cf7fcf6 100644 --- a/tests/data/test1498 +++ b/tests/data/test1498 @@ -26,8 +26,6 @@ blablabla http - - HTTP PUT from stdin using period From 3621138ffb3d231cc0c61b99212f4fb69d3bfc07 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:31:32 +0200 Subject: [PATCH 10/23] fix error handling bug --- src/tool_doswin.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 6c062ea60b..c325c42332 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -838,6 +838,8 @@ curl_socket_t win32_stdin_read_thread(void) errorf("curlx_calloc() error"); break; } + tdata->socket_w = CURL_SOCKET_BAD; + /* Create the listening socket. It is used to create the writing socket by * accepting a connection from the reading socket. */ socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); From 71e045d3fe3a6280cdb315995f1346693b310dec Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:56:21 +0200 Subject: [PATCH 11/23] make tdata static --- src/tool_doswin.c | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index c325c42332..c0c5947622 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -743,7 +743,6 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) tdata->stdin_handle = NULL; } - curlx_free(tdata); return 0; } @@ -814,7 +813,10 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) curl_socket_t win32_stdin_read_thread(void) { int rc = 0; - struct win_thread_data *tdata = NULL; + static struct win_thread_data tdata = { + .socket_w = CURL_SOCKET_BAD, + .stdin_handle = NULL + }; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; curl_socket_t socket_l = CURL_SOCKET_BAD; @@ -831,15 +833,6 @@ curl_socket_t win32_stdin_read_thread(void) curl_socklen_t socksize = 0; struct sockaddr_in selfaddr; - /* Prepare handles for thread */ - tdata = (struct win_thread_data *) - curlx_calloc(1, sizeof(struct win_thread_data)); - if(!tdata) { - errorf("curlx_calloc() error"); - break; - } - tdata->socket_w = CURL_SOCKET_BAD; - /* Create the listening socket. It is used to create the writing socket by * accepting a connection from the reading socket. */ socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -896,9 +889,9 @@ curl_socket_t win32_stdin_read_thread(void) /* Accept the connection on the other end, creating the writing socket * which will be given to the background thread */ - tdata->socket_w = CURL_ACCEPT(socket_l, NULL, NULL); + tdata.socket_w = CURL_ACCEPT(socket_l, NULL, NULL); - if(tdata->socket_w == CURL_SOCKET_BAD) { + if(tdata.socket_w == CURL_SOCKET_BAD) { errorf("accept error: %d", SOCKERRNO); break; } @@ -918,7 +911,7 @@ curl_socket_t win32_stdin_read_thread(void) sizeof(auth_rnd))) break; - if(read_auth_val(tdata->socket_w, &recvd_val)) + if(read_auth_val(tdata.socket_w, &recvd_val)) break; if(recvd_val != auth_rnd) { @@ -926,7 +919,7 @@ curl_socket_t win32_stdin_read_thread(void) break; } - if(shutdown(tdata->socket_w, SHUT_RD)) { + if(shutdown(tdata.socket_w, SHUT_RD)) { errorf("shutdown error: %d", SOCKERRNO); break; } @@ -938,7 +931,7 @@ curl_socket_t win32_stdin_read_thread(void) /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), - GetCurrentProcess(), &tdata->stdin_handle, + GetCurrentProcess(), &tdata.stdin_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { errorf("DuplicateHandle error: 0x%08lx", GetLastError()); break; @@ -955,12 +948,11 @@ curl_socket_t win32_stdin_read_thread(void) from the stdin handle or file descriptor 0 is reading from the socket that is fed by the thread. */ stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, - tdata, 0, NULL); + &tdata, 0, NULL); if(!stdin_thread) { errorf("CreateThread error: 0x%08lx", GetLastError()); break; } - tdata = NULL; /* win_stdin_thread_func owns it now */ /* Starting the thread is the last thing we do, since there aren't any * reliable ways to close it in case of subsequent errors. */ @@ -969,12 +961,14 @@ curl_socket_t win32_stdin_read_thread(void) } while(0); if(rc != 1) { + /* we rely on the background thread not running at this point, as there + * could be TOCTOU bugs otherwise */ if(socket_r != CURL_SOCKET_BAD) { if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r && - tdata && tdata->stdin_handle) { + tdata.stdin_handle) { /* restore STDIN */ - SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle); - tdata->stdin_handle = NULL; + SetStdHandle(STD_INPUT_HANDLE, tdata.stdin_handle); + tdata.stdin_handle = NULL; } sclose(socket_r); @@ -984,14 +978,11 @@ curl_socket_t win32_stdin_read_thread(void) if(socket_l != CURL_SOCKET_BAD) sclose(socket_l); - if(tdata) { - if(tdata->stdin_handle) - CloseHandle(tdata->stdin_handle); - if(tdata->socket_w != CURL_SOCKET_BAD) - sclose(tdata->socket_w); + if(tdata.stdin_handle) + CloseHandle(tdata.stdin_handle); + if(tdata.socket_w != CURL_SOCKET_BAD) + sclose(tdata.socket_w); - curlx_free(tdata); - } return CURL_SOCKET_BAD; } From 9ae25134831466f99d139029d6bb60080f64433b Mon Sep 17 00:00:00 2001 From: Emanuel Krollmann <115734183+11soda11@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:50:25 +0200 Subject: [PATCH 12/23] fix tdata init literal --- src/tool_doswin.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index c0c5947622..8552011942 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -813,10 +813,7 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) curl_socket_t win32_stdin_read_thread(void) { int rc = 0; - static struct win_thread_data tdata = { - .socket_w = CURL_SOCKET_BAD, - .stdin_handle = NULL - }; + static struct win_thread_data tdata = { CURL_SOCKET_BAD, NULL }; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; curl_socket_t socket_l = CURL_SOCKET_BAD; From ff2192990226ec6ddcea1862fc5c94ac3d710843 Mon Sep 17 00:00:00 2001 From: Emanuel Krollmann <115734183+11soda11@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:55:03 +0200 Subject: [PATCH 13/23] fix init again --- src/tool_doswin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 8552011942..070e405118 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -813,7 +813,8 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) curl_socket_t win32_stdin_read_thread(void) { int rc = 0; - static struct win_thread_data tdata = { CURL_SOCKET_BAD, NULL }; + static struct win_thread_data tdata = + (struct win_thread_data){ CURL_SOCKET_BAD, NULL }; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; curl_socket_t socket_l = CURL_SOCKET_BAD; From 50b4999ea6ba5210562ef5e7e5edb693922f0177 Mon Sep 17 00:00:00 2001 From: Emanuel Krollmann <115734183+11soda11@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:59:29 +0200 Subject: [PATCH 14/23] switch order --- src/tool_doswin.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 070e405118..23bb432ffb 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -813,8 +813,7 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) curl_socket_t win32_stdin_read_thread(void) { int rc = 0; - static struct win_thread_data tdata = - (struct win_thread_data){ CURL_SOCKET_BAD, NULL }; + static struct win_thread_data tdata = { NULL, CURL_SOCKET_BAD }; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; curl_socket_t socket_l = CURL_SOCKET_BAD; From a7a9ea6d459b8eafcd161cba1c694b8d30c7e362 Mon Sep 17 00:00:00 2001 From: Emanuel Krollmann <115734183+11soda11@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:04:25 +0200 Subject: [PATCH 15/23] remove duplicate empty line --- src/tool_doswin.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 23bb432ffb..87c6ae3887 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -980,7 +980,6 @@ curl_socket_t win32_stdin_read_thread(void) if(tdata.socket_w != CURL_SOCKET_BAD) sclose(tdata.socket_w); - return CURL_SOCKET_BAD; } From 984d3bef520daf4a22310c15a3967bebef32102f Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Tue, 28 Jul 2026 03:26:55 +0200 Subject: [PATCH 16/23] fix valgrind errors --- src/tool_doswin.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 87c6ae3887..6333464460 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -704,45 +704,50 @@ static void init_terminal(void) #ifdef USE_WINSOCK /* The following STDIN non - blocking read techniques are heavily inspired by nmap and ncat (https://nmap.org/ncat/) */ -struct win_thread_data { +static struct win_thread_data { /* This is a copy of the true stdin file handle before any redirection. It is read by the thread. */ HANDLE stdin_handle; /* this is the socket the thread will forward stdin to. It is connected to * the socket which replaces the stdin handle. */ curl_socket_t socket_w; -}; +} tdata = { NULL, CURL_SOCKET_BAD }; + +static void cleanup_tdata(void) +{ + if(tdata.stdin_handle) { + CloseHandle(tdata.stdin_handle); + tdata.stdin_handle = NULL; + } + + if(tdata.socket_w != CURL_SOCKET_BAD) { + sclose(tdata.socket_w); + tdata.socket_w = CURL_SOCKET_BAD; + } +} static DWORD WINAPI win_stdin_thread_func(void *thread_data) { - struct win_thread_data *tdata = (struct win_thread_data *)thread_data; + (void)thread_data; + + atexit(cleanup_tdata); for(;;) { DWORD n; ssize_t nwritten; char buffer[BUFSIZ]; - if(!ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL)) + if(!ReadFile(tdata.stdin_handle, buffer, sizeof(buffer), &n, NULL)) break; if(n == 0) break; - nwritten = swrite(tdata->socket_w, buffer, n); + nwritten = swrite(tdata.socket_w, buffer, n); if(nwritten == -1) break; if((DWORD)nwritten != n) break; } - if(tdata->socket_w != CURL_SOCKET_BAD) { - sclose(tdata->socket_w); - tdata->socket_w = CURL_SOCKET_BAD; - } - - if(tdata->stdin_handle) { - CloseHandle(tdata->stdin_handle); - tdata->stdin_handle = NULL; - } - return 0; } @@ -813,7 +818,6 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) curl_socket_t win32_stdin_read_thread(void) { int rc = 0; - static struct win_thread_data tdata = { NULL, CURL_SOCKET_BAD }; static HANDLE stdin_thread = NULL; static curl_socket_t socket_r = CURL_SOCKET_BAD; curl_socket_t socket_l = CURL_SOCKET_BAD; @@ -945,7 +949,7 @@ curl_socket_t win32_stdin_read_thread(void) from the stdin handle or file descriptor 0 is reading from the socket that is fed by the thread. */ stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, - &tdata, 0, NULL); + NULL, 0, NULL); if(!stdin_thread) { errorf("CreateThread error: 0x%08lx", GetLastError()); break; @@ -975,10 +979,7 @@ curl_socket_t win32_stdin_read_thread(void) if(socket_l != CURL_SOCKET_BAD) sclose(socket_l); - if(tdata.stdin_handle) - CloseHandle(tdata.stdin_handle); - if(tdata.socket_w != CURL_SOCKET_BAD) - sclose(tdata.socket_w); + cleanup_tdata(); return CURL_SOCKET_BAD; } From 13f10b2fe0ee03ea47a26d9c8b9f032bc58e16d8 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Tue, 28 Jul 2026 04:16:11 +0200 Subject: [PATCH 17/23] second try with atexit --- src/tool_doswin.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 6333464460..390d0aba91 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -730,7 +730,6 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) { (void)thread_data; - atexit(cleanup_tdata); for(;;) { DWORD n; @@ -834,6 +833,12 @@ curl_socket_t win32_stdin_read_thread(void) curl_socklen_t socksize = 0; struct sockaddr_in selfaddr; + /* prevent mem leak warnings */ + if(atexit(&cleanup_tdata)) { + errorf("atexit() error"); + break; + } + /* Create the listening socket. It is used to create the writing socket by * accepting a connection from the reading socket. */ socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); From f89318e65c3ce15a7aa49108210824cb1771e25b Mon Sep 17 00:00:00 2001 From: Emanuel Krollmann <115734183+11soda11@users.noreply.github.com> Date: Tue, 28 Jul 2026 05:04:17 +0200 Subject: [PATCH 18/23] remove duplicate newline --- src/tool_doswin.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 390d0aba91..c4c2f696c6 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -730,7 +730,6 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) { (void)thread_data; - for(;;) { DWORD n; ssize_t nwritten; From 8266ff2354fc11015f9412f739580b113954dfa4 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Wed, 29 Jul 2026 00:26:03 +0200 Subject: [PATCH 19/23] fix buffer overflow + duplicate fds inside fd_set --- src/tool_doswin.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index c4c2f696c6..c195742e3a 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -759,10 +759,11 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); + FD_SET(nonblock_sock, &fdwrite); do { ssize_t ret; - FD_SET(nonblock_sock, &fdwrite); + FD_SET(nonblock_sock, &fdexcep); if(select(0, NULL, &fdwrite, &fdexcep, NULL) <= 0) { @@ -797,7 +798,7 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) size_t nread = 0; do { - ssize_t ret = sread(sock, buf + nread, sizeof(*buf) - nread); + ssize_t ret = sread(sock, (unsigned char*)buf + nread, sizeof(*buf) - nread); if(ret <= 0) { if(!ret) errorf("stdin relay peer disconnected"); From bc1999e217069f6f8c53deb55e689795b0f43799 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Wed, 29 Jul 2026 00:29:55 +0200 Subject: [PATCH 20/23] checksrc fix --- src/tool_doswin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index c195742e3a..0a81692cd0 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -798,7 +798,8 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) size_t nread = 0; do { - ssize_t ret = sread(sock, (unsigned char*)buf + nread, sizeof(*buf) - nread); + ssize_t ret = sread(sock, (unsigned char *)buf + nread, + sizeof(*buf) - nread); if(ret <= 0) { if(!ret) errorf("stdin relay peer disconnected"); From 7f0458823d5e0aee49a040d48a73cf96ec9ef142 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Wed, 29 Jul 2026 00:52:23 +0200 Subject: [PATCH 21/23] naming changes + default value for selfaddr --- src/tool_doswin.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 0a81692cd0..d218e0dd83 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -751,7 +751,7 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, unsigned char *data, - size_t len) + size_t nbytes) { fd_set fdwrite; fd_set fdexcep; @@ -780,7 +780,7 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, return -1; } - ret = swrite(nonblock_sock, data + nwritten, len - nwritten); + ret = swrite(nonblock_sock, data + nwritten, nbytes - nwritten); if(ret <= 0) { errorf("socket write error: %d", SOCKERRNO); @@ -788,18 +788,18 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, } nwritten += ret; - } while(nwritten < len); + } while(nwritten < nbytes); return 0; } -static int read_auth_val(curl_socket_t sock, uint64_t* buf) +static int read_auth_val(curl_socket_t sock, uint64_t* auth_val_ptr) { size_t nread = 0; do { - ssize_t ret = sread(sock, (unsigned char *)buf + nread, - sizeof(*buf) - nread); + ssize_t ret = sread(sock, (unsigned char *)auth_val_ptr + nread, + sizeof(*auth_val_ptr) - nread); if(ret <= 0) { if(!ret) errorf("stdin relay peer disconnected"); @@ -809,7 +809,7 @@ static int read_auth_val(curl_socket_t sock, uint64_t* buf) return -1; } nread += ret; - } while(nread < sizeof(*buf)); + } while(nread < sizeof(*auth_val_ptr)); return 0; } @@ -832,7 +832,7 @@ curl_socket_t win32_stdin_read_thread(void) do { curl_socklen_t socksize = 0; - struct sockaddr_in selfaddr; + struct sockaddr_in selfaddr = {0}; /* prevent mem leak warnings */ if(atexit(&cleanup_tdata)) { From 5635e6b94de7f8ee7c2a1883139ea9f0678d8362 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Wed, 29 Jul 2026 01:08:00 +0200 Subject: [PATCH 22/23] add EAGAIN check on write to nonblock sock --- src/tool_doswin.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index d218e0dd83..b8a94ebc4d 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -759,6 +759,7 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); + FD_SET(nonblock_sock, &fdwrite); do { @@ -783,6 +784,9 @@ static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, ret = swrite(nonblock_sock, data + nwritten, nbytes - nwritten); if(ret <= 0) { + if(SOCK_EAGAIN(SOCKERRNO)) + continue; + errorf("socket write error: %d", SOCKERRNO); return -1; } From c8cd3ef1960cc0fda415a82f96f095a736caeb80 Mon Sep 17 00:00:00 2001 From: 11soda11 <115734183+Sodastream11@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:39:03 +0200 Subject: [PATCH 23/23] address further comments --- src/tool_doswin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index b8a94ebc4d..4c2ab885a1 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -750,7 +750,7 @@ static DWORD WINAPI win_stdin_thread_func(void *thread_data) } static int swrite_blocking_on_nonblock(curl_socket_t nonblock_sock, - unsigned char *data, + const unsigned char *data, size_t nbytes) { fd_set fdwrite; @@ -836,7 +836,7 @@ curl_socket_t win32_stdin_read_thread(void) do { curl_socklen_t socksize = 0; - struct sockaddr_in selfaddr = {0}; + struct sockaddr_in selfaddr; /* prevent mem leak warnings */ if(atexit(&cleanup_tdata)) {