Skip to content

Commit b1375cd

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

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
@@ -284,7 +284,11 @@ mod lazy {
284284
}
285285

286286
pub unsafe fn get(&self) -> Option<&'static T> {
287-
(*self.inner.get()).as_ref()
287+
// SAFETY: No reference is ever handed out to the inner cell nor
288+
// mutable reference to the Option<T> inside said cell. This make it
289+
// safe to hand a reference, though the lifetime of 'static
290+
// is itself unsafe, making the get method unsafe.
291+
unsafe { (*self.inner.get()).as_ref() }
288292
}
289293

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

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

321333
#[allow(unused)]
322334
pub unsafe fn take(&mut self) -> Option<T> {
323-
(*self.inner.get()).take()
335+
// SAFETY: The other methods hand out references while taking &self.
336+
// As such, calling this method when such references are still alive
337+
// will fail because it takes a &mut self, conflicting with them.
338+
unsafe { (*self.inner.get()).take() }
324339
}
325340
}
326341
}
@@ -409,9 +424,17 @@ pub mod fast {
409424
}
410425

411426
pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
412-
match self.inner.get() {
413-
Some(val) => Some(val),
414-
None => self.try_initialize(init),
427+
// SAFETY: See the definitions of `LazyKeyInner::get` and
428+
// `try_initialize` for more informations.
429+
//
430+
// The call to `get` is made safe because no mutable references are
431+
// ever handed out and the `try_initialize` is dependant on the
432+
// passed `init` function.
433+
unsafe {
434+
match self.inner.get() {
435+
Some(val) => Some(val),
436+
None => self.try_initialize(init),
437+
}
415438
}
416439
}
417440

@@ -425,8 +448,10 @@ pub mod fast {
425448
// LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
426449
#[cold]
427450
unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
428-
if !mem::needs_drop::<T>() || self.try_register_dtor() {
429-
Some(self.inner.initialize(init))
451+
// SAFETY: See comment above.
452+
if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
453+
// SAFETY: See comment above.
454+
Some(unsafe { self.inner.initialize(init) })
430455
} else {
431456
None
432457
}
@@ -438,8 +463,12 @@ pub mod fast {
438463
unsafe fn try_register_dtor(&self) -> bool {
439464
match self.dtor_state.get() {
440465
DtorState::Unregistered => {
441-
// dtor registration happens before initialization.
442-
register_dtor(self as *const _ as *mut u8, destroy_value::<T>);
466+
// SAFETY: dtor registration happens before initialization.
467+
// Passing `self` as a pointer while using `destroy_value<T>`
468+
// is safe because the function will build a pointer to a
469+
// Key<T>, which is the type of self and so find the correct
470+
// size.
471+
unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
443472
self.dtor_state.set(DtorState::Registered);
444473
true
445474
}
@@ -455,13 +484,21 @@ pub mod fast {
455484
unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
456485
let ptr = ptr as *mut Key<T>;
457486

487+
// SAFETY:
488+
//
489+
// The pointer `ptr` has been built just above and comes from
490+
// `try_register_dtor` where it is originally a Key<T> coming from `self`,
491+
// making it non-NUL and of the correct type.
492+
//
458493
// Right before we run the user destructor be sure to set the
459494
// `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
460495
// causes future calls to `get` to run `try_initialize_drop` again,
461496
// which will now fail, and return `None`.
462-
let value = (*ptr).inner.take();
463-
(*ptr).dtor_state.set(DtorState::RunningOrHasRun);
464-
drop(value);
497+
unsafe {
498+
let value = (*ptr).inner.take();
499+
(*ptr).dtor_state.set(DtorState::RunningOrHasRun);
500+
drop(value);
501+
}
465502
}
466503
}
467504

@@ -530,23 +567,29 @@ pub mod os {
530567
ptr
531568
};
532569

533-
Some((*ptr).inner.initialize(init))
570+
// SAFETY: ptr has been ensured as non-NUL just above an so can be
571+
// dereferenced safely.
572+
unsafe { Some((*ptr).inner.initialize(init)) }
534573
}
535574
}
536575

537576
unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
577+
// SAFETY:
578+
//
538579
// The OS TLS ensures that this key contains a NULL value when this
539580
// destructor starts to run. We set it back to a sentinel value of 1 to
540581
// ensure that any future calls to `get` for this thread will return
541582
// `None`.
542583
//
543584
// Note that to prevent an infinite loop we reset it back to null right
544585
// before we return from the destructor ourselves.
545-
let ptr = Box::from_raw(ptr as *mut Value<T>);
546-
let key = ptr.key;
547-
key.os.set(1 as *mut u8);
548-
drop(ptr);
549-
key.os.set(ptr::null_mut());
586+
unsafe {
587+
let ptr = Box::from_raw(ptr as *mut Value<T>);
588+
let key = ptr.key;
589+
key.os.set(1 as *mut u8);
590+
drop(ptr);
591+
key.os.set(ptr::null_mut());
592+
}
550593
}
551594
}
552595

library/std/src/thread/mod.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
//! [`with`]: struct.LocalKey.html#method.with
156156
157157
#![stable(feature = "rust1", since = "1.0.0")]
158+
#![deny(unsafe_op_in_unsafe_fn)]
158159

159160
use crate::any::Any;
160161
use crate::cell::UnsafeCell;
@@ -470,14 +471,23 @@ impl Builder {
470471
imp::Thread::set_name(name);
471472
}
472473

473-
thread_info::set(imp::guard::current(), their_thread);
474+
// SAFETY: the stack guard passed is the one for the current thread.
475+
// This means the current thread's stack and the new thread's stack
476+
// are properly set and protected from each other.
477+
thread_info::set(unsafe { imp::guard::current() }, their_thread);
474478
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
475479
crate::sys_common::backtrace::__rust_begin_short_backtrace(f)
476480
}));
477-
*their_packet.get() = Some(try_result);
481+
// SAFETY: `their_packet` as been built just above and moved by the
482+
// closure (it is an Arc<...>) and `my_packet` will be stored in the
483+
// same `JoinInner` as this closure meaning the mutation will be
484+
// safe (not modify it and affect a value far away).
485+
unsafe { *their_packet.get() = Some(try_result) };
478486
};
479487

480488
Ok(JoinHandle(JoinInner {
489+
// SAFETY:
490+
//
481491
// `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed
482492
// through FFI or otherwise used with low-level threading primitives that have no
483493
// notion of or way to enforce lifetimes.
@@ -489,12 +499,14 @@ impl Builder {
489499
// Similarly, the `sys` implementation must guarantee that no references to the closure
490500
// exist after the thread has terminated, which is signaled by `Thread::join`
491501
// returning.
492-
native: Some(imp::Thread::new(
493-
stack_size,
494-
mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(Box::new(
495-
main,
496-
)),
497-
)?),
502+
native: unsafe {
503+
Some(imp::Thread::new(
504+
stack_size,
505+
mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(
506+
Box::new(main),
507+
),
508+
)?)
509+
},
498510
thread: my_thread,
499511
packet: Packet(my_packet),
500512
}))

0 commit comments

Comments
 (0)