diff --git a/changelog.d/2254.fixed.md b/changelog.d/2254.fixed.md new file mode 100644 index 00000000000..ef82bc399e3 --- /dev/null +++ b/changelog.d/2254.fixed.md @@ -0,0 +1 @@ +Fixed issue with Golang calling fstat on Linux causing crash \ No newline at end of file diff --git a/mirrord/layer/src/file/hooks.rs b/mirrord/layer/src/file/hooks.rs index c18dcd794a7..00d74e6520b 100644 --- a/mirrord/layer/src/file/hooks.rs +++ b/mirrord/layer/src/file/hooks.rs @@ -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); + crate::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, diff --git a/mirrord/layer/src/macros.rs b/mirrord/layer/src/macros.rs index e3038141567..f15f06f334d 100644 --- a/mirrord/layer/src/macros.rs +++ b/mirrord/layer/src/macros.rs @@ -60,6 +60,65 @@ 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_with_fallback!( +/// hook_manager, +/// "close", +/// close_detour, +/// FnClose, +/// FN_CLOSE, +/// libc::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`. ///