Skip to content

Commit 8c9cb06

Browse files
committed
Deny unsafe op in unsafe fns without the unsafe keyword, first part for std/thread
1 parent 4eff9b0 commit 8c9cb06

File tree

2 files changed

+91
-36
lines changed

2 files changed

+91
-36
lines changed

library/std/src/thread/local.rs

+71-28
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,11 @@ mod lazy {
288288
}
289289

290290
pub unsafe fn get(&self) -> Option<&'static T> {
291-
(*self.inner.get()).as_ref()
291+
// SAFETY: No reference is ever handed out to the inner cell nor
292+
// mutable reference to the Option<T> inside said cell. This make it
293+
// safe to hand a reference, though the lifetime of 'static
294+
// is itself unsafe, making the get method unsafe.
295+
unsafe { (*self.inner.get()).as_ref() }
292296
}
293297

294298
pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
@@ -297,6 +301,8 @@ mod lazy {
297301
let value = init();
298302
let ptr = self.inner.get();
299303

304+
// SAFETY:
305+
//
300306
// note that this can in theory just be `*ptr = Some(value)`, but due to
301307
// the compiler will currently codegen that pattern with something like:
302308
//
@@ -309,22 +315,31 @@ mod lazy {
309315
// value (an aliasing violation). To avoid setting the "I'm running a
310316
// destructor" flag we just use `mem::replace` which should sequence the
311317
// operations a little differently and make this safe to call.
312-
let _ = mem::replace(&mut *ptr, Some(value));
313-
314-
// After storing `Some` we want to get a reference to the contents of
315-
// what we just stored. While we could use `unwrap` here and it should
316-
// always work it empirically doesn't seem to always get optimized away,
317-
// which means that using something like `try_with` can pull in
318-
// panicking code and cause a large size bloat.
319-
match *ptr {
320-
Some(ref x) => x,
321-
None => hint::unreachable_unchecked(),
318+
unsafe {
319+
let _ = mem::replace(&mut *ptr, Some(value));
320+
}
321+
322+
// SAFETY: the *ptr operation is made safe by the `mem::replace`
323+
// call above that made sure a valid value is present behind it.
324+
unsafe {
325+
// After storing `Some` we want to get a reference to the contents of
326+
// what we just stored. While we could use `unwrap` here and it should
327+
// always work it empirically doesn't seem to always get optimized away,
328+
// which means that using something like `try_with` can pull in
329+
// panicking code and cause a large size bloat.
330+
match *ptr {
331+
Some(ref x) => x,
332+
None => hint::unreachable_unchecked(),
333+
}
322334
}
323335
}
324336

325337
#[allow(unused)]
326338
pub unsafe fn take(&mut self) -> Option<T> {
327-
(*self.inner.get()).take()
339+
// SAFETY: The other methods hand out references while taking &self.
340+
// As such, calling this method when such references are still alive
341+
// will fail because it takes a &mut self, conflicting with them.
342+
unsafe { (*self.inner.get()).take() }
328343
}
329344
}
330345
}
@@ -413,9 +428,17 @@ pub mod fast {
413428
}
414429

415430
pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
416-
match self.inner.get() {
417-
Some(val) => Some(val),
418-
None => self.try_initialize(init),
431+
// SAFETY: See the definitions of `LazyKeyInner::get` and
432+
// `try_initialize` for more informations.
433+
//
434+
// The call to `get` is made safe because no mutable references are
435+
// ever handed out and the `try_initialize` is dependant on the
436+
// passed `init` function.
437+
unsafe {
438+
match self.inner.get() {
439+
Some(val) => Some(val),
440+
None => self.try_initialize(init),
441+
}
419442
}
420443
}
421444

@@ -428,8 +451,10 @@ pub mod fast {
428451
// LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
429452
#[inline(never)]
430453
unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
431-
if !mem::needs_drop::<T>() || self.try_register_dtor() {
432-
Some(self.inner.initialize(init))
454+
// SAFETY: See comment above.
455+
if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
456+
// SAFETY: See comment above.
457+
Some(unsafe { self.inner.initialize(init) })
433458
} else {
434459
None
435460
}
@@ -441,8 +466,12 @@ pub mod fast {
441466
unsafe fn try_register_dtor(&self) -> bool {
442467
match self.dtor_state.get() {
443468
DtorState::Unregistered => {
444-
// dtor registration happens before initialization.
445-
register_dtor(self as *const _ as *mut u8, destroy_value::<T>);
469+
// SAFETY: dtor registration happens before initialization.
470+
// Passing `self` as a pointer while using `destroy_value<T>`
471+
// is safe because the function will build a pointer to a
472+
// Key<T>, which is the type of self and so find the correct
473+
// size.
474+
unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
446475
self.dtor_state.set(DtorState::Registered);
447476
true
448477
}
@@ -458,13 +487,21 @@ pub mod fast {
458487
unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
459488
let ptr = ptr as *mut Key<T>;
460489

490+
// SAFETY:
491+
//
492+
// The pointer `ptr` has been built just above and comes from
493+
// `try_register_dtor` where it is originally a Key<T> coming from `self`,
494+
// making it non-NUL and of the correct type.
495+
//
461496
// Right before we run the user destructor be sure to set the
462497
// `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
463498
// causes future calls to `get` to run `try_initialize_drop` again,
464499
// which will now fail, and return `None`.
465-
let value = (*ptr).inner.take();
466-
(*ptr).dtor_state.set(DtorState::RunningOrHasRun);
467-
drop(value);
500+
unsafe {
501+
let value = (*ptr).inner.take();
502+
(*ptr).dtor_state.set(DtorState::RunningOrHasRun);
503+
drop(value);
504+
}
468505
}
469506
}
470507

@@ -533,22 +570,28 @@ pub mod os {
533570
ptr
534571
};
535572

536-
Some((*ptr).inner.initialize(init))
573+
// SAFETY: ptr has been ensured as non-NUL just above an so can be
574+
// dereferenced safely.
575+
unsafe { Some((*ptr).inner.initialize(init)) }
537576
}
538577
}
539578

540579
unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
580+
// SAFETY:
581+
//
541582
// The OS TLS ensures that this key contains a NULL value when this
542583
// destructor starts to run. We set it back to a sentinel value of 1 to
543584
// ensure that any future calls to `get` for this thread will return
544585
// `None`.
545586
//
546587
// Note that to prevent an infinite loop we reset it back to null right
547588
// before we return from the destructor ourselves.
548-
let ptr = Box::from_raw(ptr as *mut Value<T>);
549-
let key = ptr.key;
550-
key.os.set(1 as *mut u8);
551-
drop(ptr);
552-
key.os.set(ptr::null_mut());
589+
unsafe {
590+
let ptr = Box::from_raw(ptr as *mut Value<T>);
591+
let key = ptr.key;
592+
key.os.set(1 as *mut u8);
593+
drop(ptr);
594+
key.os.set(ptr::null_mut());
595+
}
553596
}
554597
}

library/std/src/thread/mod.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
//! [`with`]: LocalKey::with
145145
146146
#![stable(feature = "rust1", since = "1.0.0")]
147+
#![deny(unsafe_op_in_unsafe_fn)]
147148

148149
#[cfg(all(test, not(target_os = "emscripten")))]
149150
mod tests;
@@ -456,14 +457,23 @@ impl Builder {
456457
imp::Thread::set_name(name);
457458
}
458459

459-
thread_info::set(imp::guard::current(), their_thread);
460+
// SAFETY: the stack guard passed is the one for the current thread.
461+
// This means the current thread's stack and the new thread's stack
462+
// are properly set and protected from each other.
463+
thread_info::set(unsafe { imp::guard::current() }, their_thread);
460464
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
461465
crate::sys_common::backtrace::__rust_begin_short_backtrace(f)
462466
}));
463-
*their_packet.get() = Some(try_result);
467+
// SAFETY: `their_packet` as been built just above and moved by the
468+
// closure (it is an Arc<...>) and `my_packet` will be stored in the
469+
// same `JoinInner` as this closure meaning the mutation will be
470+
// safe (not modify it and affect a value far away).
471+
unsafe { *their_packet.get() = Some(try_result) };
464472
};
465473

466474
Ok(JoinHandle(JoinInner {
475+
// SAFETY:
476+
//
467477
// `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed
468478
// through FFI or otherwise used with low-level threading primitives that have no
469479
// notion of or way to enforce lifetimes.
@@ -475,12 +485,14 @@ impl Builder {
475485
// Similarly, the `sys` implementation must guarantee that no references to the closure
476486
// exist after the thread has terminated, which is signaled by `Thread::join`
477487
// returning.
478-
native: Some(imp::Thread::new(
479-
stack_size,
480-
mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(Box::new(
481-
main,
482-
)),
483-
)?),
488+
native: unsafe {
489+
Some(imp::Thread::new(
490+
stack_size,
491+
mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(
492+
Box::new(main),
493+
),
494+
)?)
495+
},
484496
thread: my_thread,
485497
packet: Packet(my_packet),
486498
}))

0 commit comments

Comments
 (0)