1

From this answer, I learned that a file descriptor can be read using unsafe:

use std::{
    fs::File,
    io::{self, Read},
    os::unix::io::FromRawFd,
};

fn main() -> io::Result<()> {
    let mut f = unsafe { File::from_raw_fd(3) };
    let mut input = String::new();
    f.read_to_string(&mut input)?;

    println!("I read: {}", input);

    Ok(())
}
$ cat /tmp/output
Hello, world!
$ target/debug/example 3< /tmp/output
I read: Hello, world!

How can I achieve the same result without using unsafe?

I am currently creating a file descriptor like this (zsh shell):

function test_fd {
   if ! read -r line <&$1; then
       line="[Read on fd $1 failed]"
   fi

   echo $line

   # Remove the handler and close the fd
   zle -F $1
   exec {1}<&-
}

exec {FD}< <(/path/to/my/app)
zle -F $FD test_fd

I would like to replace the test_fd with something that could read or better if it could read & close the provided file descriptor so that I could end with something like:

function test_fd {
   /something/in/rust "$@"
}

exec {FD}< <(/path/to/my/app)
zle -F $FD test_fd
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
nbari
  • 25,603
  • 10
  • 76
  • 131

1 Answers1

2

You cannot do this. Your only avenue is to use unsafe.

As stated in the documentation for FromRawFd:

This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause memory unsafety in code that relies on it being true.

You may be able to make use of the fcntl function to test if a given descriptor is valid, but I don't know the details about how these work in the presence of threading — it's possible that one thread checks for validity of the file descriptor and it's valid, another thread closes it, then the first attempts to use it. This is a straight-forward Time-of-check to time-of-use issue.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • How to deal with `unsafe` in these cases? is there a way to "trap/catch" if the file descriptor is not valid, etc? – nbari Jun 12 '19 at 18:31
  • It's not clear to me where the UB comes from, but I also don't understand enough about system calls to know what could go wrong when doing IO on a bad FD. How would a "safe" construct like the CPython interpreter handle something like this? – turbulencetoo Jun 12 '19 at 18:36
  • @turbulencetoo that's a good point; I have gotten error messages about invalid file descriptors, so there might be some checking at the OS level that prevents that. I'll see if I can make that text more accurate. – Shepmaster Jun 12 '19 at 18:38
  • 1
    @turbulencetoo I've updated to use the docs exact wording, which uses "memory unsafety" as opposed to "undefined behavior" – Shepmaster Jun 12 '19 at 18:47