Skip to content

Commit 3a0bcde

Browse files
authored
Unrolled build for rust-lang#137759
Rollup merge of rust-lang#137759 - joshtriplett:command-chroot, r=Amanieu Add `std::os::unix::process::CommandExt::chroot` to safely chroot a child process This adds a `chroot` method to the `CommandExt` extension trait for the `Command` builder, to set a directory to chroot into. This will chroot the child process into that directory right before calling chdir for the `Command`'s working directory. To avoid allowing a process to have a working directory outside of the chroot, if the `Command` does not yet have a working directory set, `chroot` will set its working directory to "/". --- ACP: rust-lang/libs-team#551 This PR currently has the tracking issue set to "none"; if the ACP is approved, I'll file a tracking issue and update the PR.
2 parents bbd3a5a + 348c1b0 commit 3a0bcde

File tree

4 files changed

+47
-0
lines changed

4 files changed

+47
-0
lines changed

library/std/src/os/unix/process.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use cfg_if::cfg_if;
88

99
use crate::ffi::OsStr;
1010
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
11+
use crate::path::Path;
1112
use crate::sealed::Sealed;
1213
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
1314
use crate::{io, process, sys};
@@ -197,6 +198,18 @@ pub trait CommandExt: Sealed {
197198
/// ```
198199
#[stable(feature = "process_set_process_group", since = "1.64.0")]
199200
fn process_group(&mut self, pgroup: i32) -> &mut process::Command;
201+
202+
/// Set the root of the child process. This calls `chroot` in the child process before executing
203+
/// the command.
204+
///
205+
/// This happens before changing to the directory specified with
206+
/// [`process::Command::current_dir`], and that directory will be relative to the new root.
207+
///
208+
/// If no directory has been specified with [`process::Command::current_dir`], this will set the
209+
/// directory to `/`, to avoid leaving the current directory outside the chroot. (This is an
210+
/// intentional difference from the underlying `chroot` system call.)
211+
#[unstable(feature = "process_chroot", issue = "141298")]
212+
fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command;
200213
}
201214

202215
#[stable(feature = "rust1", since = "1.0.0")]
@@ -242,6 +255,11 @@ impl CommandExt for process::Command {
242255
self.as_inner_mut().pgroup(pgroup);
243256
self
244257
}
258+
259+
fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command {
260+
self.as_inner_mut().chroot(dir.as_ref());
261+
self
262+
}
245263
}
246264

247265
/// Unix-specific extensions to [`process::ExitStatus`] and

library/std/src/sys/process/unix/common.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ pub struct Command {
8888

8989
program_kind: ProgramKind,
9090
cwd: Option<CString>,
91+
chroot: Option<CString>,
9192
uid: Option<uid_t>,
9293
gid: Option<gid_t>,
9394
saw_nul: bool,
@@ -182,6 +183,7 @@ impl Command {
182183
program_kind,
183184
env: Default::default(),
184185
cwd: None,
186+
chroot: None,
185187
uid: None,
186188
gid: None,
187189
saw_nul,
@@ -206,6 +208,7 @@ impl Command {
206208
program_kind,
207209
env: Default::default(),
208210
cwd: None,
211+
chroot: None,
209212
uid: None,
210213
gid: None,
211214
saw_nul,
@@ -254,6 +257,12 @@ impl Command {
254257
pub fn pgroup(&mut self, pgroup: pid_t) {
255258
self.pgroup = Some(pgroup);
256259
}
260+
pub fn chroot(&mut self, dir: &Path) {
261+
self.chroot = Some(os2c(dir.as_os_str(), &mut self.saw_nul));
262+
if self.cwd.is_none() {
263+
self.cwd(&OsStr::new("/"));
264+
}
265+
}
257266

258267
#[cfg(target_os = "linux")]
259268
pub fn create_pidfd(&mut self, val: bool) {
@@ -326,6 +335,10 @@ impl Command {
326335
pub fn get_pgroup(&self) -> Option<pid_t> {
327336
self.pgroup
328337
}
338+
#[allow(dead_code)]
339+
pub fn get_chroot(&self) -> Option<&CStr> {
340+
self.chroot.as_deref()
341+
}
329342

330343
pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
331344
&mut self.closures

library/std/src/sys/process/unix/unix.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,15 @@ impl Command {
323323
cvt(libc::setuid(u as uid_t))?;
324324
}
325325
}
326+
if let Some(chroot) = self.get_chroot() {
327+
#[cfg(not(target_os = "fuchsia"))]
328+
cvt(libc::chroot(chroot.as_ptr()))?;
329+
#[cfg(target_os = "fuchsia")]
330+
return Err(io::const_error!(
331+
io::ErrorKind::Unsupported,
332+
"chroot not supported by fuchsia"
333+
));
334+
}
326335
if let Some(cwd) = self.get_cwd() {
327336
cvt(libc::chdir(cwd.as_ptr()))?;
328337
}
@@ -447,6 +456,7 @@ impl Command {
447456
|| (self.env_saw_path() && !self.program_is_path())
448457
|| !self.get_closures().is_empty()
449458
|| self.get_groups().is_some()
459+
|| self.get_chroot().is_some()
450460
{
451461
return Ok(None);
452462
}

library/std/src/sys/process/unix/vxworks.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ impl Command {
2727
"nul byte found in provided data",
2828
));
2929
}
30+
if self.get_chroot().is_some() {
31+
return Err(io::const_error!(
32+
ErrorKind::Unsupported,
33+
"chroot not supported by vxworks",
34+
));
35+
}
3036
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
3137
let mut p = Process { pid: 0, status: None };
3238

0 commit comments

Comments
 (0)