Looking for an IPC crate

Hi,

Have you looked at tokio_pipe - Rust ?
I've never used it but from cursory look at the source code it seams to tick all the boxes.

Example usage:

I wrote an IPC communication on Linux some time ago.
Then I used:

  1. std::os::unix::net::UnixListener
  2. std::os::unix::net::UnixStream

Looks like it's lacking the cross platform tick box.

@budziq: (Sorry for the delayed response. Life [and meetings] got in the way.) Yes it does check all the boxes. There isn't a lot in the way of examples, so it's probably not surprising that my first attempt at using it failed to compile. I'll give it a more serious attempt now that I'm back at the keyboard.

@mhanusek: I'll use a Unix-only solution if I have to, but I'd really prefer one that supports MacOs, too. Windows support is a nice-to-have.

@droundy: The documentation lists Mac and Linux as supported platforms.

I now have an additional problem, getting the pipe handle to the child. Normally, the handle would be there after the fork, but that's not the case with Rust. I found three options.

InteritablePipe that turns off the FD_CLOEXEC flag. The problem is that the child then inherits all the open file handles when it should only get a subset. I'll have to write code to close the ones it shouldn't have.

Pass the file handle to the child over a named pipe that gets closed once the child has the handle to the anonymous pipe. There's even a crate for it, but it's hardly been used and hasn't been updated in a long time.

Use fs::File::from_raw_fd directly, which now has me worried about my whole approach. Can any number of processes attach to the pipe knowing only the small integer associated with the file descriptor, or does it work only for the first to connect?

Any advice on which approach I should try first?

There isn't a lot in the way of examples

Yes, that is why I've included the only serious usage of this crate I could find with yskszk63/chrome-remote-interface-rs, from the author of this crate

I now have an additional problem, getting the pipe handle to the child. Normally, the handle would be there after the fork, but that's not the case with Rust.

please check the edit_command_and_new fn on how the author solves this problem, basically one of the strategies you've enumerated but with "tokio::process".

Use fs::File::from_raw_fd directly, which now has me worried about my whole approach. Can any number of processes attach to the pipe knowing only the small integer associated with the file descriptor, or does it work only for the first to connect?

Sadly yes. While harder than with named pipes, one can connect to anonymous pipes just as well on account of them being file (like dma-bufs, or other such constructs). Another process can poke in your /proc/PID/fd one way or another and open that path (fd number is pid specific of course, but /proc/PID/fd paths are open-able). Thus this does not provide much in the way of security hardening if this is what you are trying to achieve.

Security hardening is exactly the motivation for using anonymous pipes.

I did some experiments on my Mac to see if File::from_raw_fd would succeed in hijacking an anonymous pipe.

On process 1:

  1. Open a file to get a file descriptor f.
  2. Get fd, an i32, from f.into_raw_fd().
  3. Convert that into a file descriptor ff with unsafe{File::from_raw_fd(fd)}.
  4. write!(ff, "foo") works.

On process 2, which was started by Process 1 with std::process::Command:

  1. Use the i32 value of fd to create a file handle ff with unsafe{File::from_raw_fd(6)}.
  2. write!(ff,"bar") fails with "bad file descriptor".

I believe the test fails on process 2 because the MacOS closes all file handles between exec and fork by default. If I understand correctly, that's not the Unix default, but that default can be changed. Also, trying to use a file descriptor from /proc/PID/fd should fail for the same reason, but MacOS doesn't have /proc, so I can't run the test.

I conclude from this test that is is impossible for a process to connect to an anonymous pipe unless the handle is passed to it. My next task will be to verify that passing the file handle works. It might not for the same reason that the second test failed.

Its fairly easy to verify on systems with /proc

# toy example with bash pipes for clarity sake. It will work with other unnamed pipe setups on linux.
# pipe yes to more for infinite input we can listen to
$ yes | more
# on other terminal get pid of "more"
$ pidof more
25982
# as expected "more" process stdin is in fact a pipe
$ ls -al /proc/25982/fd/0
lr-x------ 1 budziq budziq 64 /proc/25982/fd/0 -> pipe:[15581113]
# we can read or write as we please
$ cat /proc/25982/fd/0
$ echo "TEST" >  /proc/25982/fd/0

The only protection we have, are the standard file permissions.

As far as I understand sysctl(3) has some functional equivalency to procfs on OSX and looking on the manpage I'm guessing that it might be possible to get other process file handles but unfortunately I do not have access to MacOSX machine to verify my assumption.