Skip to content

Commit b048100

Browse files
author
Vasily Galkin
committed
recursion in dlsym: Abort instead of crashing on stack overflow
1 parent f498260 commit b048100

File tree

1 file changed

+40
-1
lines changed

1 file changed

+40
-1
lines changed

src/ld_preload.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ extern "C" {
55
fn dlsym(handle: *const c_void, symbol: *const c_char) -> *const c_void;
66
}
77

8+
extern "C" {
9+
fn write(fd: i32, message: *const c_char, message_size: usize) -> isize;
10+
}
11+
12+
pub fn abort_with_message(message: &'static str) -> ! {
13+
const STDERR_FD: i32 = 2;
14+
//Rust io may be uninialized yet, so use purest os syscall
15+
unsafe
16+
{
17+
write(STDERR_FD, message.as_ptr() as *const c_char, message.len());
18+
}
19+
std::process::abort()
20+
}
21+
822
const RTLD_NEXT: *const c_void = -1isize as *const c_void;
923

1024
pub unsafe fn dlsym_next(symbol: &'static str) -> *const u8 {
@@ -26,14 +40,39 @@ macro_rules! hook {
2640
mod $real_fn {
2741
use super::*; //required for parameter types
2842
pub fn get_dlsym_next() -> unsafe extern fn ( $($v : $t),* ) -> $r {
29-
use ::std::sync::{Once, ONCE_INIT};
43+
use ::std::sync::{Once, ONCE_INIT, atomic::{AtomicUsize, Ordering}};
3044

3145
static mut REAL: *const u8 = 0 as *const u8;
3246
static mut ONCE: Once = ONCE_INIT;
3347

48+
static CALL_ONCE_STACK : AtomicUsize = AtomicUsize::new(0); // Used during call_once to check recursion.
49+
50+
// Some core functions are sometimes called from dlsym implementation.
51+
// This may cause a recursive call here if such core function is preloaded causing obscure-to-debug behaviour.
52+
// Using "complex" calls like thread::current() to find recursion increaese the chance of recusrsion itself!
53+
// So use a very naive approach of detection recursion:
54+
// compare surrent stack address to a stack adress saved from a call_once.
55+
56+
let call_once_stack = CALL_ONCE_STACK.load(Ordering::Relaxed); // atomicity is enough
57+
let this_thread_stack = &call_once_stack as *const usize as usize;
58+
if call_once_stack != 0usize {
59+
// Some thread is executing ONCE.call_once now - maybe this, maybe other.
60+
// Compare current stack address with the one saved from thread executed call_once.
61+
// If the difference is big it's other thread - just continue.
62+
// If small - call abort since recusrive call of a hook from dlsym is unrecoverable problem.
63+
const STACK_DIFF_FOR_RECURSION: usize = 0x1000; // one page
64+
if call_once_stack.wrapping_sub(this_thread_stack) < STACK_DIFF_FOR_RECURSION {
65+
$crate::ld_preload::abort_with_message(concat!(
66+
"LD_PRELOAD hook aborting process: recursive call detected while calling dlsym(RTLD_NEXT, \"",
67+
stringify!($real_fn), "\")\n"));
68+
}
69+
}
70+
3471
unsafe {
3572
ONCE.call_once(|| {
73+
CALL_ONCE_STACK.store(this_thread_stack, Ordering::Relaxed);
3674
REAL = $crate::ld_preload::dlsym_next(concat!(stringify!($real_fn), "\0"));
75+
CALL_ONCE_STACK.store(0, Ordering::Relaxed);
3776
});
3877
::std::mem::transmute(REAL)
3978
}

0 commit comments

Comments
 (0)