Skip to content

Commit 2afb64e

Browse files
authored
Rollup merge of #148026 - joboet:dont-leak-thread-closure, r=Mark-Simulacrum
std: don't leak the thread closure if destroying the thread attributes fails The comment about double-free is wrong – we can safely drop both the thread attributes and the thread closure. Here, I've used `DropGuard` for the attributes and moved the `Box::into_raw` to just before the `pthread_create`.
2 parents 4228b53 + a7f08de commit 2afb64e

File tree

1 file changed

+13
-15
lines changed

1 file changed

+13
-15
lines changed

library/std/src/sys/thread/unix.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
target_os = "aix",
88
)))]
99
use crate::ffi::CStr;
10-
use crate::mem::{self, ManuallyDrop};
10+
use crate::mem::{self, DropGuard, ManuallyDrop};
1111
use crate::num::NonZero;
1212
#[cfg(all(target_os = "linux", target_env = "gnu"))]
1313
use crate::sys::weak::dlsym;
@@ -52,10 +52,13 @@ impl Thread {
5252
name: Option<&str>,
5353
f: Box<dyn FnOnce()>,
5454
) -> io::Result<Thread> {
55-
let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f }));
56-
let mut native: libc::pthread_t = mem::zeroed();
55+
let data = Box::new(ThreadData { name: name.map(Box::from), f });
56+
5757
let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
5858
assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
59+
let mut attr = DropGuard::new(&mut attr, |attr| {
60+
assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0)
61+
});
5962

6063
#[cfg(any(target_os = "espidf", target_os = "nuttx"))]
6164
if stack > 0 {
@@ -90,8 +93,6 @@ impl Thread {
9093
// on the stack size, in which case we can only gracefully return
9194
// an error here.
9295
if libc::pthread_attr_setstacksize(attr.as_mut_ptr(), stack_size) != 0 {
93-
assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0);
94-
drop(Box::from_raw(data));
9596
return Err(io::const_error!(
9697
io::ErrorKind::InvalidInput,
9798
"invalid stack size"
@@ -101,19 +102,16 @@ impl Thread {
101102
};
102103
}
103104

105+
let data = Box::into_raw(data);
106+
let mut native: libc::pthread_t = mem::zeroed();
104107
let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, data as *mut _);
105-
// Note: if the thread creation fails and this assert fails, then p will
106-
// be leaked. However, an alternative design could cause double-free
107-
// which is clearly worse.
108-
assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0);
109-
110-
return if ret != 0 {
111-
// The thread failed to start and as a result p was not consumed. Therefore, it is
112-
// safe to reconstruct the box so that it gets deallocated.
108+
return if ret == 0 {
109+
Ok(Thread { id: native })
110+
} else {
111+
// The thread failed to start and as a result `data` was not consumed.
112+
// Therefore, it is safe to reconstruct the box so that it gets deallocated.
113113
drop(Box::from_raw(data));
114114
Err(io::Error::from_raw_os_error(ret))
115-
} else {
116-
Ok(Thread { id: native })
117115
};
118116

119117
extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void {

0 commit comments

Comments
 (0)