Skip to content

Cloned sockets are inherited by child processes on windows #70719

Description

@mwrock

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ioArea: `std::io`, `std::fs`, `std::net` and `std::path`A-processArea: `std::process` and `std::env`C-bugCategory: This is a bug.O-windowsOperating system: WindowsT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions