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

Complete socketpair support #3442

Open
RalfJung opened this issue Apr 3, 2024 · 20 comments · May be fixed by #3609
Open

Complete socketpair support #3442

RalfJung opened this issue Apr 3, 2024 · 20 comments · May be fixed by #3609
Assignees
Labels
A-files Area: related to files, paths, sockets, file descriptors, or handles A-shims Area: This affects the external function shims C-project Category: a larger project is being tracked here, usually with checkmarks for individual steps E-good-second-issue A good issue to pick up if you've already seen some parts of Miri, mentoring is available

Comments

@RalfJung
Copy link
Member

RalfJung commented Apr 3, 2024

We pretend to implement socketpair, but that's a complete lie. Completing this however doesn't need any involvement with the rest of the epoll plans. We "just" need to allocate somewhere two buffers for the data to be sent between these two endpoints, and then wire up the read/write of the two sockets with the right buffers. This can be tested by directly using socketpair/read/write.

There's also #3231.

We can provide more detailed mentoring instructions if needed.

@RalfJung RalfJung added A-shims Area: This affects the external function shims E-good-first-issue A good way to start contributing, mentoring is available C-enhancement Category: a PR with an enhancement or an issue tracking an accepted enhancement labels Apr 3, 2024
@RalfJung
Copy link
Member Author

RalfJung commented Apr 3, 2024

I think this should follow the proposed "project" process: #3443.

@oli-obk
Copy link
Contributor

oli-obk commented Apr 3, 2024

I wonder if there are libc compliance test suites that are licensed well enough for us to extract tests from

@RalfJung RalfJung added C-project Category: a larger project is being tracked here, usually with checkmarks for individual steps and removed C-enhancement Category: a PR with an enhancement or an issue tracking an accepted enhancement labels Apr 4, 2024
@tiif
Copy link
Contributor

tiif commented Apr 5, 2024

Hi, I am planning to work on this first since it is related to #3448. I will send the link to the proposal here once I finished localtime_r.

@tiif
Copy link
Contributor

tiif commented Apr 17, 2024

@rustbot claim

@rustbot
Copy link
Collaborator

rustbot commented Apr 17, 2024

Error: The feature assign is not enabled in this repository.
To enable it add its section in the triagebot.toml in the root of the repository.

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

@tiif
Copy link
Contributor

tiif commented Apr 30, 2024

I wonder if there are libc compliance test suites that are licensed well enough for us to extract tests from

Hmm I couldn't find any test for libc for now, if anyone knows where can I find one please let me know, thanks. Currently I imagine minimally the test should look like this (Note: they wrapped libc around their API, so it looks slightly different)

#[test]
pub fn test_socketpair() {
    use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType};
    use nix::unistd::{read, write};

    let (fd1, fd2) = socketpair(
        AddressFamily::Unix,
        SockType::Stream,
        None,
        SockFlag::empty(),
    )
    .unwrap();
    write(&fd1, b"hello").unwrap();
    let mut buf = [0; 5];
    read(fd2.as_raw_fd(), &mut buf).unwrap();

    assert_eq!(&buf[..], b"hello");
}
//source: https://github.com/nix-rust/nix/blob/590ab4d5d0b6228f0529b036e4f8a42265ba6d31/test/sys/test_socket.rs#L311. 

source: nix-rust/nix

I might add similar test in the proposal that I am going to send soon.

@tiif
Copy link
Contributor

tiif commented May 3, 2024

This is the proposal for socketpair shim: https://hackmd.io/@tiif/Skhc1t0-C. It is still under development, more attention is needed for the problems below:

  1. Should we block write when the buffer is full?
  2. How can miri differentiate read and write of socketpair file descriptors and other kind of file descriptors?

Feel free to provide any ideas or suggestion especially for the buffer mechanism part.

@oli-obk
Copy link
Contributor

oli-obk commented May 3, 2024

2. How can miri differentiate read and write of socketpair file descriptors and other kind of file descriptors?

Why does it need to? Shouldn't the abstraction provided by the FileDescriptor trait hide all thatvand only the socket pair type implementing that trait would care?

@oli-obk
Copy link
Contributor

oli-obk commented May 3, 2024

Left some comments,on the doc

@RalfJung
Copy link
Member Author

RalfJung commented May 3, 2024

Should we block write when the buffer is full?

Yes, we should.

How can miri differentiate read and write of socketpair file descriptors and other kind of file descriptors?

As Oli said: you "just" have to implement the read and write functions of the SocketPair type; the general file descriptor machinery will make sure to dispatch to that when read/write is called on an FD that is (part of) a socketpair.

@RalfJung
Copy link
Member Author

RalfJung commented May 3, 2024

This is the proposal for socketpair shim: https://hackmd.io/@tiif/Skhc1t0-C. It is still under development, more attention is needed for the problems below:

Thanks! The API description is very helpful. I have left some comments.

What is missing for me is a plan for how the Miri state looks like. In particular, the proposal should say what the fields of struct SocketPair will be. I currently think it will likely be something like

struct SocketPair {
  write_buf: Rc<SocketBuf>,
  read_buf: Rc<SocketBuf>,
}

but that then raises the question of what the fields of SocketBuf are. :) Maybe data: VecDeque<u8> as you sketched out is sufficient.

@RalfJung RalfJung added E-good-second-issue A good issue to pick up if you've already seen some parts of Miri, mentoring is available and removed E-good-first-issue A good way to start contributing, mentoring is available labels May 5, 2024
@RalfJung RalfJung added the A-files Area: related to files, paths, sockets, file descriptors, or handles label May 5, 2024
@RalfJung
Copy link
Member Author

RalfJung commented May 7, 2024

FWIW, once this is done it should be fairly easy to also support pipe/pipe2. Those are basically just one-directional sockets, for our purposes.

@tiif tiif linked a pull request May 16, 2024 that will close this issue
@tiif
Copy link
Contributor

tiif commented May 20, 2024

After reading eventfd, I realised both eventfd and socketpair have non-blocking flag, and I think it is good to implement them (and shouldn't be hard?). But currently blocking file descriptor is not implemented yet, and has higher priority, so maybe non-blocking can be left as a FIXME?

Besides, it is likely that SOCK_DGRAM and SOCK_STREAM have some difference in data size handling, but I think maybe we can leave it as a FIXME too?

@RalfJung
Copy link
Member Author

RalfJung commented May 20, 2024 via email

@tiif
Copy link
Contributor

tiif commented May 20, 2024

Does tokio need non blocking sockets?

Yes, I just realised mio seems to support both SOCK_NONBLOCK and SOCK_CLOEXEC that I initially thought we don't need to support 👀. We also need non-blocking file descriptor for epoll_ctl because there is a flag called EPOLLET for edge-triggered notification1 file descriptor, and mio used them (tokio used that too). For edge-triggered notification, nonblocking file descriptors is used2. I guess the behaviour for non-blocking file descriptors should be similar, so once we get the design down, it should be applicable to other kind of file descriptors too.

FWIW, we might also need to provide support for "block with timeout" at some point too because apparently mio has them, tokio has related functions too :)

But I will go through all the flags and what should we do with them again in detail once we start to discuss epoll.

SOCK_DGRAM needs a pretty different implementation (we need to preserve message boundaries) so I think that is also out for now.

Hmm, now I think about it, should we throw_unsup_format for SOCK_DGRAM? Personally, I prefer to not throw error and let SOCK_DGRAM run without message boundaries. We can let the messages go through first, and message boundaries come later as an enhancement.

Footnotes

  1. This means notification is provided if there is IO activity on the file descriptor since it was last monitored. Another type is level-triggered notification, which means a file descriptor is considered ready when it is possible to perform an IO without blocking.

  2. The Linux Programming Interface 63.4.6 (for my own record)

@RalfJung
Copy link
Member Author

RalfJung commented May 21, 2024

Hmm, now I think about it, should we throw_unsup_format for SOCK_DGRAM?

Yes, definitely. We don't currently support it. We should throw_unsup_format on everything we do not support. (The current shim is buggy in that it does not properly do this.)
Not preserving message boundaries gives just the completely wrong semantics, we should never do that.
(I am not even sure what read/write do on such a socket, one would usually use sendmsg/recvmsg I think.)

For edge-triggered notification, nonblocking file descriptors is used2.

I don't know what you mean by that. Does tokio set SOCK_NONBLOCK or not?
If not, then clearly EPOLLET doesn't require SOCK_NONBLOCK.

@tiif
Copy link
Contributor

tiif commented May 21, 2024

Not preserving message boundaries gives just the completely wrong semantics, we should never do that.

I see why it is important to throw_unsup_format for that now, thanks for the explanation.

I am not even sure what read/write do on such a socket, one would usually use sendmsg/recvmsg I think.

I didn't know sendmsg/recvmsg exist, thanks for pointing it out. read/recv/recvfrom and send/write/sendto have the same behaviour when it comes to message boundaries 1. For either of them, if they are datagram socket, read/recv/recvfrom will retrieve exactly one message. If the message size exceed the length byte, the message is silently truncated to length byte.

// Recall that read looks like this:
ssize_t read(int fd, void* buffer, size_t length);
// Return number of bytes read, or -1 on error

recvmsg is different as user can find out if a datagram is truncated through the MSG_TRUNC flag. 2

Does tokio set SOCK_NONBLOCK or not?

Yes, both SOCK_NONBLOCK and SOCK_CLOEXEC. Is it a good idea to delay the support for these two flags until I finish implementing basic functionality of eventfd? We need to support non-blocking and close-on-exec flags for eventfd too because tokio uses it. The design should be similar so I am thinking of doing both together.

I don't know what you mean by that.

Oh that's only for epoll, I will explain it again when we get to epoll.

Footnotes

  1. https://manpages.ubuntu.com/manpages/focal/en/man2/recvfrom.2.html

  2. https://man7.org/linux/man-pages/man3/recvmsg.3p.html

@RalfJung
Copy link
Member Author

Yes, both SOCK_NONBLOCK and SOCK_CLOEXEC.

Ah. In that case you probably should implement non-blocking sockets first -- i.e., store during socket creation whether the socket is blocking, and then in read/write, if we would have to block, throw_unsup_format!.

It is up to you whether you want to implement support for blocking sockets at all, if it is not needed for tokio.

@tiif
Copy link
Contributor

tiif commented May 25, 2024

SOCK_NONBLOCK and SOCK_CLOEXEC are Linux-specific. 1

x86_64-apple-darwin doesn't have these two flags. How mio handles them is 2

  1. Create a socketpair: socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr())
  2. Use fcntl to set the NON_BLOCK flag instead
syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;

I am only mentioning NONBLOCK here becaues SOCK_CLOEXEC will be ignored since miri don't support exec

So to actually support x86_64-apple-darwin, we might need to:

  1. Add O_NONBLOCK flag handling in fcntl
  2. Find a way to notify socketpair that this socketpair file descriptor has been set to non-blocking.

Footnotes

  1. https://man7.org/linux/man-pages/man2/socket.2.html

  2. https://github.com/tokio-rs/mio/blob/309daae21ecb1d46203a7dbc0cf4c80310240cba/src/sys/unix/uds/mod.rs#L74

@RalfJung
Copy link
Member Author

Don't worry about running mio on macOS for now. That's going to be completely different anyway, it can't be using epoll.

But the NONBLOCK check in the socketpair function should be gated on Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-files Area: related to files, paths, sockets, file descriptors, or handles A-shims Area: This affects the external function shims C-project Category: a larger project is being tracked here, usually with checkmarks for individual steps E-good-second-issue A good issue to pick up if you've already seen some parts of Miri, mentoring is available
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants