Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-accepted sockets and their parent sockets are never disassociated/freed #1781

Open
stevenengler opened this issue Nov 19, 2021 · 1 comment
Labels
Type: Bug Error or flaw producing unexpected results

Comments

@stevenengler
Copy link
Contributor

When a listening socket that has child sockets is closed, the listening socket will remain bound to the network interface until all child sockets have closed. If a listening socket has incoming connections which have not yet been accepted, and then that listening socket is closed, it is impossible to close those child sockets as the file handles for those non-accepted sockets have not yet been provided to the user space process*. The non-accepted child sockets will remain open forever, and so will the parent listening socket.

When a listening socket is closed, Shadow should close any pending (TCPCS_PENDING) child sockets, and when any incomplete (TCPCS_INCOMPLETE) child sockets become pending, they should be immediately closed. Once these pending and incomplete sockets are closed, the listening socket can be disassociated from the network interface and freed.

* Shadow currently registers child sockets in the descriptor table before the child socket has been accept()ed by the user space process; see #1780.

@stevenengler stevenengler added the Type: Bug Error or flaw producing unexpected results label Nov 19, 2021
@stevenengler
Copy link
Contributor Author

stevenengler commented Nov 20, 2021

An example that fails due to this bug (the second bind() fails):

fn main() {
    let fd_server = unsafe { libc::socket(libc::AF_INET, sock_type | libc::SOCK_NONBLOCK, 0) };
    let fd_client = unsafe { libc::socket(libc::AF_INET, sock_type | libc::SOCK_NONBLOCK, 0) };
    assert!(fd_server >= 0);
    assert!(fd_client >= 0);

    // the server address
    let server_addr = libc::sockaddr_in {
        sin_family: libc::AF_INET as u16,
        sin_port: 11111u16.to_be(),
        sin_addr: libc::in_addr {
            s_addr: libc::INADDR_ANY.to_be(),
        },
        sin_zero: [0; 8],
    };

    // bind on the server address
    {
        let rv = unsafe {
            libc::bind(
                fd_server,
                &server_addr as *const libc::sockaddr_in as *const libc::sockaddr,
                std::mem::size_of_val(&server_addr) as u32,
            )
        };
        assert_eq!(rv, 0);
    }

    // listen for connections
    let rv = unsafe { libc::listen(fd_server, 10) };
    assert_eq!(rv, 0);

    // connect a socket to the server, but the server doesn't accept() the connection
    let rv = unsafe {
        libc::connect(
            fd_client,
            &server_addr as *const libc::sockaddr_in as *const libc::sockaddr,
            std::mem::size_of_val(&server_addr) as u32,
        )
    };
    let errno = std::io::Error::last_os_error().raw_os_error().unwrap();
    assert!(rv == 0 || (rv == -1 && errno == libc::EINPROGRESS));

    // shadow needs to run events
    let rv = unsafe { libc::usleep(2000) };
    assert_eq!(rv, 0);

    // close the server socket
    let rv = unsafe { libc::close(fd_server) };
    assert_eq!(rv, 0);

    let rv = unsafe { libc::sleep(5) };
    assert_eq!(rv, 0);

    // create a new server socket
    let fd_server = unsafe { libc::socket(libc::AF_INET, sock_type | libc::SOCK_NONBLOCK, 0) };
    assert!(fd_server >= 0);

    // bind on the same server address
    {
        let rv = unsafe {
            libc::bind(
                fd_server,
                &server_addr as *const libc::sockaddr_in as *const libc::sockaddr,
                std::mem::size_of_val(&server_addr) as u32,
            )
        };
        assert_eq!(rv, 0);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug Error or flaw producing unexpected results
Projects
None yet
Development

No branches or pull requests

1 participant