截获posix_spawnp的RUST LD_PRELOAD程序挂起,但对execvp等工作正常并且仅当启用从tokio进行跟踪时

问题描述

我正在RUST中编写一个开源LD_PRELOAD库,该库可跟踪文件系统的活动。 我还使用RUST tokio跟踪箱来对库启用跟踪。 我能够成功拦截和跟踪许多libc API,例如open(),execvp等。 当我启用posix_spawnp的拦截时,启用了跟踪,它将失败。 如果启用posix_spawnp的拦截功能,并且禁用/注释了交易代码,则测试有效。

测试是将以下代码定义为生成例如libpreload.so的cargo项目。将其复制到目标/调试之外。 然后运行

cargo clean; LD_PRELOAD=libpreload.so cargo build

它挂了。 完整代码如下

extern crate core;
extern crate libc;
extern crate tracing;
extern crate tracing_appender;
extern crate tracing_subscriber;


use std::env;
use core::cell::Cell;
use libc::{c_void,c_char,c_int,size_t,ssize_t};
use tracing::{Level,event,};
use tracing::dispatcher::{with_default,dispatch};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::FmtSubscriber;

use std::sync::atomic;

#[cfg(any(target_os = "macos",target_os = "ios"))]
pub mod dyld_insert_libraries;

/* Some Rust library functionality (e.g.,jemalloc) initializes
 * lazily,after the hooking library has inserted itself into the call
 * path. If the initialization uses any hooked functions,this will lead
 * to an infinite loop. Work around this by running some initialization
 * code in a static constructor,and bypassing all hooks until it has
 * completed. */

static INIT_STATE: atomic::AtomicBool = atomic::AtomicBool::new(false);

pub fn initialized() -> bool {
    INIT_STATE.load(atomic::Ordering::SeqCst)
}

extern "C" fn initialize() {
    Box::new(0u8);
    INIT_STATE.store(true,atomic::Ordering::SeqCst);
}


#[link(name = "dl")]
extern "C" {
    fn dlsym(handle: *const c_void,symbol: *const c_char) -> *const c_void;
}

const RTLD_NEXT: *const c_void = -1isize as *const c_void;

pub unsafe fn dlsym_next(symbol: &'static str) -> *const u8 {
    let ptr = dlsym(RTLD_NEXT,symbol.as_ptr() as *const c_char);
    if ptr.is_null() {
        panic!("redhook: Unable to find underlying function for {}",symbol);
    }
    ptr as *const u8
}

/* Rust doesn't directly expose __attribute__((constructor)),but this
 * is how GNU implements it. */
#[link_section = ".init_array"]
pub static INITIALIZE_CTOR: extern "C" fn() = ::initialize;

pub fn make_dispatch(tracevar: &str) -> (bool,dispatch,WorkerGuard) {
    let file_appender;
    let tracing;
    if let Ok(tracefile) =  env::var(tracevar) {
        file_appender = tracing_appender::rolling::never("",tracefile);
        tracing = true
    } else {
        file_appender = tracing_appender::rolling::never("","/dev/null");
        tracing = false
    }
    let (non_blocking,guard) = tracing_appender::non_blocking(file_appender);
    let subscriber = FmtSubscriber::builder()
        .with_max_level(Level::TRACE)
        .with_writer(non_blocking)
        .finish();
    (tracing,dispatch::new(subscriber),guard)
}


thread_local! {
    #[allow(nonstandard_style)]
    static MY_disPATCH_initialized: ::core::cell::Cell<bool> = false.into();
}
thread_local! {
    static MY_disPATCH: (bool,WorkerGuard) = {
        let ret = make_dispatch("WISK_TRACE");
        // println!("Tracing: {}",ret.0);
        MY_disPATCH_initialized.with(|it| it.set(true));
        ret
    };
}


#[allow(non_camel_case_types)]
pub struct posix_spawnp {__private_field: ()}
#[allow(non_upper_case_globals)]
static posix_spawnp: posix_spawnp = posix_spawnp {__private_field: ()};

impl posix_spawnp {
    fn get(&self) -> unsafe extern fn (pid: *mut libc::pid_t,file: *const libc::c_char,file_actions: *const libc::posix_spawn_file_actions_t,attrp: *const libc::posix_spawnattr_t,argv: *const *const libc::c_char,envp: *const *const libc::c_char) -> libc::c_int  {
        use ::std::sync::Once;

        static mut REAL: *const u8 = 0 as *const u8;
        static mut ONCE: Once = Once::new();

        unsafe {
            ONCE.call_once(|| {
                REAL = dlsym_next(concat!("posix_spawnp","\0"));
            });
            ::std::mem::transmute(REAL)
        }
    }

    #[no_mangle]
    pub unsafe extern "C" fn posix_spawnp(pid: *mut libc::pid_t,envp: *const *const libc::c_char) -> libc::c_int {
        if initialized() {
            ::std::panic::catch_unwind(|| my_posix_spawnp ( pid,file,file_actions,attrp,argv,envp )).ok()
        } else {
            None
        }.unwrap_or_else(|| posix_spawnp.get() ( pid,envp ))
    }
}

pub unsafe fn my_posix_spawnp(pid: *mut libc::pid_t,envp: *const *const libc::c_char) -> libc::c_int {
        MY_disPATCH.with(|(tracing,my_dispatch,_guard)| {
            with_default(&my_dispatch,|| {
                posix_spawnp.get()(pid,envp)
            })
        })
}

需要将货物编译为.so

[package]
name = "readlink"
version = "0.1.0"
authors = ["Saravanan Shanmugham <sarvi@cisco.com>"]

# See more keys and their deFinitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "readlink"
crate_type = ["dylib"]

[dependencies]
libc = "0.2"

分层显示

statx(AT_FDCWD,"/users/sarvi/.rustup/toolchains/nightly-x86_64-unkNown-linux-gnu/bin/rustc",AT_STATX_SYNC_AS_STAT,STATX_ALL,{stx_mask=STATX_BASIC_STATS,stx_attributes=0,stx_mode=S_IFREG|0755,stx_size=2707888,...}) = 0
openat(AT_FDCWD,"/ws/sarvi-sjc/redhook/examples/readlink/target/.rustc_info.json",O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD,"/dev/null",O_RDONLY|O_CLOEXEC) = 3
pipe2([4,5],O_CLOEXEC)                = 0
pipe2([6,7],O_CLOEXEC)                = 0
futex(0x7f01c70f50e8,FUTEX_WAKE_PRIVATE,2147483647) = 0
prlimit64(0,RLIMIT_NOFILE,NULL,{rlim_cur=4*1024,rlim_max=4*1024}) = 0
prlimit64(0,rlim_max=4*1024}) = 0
futex(0x5621cf2c9fa0,FUTEX_WAIT_PRIVATE,2,NULL

如果我改变

        MY_disPATCH.with(|(tracing,envp)
            })
        })

下面的内容只是使跟踪变得不完整,但实际上并没有生成任何跟踪,它仍然挂起

        MY_disPATCH.with(|(tracing,_guard)| {
                posix_spawnp.get()(pid,envp)
        })

这不会挂起并且可以正常工作

      posix_spawnp.get()(pid,envp)

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)