From 555e595d74b2c863e33773c08a754fc9981fb1eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 17:24:36 +0000 Subject: [PATCH 1/3] fix nio read/write iovec bugs causing deadlock and infinite loop Agent-Logs-Url: https://github.com/acl-dev/open-coroutine/sessions/3ee10e36-26e4-49a6-88c3-bc465b166e2f Co-authored-by: loongs-zhang <38336731+loongs-zhang@users.noreply.github.com> --- core/src/syscall/unix/mod.rs | 12 +++++------- core/src/syscall/windows/mod.rs | 18 +++++++++--------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/core/src/syscall/unix/mod.rs b/core/src/syscall/unix/mod.rs index f1198be4..1418d382 100644 --- a/core/src/syscall/unix/mod.rs +++ b/core/src/syscall/unix/mod.rs @@ -566,8 +566,8 @@ macro_rules! impl_nio_read_iovec { let mut received = 0usize; let mut r = -1; let mut index = 0; - for iovec in &vec { - let mut offset = received.saturating_sub(length); + 'outer: for iovec in &vec { + let offset = received.saturating_sub(length); length += iovec.iov_len; if received > length { index += 1; @@ -603,11 +603,9 @@ macro_rules! impl_nio_read_iovec { } else if r != -1 { $crate::syscall::reset_errno(); received += libc::size_t::try_from(r).expect("r overflow"); - if received >= length { - r = received.try_into().expect("received overflow"); - break; - } - offset = received.saturating_sub(length); + r = received.try_into().expect("received overflow"); + // readv returns as soon as any data is received + break 'outer; } let error_kind = std::io::Error::last_os_error().kind(); if error_kind == std::io::ErrorKind::WouldBlock { diff --git a/core/src/syscall/windows/mod.rs b/core/src/syscall/windows/mod.rs index d3f418b8..bef7a8bc 100644 --- a/core/src/syscall/windows/mod.rs +++ b/core/src/syscall/windows/mod.rs @@ -533,7 +533,7 @@ macro_rules! impl_nio_read_iovec { let mut received = 0usize; let mut r = windows_sys::Win32::Networking::WinSock::SOCKET_ERROR; let mut index = 0; - for iovec in &vec { + 'outer: for iovec in &vec { let mut offset = received.saturating_sub(length); length += iovec.len as usize; if received > length { @@ -563,13 +563,12 @@ macro_rules! impl_nio_read_iovec { ); if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR { $crate::syscall::reset_errno(); - received += usize::try_from(r).expect("overflow"); - if received >= length { - r = 0; - unsafe{ $recvd.write(received.try_into().expect("overflow")) }; - break; - } - offset = received.saturating_sub(length); + // WSARecv returns 0 on success; actual byte count is in *$recvd + received += unsafe { *$recvd } as usize; + r = 0; + unsafe{ $recvd.write(received.try_into().expect("overflow")) }; + // WSARecv returns as soon as any data is received + break 'outer; } let error_kind = std::io::Error::last_os_error().kind(); if error_kind == std::io::ErrorKind::WouldBlock { @@ -803,7 +802,8 @@ macro_rules! impl_nio_write_iovec { ); if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR { $crate::syscall::reset_errno(); - sent += usize::try_from(r).expect("overflow"); + // WSASend returns 0 on success; actual byte count is in *$sent + sent += unsafe { *$sent } as usize; if sent >= length { r = 0; unsafe{ $sent.write(sent.try_into().expect("overflow")) }; From b1a1896d40e6a4ffad433996a2c8eb49fa97aabb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 17:28:53 +0000 Subject: [PATCH 2/3] remove dead offset code from impl_nio_read_iovec (readv returns on first data) Agent-Logs-Url: https://github.com/acl-dev/open-coroutine/sessions/3ee10e36-26e4-49a6-88c3-bc465b166e2f Co-authored-by: loongs-zhang <38336731+loongs-zhang@users.noreply.github.com> --- core/src/syscall/unix/mod.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/syscall/unix/mod.rs b/core/src/syscall/unix/mod.rs index 1418d382..cab27a1d 100644 --- a/core/src/syscall/unix/mod.rs +++ b/core/src/syscall/unix/mod.rs @@ -567,7 +567,6 @@ macro_rules! impl_nio_read_iovec { let mut r = -1; let mut index = 0; 'outer: for iovec in &vec { - let offset = received.saturating_sub(length); length += iovec.iov_len; if received > length { index += 1; @@ -578,12 +577,6 @@ macro_rules! impl_nio_read_iovec { arg.push(*i); } while received < length && left_time > 0 { - if 0 != offset { - arg[0] = libc::iovec { - iov_base: (arg[0].iov_base as usize + offset) as *mut std::ffi::c_void, - iov_len: arg[0].iov_len - offset, - }; - } r = self.inner.$syscall( fn_ptr, $fd, From fd44e970efaa8f6aeaa34f8d906c72d18b2fce7e Mon Sep 17 00:00:00 2001 From: loongs-zhang <1936978077@qq.com> Date: Sat, 2 May 2026 17:07:53 +0800 Subject: [PATCH 3/3] fix impl_nio_read_iovec/impl_nio_write_iovec bug --- core/src/syscall/unix/mod.rs | 24 ++++++++++++++++++------ core/src/syscall/windows/mod.rs | 27 +++++++++++++++------------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/core/src/syscall/unix/mod.rs b/core/src/syscall/unix/mod.rs index cab27a1d..ecb0e301 100644 --- a/core/src/syscall/unix/mod.rs +++ b/core/src/syscall/unix/mod.rs @@ -566,7 +566,9 @@ macro_rules! impl_nio_read_iovec { let mut received = 0usize; let mut r = -1; let mut index = 0; - 'outer: for iovec in &vec { + for iovec in &vec { + let stage = length; + let mut offset = received.saturating_sub(stage); length += iovec.iov_len; if received > length { index += 1; @@ -577,6 +579,13 @@ macro_rules! impl_nio_read_iovec { arg.push(*i); } while received < length && left_time > 0 { + // Assuming iov_len is 4, but only 1 is read, at this point we should continue trying to fill the current iovec + if 0 != offset { + arg[0] = libc::iovec { + iov_base: (arg[0].iov_base as usize + offset) as *mut std::ffi::c_void, + iov_len: arg[0].iov_len - offset, + }; + } r = self.inner.$syscall( fn_ptr, $fd, @@ -596,9 +605,11 @@ macro_rules! impl_nio_read_iovec { } else if r != -1 { $crate::syscall::reset_errno(); received += libc::size_t::try_from(r).expect("r overflow"); - r = received.try_into().expect("received overflow"); - // readv returns as soon as any data is received - break 'outer; + if received >= length { + r = received.try_into().expect("received overflow"); + break; + } + offset = received.saturating_sub(stage); } let error_kind = std::io::Error::last_os_error().kind(); if error_kind == std::io::ErrorKind::WouldBlock { @@ -781,7 +792,8 @@ macro_rules! impl_nio_write_iovec { let mut r = -1; let mut index = 0; for iovec in &vec { - let mut offset = sent.saturating_sub(length); + let stage = length; + let mut offset = sent.saturating_sub(stage); length += iovec.iov_len; if sent > length { index += 1; @@ -814,7 +826,7 @@ macro_rules! impl_nio_write_iovec { r = sent.try_into().expect("sent overflow"); break; } - offset = sent.saturating_sub(length); + offset = sent.saturating_sub(stage); } let error_kind = std::io::Error::last_os_error().kind(); if error_kind == std::io::ErrorKind::WouldBlock { diff --git a/core/src/syscall/windows/mod.rs b/core/src/syscall/windows/mod.rs index bef7a8bc..9bf09376 100644 --- a/core/src/syscall/windows/mod.rs +++ b/core/src/syscall/windows/mod.rs @@ -533,8 +533,9 @@ macro_rules! impl_nio_read_iovec { let mut received = 0usize; let mut r = windows_sys::Win32::Networking::WinSock::SOCKET_ERROR; let mut index = 0; - 'outer: for iovec in &vec { - let mut offset = received.saturating_sub(length); + for iovec in &vec { + let stage = length; + let mut offset = received.saturating_sub(stage); length += iovec.len as usize; if received > length { index += 1; @@ -545,6 +546,7 @@ macro_rules! impl_nio_read_iovec { arg.push(*i); } while received < length && left_time > 0 { + // Assuming len is 4, but only 1 is read, at this point we should continue trying to fill the current WSABUF if 0 != offset { arg[0] = windows_sys::Win32::Networking::WinSock::WSABUF { buf: (arg[0].buf as usize + offset) as windows_sys::core::PSTR, @@ -563,12 +565,13 @@ macro_rules! impl_nio_read_iovec { ); if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR { $crate::syscall::reset_errno(); - // WSARecv returns 0 on success; actual byte count is in *$recvd - received += unsafe { *$recvd } as usize; - r = 0; - unsafe{ $recvd.write(received.try_into().expect("overflow")) }; - // WSARecv returns as soon as any data is received - break 'outer; + received += unsafe{ usize::try_from(*$recvd).expect("overflow") }; + if received >= length { + r = 0; + unsafe{ $recvd.write(received.try_into().expect("overflow")) }; + break; + } + offset = received.saturating_sub(stage); } let error_kind = std::io::Error::last_os_error().kind(); if error_kind == std::io::ErrorKind::WouldBlock { @@ -773,7 +776,8 @@ macro_rules! impl_nio_write_iovec { let mut r = windows_sys::Win32::Networking::WinSock::SOCKET_ERROR; let mut index = 0; for iovec in &vec { - let mut offset = sent.saturating_sub(length); + let stage = length; + let mut offset = sent.saturating_sub(stage); length += iovec.len as usize; if sent > length { index += 1; @@ -802,14 +806,13 @@ macro_rules! impl_nio_write_iovec { ); if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR { $crate::syscall::reset_errno(); - // WSASend returns 0 on success; actual byte count is in *$sent - sent += unsafe { *$sent } as usize; + sent += unsafe{ usize::try_from(*$sent).expect("overflow")}; if sent >= length { r = 0; unsafe{ $sent.write(sent.try_into().expect("overflow")) }; break; } - offset = sent.saturating_sub(length); + offset = sent.saturating_sub(stage); } let error_kind = std::io::Error::last_os_error().kind(); if error_kind == std::io::ErrorKind::WouldBlock {