memfd_exec/
executable.rs

1//! This essentially reimplements the code at:
2//! which is an internal implementation of the code at:
3//! <https://github.com/rust-lang/rust/blob/master/library/std/src/process.rs>
4//! <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/unix/process/process_unix.rs>
5//! <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/unix/process/process_common.rs>
6//! for external use to provide a very similar interface to process::Command for in-memory executables
7
8use std::{
9    collections::BTreeMap,
10    ffi::{CStr, CString, OsStr, OsString},
11    io::{Error, ErrorKind, Result},
12    mem::MaybeUninit,
13    os::unix::prelude::{OsStrExt, OsStringExt},
14    path::Path,
15    ptr::null_mut,
16};
17
18use libc::{pid_t, sigemptyset, signal};
19use nix::{
20    sys::memfd::{memfd_create, MemFdCreateFlag},
21    unistd::{close, fexecve, write},
22};
23
24use crate::{
25    anon_pipe::anon_pipe,
26    child::Child,
27    command_env::CommandEnv,
28    cvt::{cvt, cvt_nz, cvt_r},
29    output::Output,
30    process::{ExitStatus, Process},
31    stdio::{ChildPipes, Stdio, StdioPipes},
32};
33
34/// This is the main struct used to create an in-memory only executable. Wherever possible, it
35/// is intended to be a drop-in replacement for the standard library's `process::Command` struct.
36///
37/// # Examples
38///
39/// This example is the "motivating case" for this library. It shows how to execute a binary
40/// entirely from memory, without writing it to disk. This is useful for executing binaries
41/// sneakily, or (the real reason) for bundling executables that are a pain to set up and
42/// compile, but whose static versions are very portable. Here's a "sneaky" example:
43///
44/// ```no_compile
45/// use memfd_exec::{MemFdExecutable, Stdio};
46///
47/// // You can include the entirety of a binary (for example, nc)
48/// let nc_binary= include_bytes!("/usr/bin/nc-static");
49///
50///
51/// // The first argument is just the name for the program, you can pick anything but
52/// // if the program expects a specific argv[0] value, use that.
53/// // The second argument is the binary code to execute.
54/// let mut cmd = MemFdExecutable::new("nc", nc_binary)
55///     // We can pass arbitrary args just like with Command. Here, we'll execute nc
56///     // to listen on a port and run a shell for connections, entirely from memory.
57///     .arg("-l")
58///     .arg("1234")
59///     .arg("-e")
60///     .arg("/bin/sh")
61///     // And we can get piped stdin/stdout just like with Command
62///     .stdout(Stdio::piped())
63///     // Spawn starts the child process and gives us a handle back
64///     .spawn()
65///     .expect("failed to execute process");
66///
67/// // Then, we can wait for the program to exit.
68/// cmd.wait();
69/// ```
70#[derive(Debug)]
71pub struct MemFdExecutable<'a> {
72    /// The contents of the ELF executable to run. This content can be included in the file
73    /// using the `include_bytes!()` macro, or you can do fancy things like read it in from
74    /// a socket.
75    code: &'a [u8],
76    /// The name of the program, this value is the argv\[0\] argument to the binary when
77    /// executed. If the program expects something specific here, that value should be
78    /// used, otherwise any name will do
79    program: CString,
80    /// The arguments to the program, excluding the program name
81    args: Vec<CString>,
82    /// The whole argv array, including the program name
83    argv: Argv,
84    /// The environment variables to set for the program
85    env: CommandEnv,
86    /// The current working directory to set for the program
87    cwd: Option<CString>,
88    /// The program's stdin handle
89    pub stdin: Option<Stdio>,
90    /// The program's stdout handle
91    pub stdout: Option<Stdio>,
92    /// The program's stderr handle
93    pub stderr: Option<Stdio>,
94    /// Holdover from Command, whether there was a NUL in the arguments or not
95    saw_nul: bool,
96}
97
98#[derive(Debug)]
99struct Argv(Vec<CString>);
100
101unsafe impl Send for Argv {}
102unsafe impl Sync for Argv {}
103
104fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
105    CString::new(s.as_bytes()).unwrap_or_else(|_e| {
106        *saw_nul = true;
107        CString::new("<string-with-nul>").unwrap()
108    })
109}
110
111fn construct_envp(env: BTreeMap<OsString, OsString>, saw_nul: &mut bool) -> Vec<CString> {
112    let mut result = Vec::with_capacity(env.len());
113    for (mut k, v) in env {
114        // Reserve additional space for '=' and null terminator
115        k.reserve_exact(v.len() + 2);
116        k.push("=");
117        k.push(&v);
118
119        // Add the new entry into the array
120        if let Ok(item) = CString::new(k.into_vec()) {
121            result.push(item);
122        } else {
123            *saw_nul = true;
124        }
125    }
126
127    result
128}
129
130impl<'a> MemFdExecutable<'a> {
131    /// Create a new MemFdExecutable with the given name and code. The name is the name of the
132    /// program, and is used as the argv\[0\] argument to the program. The code is the binary
133    /// code to execute (usually, the entire contents of an ELF file).
134    ///
135    /// # Examples
136    ///
137    /// You can run code that is included directly in your executable with `include_bytes!()`:
138    ///
139    /// ```no_compile
140    /// use memfd_exec::MemFdExecutable;
141    ///
142    /// let code = include_bytes!("/usr/bin/nc-static");
143    ///
144    /// let mut cmd = MemFdExecutable::new("nc", code)
145    ///     .arg("-l")
146    ///     .arg("1234")
147    ///     .arg("-e")
148    ///     .arg("/bin/sh")
149    ///     .status()
150    ///     .expect("failed to execute process");
151    /// ```
152    ///
153    pub fn new<S: AsRef<OsStr>>(name: S, code: &'a [u8]) -> Self {
154        let mut saw_nul = false;
155        let name = os2c(name.as_ref(), &mut saw_nul);
156        Self {
157            code,
158            program: name.clone(),
159            args: vec![name.clone()],
160            argv: Argv(vec![name]),
161            env: Default::default(),
162            cwd: None,
163            stdin: None,
164            stdout: None,
165            stderr: None,
166            saw_nul,
167        }
168    }
169
170    /// Add an argument to the program. This is equivalent to `Command::arg()`.
171    pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
172        let arg = os2c(arg.as_ref(), &mut self.saw_nul);
173        self.argv.0.push(arg.clone());
174        self.args.push(arg);
175        self
176    }
177
178    /// Add multiple arguments to the program. This is equivalent to `Command::args()`.
179    pub fn args<I, S>(&mut self, args: I) -> &mut Self
180    where
181        I: IntoIterator<Item = S>,
182        S: AsRef<OsStr>,
183    {
184        for arg in args {
185            self.arg(arg.as_ref());
186        }
187        self
188    }
189
190    /// Add an environment variable to the program. This is equivalent to `Command::env()`.
191    pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
192    where
193        K: AsRef<OsStr>,
194        V: AsRef<OsStr>,
195    {
196        self.env_mut().set(key.as_ref(), val.as_ref());
197        self
198    }
199
200    /// Add multiple environment variables to the program. This is equivalent to `Command::envs()`.
201    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
202    where
203        I: IntoIterator<Item = (K, V)>,
204        K: AsRef<OsStr>,
205        V: AsRef<OsStr>,
206    {
207        for (ref key, ref val) in vars {
208            self.env_mut().set(key.as_ref(), val.as_ref());
209        }
210        self
211    }
212
213    /// Remove an environment variable from the program. This is equivalent to `Command::env_remove()`.
214    pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
215        self.env_mut().remove(key.as_ref());
216        self
217    }
218
219    /// Clear all environment variables from the program. This is equivalent to `Command::env_clear()`.
220    pub fn env_clear(&mut self) -> &mut Self {
221        self.env_mut().clear();
222        self
223    }
224
225    /// Set the current working directory for the program. This is equivalent to `Command::current_dir()`.
226    pub fn cwd<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
227        self.cwd = Some(os2c(dir.as_ref().as_ref(), &mut self.saw_nul));
228        self
229    }
230
231    /// Set the stdin handle for the program. This is equivalent to `Command::stdin()`. The
232    /// default is to inherit the current process's stdin. Note that this `Stdio` is not the
233    /// same exactly as `process::Stdio`, but it is feature-equivalent.
234    ///
235    /// # Examples
236    ///
237    /// This example creates a `cat` process that will read in the contents passed to its
238    /// stdin handle and write them to a null stdout (i.e. it will be discarded). The same
239    /// methodology can be used to read from stderr/stdout.
240    ///
241    /// ```no_run
242    /// use std::thread::spawn;
243    /// use std::io::Write;
244    ///
245    /// use memfd_exec::{MemFdExecutable, Stdio};
246    ///
247    /// let mut cat_cmd = MemFdExecutable::new("cat", include_bytes!("/bin/cat"))
248    ///    .stdin(Stdio::piped())
249    ///    .stdout(Stdio::null())
250    ///    .spawn()
251    ///    .expect("failed to spawn cat");
252    ///
253    /// let mut cat_stdin = cat_cmd.stdin.take().expect("failed to open stdin");
254    /// spawn(move || {
255    ///    cat_stdin.write_all(b"hello world").expect("failed to write to stdin");
256    /// });
257    /// ```
258    pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
259        self.stdin = Some(cfg.into());
260        self
261    }
262
263    /// Set the stdout handle for the program. This is equivalent to `Command::stdout()`. The
264    ///
265    /// # Arguments
266    /// * `cfg` - The configuration for the stdout handle. This will usually be one of the following:
267    ///     * `Stdio::inherit()` - Inherit the current process's stdout handle
268    ///     * `Stdio::piped()` - Create a pipe to the child process's stdout. This can be read
269    ///     * `Stdio::null()` - Discard all output to stdout
270    ///
271    /// # Examples
272    ///
273    /// This example creates a `cat` process that will read in the contents passed to its stdin handle
274    /// and read them from its stdout handle. The same methodology can be used to read from stderr/stdout.
275    ///
276    /// ```
277    /// use std::thread::spawn;
278    /// use std::fs::read;
279    /// use std::io::{Read, Write};
280    ///
281    /// use memfd_exec::{MemFdExecutable, Stdio};
282    ///
283    /// let mut cat = MemFdExecutable::new("cat", &read("/bin/cat").unwrap())
284    ///     .stdin(Stdio::piped())
285    ///     .stdout(Stdio::piped())
286    ///     .spawn()
287    ///     .expect("failed to spawn cat");
288    ///
289    /// let mut cat_stdin = cat.stdin.take().expect("failed to open stdin");
290    /// let mut cat_stdout = cat.stdout.take().expect("failed to open stdout");
291    ///
292    /// spawn(move || {
293    ///    cat_stdin.write_all(b"hello world").expect("failed to write to stdin");
294    /// });
295    ///
296    /// let mut output = Vec::new();
297    /// cat_stdout.read_to_end(&mut output).expect("failed to read from stdout");
298    /// assert_eq!(output, b"hello world");
299    /// cat.wait().expect("failed to wait on cat");
300    /// ```
301    pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
302        self.stdout = Some(cfg.into());
303        self
304    }
305
306    /// Set the stderr handle for the program. This is equivalent to `Command::stderr()`. The
307    ///
308    /// # Arguments
309    /// * `cfg` - The configuration for the stderr handle. This will usually be one of the following:
310    ///    * `Stdio::inherit()` - Inherit the current process's stderr handle
311    ///    * `Stdio::piped()` - Create a pipe to the child process's stderr. This can be read
312    ///    * `Stdio::null()` - Discard all output to stderr
313    pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
314        self.stderr = Some(cfg.into());
315        self
316    }
317
318    /// Spawn the program as a child process. This is equivalent to `Command::spawn()`.
319    pub fn spawn(&mut self) -> Result<Child> {
320        let default = Stdio::Inherit;
321        let needs_stdin = true;
322        const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
323
324        let envp = self.capture_env();
325
326        if self.saw_nul() {
327            // TODO: Need err?
328        }
329
330        let (ours, theirs) = self.setup_io(default, needs_stdin)?;
331
332        let (input, output) = anon_pipe()?;
333
334        // Whatever happens after the fork is almost for sure going to touch or
335        // look at the environment in one way or another (PATH in `execvp` or
336        // accessing the `environ` pointer ourselves). Make sure no other thread
337        // is accessing the environment when we do the fork itself.
338        //
339        // Note that as soon as we're done with the fork there's no need to hold
340        // a lock any more because the parent won't do anything and the child is
341        // in its own process. Thus the parent drops the lock guard while the child
342        // forgets it to avoid unlocking it on a new thread, which would be invalid.
343        // TODO: Yeah....I had to remove the env lock. Whoops! Don't multithread env with this
344        // you insane person
345        let pid = unsafe { self.do_fork()? };
346
347        if pid == 0 {
348            drop(input);
349            let Err(err) = (unsafe { self.do_exec(theirs, envp) }) else { unreachable!("..."); };
350            panic!("failed to exec: {}", err);
351        }
352
353        drop(output);
354
355        // Safety: We obtained the pidfd from calling `clone3` with
356        // `CLONE_PIDFD` so it's valid an otherwise unowned.
357        let mut p = unsafe { Process::new(pid) };
358        let mut bytes = [0; 8];
359
360        // loop to handle EINTR
361        loop {
362            match input.read(&mut bytes) {
363                Ok(0) => return Ok(Child::new(p, ours)),
364                Ok(8) => {
365                    let (errno, footer) = bytes.split_at(4);
366                    assert_eq!(
367                        CLOEXEC_MSG_FOOTER, footer,
368                        "Validation on the CLOEXEC pipe failed: {:?}",
369                        bytes
370                    );
371                    let errno = i32::from_be_bytes(errno.try_into().unwrap());
372                    assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
373                    return Err(Error::from_raw_os_error(errno));
374                }
375                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
376                Err(e) => {
377                    assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
378                    panic!("the CLOEXEC pipe failed: {e:?}")
379                }
380                Ok(..) => {
381                    // pipe I/O up to PIPE_BUF bytes should be atomic
382                    assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
383                    panic!("short read on the CLOEXEC pipe")
384                }
385            }
386        }
387    }
388
389    /// Spawn the program as a child process and wait for it to complete, obtaining the
390    /// output and exit status. This is equivalent to `Command::output()`.
391    pub fn output(&mut self) -> Result<Output> {
392        self.spawn()?.wait_with_output()
393    }
394
395    /// Spawn the program as a child process and wait for it to complete, obtaining the
396    /// exit status. This is equivalent to `Command::status()`.
397    pub fn status(&mut self) -> Result<ExitStatus> {
398        self.spawn()?.wait()
399    }
400
401    /// Set the program name (argv\[0\]) to a new value.
402    ///
403    /// # Arguments
404    /// * `name` - The new name for the program. This will be used as the first argument
405    pub fn set_program(&mut self, program: &OsStr) {
406        let arg = os2c(program, &mut self.saw_nul);
407        self.argv.0[0] = arg.clone();
408        self.args[0] = arg;
409    }
410
411    fn env_mut(&mut self) -> &mut CommandEnv {
412        &mut self.env
413    }
414
415    fn setup_io(&self, default: Stdio, needs_stdin: bool) -> Result<(StdioPipes, ChildPipes)> {
416        let null = Stdio::Null;
417        let default_stdin = if needs_stdin { &default } else { &null };
418        let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
419        let stdout = self.stdout.as_ref().unwrap_or(&default);
420        let stderr = self.stderr.as_ref().unwrap_or(&default);
421        let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
422        let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
423        let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
424        let ours = StdioPipes {
425            stdin: our_stdin,
426            stdout: our_stdout,
427            stderr: our_stderr,
428        };
429        let theirs = ChildPipes {
430            stdin: their_stdin,
431            stdout: their_stdout,
432            stderr: their_stderr,
433        };
434        Ok((ours, theirs))
435    }
436
437    fn saw_nul(&self) -> bool {
438        self.saw_nul
439    }
440
441    /// Get the current working directory for the child process.
442    pub fn get_cwd(&self) -> &Option<CString> {
443        &self.cwd
444    }
445
446    unsafe fn do_fork(&mut self) -> Result<pid_t> {
447        cvt(libc::fork())
448    }
449
450    fn capture_env(&mut self) -> Option<Vec<CString>> {
451        let maybe_env = self.env.capture_if_changed();
452        maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
453    }
454
455    /// Execute the command as a new process, replacing the current process.
456    ///
457    /// This function will not return.
458    ///
459    /// # Arguments
460    /// * `default` - The default stdio to use if the child process does not specify.
461    pub fn exec(&mut self, default: Stdio) -> Error {
462        let envp = self.capture_env();
463
464        if self.saw_nul() {
465            return Error::new(ErrorKind::InvalidInput, "nul byte found in provided data");
466        }
467
468        match self.setup_io(default, true) {
469            Ok((_, theirs)) => unsafe {
470                let Err(e) = self.do_exec(theirs, envp) else { unreachable!("..."); };
471                e
472            },
473            Err(e) => e,
474        }
475    }
476
477    /// Get the program name to use for the child process as a C string.
478    pub fn get_program_cstr(&self) -> &CStr {
479        &self.program
480    }
481
482    /// Get the program argv to use for the child process.
483    pub fn get_argv(&self) -> &Vec<CString> {
484        &self.argv.0
485    }
486
487    /// Get whether PATH has been affected by changes to the environment variables
488    /// of this command.
489    pub fn env_saw_path(&self) -> bool {
490        self.env.have_changed_path()
491    }
492
493    /// Get whether the program (argv\[0\]) is a path, as opposed to a name.
494    pub fn program_is_path(&self) -> bool {
495        self.program.to_bytes().contains(&b'/')
496    }
497
498    unsafe fn do_exec(
499        &mut self,
500        stdio: ChildPipes,
501        maybe_envp: Option<Vec<CString>>,
502    ) -> Result<()> {
503        if let Some(fd) = stdio.stdin.fd() {
504            cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
505        }
506        if let Some(fd) = stdio.stdout.fd() {
507            cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
508        }
509        if let Some(fd) = stdio.stderr.fd() {
510            cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
511        }
512
513        if let Some(ref cwd) = *self.get_cwd() {
514            cvt(libc::chdir(cwd.as_ptr()))?;
515        }
516
517        {
518            // Reset signal handling so the child process starts in a
519            // standardized state. libstd ignores SIGPIPE, and signal-handling
520            // libraries often set a mask. Child processes inherit ignored
521            // signals and the signal mask from their parent, but most
522            // UNIX programs do not reset these things on their own, so we
523            // need to clean things up now to avoid confusing the program
524            // we're about to run.
525            let mut set = MaybeUninit::<libc::sigset_t>::uninit();
526            cvt(sigemptyset(set.as_mut_ptr()))?;
527            cvt_nz(libc::pthread_sigmask(
528                libc::SIG_SETMASK,
529                set.as_ptr(),
530                null_mut(),
531            ))?;
532
533            {
534                let ret = signal(libc::SIGPIPE, libc::SIG_DFL);
535                if ret == libc::SIG_ERR {
536                    return Err(Error::last_os_error());
537                }
538            }
539        }
540
541        // TODO: Env resetting isn't implemented because we're using fexecve not execvp
542
543        // Map the executable last, because it's a huge hit to memory if something else failed
544        let mfd = memfd_create(
545            CString::new("rust_exec").unwrap().as_c_str(),
546            MemFdCreateFlag::MFD_CLOEXEC,
547        )
548        .unwrap();
549
550        if let Ok(n) = write(mfd, self.code) {
551            if n != self.code.len() {
552                return Err(Error::new(
553                    ErrorKind::BrokenPipe,
554                    "Failed to write to memfd",
555                ));
556            }
557        } else {
558            return Err(Error::last_os_error());
559        }
560
561        let argv = self
562            .get_argv()
563            .iter()
564            .map(|s| s.as_c_str())
565            .collect::<Vec<_>>();
566
567        let maybe_envp = maybe_envp.unwrap_or_default();
568
569        let envp = maybe_envp.iter().map(|s| s.as_c_str()).collect::<Vec<_>>();
570
571        if let Err(err) = fexecve(mfd, &argv, &envp) {
572            // If we failed to exec, we need to close the memfd
573            // so that the child process doesn't leak it
574            let _ = close(mfd);
575            return Err(Error::new(ErrorKind::BrokenPipe, err));
576        }
577        Err(Error::last_os_error())
578    }
579}