Letting child process read stdin after dropping privileges

I'm trying to execute a command from my Rust program with reduced privileges, but when that process is trying to open its own /dev/fd/0 it's getting -1 EACCES (Permission denied). This is rather unpleasant as then the command I'm running can't read stdin (or others) as a file.

I've set Command to use Stdio::piped() but am I wrong to conclude that those pipes are created(?) with the wrong permissions when gid and pid are set?

Minimal example:

use std::process::Stdio;

use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::process::Command;

#[tokio::main]
async fn main() {
    tokio::task::spawn(async move {
        let mut command = Command::new("/bin/cat");
        command.arg("/dev/fd/0")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .uid(35565)
            .gid(35565);

        let mut child = command.spawn().unwrap();
        child.stdin.take().unwrap().write_all("foo".as_ref()).await.unwrap();
    });
}

Is there a workaround here or is this a bug in Tokio or std that this caveat is not handled?

Side note, possibly will not resolve your issue. I would expect that your file argument for tee should be stdout (/dev/fd/1) instead of stdin.

tee in this case is just an example of an utility that would exhibit getting EACCES when trying to open /dev/fd/0, I'll change it to cat, but it doesn't matter much.

I've simplified your demo further, and it appears that this has nothing to do with tokio (or the rust std lib, for that matter) but rather with the way that the /dev/fd virtual file system works. This post here appears to handle a similar issue: Permission denied on /dev/stderr after sudo - Super User

1 Like

Thanks, it indeed seems like a /dev/fd quirk.

However, I'd kinda expect Command (be it std or tokio) to return an error if the requested combination/configuration would be practically unusable unless other steps are taken beforehand. No? (Ideally even just silently handle the case in the background)

I also don't know how to resolve this in safe Rust meanwhile :pensive:

Maybe a more concrete use case would be helpful here: has the program you want to start an explicit parameter that tells it to read from stdin? For example for cat and many other *nix utilities this is -.

Unfortunately not.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.