Skip to content

Commit

Permalink
Allow zero-size reads/writes on null pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
saethlin committed Oct 9, 2024
1 parent 9ca739e commit 528dda2
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 19 deletions.
19 changes: 12 additions & 7 deletions core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#![allow(missing_docs)]

use crate::marker::{DiscriminantKind, Tuple};
use crate::mem::SizedTypeProperties;
use crate::{ptr, ub_checks};

pub mod mir;
Expand Down Expand Up @@ -3311,10 +3312,12 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
size: usize = size_of::<T>(),
align: usize = align_of::<T>(),
count: usize = count,
) =>
ub_checks::is_aligned_and_not_null(src, align)
&& ub_checks::is_aligned_and_not_null(dst, align)
&& ub_checks::is_nonoverlapping(src, dst, size, count)
) => {
let zero_size = count == 0 || size == 0;
ub_checks::is_aligned_and_not_null(src, align, zero_size)
&& ub_checks::is_aligned_and_not_null(dst, align, zero_size)
&& ub_checks::is_nonoverlapping(src, dst, size, count)
}
);

// SAFETY: the safety contract for `copy_nonoverlapping` must be
Expand Down Expand Up @@ -3412,9 +3415,10 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
src: *const () = src as *const (),
dst: *mut () = dst as *mut (),
align: usize = align_of::<T>(),
zero_size: bool = T::IS_ZST || count == 0,
) =>
ub_checks::is_aligned_and_not_null(src, align)
&& ub_checks::is_aligned_and_not_null(dst, align)
ub_checks::is_aligned_and_not_null(src, align, zero_size)
&& ub_checks::is_aligned_and_not_null(dst, align, zero_size)
);
copy(src, dst, count)
}
Expand Down Expand Up @@ -3491,7 +3495,8 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
(
addr: *const () = dst as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
zero_size: bool = T::IS_ZST || count == 0,
) => ub_checks::is_aligned_and_not_null(addr, align, zero_size)
);
write_bytes(dst, val, count)
}
Expand Down
21 changes: 13 additions & 8 deletions core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@

use crate::cmp::Ordering;
use crate::marker::FnPtr;
use crate::mem::{self, MaybeUninit};
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
use crate::{fmt, hash, intrinsics, ub_checks};

mod alignment;
Expand Down Expand Up @@ -1165,10 +1165,12 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
size: usize = size_of::<T>(),
align: usize = align_of::<T>(),
count: usize = count,
) =>
ub_checks::is_aligned_and_not_null(x, align)
&& ub_checks::is_aligned_and_not_null(y, align)
&& ub_checks::is_nonoverlapping(x, y, size, count)
) => {
let zero_size = size == 0 || count == 0;
ub_checks::is_aligned_and_not_null(x, align, zero_size)
&& ub_checks::is_aligned_and_not_null(y, align, zero_size)
&& ub_checks::is_nonoverlapping(x, y, size, count)
}
);

// Split up the slice into small power-of-two-sized chunks that LLVM is able
Expand Down Expand Up @@ -1277,7 +1279,8 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
(
addr: *const () = dst as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
mem::replace(&mut *dst, src)
}
Expand Down Expand Up @@ -1806,7 +1809,8 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
(
addr: *const () = src as *const (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
intrinsics::volatile_load(src)
}
Expand Down Expand Up @@ -1885,7 +1889,8 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
(
addr: *mut () = dst as *mut (),
align: usize = align_of::<T>(),
) => ub_checks::is_aligned_and_not_null(addr, align)
is_zst: bool = T::IS_ZST,
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
);
intrinsics::volatile_store(dst, src);
}
Expand Down
4 changes: 2 additions & 2 deletions core/src/slice/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
align: usize = align_of::<T>(),
len: usize = len,
) =>
ub_checks::is_aligned_and_not_null(data, align)
ub_checks::is_aligned_and_not_null(data, align, false)
&& ub_checks::is_valid_allocation_size(size, len)
);
&*ptr::slice_from_raw_parts(data, len)
Expand Down Expand Up @@ -187,7 +187,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
align: usize = align_of::<T>(),
len: usize = len,
) =>
ub_checks::is_aligned_and_not_null(data, align)
ub_checks::is_aligned_and_not_null(data, align, false)
&& ub_checks::is_valid_allocation_size(size, len)
);
&mut *ptr::slice_from_raw_parts_mut(data, len)
Expand Down
4 changes: 2 additions & 2 deletions core/src/ub_checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ pub(crate) const fn check_language_ub() -> bool {
/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
/// check is anyway not executed in `const`.
#[inline]
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool {
!ptr.is_null() && ptr.is_aligned_to(align)
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
if is_zst { ptr.is_aligned_to(align) } else { !ptr.is_null() && ptr.is_aligned_to(align) }
}

#[inline]
Expand Down

0 comments on commit 528dda2

Please sign in to comment.