1#[macro_use]
5pub mod enum_passthrough;
6#[macro_use]
7pub mod macros;
8
9pub mod byte_queue;
10pub mod callback_queue;
11pub mod childpid_watcher;
12pub mod counter;
13pub mod give;
14pub mod interval_map;
15pub mod legacy_callback_queue;
16pub mod once_set;
17pub mod pcap_writer;
18pub mod perf_timer;
19pub mod proc_maps;
20pub mod shm_cleanup;
21pub mod sockaddr;
22pub mod status_bar;
23pub mod stream_len;
24pub mod syscall;
25pub mod units;
26
27use std::collections::HashSet;
28use std::ffi::{CString, OsStr};
29use std::io::Read;
30use std::marker::PhantomData;
31use std::os::unix::fs::{DirBuilderExt, MetadataExt};
32use std::os::unix::prelude::OsStrExt;
33use std::path::{Path, PathBuf};
34use std::sync::RwLock;
35
36use once_cell::sync::Lazy;
37use shadow_shim_helper_rs::HostId;
38
39use crate::core::worker::Worker;
40use crate::host::host::Host;
41
42#[derive(Debug)]
45pub struct HostTreePointer<T> {
46    host_id: HostId,
47    ptr: *mut T,
48}
49
50impl<T> Copy for HostTreePointer<T> {}
53impl<T> Clone for HostTreePointer<T> {
54    fn clone(&self) -> Self {
55        *self
56    }
57}
58
59unsafe impl<T> Send for HostTreePointer<T> {}
60unsafe impl<T> Sync for HostTreePointer<T> {}
61
62impl<T> HostTreePointer<T> {
63    pub fn new_for_host(host_id: HostId, ptr: *mut T) -> Self {
66        Self { host_id, ptr }
67    }
68
69    pub fn new(ptr: *mut T) -> Self {
72        let host_id = Worker::with_active_host(|h| h.info().id);
73        Self::new_for_host(host_id.unwrap(), ptr)
74    }
75
76    pub unsafe fn ptr(&self) -> *mut T {
86        Worker::with_active_host(|h| unsafe { self.ptr_with_host(h) }).unwrap()
95    }
96
97    pub unsafe fn ptr_with_host(&self, host: &Host) -> *mut T {
107        assert_eq!(self.host_id, host.info().id);
108        self.ptr
109    }
110
111    pub unsafe fn ptr_unchecked(&self) -> *mut T {
119        self.ptr
120    }
121}
122
123pub trait IsSend: Send {}
125
126pub trait IsSync: Sync {}
128
129#[derive(Debug)]
139pub struct Magic<T: 'static> {
140    #[cfg(debug_assertions)]
141    magic: std::any::TypeId,
142    _phantom: PhantomData<T>,
148}
149
150impl<T> Magic<T> {
151    pub fn new() -> Self {
152        Self {
153            #[cfg(debug_assertions)]
154            magic: std::any::TypeId::of::<T>(),
155            _phantom: PhantomData,
156        }
157    }
158
159    pub fn debug_check(&self) {
160        #[cfg(debug_assertions)]
161        {
162            if unsafe { std::ptr::read_volatile(&self.magic) } != std::any::TypeId::of::<T>() {
163                std::process::abort();
167            }
168            std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst);
170        }
171    }
172}
173
174impl<T> Default for Magic<T> {
175    fn default() -> Self {
176        Self::new()
177    }
178}
179
180impl<T> Drop for Magic<T> {
181    fn drop(&mut self) {
182        self.debug_check();
183        #[cfg(debug_assertions)]
184        unsafe {
185            std::ptr::write_volatile(&mut self.magic, std::any::TypeId::of::<()>())
186        };
187    }
188}
189
190impl<T> Clone for Magic<T> {
191    fn clone(&self) -> Self {
192        self.debug_check();
193        Self::new()
194    }
195}
196
197#[derive(Debug)]
199pub struct ObjectCounter {
200    name: &'static str,
201}
202
203impl ObjectCounter {
204    pub fn new(name: &'static str) -> Self {
205        Worker::increment_object_alloc_counter(name);
206        Self { name }
207    }
208}
209
210impl Drop for ObjectCounter {
211    fn drop(&mut self) {
212        Worker::increment_object_dealloc_counter(self.name);
213    }
214}
215
216impl Clone for ObjectCounter {
217    fn clone(&self) -> Self {
218        Worker::increment_object_alloc_counter(self.name);
219        Self { name: self.name }
220    }
221}
222
223pub fn tilde_expansion(path: &str) -> std::path::PathBuf {
224    if let Some(x) = path.strip_prefix('~') {
226        let (tilde_prefix, remainder) = x.split_once('/').unwrap_or((x, ""));
228
229        if tilde_prefix.is_empty() {
230            if let Ok(ref home) = std::env::var("HOME") {
231                return [home, remainder].iter().collect::<std::path::PathBuf>();
232            }
233        } else if ['+', '-'].contains(&tilde_prefix.chars().next().unwrap()) {
234            } else {
236            return ["/home", tilde_prefix, remainder]
237                .iter()
238                .collect::<std::path::PathBuf>();
239        }
240    }
241
242    std::path::PathBuf::from(path)
244}
245
246pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
249    struct DirCopyTask {
251        src: PathBuf,
252        dst: PathBuf,
253        mode: u32,
254    }
255
256    let mut stack: Vec<DirCopyTask> = vec![];
258
259    stack.push(DirCopyTask {
260        src: src.as_ref().to_path_buf(),
261        dst: dst.as_ref().to_path_buf(),
262        mode: src.as_ref().metadata()?.mode(),
263    });
264
265    while let Some(DirCopyTask { src, dst, mode }) = stack.pop() {
266        create_dir_with_mode(&dst, mode)?;
268
269        for entry in std::fs::read_dir(src)? {
271            let entry = entry?;
272            let meta = entry.metadata()?;
273            let new_dst_path = dst.join(entry.file_name());
274
275            if meta.is_dir() {
276                stack.push(DirCopyTask {
277                    src: entry.path(),
278                    dst: new_dst_path,
279                    mode: meta.mode(),
280                });
281            } else {
282                std::fs::copy(entry.path(), &new_dst_path)?;
284            }
285        }
286    }
287
288    Ok(())
289}
290
291fn create_dir_with_mode(path: impl AsRef<Path>, mode: u32) -> std::io::Result<()> {
292    let mut dir_builder = std::fs::DirBuilder::new();
293    dir_builder.mode(mode);
294    dir_builder.create(&path)
295}
296
297pub fn pathbuf_to_nul_term_cstring(buf: PathBuf) -> CString {
299    let mut bytes = buf.as_os_str().to_os_string().as_bytes().to_vec();
300    bytes.push(0);
301    CString::from_vec_with_nul(bytes).unwrap()
302}
303
304pub fn return_code_for_signal(signal: nix::sys::signal::Signal) -> i32 {
307    (signal as i32).checked_add(128).unwrap()
309}
310
311pub fn u8_to_i8_slice(s: &[u8]) -> &[i8] {
314    assert!(s.iter().all(|x| i8::try_from(*x).is_ok()));
316    unsafe { std::slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
317}
318
319pub fn i8_to_u8_slice(s: &[i8]) -> &[u8] {
322    assert!(s.iter().all(|x| u8::try_from(*x).is_ok()));
324    unsafe { std::slice::from_raw_parts(s.as_ptr() as *const u8, s.len()) }
325}
326
327pub fn case_insensitive_eq(a: &[u8], b: &[u8]) -> bool {
330    if a.len() != b.len() {
331        return false;
332    }
333
334    a.iter().zip(b).all(|(x, y)| x.eq_ignore_ascii_case(y))
335}
336
337#[derive(Debug)]
338pub enum VerifyPluginPathError {
339    NotFound,
340    NotFile,
342    NotExecutable,
344    NotDynamicallyLinkedElf,
347    IncompatibleInterpreter(Box<VerifyPluginPathError>),
350    UnknownFileType,
352    PathPermissionDenied,
354    UnhandledIoError(std::io::Error),
355}
356impl std::error::Error for VerifyPluginPathError {}
357
358impl From<std::io::Error> for VerifyPluginPathError {
359    fn from(value: std::io::Error) -> Self {
360        match value.kind() {
361            std::io::ErrorKind::NotFound => VerifyPluginPathError::NotFound,
362            std::io::ErrorKind::PermissionDenied => VerifyPluginPathError::PathPermissionDenied,
363            _ => {
366                log::warn!("Unhandled IO error");
367                VerifyPluginPathError::UnhandledIoError(value)
368            }
369        }
370    }
371}
372
373impl std::fmt::Display for VerifyPluginPathError {
374    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375        match self {
376            VerifyPluginPathError::NotFound => f.write_str("path not found"),
377            VerifyPluginPathError::NotFile => f.write_str("not a file"),
378            VerifyPluginPathError::NotExecutable => f.write_str("not executable"),
379            VerifyPluginPathError::NotDynamicallyLinkedElf => {
380                f.write_str("not a dynamically linked ELF")
381            }
382            VerifyPluginPathError::PathPermissionDenied => {
383                f.write_str("permission denied traversing path")
384            }
385            VerifyPluginPathError::UnhandledIoError(e) => write!(f, "unhandled io error: {e}"),
386            VerifyPluginPathError::IncompatibleInterpreter(e) => {
387                write!(f, "script with incompatible interpreter: {e}")
388            }
389            VerifyPluginPathError::UnknownFileType => f.write_str("Uncrecognized file type"),
390        }
391    }
392}
393
394fn verify_plugin_path_internal(
396    path: impl AsRef<std::path::Path> + std::fmt::Debug,
397) -> Result<(), VerifyPluginPathError> {
398    let file = std::fs::File::open(&path)?;
399    let metadata = file.metadata()?;
400    if !metadata.is_file() {
401        return Err(VerifyPluginPathError::NotFile);
402    }
403    let mask = libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH;
406    if (metadata.mode() & mask) == 0 {
407        log::debug!("{path:?} not executable");
408        return Err(VerifyPluginPathError::NotExecutable);
409    }
410    let mut buf = Vec::with_capacity(linux_api::limits::PATH_MAX);
413    file.take(linux_api::limits::PATH_MAX.try_into().unwrap())
414        .read_to_end(&mut buf)?;
415
416    if buf.starts_with(b"\x7fELF") {
417        if is_dynamic_bin(&path) {
419            Ok(())
420        } else {
421            log::debug!("{path:?} is ELF, but not dynamically linked");
422            Err(VerifyPluginPathError::NotDynamicallyLinkedElf)
423        }
424    } else if let Some(interp) = get_interpreter(&buf) {
425        log::debug!("{path:?} has interpreter {interp:?}; checking");
429        verify_plugin_path(interp)
430            .map_err(|e| VerifyPluginPathError::IncompatibleInterpreter(Box::new(e)))
431    } else {
432        Err(VerifyPluginPathError::UnknownFileType)
433    }
434}
435
436pub fn verify_plugin_path(path: impl AsRef<std::path::Path>) -> Result<(), VerifyPluginPathError> {
441    let path = path.as_ref();
442
443    static CHECKED_BINS: Lazy<RwLock<HashSet<PathBuf>>> = Lazy::new(|| RwLock::new(HashSet::new()));
446
447    if CHECKED_BINS.read().unwrap().contains(path) {
448        return Ok(());
449    }
450
451    let res = verify_plugin_path_internal(path);
452    if res.is_ok() {
453        CHECKED_BINS.write().unwrap().insert(path.to_path_buf());
454    }
455    res
456}
457
458fn get_interpreter(header: &[u8]) -> Option<&Path> {
459    let mut header = header.strip_prefix(b"#!")?;
461    while header.first() == Some(&b' ') {
463        header = &header[1..];
464    }
465    let interp_path = header.split(|b| b == &b' ' || b == &b'\n').next()?;
467    let p = OsStr::from_bytes(interp_path);
468    Some(Path::new(p))
469}
470
471fn is_dynamic_bin(path: impl AsRef<std::path::Path>) -> bool {
472    let path = path.as_ref();
473
474    let ld_path = "/lib64/ld-linux-x86-64.so.2";
476    let ld_output = std::process::Command::new(ld_path)
477        .arg("--verify")
478        .arg(path)
479        .output()
480        .expect("Unable to run '{ld_path}'");
481
482    if ld_output.status.success() {
483        true
484    } else {
485        log::debug!("ld stderr: {:?}", ld_output.stderr);
486        false
489    }
490}
491
492pub fn inject_preloads(mut envv: Vec<CString>, injected_preloads: &[PathBuf]) -> Vec<CString> {
499    let ld_preload_key = CString::new("LD_PRELOAD=").unwrap();
500
501    let ld_preload_kv;
502    if let Some(kv) = envv
503        .iter_mut()
504        .find(|v| v.to_bytes().starts_with(ld_preload_key.as_bytes()))
505    {
506        ld_preload_kv = kv;
511    } else {
512        envv.push(ld_preload_key.clone());
514        ld_preload_kv = envv.last_mut().unwrap();
515    }
516
517    let previous_preloads_string = ld_preload_kv
518        .as_bytes()
519        .strip_prefix(ld_preload_key.as_bytes())
520        .unwrap();
521
522    let injected_preloads_bytes = injected_preloads
523        .iter()
524        .map(|path| path.as_os_str().as_bytes());
525
526    for p in injected_preloads_bytes.clone() {
527        assert!(
530            !p.iter().any(|c| *c == b' ' || *c == b':'),
531            "Preload path contains LD_PRELOAD separator"
532        );
533    }
534
535    let previous_preloads = previous_preloads_string.split(|c| *c == b':' || *c == b' ');
538
539    let filtered_previous_preloads =
543        previous_preloads.filter(|p| !injected_preloads_bytes.clone().any(|q| &q == p));
544
545    let injected_preloads_bytes = injected_preloads
546        .iter()
547        .map(|path| path.as_os_str().as_bytes());
548
549    let mut preloads = injected_preloads_bytes.chain(filtered_previous_preloads);
550
551    let mut output = Vec::<u8>::new();
553    output.extend(ld_preload_key.as_bytes());
554    if let Some(p) = preloads.next() {
556        output.extend(p);
557    }
558    for preload in preloads {
560        output.push(b':');
561        output.extend(preload);
562    }
563
564    *ld_preload_kv = CString::new(output).unwrap();
567
568    envv
569}
570
571pub fn debug_assert_cloexec(file: &(impl std::os::fd::AsRawFd + std::fmt::Debug)) {
586    #[cfg(debug_assertions)]
587    {
588        let flags = nix::fcntl::fcntl(file.as_raw_fd(), nix::fcntl::FcntlArg::F_GETFD).unwrap();
589        let flags = nix::fcntl::FdFlag::from_bits_retain(flags);
590        debug_assert!(
591            flags.contains(nix::fcntl::FdFlag::FD_CLOEXEC),
592            "{file:?} is unexpectedly not FD_CLOEXEC, which may lead to resource leaks or strange behavior"
593        );
594    }
595    #[cfg(not(debug_assertions))]
596    {
597        let _ = file;
599    }
600}
601
602#[cfg(test)]
603mod tests {
604    use super::*;
605
606    #[test]
607    fn test_tilde_expansion() {
608        if let Ok(ref home) = std::env::var("HOME") {
609            assert_eq!(
610                tilde_expansion("~/test"),
611                [home, "test"].iter().collect::<std::path::PathBuf>()
612            );
613
614            assert_eq!(
615                tilde_expansion("~"),
616                [home].iter().collect::<std::path::PathBuf>()
617            );
618
619            assert_eq!(
620                tilde_expansion("~/"),
621                [home].iter().collect::<std::path::PathBuf>()
622            );
623
624            assert_eq!(
625                tilde_expansion("~someuser/test"),
626                ["/home", "someuser", "test"]
627                    .iter()
628                    .collect::<std::path::PathBuf>()
629            );
630
631            assert_eq!(
632                tilde_expansion("/~/test"),
633                ["/", "~", "test"].iter().collect::<std::path::PathBuf>()
634            );
635
636            assert_eq!(
637                tilde_expansion(""),
638                [""].iter().collect::<std::path::PathBuf>()
639            );
640        }
641    }
642
643    #[test]
644    fn test_inject_preloads() {
645        assert_eq!(
647            inject_preloads(vec![], &[]),
648            vec![CString::new("LD_PRELOAD=").unwrap()]
649        );
650
651        assert_eq!(
653            inject_preloads(
654                vec![
655                    CString::new("foo=foo").unwrap(),
656                    CString::new("bar=bar").unwrap(),
657                ],
658                &[]
659            ),
660            vec![
661                CString::new("foo=foo").unwrap(),
662                CString::new("bar=bar").unwrap(),
663                CString::new("LD_PRELOAD=").unwrap()
664            ]
665        );
666
667        assert_eq!(
669            inject_preloads(
670                vec![CString::new("LD_PRELOAD=/existing.so").unwrap()],
671                &[PathBuf::from("/injected.so")]
672            ),
673            vec![CString::new("LD_PRELOAD=/injected.so:/existing.so").unwrap()]
674        );
675
676        assert_eq!(
678            inject_preloads(
679                vec![CString::new("LD_PRELOAD=/injected.so").unwrap()],
680                &[PathBuf::from("/injected.so")]
681            ),
682            &[CString::new("LD_PRELOAD=/injected.so").unwrap()]
683        );
684
685        assert_eq!(
687            inject_preloads(
688                vec![
689                    CString::new("foo=foo").unwrap(),
690                    CString::new("LD_PRELOAD=/existing1.so:/injected1.so:/existing2.so").unwrap(),
691                    CString::new("bar=bar").unwrap()
692                ],
693                &[
694                    PathBuf::from("/injected1.so"),
695                    PathBuf::from("/injected2.so"),
696                ],
697            ),
698            &[
699                CString::new("foo=foo").unwrap(),
700                CString::new("LD_PRELOAD=/injected1.so:/injected2.so:/existing1.so:/existing2.so")
701                    .unwrap(),
702                CString::new("bar=bar").unwrap(),
703            ]
704        );
705    }
706}
707
708mod export {
709    use std::io::IsTerminal;
710
711    #[unsafe(no_mangle)]
712    pub unsafe extern "C-unwind" fn utility_handleErrorInner(
713        file_name: *const libc::c_char,
714        line: libc::c_int,
715        fn_name: *const libc::c_char,
716        format: *const libc::c_char,
717        va_list: *mut libc::c_void,
718    ) -> ! {
719        use std::ffi::CStr;
720        let file_name = unsafe { CStr::from_ptr(file_name) };
721        let file_name = file_name.to_bytes().escape_ascii();
722
723        let fn_name = unsafe { CStr::from_ptr(fn_name) };
724        let fn_name = fn_name.to_bytes().escape_ascii();
725
726        log::logger().flush();
727
728        let indent = "    ";
729
730        let backtrace = format!("{:?}", backtrace::Backtrace::new());
732        let backtrace = backtrace
733            .trim_end()
734            .split('\n')
735            .map(|x| format!("{indent}{x}"))
736            .collect::<Vec<String>>()
737            .join("\n");
738
739        let pid = nix::unistd::getpid();
740        let ppid = nix::unistd::getppid();
741
742        let error_msg = unsafe { vsprintf::vsprintf_raw(format, va_list).unwrap() };
743        let error_msg = error_msg.escape_ascii();
744
745        let error_msg = format!(
746            "**ERROR ENCOUNTERED**\n\
747              {indent}At process: {pid} (parent {ppid})\n\
748              {indent}At file: {file_name}\n\
749              {indent}At line: {line}\n\
750              {indent}At function: {fn_name}\n\
751              {indent}Message: {error_msg}\n\
752            **BEGIN BACKTRACE**\n\
753            {backtrace}\n\
754            **END BACKTRACE**\n\
755            **ABORTING**"
756        );
757
758        eprintln!("{error_msg}");
759
760        if std::io::stderr().lock().is_terminal() && !std::io::stdout().lock().is_terminal() {
764            println!("{error_msg}");
765        }
766
767        std::process::abort()
768    }
769}