On target_os = "linux", ensure that only one Rust thread calls `lib… · model-checking/verify-rust-std@1530977 (original) (raw)

``

1

`+

cfg_if::cfg_if! {

`

``

2

`+

if #[cfg(target_os = "linux")] {

`

``

3

`+

/// Mitigation for https://github.com/rust-lang/rust/issues/126600

`

``

4

`+

///

`

``

5

`` +

/// On unix (where libc::exit may not be thread-safe), ensure that only one Rust thread

``

``

6

`` +

/// calls libc::exit (or returns from main) by calling this function before calling

``

``

7

`` +

/// libc::exit (or returning from main).

``

``

8

`+

///

`

``

9

`+

/// Technically not enough to ensure soundness, since other code directly calling

`

``

10

`+

/// libc::exit will still race with this.

`

``

11

`+

///

`

``

12

`` +

/// This function does not itself call libc::exit. This is so it can also be used

``

``

13

`` +

/// to guard returning from main.

``

``

14

`+

///

`

``

15

`+

/// This function will return only the first time it is called in a process.

`

``

16

`+

///

`

``

17

`+

/// * If it is called again on the same thread as the first call, it will abort.

`

``

18

`` +

/// * If it is called again on a different thread, it will thread::park() in a loop

``

``

19

`+

/// (waiting for the process to exit).

`

``

20

`+

pub(crate) fn unique_thread_exit() {

`

``

21

`+

let this_thread_id = unsafe { libc::gettid() };

`

``

22

`+

debug_assert_ne!(this_thread_id, 0, "thread ID cannot be zero");

`

``

23

`+

#[cfg(target_has_atomic = "32")]

`

``

24

`+

{

`

``

25

`+

use crate::sync::atomic::{AtomicI32, Ordering};

`

``

26

`+

static EXITING_THREAD_ID: AtomicI32 = AtomicI32::new(0);

`

``

27

`+

match EXITING_THREAD_ID.compare_exchange(

`

``

28

`+

0,

`

``

29

`+

this_thread_id,

`

``

30

`+

Ordering::Relaxed,

`

``

31

`+

Ordering::Relaxed,

`

``

32

`+

) {

`

``

33

`+

Ok(_zero) => {

`

``

34

`` +

// This is the first thread to call unique_thread_exit,

``

``

35

`+

// and this is the first time it is called.

`

``

36

`+

// Set EXITING_THREAD_ID to this thread's ID (done by the

`

``

37

`+

// compare_exchange) and return.

`

``

38

`+

}

`

``

39

`+

Err(id) if id == this_thread_id => {

`

``

40

`` +

// This is the first thread to call unique_thread_exit,

``

``

41

`+

// but this is the second time it is called.

`

``

42

`+

// Abort the process.

`

``

43

`+

core::panicking::panic_nounwind("std::process::exit called re-entrantly")

`

``

44

`+

}

`

``

45

`+

Err(_) => {

`

``

46

`` +

// This is not the first thread to call unique_thread_exit.

``

``

47

`+

// Park until the process exits.

`

``

48

`+

loop {

`

``

49

`+

crate::thread::park();

`

``

50

`+

}

`

``

51

`+

}

`

``

52

`+

}

`

``

53

`+

}

`

``

54

`+

#[cfg(not(target_has_atomic = "32"))]

`

``

55

`+

{

`

``

56

`+

use crate::sync::{Mutex, PoisonError};

`

``

57

`+

static EXITING_THREAD_ID: Mutex = Mutex::new(0);

`

``

58

`+

let mut exiting_thread_id =

`

``

59

`+

EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner);

`

``

60

`+

if *exiting_thread_id == 0 {

`

``

61

`` +

// This is the first thread to call unique_thread_exit,

``

``

62

`+

// and this is the first time it is called.

`

``

63

`+

// Set EXITING_THREAD_ID to this thread's ID and return.

`

``

64

`+

*exiting_thread_id = this_thread_id;

`

``

65

`+

} else if *exiting_thread_id == this_thread_id {

`

``

66

`` +

// This is the first thread to call unique_thread_exit,

``

``

67

`+

// but this is the second time it is called.

`

``

68

`+

// Abort the process.

`

``

69

`+

core::panicking::panic_nounwind("std::process::exit called re-entrantly")

`

``

70

`+

} else {

`

``

71

`` +

// This is not the first thread to call unique_thread_exit.

``

``

72

`+

// Park until the process exits.

`

``

73

`+

drop(exiting_thread_id);

`

``

74

`+

loop {

`

``

75

`+

crate::thread::park();

`

``

76

`+

}

`

``

77

`+

}

`

``

78

`+

}

`

``

79

`+

}

`

``

80

`+

} else {

`

``

81

`+

/// Mitigation for https://github.com/rust-lang/rust/issues/126600

`

``

82

`+

///

`

``

83

`+

/// Mitigation is NOT implemented on this platform, either because this platform is not affected, or because mitigation is not yet implemented for this platform.

`

``

84

`+

pub(crate) fn unique_thread_exit() {

`

``

85

`` +

// Mitigation not required on platforms where exit is thread-safe.

``

``

86

`+

}

`

``

87

`+

}

`

``

88

`+

}

`