Skip to content

Commit

Permalink
add replace_with_fallback to use when functions might not be availabl…
Browse files Browse the repository at this point in the history
…e in Go hook context [specifically, fstat]
  • Loading branch information
aviramha committed Feb 21, 2024
1 parent ce7e55a commit 829fa50
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 2 deletions.
11 changes: 9 additions & 2 deletions mirrord/layer/src/file/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::{
ops::{access, lseek, open, read, write},
},
hooks::HookManager,
replace,
replace, replace_with_fallback,

Check failure on line 43 in mirrord/layer/src/file/hooks.rs

View workflow job for this annotation

GitHub Actions / macos_tests

unused import: `replace_with_fallback`

Check failure on line 43 in mirrord/layer/src/file/hooks.rs

View workflow job for this annotation

GitHub Actions / build_binaries_macos

unused import: `replace_with_fallback`

Check failure on line 43 in mirrord/layer/src/file/hooks.rs

View workflow job for this annotation

GitHub Actions / macos_tests

unused import: `replace_with_fallback`
};

#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -1212,7 +1212,14 @@ pub(crate) unsafe fn enable_file_hooks(hook_manager: &mut HookManager) {
FN___LXSTAT64
);
replace!(hook_manager, "lstat", lstat_detour, FnLstat, FN_LSTAT);
replace!(hook_manager, "fstat", fstat_detour, FnFstat, FN_FSTAT);
replace_with_fallback!(
hook_manager,
"fstat",
fstat_detour,
FnFstat,
FN_FSTAT,
libc::fstat
);
replace!(hook_manager, "stat", stat_detour, FnStat, FN_STAT);
replace!(
hook_manager,
Expand Down
52 changes: 52 additions & 0 deletions mirrord/layer/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,58 @@ macro_rules! replace {
}};
}

/// Replaces the `$func` [`libc`] function, with the equivalent hook `$detour_function`, by calling
/// `HookManager::hook_export_or_any`. This variant accepts a fallback function to put in the
/// original function if the hook fails. This is useful in Go when we actually don't hook any libc
/// but still need to call it.
///
/// ## Parameters
///
/// - `$hook_manager`: a valid [`HookManager`](crate::hooks::HookManager) instance that is used to
/// replace the [`libc`] function;
///
/// - `$func`: the function we want to replace;
///
/// - `$detour_function`: one of our detour functions;
///
/// - `$detour_type`: the type alias that was generated by `hook_fn` for this function type;
///
/// - `$hook_fn`: stores the original function pointer as a [`HookFn`](crate::detour::HookFn) that
/// is created by `hook_fn`.
/// - `$fallback_fn`: stores the libc function to fallback to if the hook fails.
///
/// ## Examples
///
/// - Replacing [`libc::close`] with [`close_detour`](crate::close_detour):
///
/// ```rust, no_run
/// unsafe {
/// replace!(&mut hook_manager, "close", close_detour, FnClose, FN_CLOSE);
/// }
/// ```
#[macro_export]
macro_rules! replace_with_fallback {
($hook_manager:expr, $func:expr, $detour_function:expr, $detour_type:ty, $hook_fn:expr, $fallback_fn:expr) => {{
let intercept = |hook_manager: &mut $crate::hooks::HookManager,
symbol_name,
detour: $detour_type|
-> $crate::error::Result<$detour_type> {
let replaced =
hook_manager.hook_export_or_any(symbol_name, detour as *mut libc::c_void)?;
let original_fn: $detour_type = std::mem::transmute(replaced);

tracing::trace!("hooked {symbol_name:?}");
Ok(original_fn)
};

let _ = intercept($hook_manager, $func, $detour_function)
.and_then(|hooked| Ok($hook_fn.set(hooked).unwrap()))
.unwrap_or_else(|_| {
$hook_fn.set($fallback_fn).unwrap();
});
}};
}

/// Used by [`go_hooks`](crate::go_hooks) to hook go syscalls with
/// `HookManager::hook_symbol_main_module`.
///
Expand Down

0 comments on commit 829fa50

Please sign in to comment.