In corporated the assert you suuggested. Found that some of the scripts were clearing the environment and hence RUST_BACKTRACE=1 as well.
I then coded to always env::set_var("RUST_BACKTRACE","1");
After that the backtrace is always printed.
On the topic of "Bad file descriptor" though, I working with file descriptors to translate fd to path
fn fd2path (fd : c_int ) -> PathBuf {
let f = unsafe { File::from_raw_fd(fd) };
let fp = f.path().unwrap();
f.into_raw_fd();
fp
}
This is the only raw file descriptor handling. And this API was onnly used to map fd to path for logging the paths associated with intercepted API like open(), or symlinkat(), etc.
I went ahead and disabled the above code as well. Just to test things.
and I still ran into the traceback of Bad file descriptor as follows
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 9, kind: Other, message: "Bad file descriptor" }', src/tracker.rs:223:113
stack backtrace:
0: rust_begin_unwind
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/std/src/panicking.rs:475
1: core::panicking::panic_fmt
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/core/src/panicking.rs:85
2: core::option::expect_none_failed
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/core/src/option.rs:1219
3: core::result::Result<T,E>::unwrap
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/core/src/result.rs:1005
4: wisktrack::tracker::Tracker::report
at /ws/sarvi-sjc/wisktrack/src/tracker.rs:223
5: wisktrack::tracker::Tracker::reportexecv
at /ws/sarvi-sjc/wisktrack/src/tracker.rs:341
6: wisktrack::my_execv::{{closure}}
at /ws/sarvi-sjc/wisktrack/src/lib.rs:157
7: std::thread::local::LocalKey<T>::try_with
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/std/src/thread/local.rs:267
8: std::thread::local::LocalKey<T>::with
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/std/src/thread/local.rs:243
9: wisktrack::my_execv
at /users/sarvi/.cargo/git/checkouts/redhook-16e0a55232cdd48a/293568e/src/ld_preload.rs:87
10: wisktrack::execv::{{closure}}
at /users/sarvi/.cargo/git/checkouts/redhook-16e0a55232cdd48a/293568e/src/ld_preload.rs:104
11: std::panicking::try::do_call
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/std/src/panicking.rs:373
12: __rust_try
13: std::panicking::try
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/std/src/panicking.rs:337
14: std::panic::catch_unwind
at /rustc/7e6d6e5f535321c2223f044caba16f97b825009c/library/std/src/panic.rs:394
15: execv
at /users/sarvi/.cargo/git/checkouts/redhook-16e0a55232cdd48a/293568e/src/ld_preload.rs:104
16: <unknown>
17: <unknown>
18: <unknown>
19: <unknown>
20: <unknown>
21: <unknown>
22: <unknown>
23: <unknown>
24: <unknown>
25: <unknown>
26: <unknown>
27: <unknown>
28: <unknown>
29: <unknown>
30: <unknown>
31: <unknown>
32: <unknown>
33: <unknown>
34: <unknown>
35: <unknown>
36: <unknown>
37: <unknown>
38: <unknown>
39: <unknown>
40: <unknown>
41: <unknown>
42: <unknown>
43: <unknown>
44: <unknown>
45: <unknown>
46: <unknown>
47: <unknown>
48: <unknown>
49: <unknown>
50: <unknown>
51: <unknown>
52: <unknown>
53: __libc_start_main
54: <unknown>
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
So if rust indeed does take care of managing the file descripter associaited with a file object, I am not sure where the the "file" object is being deleted. Considering the file object is part of a global lazy_static definition.
pub struct Tracker {
pub wsroot: String,
pub cwd: String,
pub filename: String,
pub file: File,
pub uuid: String,
pub puuid: String,
pub pid: String,
pub env : HashMap<String, String>,
pub wiskfields : Vec<(String, String)>,
pub cmdline: Vec<String>,
}
thread_local! {
#[allow(nonstandard_style)]
pub static MY_DISPATCH_initialized: ::core::cell::Cell<bool> = false.into();
}
lazy_static! {
pub static ref TRACKER : Tracker = Tracker::init();
}
thread_local! {
pub static MY_DISPATCH: (bool, Dispatch, WorkerGuard) = {
// debug(format_args!("Trace Initialize\n"));
let ret = make_dispatch("WISK_TRACE");
if ret.0 {
with_default(&ret.1, || {
lazy_static::initialize(&TRACKER);
});
} else {
lazy_static::initialize(&TRACKER);
}
MY_DISPATCH_initialized.with(|it| it.set(true));
// debug(format_args!("Trace Initialize: Complete\n"));
ret
};
}
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)
}
The one problem I see with the above code is while 2 of the above is thread local, TRACKER is lazey_static. And I suspect its initialization might need protection from initialization from multiple threads.
That said, its intialization is currenntly being done in the LD_PRELOAD library constructor function as follows
#[ctor]
fn cfoo() {
// debug(format_args!("Constructor: {}\n", std::process::id()));
// real!(readlink);
redhook::initialize();
MY_DISPATCH.with(|(tracing, my_dispatch, _guard)| { });
// debug(format_args!("Constructor Complete\n"));
}
I would think the no threads have started at this point. Is it possible for a process to start with multiple threads before the constructor of an LD_PRELOAD library ?
Update: May be I am coding my intent wrong in rust. My goal is to have a common global initialization for the follow 2 items
- tokio-tracing - for tracing/debugging issues. That trace all operations to a common log file, across all processes execed/propagated from the top level make program that is being tracked.
- tracking file - Where I write data associated with each libc API I intercept in my LD_PRELOAD library. Again a common file across processes execed/propagated from the top level make program that is being tracked
In a multi threaded program that is exec'ed, Then the tokio tracing code would probably open the same log file in append mode multiple times, I would venture to guess.
while the Tracker object would get initialized once globably and the "file" object it contains and the fd would be used by multiple threads within that process that might be invoking intercepted libc API that will be writing tracking data to the same tracking file/file-descriptor from different threads.
Not sure if this is not acceptable in RUST to be writing to the same file multiple times. Or if there is a bug in the lazy_static crate that destroys TRACKER object. If that is the case, I would have expected the tracker object to be deleted/gone if the file/file descriptor within it is deleted and bad.
So a bit lost.
- Is there problem with opening the same file multiple times from different threads? as is happenning tokio tracing to that is going to a single file across multiple threads and multiple processes
- Is there a problem with writing tracking data to a single tracking file opened globally as a single "file" object with a single file descriptor, and potentially being written to from multiple threads within that process?