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

Add std{in,out} for communication with child process #31

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ name = "system-info-read"
path = "tests/system-info-read.rs"
harness = false

[[test]]
name = "stdio"
path = "tests/stdio.rs"
harness = false

29 changes: 26 additions & 3 deletions platform/linux/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use profile::{Operation, PathPattern, Profile};
use sandbox::Command;

use libc::{self, c_char, c_int, c_ulong, c_void, gid_t, pid_t, size_t, ssize_t, uid_t};
use libc::dup2;
use libc::{STDIN_FILENO, STDOUT_FILENO};
use std::env;
use std::ffi::{CString, OsStr, OsString};
use std::fs::{self, File};
Expand All @@ -26,6 +28,7 @@ use std::mem;
use std::os::unix::prelude::OsStrExt;
use std::path::{Path, PathBuf};
use std::ptr;
use std::os::unix::io::FromRawFd;

/// Creates a namespace and sets up a chroot jail.
pub fn activate(profile: &Profile) -> Result<(),c_int> {
Expand Down Expand Up @@ -227,11 +230,17 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result<Process> {
unsafe {
// Create a pipe so we can communicate the PID of our grandchild back.
let mut pipe_fds = [0, 0];
assert!(libc::pipe(&mut pipe_fds[0]) == 0);
assert_eq!(libc::pipe(&mut pipe_fds[0]), 0);

// Create two other pipes for stdin and stdout
let mut io1_fds = [0, 0];
let mut io2_fds = [0, 0];
assert_eq!(libc::pipe(&mut io1_fds[0]), 0);
assert_eq!(libc::pipe(&mut io2_fds[0]), 0);

// Set this `prctl` flag so that we can wait on our grandchild. (Otherwise it'll be
// reparented to init.)
assert!(seccomp::prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == 0);
assert_eq!(seccomp::prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this cleanup to a separate commit?


// Fork so that we can unshare without removing our ability to create threads.
if fork() == 0 {
Expand All @@ -246,7 +255,16 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result<Process> {
match fork() {
0 => {
// Enter the auxiliary namespaces.
assert!(unshare(unshare_flags) == 0);
assert_eq!(unshare(unshare_flags), 0);

assert_eq!(libc::close(io1_fds[1]), 0);
assert_eq!(libc::close(io2_fds[0]), 0);

assert_eq!(dup2(io1_fds[0], STDIN_FILENO), STDIN_FILENO);
assert_eq!(libc::close(io1_fds[0]), 0);

assert_eq!(dup2(io2_fds[1], STDOUT_FILENO), STDOUT_FILENO);
assert_eq!(libc::close(io2_fds[1]), 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indent these to match the other statements in this block.


// Go ahead and start the command.
drop(unix::process::exec(command));
Expand All @@ -266,6 +284,9 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result<Process> {
// Grandparent execution continues here. First, close the writing end of the pipe.
libc::close(pipe_fds[1]);

libc::close(io1_fds[0]);
libc::close(io2_fds[1]);

// Retrieve our grandchild's PID.
let mut grandchild_pid: pid_t = 0;
assert!(libc::read(pipe_fds[0],
Expand All @@ -274,6 +295,8 @@ pub fn start(profile: &Profile, command: &mut Command) -> io::Result<Process> {
mem::size_of::<pid_t>() as ssize_t);
Ok(Process {
pid: grandchild_pid,
stdin: File::from_raw_fd(io1_fds[1]),
stdout: File::from_raw_fd(io2_fds[0]),
})
}
}
Expand Down
33 changes: 32 additions & 1 deletion platform/unix/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@

use sandbox::Command;

use libc::{c_char, c_int, pid_t};
use libc::{self, c_char, c_int, pid_t};
use libc::pipe;
use libc::dup2;
use libc::{STDIN_FILENO, STDOUT_FILENO};
use std::ffi::CString;
use std::io;
use std::fs::File;
use std::ptr;
use std::str;
use std::os::unix::io::FromRawFd;

pub fn exec(command: &Command) -> io::Error {
let mut args: Vec<_> = vec![command.module_path.as_ptr()];
Expand All @@ -43,15 +48,39 @@ pub fn exec(command: &Command) -> io::Error {
}

pub fn spawn(command: &Command) -> io::Result<Process> {
let mut fd1 = [0 as c_int; 2];
let mut fd2 = [0 as c_int; 2];

if unsafe { pipe(&mut fd1[0]) } < 0 {
return Err(io::Error::last_os_error());
}
if unsafe { pipe(&mut fd2[0]) } < 0 {
return Err(io::Error::last_os_error());
}

unsafe {
match fork() {
0 => {
libc::close(fd1[1]);
libc::close(fd2[0]);

assert_eq!(dup2(fd1[0], STDIN_FILENO), STDIN_FILENO);
libc::close(fd1[0]);

assert_eq!(dup2(fd2[1], STDOUT_FILENO), STDOUT_FILENO);
libc::close(fd2[1]);

drop(exec(command));
panic!()
}
pid => {
libc::close(fd1[0]);
libc::close(fd2[1]);

Ok(Process {
pid: pid,
stdin: File::from_raw_fd(fd1[1]),
stdout: File::from_raw_fd(fd2[0]),
})
}
}
Expand All @@ -61,6 +90,8 @@ pub fn spawn(command: &Command) -> io::Result<Process> {
#[allow(missing_copy_implementations)]
pub struct Process {
pub pid: pid_t,
pub stdin: File,
pub stdout: File,
}

impl Process {
Expand Down
43 changes: 43 additions & 0 deletions tests/stdio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/

extern crate gaol;

//use gaol::profile::{AddressPattern, Operation, OperationSupport, OperationSupportLevel};
//use gaol::profile::{PathPattern, Profile};
use gaol::profile::Profile;
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods, Command, Sandbox, SandboxMethods};
use std::env;
use std::io;
use std::io::{Read, Write};

fn main() {
match env::args().skip(1).next() {
Some(ref arg) if arg == "child" => {
// This is the child process.
ChildSandbox::new(Profile::new(vec![]).unwrap()).activate().unwrap();

let mut buf = vec![0];
io::stdin().read_exact(&mut buf[..]).unwrap();
assert_eq!(buf, b"A");

io::stdout().write_all(b"B").unwrap();
io::stdout().flush().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: fix indentation here.

}
_ => {
// This is the parent process.
let mut command = Command::me().unwrap();
let mut cmd = Sandbox::new(Profile::new(vec![]).unwrap()).start(command.arg("child")).unwrap();

cmd.stdin.write_all(b"A").unwrap();
cmd.stdin.flush().unwrap();

let mut buf = vec![0];
cmd.stdout.read_exact(&mut buf[..]).unwrap();
assert_eq!(buf, b"B");

cmd.wait().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: fix indentation here too.

}
}
}