Skip to content

Commit 36e82c0

Browse files
committed
fix impl_nio_read_iovec/impl_nio_write_iovec bug
1 parent b1a1896 commit 36e82c0

2 files changed

Lines changed: 36 additions & 14 deletions

File tree

core/src/syscall/unix/mod.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,9 @@ macro_rules! impl_nio_read_iovec {
566566
let mut received = 0usize;
567567
let mut r = -1;
568568
let mut index = 0;
569-
'outer: for iovec in &vec {
569+
for iovec in &vec {
570+
let stage = length;
571+
let mut offset = received.saturating_sub(stage);
570572
length += iovec.iov_len;
571573
if received > length {
572574
index += 1;
@@ -577,6 +579,13 @@ macro_rules! impl_nio_read_iovec {
577579
arg.push(*i);
578580
}
579581
while received < length && left_time > 0 {
582+
// Assuming iov_len is 4, but only 1 is read, at this point we should continue trying to fill the current iovec
583+
if 0 != offset {
584+
arg[0] = libc::iovec {
585+
iov_base: (arg[0].iov_base as usize + offset) as *mut std::ffi::c_void,
586+
iov_len: arg[0].iov_len - offset,
587+
};
588+
}
580589
r = self.inner.$syscall(
581590
fn_ptr,
582591
$fd,
@@ -596,9 +605,11 @@ macro_rules! impl_nio_read_iovec {
596605
} else if r != -1 {
597606
$crate::syscall::reset_errno();
598607
received += libc::size_t::try_from(r).expect("r overflow");
599-
r = received.try_into().expect("received overflow");
600-
// readv returns as soon as any data is received
601-
break 'outer;
608+
if received >= length {
609+
r = received.try_into().expect("received overflow");
610+
break;
611+
}
612+
offset = received.saturating_sub(stage);
602613
}
603614
let error_kind = std::io::Error::last_os_error().kind();
604615
if error_kind == std::io::ErrorKind::WouldBlock {

core/src/syscall/windows/mod.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,9 @@ macro_rules! impl_nio_read_iovec {
533533
let mut received = 0usize;
534534
let mut r = windows_sys::Win32::Networking::WinSock::SOCKET_ERROR;
535535
let mut index = 0;
536-
'outer: for iovec in &vec {
537-
let mut offset = received.saturating_sub(length);
536+
for iovec in &vec {
537+
let stage = length;
538+
let mut offset = received.saturating_sub(stage);
538539
length += iovec.len as usize;
539540
if received > length {
540541
index += 1;
@@ -545,6 +546,7 @@ macro_rules! impl_nio_read_iovec {
545546
arg.push(*i);
546547
}
547548
while received < length && left_time > 0 {
549+
// Assuming len is 4, but only 1 is read, at this point we should continue trying to fill the current WSABUF
548550
if 0 != offset {
549551
arg[0] = windows_sys::Win32::Networking::WinSock::WSABUF {
550552
buf: (arg[0].buf as usize + offset) as windows_sys::core::PSTR,
@@ -561,14 +563,24 @@ macro_rules! impl_nio_read_iovec {
561563
$recvd,
562564
$($arg, )*
563565
);
564-
if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR {
565-
$crate::syscall::reset_errno();
566-
// WSARecv returns 0 on success; actual byte count is in *$recvd
567-
received += unsafe { *$recvd } as usize;
566+
if unsafe{ *$recvd == 0 } {
568567
r = 0;
569568
unsafe{ $recvd.write(received.try_into().expect("overflow")) };
570-
// WSARecv returns as soon as any data is received
571-
break 'outer;
569+
std::mem::forget(vec);
570+
if blocking {
571+
$crate::syscall::set_blocking($fd);
572+
}
573+
// as success
574+
return r;
575+
} else if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR {
576+
$crate::syscall::reset_errno();
577+
received += unsafe{ usize::try_from(*$recvd).expect("overflow") };
578+
if received >= length {
579+
r = 0;
580+
unsafe{ $recvd.write(received.try_into().expect("overflow")) };
581+
break;
582+
}
583+
offset = received.saturating_sub(stage);
572584
}
573585
let error_kind = std::io::Error::last_os_error().kind();
574586
if error_kind == std::io::ErrorKind::WouldBlock {
@@ -802,8 +814,7 @@ macro_rules! impl_nio_write_iovec {
802814
);
803815
if r != windows_sys::Win32::Networking::WinSock::SOCKET_ERROR {
804816
$crate::syscall::reset_errno();
805-
// WSASend returns 0 on success; actual byte count is in *$sent
806-
sent += unsafe { *$sent } as usize;
817+
sent += unsafe{ usize::try_from(*$sent).expect("overflow")};
807818
if sent >= length {
808819
r = 0;
809820
unsafe{ $sent.write(sent.try_into().expect("overflow")) };

0 commit comments

Comments
 (0)