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