use anyhow::{Context, Result};
use memfd::FileSeal;
use nix::unistd::fexecve;
use std::{env, ffi::CString, fs, io, io::Seek, os::unix::prelude::AsRawFd};
const EXE: &str = "/proc/self/exe";
const SEALS: &[FileSeal] = &[
FileSeal::SealShrink,
FileSeal::SealGrow,
FileSeal::SealWrite,
FileSeal::SealSeal,
];
pub fn rexec() -> Result<()> {
let exe = fs::File::open(EXE).context("failed to open exe")?;
match memfd::Memfd::try_from_file(exe) {
Ok(memfd) => {
assert_eq!(
memfd.seals().context("failed to get exe seals")?,
SEALS.iter().cloned().collect()
);
Ok(())
}
Err(exe) => {
let exe_name = env::current_exe().context("failed to get path of current exe")?;
let memfd = memfd::MemfdOptions::default()
.allow_sealing(true)
.close_on_exec(true)
.create(exe_name.display().to_string())
.context("failed to create memfd")?;
let mut exe = io::BufReader::new(exe);
io::copy(&mut exe, &mut memfd.as_file()).context("failed to copy exe")?;
memfd.as_file().rewind().context("failed to seek")?;
SEALS
.iter()
.try_for_each(|seal| memfd.add_seal(*seal))
.context("failed to add seal")?;
let args = env::args()
.map(CString::new)
.collect::<Result<Vec<_>, _>>()
.context("failed to convert arg")?;
let env: Vec<_> = env::vars()
.map(|(k, v)| format!("{k}={v}"))
.map(CString::new)
.collect::<Result<Vec<_>, _>>()
.context("failed to convert env")?;
panic!("{:?}", fexecve(memfd.as_raw_fd(), &args, &env));
}
}
}