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}