Given this test code running on Windows 10 compiled with rustc 1.41.0:
use std::{net::UdpSocket, os::windows::io::AsRawSocket, process::Command};
use winapi::{
shared::minwindef::DWORD,
um::{handleapi, winbase, winnt},
};
fn main() -> std::io::Result<()> {
let socket1 = UdpSocket::bind("0.0.0.0:9999")?;
let socket2 = socket1.try_clone()?;
let mut flags: DWORD = 0;
unsafe {
handleapi::GetHandleInformation(socket1.as_raw_socket() as winnt::HANDLE, &mut flags);
}
println!(
"socket 1 HANDLE_FLAG_INHERIT is set: {}",
((flags & winbase::HANDLE_FLAG_INHERIT) != 0)
);
unsafe {
handleapi::GetHandleInformation(socket2.as_raw_socket() as winnt::HANDLE, &mut flags);
}
println!(
"socket 2 HANDLE_FLAG_INHERIT is set: {}",
((flags & winbase::HANDLE_FLAG_INHERIT) != 0)
);
Command::new("notepad.exe").spawn()?;
Ok(())
}
I would expect UDP port 9999 to no longer be listening after the code terminates and I would expect both socket1 and socket2 to report false on the state of their HANDLE_FLAG_INHERIT.
Instead port 9999 remains in a listening state until I close the spawned child process notepad and socket2 shows that its HANDLE_FLAG_INHERIT is set.
This appears to have been introduced in rust 1.38.0 via PR #60260 which now sets the WSA_FLAG_NO_HANDLE_INHERIT in WSASocketW instead of explicitly clearing the HANDLE_FLAG_INHERIT with SetHandleInformation. Just to confirm, I have compiled this against nightly-2019-07-26-x86_64-pc-windows-msvc (before PR) and nightly-2019-07-27-x86_64-pc-windows-msvc (after PR) and this code behaves as expected before the PR.
On the surface that PR looks solid and I would totally expect it to work. However it seems (surprisingly) that setting the WSA_FLAG_NO_HANDLE_INHERIT flag on a duplicated socket is not effective. I have googled until I could google no longer and alas have found no mention of this behavior.
As a workaround, doing something like:
let in_socket = socket.try_clone()?;
unsafe {
handleapi::SetHandleInformation(in_socket.as_raw_socket() as winnt::HANDLE, winbase::HANDLE_FLAG_INHERIT, 0)
};
fixes my use case.
Perhaps the correct fix is for duplicate to call set_no_inherit unless it is a UWP app. However I would love it if someone had deeper knowledge as to why this behavior is as it is.
Given this test code running on Windows 10 compiled with rustc 1.41.0:
I would expect UDP port 9999 to no longer be listening after the code terminates and I would expect both
socket1
andsocket2
to reportfalse
on the state of theirHANDLE_FLAG_INHERIT
.Instead port 9999 remains in a listening state until I close the spawned child process
notepad
andsocket2
shows that itsHANDLE_FLAG_INHERIT
is set.This appears to have been introduced in rust 1.38.0 via PR #60260 which now sets the
WSA_FLAG_NO_HANDLE_INHERIT
inWSASocketW
instead of explicitly clearing theHANDLE_FLAG_INHERIT
withSetHandleInformation
. Just to confirm, I have compiled this againstnightly-2019-07-26-x86_64-pc-windows-msvc
(before PR) andnightly-2019-07-27-x86_64-pc-windows-msvc
(after PR) and this code behaves as expected before the PR.On the surface that PR looks solid and I would totally expect it to work. However it seems (surprisingly) that setting the
WSA_FLAG_NO_HANDLE_INHERIT
flag on a duplicated socket is not effective. I have googled until I could google no longer and alas have found no mention of this behavior.As a workaround, doing something like:
fixes my use case.
Perhaps the correct fix is for
duplicate
to callset_no_inherit
unless it is a UWP app. However I would love it if someone had deeper knowledge as to why this behavior is as it is.