Skip to content

Commit 8ee1dec

Browse files
Deny unsafe ops in unsafe fns, part 1
1 parent 665190b commit 8ee1dec

File tree

11 files changed

+111
-31
lines changed

11 files changed

+111
-31
lines changed

src/libcore/alloc/global.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,12 @@ pub unsafe trait GlobalAlloc {
127127
#[stable(feature = "global_alloc", since = "1.28.0")]
128128
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
129129
let size = layout.size();
130-
let ptr = self.alloc(layout);
130+
// SAFETY: the safety contract for `alloc` must be upheld by the caller.
131+
let ptr = unsafe { self.alloc(layout) };
131132
if !ptr.is_null() {
132-
ptr::write_bytes(ptr, 0, size);
133+
// SAFETY: as allocation succeeded, the region from `ptr`
134+
// of size `size` is guaranteed to be valid for writes.
135+
unsafe { ptr::write_bytes(ptr, 0, size) };
133136
}
134137
ptr
135138
}
@@ -187,11 +190,18 @@ pub unsafe trait GlobalAlloc {
187190
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
188191
#[stable(feature = "global_alloc", since = "1.28.0")]
189192
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
190-
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
191-
let new_ptr = self.alloc(new_layout);
193+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
194+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
195+
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
196+
// SAFETY: the caller must ensure that `new_layout` is greater than zero.
197+
let new_ptr = unsafe { self.alloc(new_layout) };
192198
if !new_ptr.is_null() {
193-
ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
194-
self.dealloc(ptr, layout);
199+
// SAFETY: the previously allocated block cannot overlap the newly allocated block.
200+
// The safety contract for `dealloc` must be upheld by the caller.
201+
unsafe {
202+
ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
203+
self.dealloc(ptr, layout);
204+
}
195205
}
196206
new_ptr
197207
}

src/libcore/alloc/layout.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ impl Layout {
9090
#[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")]
9191
#[inline]
9292
pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self {
93-
Layout { size_: size, align_: NonZeroUsize::new_unchecked(align) }
93+
// SAFETY: the caller must ensure that `align` is greater than zero.
94+
Layout { size_: size, align_: unsafe { NonZeroUsize::new_unchecked(align) } }
9495
}
9596

9697
/// The minimum size in bytes for a memory block of this layout.

src/libcore/alloc/mod.rs

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Memory allocation APIs
22
33
#![stable(feature = "alloc_module", since = "1.28.0")]
4+
#![deny(unsafe_op_in_unsafe_fn)]
45

56
mod global;
67
mod layout;
@@ -54,7 +55,9 @@ impl AllocInit {
5455
#[inline]
5556
#[unstable(feature = "allocator_api", issue = "32838")]
5657
pub unsafe fn init(self, memory: MemoryBlock) {
57-
self.init_offset(memory, 0)
58+
// SAFETY: the safety contract for `init_offset` must be
59+
// upheld by the caller.
60+
unsafe { self.init_offset(memory, 0) }
5861
}
5962

6063
/// Initialize the memory block like specified by `init` at the specified `offset`.
@@ -78,7 +81,10 @@ impl AllocInit {
7881
match self {
7982
AllocInit::Uninitialized => (),
8083
AllocInit::Zeroed => {
81-
memory.ptr.as_ptr().add(offset).write_bytes(0, memory.size - offset)
84+
// SAFETY: the caller must guarantee that `offset` is smaller than or equal to `memory.size`,
85+
// so the memory from `memory.ptr + offset` of length `memory.size - offset`
86+
// is guaranteed to be contaned in `memory` and thus valid for writes.
87+
unsafe { memory.ptr.as_ptr().add(offset).write_bytes(0, memory.size - offset) }
8288
}
8389
}
8490
}
@@ -281,11 +287,23 @@ pub unsafe trait AllocRef {
281287
return Ok(MemoryBlock { ptr, size });
282288
}
283289

284-
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
290+
let new_layout =
291+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
292+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
293+
// The caller must ensure that `new_size` is greater than zero.
294+
unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
285295
let new_memory = self.alloc(new_layout, init)?;
286-
ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size);
287-
self.dealloc(ptr, layout);
288-
Ok(new_memory)
296+
297+
// SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
298+
// memory allocation are valid for reads and writes for `size` bytes. Also, because the old
299+
// allocation wasn't yet deallocated, it cannot overlap `new_memory`. Thus, the call to
300+
// `copy_nonoverlapping` is safe.
301+
// The safety contract for `dealloc` must be upheld by the caller.
302+
unsafe {
303+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size);
304+
self.dealloc(ptr, layout);
305+
Ok(new_memory)
306+
}
289307
}
290308
}
291309
}
@@ -356,11 +374,23 @@ pub unsafe trait AllocRef {
356374
return Ok(MemoryBlock { ptr, size });
357375
}
358376

359-
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
377+
let new_layout =
378+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
379+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
380+
// The caller must ensure that `new_size` is greater than zero.
381+
unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
360382
let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
361-
ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size);
362-
self.dealloc(ptr, layout);
363-
Ok(new_memory)
383+
384+
// SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new
385+
// memory allocation are valid for reads and writes for `new_size` bytes. Also, because the
386+
// old allocation wasn't yet deallocated, it cannot overlap `new_memory`. Thus, the call to
387+
// `copy_nonoverlapping` is safe.
388+
// The safety contract for `dealloc` must be upheld by the caller.
389+
unsafe {
390+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size);
391+
self.dealloc(ptr, layout);
392+
Ok(new_memory)
393+
}
364394
}
365395
}
366396
}
@@ -386,7 +416,8 @@ where
386416

387417
#[inline]
388418
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
389-
(**self).dealloc(ptr, layout)
419+
// SAFETY: the safety contract must be upheld by the caller
420+
unsafe { (**self).dealloc(ptr, layout) }
390421
}
391422

392423
#[inline]
@@ -398,7 +429,8 @@ where
398429
placement: ReallocPlacement,
399430
init: AllocInit,
400431
) -> Result<MemoryBlock, AllocErr> {
401-
(**self).grow(ptr, layout, new_size, placement, init)
432+
// SAFETY: the safety contract must be upheld by the caller
433+
unsafe { (**self).grow(ptr, layout, new_size, placement, init) }
402434
}
403435

404436
#[inline]
@@ -409,6 +441,7 @@ where
409441
new_size: usize,
410442
placement: ReallocPlacement,
411443
) -> Result<MemoryBlock, AllocErr> {
412-
(**self).shrink(ptr, layout, new_size, placement)
444+
// SAFETY: the safety contract must be upheld by the caller
445+
unsafe { (**self).shrink(ptr, layout, new_size, placement) }
413446
}
414447
}

src/libcore/cell.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
//!
188188
189189
#![stable(feature = "rust1", since = "1.0.0")]
190+
#![deny(unsafe_op_in_unsafe_fn)]
190191

191192
use crate::cmp::Ordering;
192193
use crate::fmt::{self, Debug, Display};
@@ -1005,7 +1006,12 @@ impl<T: ?Sized> RefCell<T> {
10051006
#[inline]
10061007
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
10071008
if !is_writing(self.borrow.get()) {
1008-
Ok(&*self.value.get())
1009+
// SAFETY: We check that nobody is actively writing now, but it is
1010+
// the caller's responsibility to ensure that nobody writes until
1011+
// the returned reference is no longer in use.
1012+
// Also, `self.value.get()` refers to the value owned by `self`
1013+
// and is thus guaranteed to be valid for the lifetime of `self`.
1014+
Ok(unsafe { &*self.value.get() })
10091015
} else {
10101016
Err(BorrowError { _private: () })
10111017
}

src/libcore/char/convert.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Character conversions.
22
3+
#![deny(unsafe_op_in_unsafe_fn)]
4+
35
use crate::convert::TryFrom;
46
use crate::fmt;
57
use crate::mem::transmute;
@@ -99,7 +101,8 @@ pub fn from_u32(i: u32) -> Option<char> {
99101
#[inline]
100102
#[stable(feature = "char_from_unchecked", since = "1.5.0")]
101103
pub unsafe fn from_u32_unchecked(i: u32) -> char {
102-
if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { transmute(i) }
104+
// SAFETY: the caller must guarantee that `i` is a valid char value.
105+
if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { unsafe { transmute(i) } }
103106
}
104107

105108
#[stable(feature = "char_convert", since = "1.13.0")]

src/libcore/char/methods.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! impl char {}
22
3+
#![deny(unsafe_op_in_unsafe_fn)]
4+
35
use crate::slice;
46
use crate::str::from_utf8_unchecked_mut;
57
use crate::unicode::printable::is_printable;
@@ -183,7 +185,8 @@ impl char {
183185
#[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")]
184186
#[inline]
185187
pub unsafe fn from_u32_unchecked(i: u32) -> char {
186-
super::convert::from_u32_unchecked(i)
188+
// SAFETY: the safety contract must be upheld by the caller.
189+
unsafe { super::convert::from_u32_unchecked(i) }
187190
}
188191

189192
/// Converts a digit in the given radix to a `char`.

src/libcore/convert/num.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![deny(unsafe_op_in_unsafe_fn)]
2+
13
use super::{From, TryFrom};
24
use crate::num::TryFromIntError;
35

@@ -28,7 +30,8 @@ macro_rules! impl_float_to_int {
2830
#[doc(hidden)]
2931
#[inline]
3032
unsafe fn to_int_unchecked(self) -> $Int {
31-
crate::intrinsics::float_to_int_unchecked(self)
33+
// SAFETY: the safety contract must be upheld by the caller.
34+
unsafe { crate::intrinsics::float_to_int_unchecked(self) }
3235
}
3336
}
3437
)+

src/libcore/hint.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
//! Hints to compiler that affects how code should be emitted or optimized.
44
5+
#![deny(unsafe_op_in_unsafe_fn)]
6+
57
use crate::intrinsics;
68

79
/// Informs the compiler that this point in the code is not reachable, enabling
@@ -46,7 +48,9 @@ use crate::intrinsics;
4648
#[inline]
4749
#[stable(feature = "unreachable", since = "1.27.0")]
4850
pub unsafe fn unreachable_unchecked() -> ! {
49-
intrinsics::unreachable()
51+
// SAFETY: the safety contract for `intrinsics::unreachable` must
52+
// be upheld by the caller.
53+
unsafe { intrinsics::unreachable() }
5054
}
5155

5256
/// Emits a machine instruction hinting to the processor that it is running in busy-wait

src/libcore/intrinsics.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
issue = "none"
5454
)]
5555
#![allow(missing_docs)]
56+
#![deny(unsafe_op_in_unsafe_fn)]
5657

5758
use crate::marker::DiscriminantKind;
5859
use crate::mem;
@@ -2097,7 +2098,10 @@ pub unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
20972098
// Not panicking to keep codegen impact smaller.
20982099
abort();
20992100
}
2100-
copy_nonoverlapping(src, dst, count)
2101+
2102+
// SAFETY: the safety contract for `copy_nonoverlapping` must be
2103+
// upheld by the caller.
2104+
unsafe { copy_nonoverlapping(src, dst, count) }
21012105
}
21022106

21032107
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
@@ -2163,7 +2167,9 @@ pub unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
21632167
// Not panicking to keep codegen impact smaller.
21642168
abort();
21652169
}
2166-
copy(src, dst, count)
2170+
2171+
// SAFETY: the safety contract for `copy` must be upheld by the caller.
2172+
unsafe { copy(src, dst, count) }
21672173
}
21682174

21692175
/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to
@@ -2246,5 +2252,7 @@ pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
22462252
}
22472253

22482254
debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer");
2249-
write_bytes(dst, val, count)
2255+
2256+
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
2257+
unsafe { write_bytes(dst, val, count) }
22502258
}

src/libcore/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
#![feature(const_type_id)]
149149
#![feature(const_caller_location)]
150150
#![feature(no_niche)] // rust-lang/rust#68303
151+
#![feature(unsafe_block_in_unsafe_fn)]
151152

152153
#[prelude_import]
153154
#[allow(unused)]

src/libcore/pin.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@
375375
//! [`i32`]: ../../std/primitive.i32.html
376376
377377
#![stable(feature = "pin", since = "1.33.0")]
378+
#![deny(unsafe_op_in_unsafe_fn)]
378379

379380
use crate::cmp::{self, PartialEq, PartialOrd};
380381
use crate::fmt;
@@ -679,7 +680,10 @@ impl<'a, T: ?Sized> Pin<&'a T> {
679680
{
680681
let pointer = &*self.pointer;
681682
let new_pointer = func(pointer);
682-
Pin::new_unchecked(new_pointer)
683+
684+
// SAFETY: the safety contract for `new_unchecked` must be
685+
// upheld by the caller.
686+
unsafe { Pin::new_unchecked(new_pointer) }
683687
}
684688

685689
/// Gets a shared reference out of a pin.
@@ -769,9 +773,13 @@ impl<'a, T: ?Sized> Pin<&'a mut T> {
769773
U: ?Sized,
770774
F: FnOnce(&mut T) -> &mut U,
771775
{
772-
let pointer = Pin::get_unchecked_mut(self);
776+
// SAFETY: the caller is responsible for not moving the
777+
// value out of this reference.
778+
let pointer = unsafe { Pin::get_unchecked_mut(self) };
773779
let new_pointer = func(pointer);
774-
Pin::new_unchecked(new_pointer)
780+
// SAFETY: as the value of `this` is guaranteed to not have
781+
// been moved out, this call to `new_unchecked` is safe.
782+
unsafe { Pin::new_unchecked(new_pointer) }
775783
}
776784
}
777785

0 commit comments

Comments
 (0)