@@ -5,6 +5,20 @@ extern "C" {
5
5
fn dlsym ( handle : * const c_void , symbol : * const c_char ) -> * const c_void ;
6
6
}
7
7
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
+
8
22
const RTLD_NEXT : * const c_void = -1isize as * const c_void ;
9
23
10
24
pub unsafe fn dlsym_next ( symbol : & ' static str ) -> * const u8 {
@@ -26,14 +40,39 @@ macro_rules! hook {
26
40
mod $real_fn {
27
41
use super :: * ; //required for parameter types
28
42
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 } } ;
30
44
31
45
static mut REAL : * const u8 = 0 as * const u8 ;
32
46
static mut ONCE : Once = ONCE_INIT ;
33
47
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
+
34
71
unsafe {
35
72
ONCE . call_once( || {
73
+ CALL_ONCE_STACK . store( this_thread_stack, Ordering :: Relaxed ) ;
36
74
REAL = $crate:: ld_preload:: dlsym_next( concat!( stringify!( $real_fn) , "\0 " ) ) ;
75
+ CALL_ONCE_STACK . store( 0 , Ordering :: Relaxed ) ;
37
76
} ) ;
38
77
:: std:: mem:: transmute( REAL )
39
78
}
0 commit comments