Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
47b138b
Make aliases search support partial matching
GuillaumeGomez Jul 15, 2025
b0bf51f
Update rustdoc search tester to new alias output
GuillaumeGomez Jul 15, 2025
c079c96
Add test for aliases partial match
GuillaumeGomez Jul 15, 2025
10762d5
Fix debuginfo-lto-alloc.rs test
bjorn3 Jul 17, 2025
63e1074
Update AMDGPU data layout
nikic Jul 9, 2025
12b19be
Pass wasm exception model to TargetOptions
nikic Jul 11, 2025
a65563e
Make emit-arity-indicator.rs a no_core test
nikic Jul 14, 2025
15acb0e
unicode-table-gen: clippy fixes
hkBst Jul 18, 2025
0959305
unicode-table-gen: edition 2024
hkBst Jul 18, 2025
b0073d9
unicode-table-gen: more clippy fixes
hkBst Jul 18, 2025
8a8717e
fix: don't panic on volatile access to null
LuigiPiucco Apr 20, 2025
4cebbab
Add implicit sized bound to trait ascription types
compiler-errors Jul 18, 2025
e6f2830
Remove pretty print hack for async blocks
compiler-errors Jul 18, 2025
7672e4e
interpret: fix TypeId pointers being considered data pointers
RalfJung Jul 19, 2025
ca01e7d
Stabilize `const_float_round_methods`
nxsaken Jul 7, 2025
caf4f11
Add `#[rustc_intrinsic_const_stable_indirect]` to float rounding
nxsaken Jul 7, 2025
2f14d0a
Add code comment explaining better what `Row.name` is for doc aliases
GuillaumeGomez Jul 19, 2025
61bfc09
So many test updates x_x
scottmcm Jul 12, 2025
2e959f0
...and wasm tests too
scottmcm Jul 19, 2025
67c87c9
Update cranelift tests
scottmcm Jul 13, 2025
d0d1a12
Update Miri Tests
scottmcm Jul 13, 2025
cc5f3f2
Ban projecting into SIMD types [MCP838]
scottmcm Mar 7, 2025
db1449a
Initialize mingw for the runner's user
ChrisDenton Jul 19, 2025
5628885
Remove deprecated MaybeUninit slice methods
clarfonthey Jul 18, 2025
e8cf215
Rollup merge of #141260 - LuigiPiucco:volatile-null, r=RalfJung
fee1-dead Jul 20, 2025
3a8853e
Rollup merge of #143604 - nxsaken:const_float_round_methods, r=RalfJung
fee1-dead Jul 20, 2025
b884daf
Rollup merge of #143833 - scottmcm:final-mcp-838, r=compiler-errors
fee1-dead Jul 20, 2025
ea26bf9
Rollup merge of #143988 - GuillaumeGomez:alias-inexact, r=lolbinarycat
fee1-dead Jul 20, 2025
1fb87c0
Rollup merge of #144078 - bjorn3:fix_test, r=compiler-errors
fee1-dead Jul 20, 2025
6e6e9f2
Rollup merge of #144111 - clarfonthey:maybe-uninit-deprecated, r=jhpratt
fee1-dead Jul 20, 2025
20f9541
Rollup merge of #144116 - nikic:llvm-21-fixes, r=dianqk
fee1-dead Jul 20, 2025
cc2b9c2
Rollup merge of #144134 - hkBst:cleanup-unicode-table-gen, r=Mark-Sim…
fee1-dead Jul 20, 2025
785038d
Rollup merge of #144142 - compiler-errors:itib, r=fmease
fee1-dead Jul 20, 2025
7f23f3d
Rollup merge of #144148 - compiler-errors:async-print-hack, r=lqd
fee1-dead Jul 20, 2025
5d750c3
Rollup merge of #144169 - RalfJung:type-id-fix, r=oli-obk
fee1-dead Jul 20, 2025
a793976
Rollup merge of #144196 - ChrisDenton:init-mingw, r=mati865
fee1-dead Jul 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions compiler/rustc_lint/src/ptr_nulls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,10 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
let (arg_indices, are_zsts_allowed): (&[_], _) = match diag_name {
sym::ptr_read
| sym::ptr_read_unaligned
| sym::ptr_read_volatile
| sym::ptr_replace
| sym::ptr_write
| sym::ptr_write_bytes
| sym::ptr_write_unaligned
| sym::ptr_write_volatile => (&[0], true),
| sym::ptr_write_unaligned => (&[0], true),
sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut => (&[0], false),
sym::ptr_copy
| sym::ptr_copy_nonoverlapping
Expand Down
168 changes: 93 additions & 75 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
//! undefined behavior to perform two concurrent accesses to the same location from different
//! threads unless both accesses only read from memory. Notice that this explicitly
//! includes [`read_volatile`] and [`write_volatile`]: Volatile accesses cannot
//! be used for inter-thread synchronization.
//! be used for inter-thread synchronization, regardless of whether they are acting on
//! Rust memory or not.
//! * The result of casting a reference to a pointer is valid for as long as the
//! underlying allocation is live and no reference (just raw pointers) is used to
//! access the same memory. That is, reference and pointer accesses cannot be
Expand Down Expand Up @@ -114,6 +115,10 @@
//! fully contiguous (i.e., has no "holes"), there is no guarantee that this
//! will not change in the future.
//!
//! Allocations must behave like "normal" memory: in particular, reads must not have
//! side-effects, and writes must become visible to other threads using the usual synchronization
//! primitives.
//!
//! For any allocation with `base` address, `size`, and a set of
//! `addresses`, the following are guaranteed:
//! - For all addresses `a` in `addresses`, `a` is in the range `base .. (base +
Expand Down Expand Up @@ -2021,54 +2026,61 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
}
}

/// Performs a volatile read of the value from `src` without moving it. This
/// leaves the memory in `src` unchanged.
///
/// Volatile operations are intended to act on I/O memory, and are guaranteed
/// to not be elided or reordered by the compiler across other volatile
/// operations.
///
/// # Notes
///
/// Rust does not currently have a rigorously and formally defined memory model,
/// so the precise semantics of what "volatile" means here is subject to change
/// over time. That being said, the semantics will almost always end up pretty
/// similar to [C11's definition of volatile][c11].
///
/// The compiler shouldn't change the relative order or number of volatile
/// memory operations. However, volatile memory operations on zero-sized types
/// (e.g., if a zero-sized type is passed to `read_volatile`) are noops
/// and may be ignored.
///
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
/// Performs a volatile read of the value from `src` without moving it.
///
/// Volatile operations are intended to act on I/O memory. As such, they are considered externally
/// observable events (just like syscalls, but less opaque), and are guaranteed to not be elided or
/// reordered by the compiler across other externally observable events. With this in mind, there
/// are two cases of usage that need to be distinguished:
///
/// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like
/// [`read`], except for the additional guarantee that it won't be elided or reordered (see
/// above). This implies that the operation will actually access memory and not e.g. be lowered to
/// reusing data from a previous read. Other than that, all the usual rules for memory accesses
/// apply (including provenance). In particular, just like in C, whether an operation is volatile
/// has no bearing whatsoever on questions involving concurrent accesses from multiple threads.
/// Volatile accesses behave exactly like non-atomic accesses in that regard.
///
/// - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust
/// allocation. In this use-case, the pointer does *not* have to be [valid] for reads. This is
/// typically used for CPU and peripheral registers that must be accessed via an I/O memory
/// mapping, most commonly at fixed addresses reserved by the hardware. These often have special
/// semantics associated to their manipulation, and cannot be used as general purpose memory.
/// Here, any address value is possible, including 0 and [`usize::MAX`], so long as the semantics
/// of such a read are well-defined by the target hardware. The provenance of the pointer is
/// irrelevant, and it can be created with [`without_provenance`]. The access must not trap. It
/// can cause side-effects, but those must not affect Rust-allocated memory in any way. This
/// access is still not considered [atomic], and as such it cannot be used for inter-thread
/// synchronization.
///
/// Note that volatile memory operations where T is a zero-sized type are noops and may be ignored.
///
/// [allocation]: crate::ptr#allocated-object
/// [atomic]: crate::sync::atomic#memory-model-for-atomic-accesses
///
/// # Safety
///
/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of whether `T` is
/// [`Copy`]. If `T` is not [`Copy`], using both the returned value and the value at `*src` can
/// [violate memory safety][read-ownership]. However, storing non-[`Copy`] types in volatile memory
/// is almost certainly incorrect.
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `src` must be [valid] for reads.
/// * `src` must be either [valid] for reads, or it must point to memory outside of all Rust
/// allocations and reading from that memory must:
/// - not trap, and
/// - not cause any memory inside a Rust allocation to be modified.
///
/// * `src` must be properly aligned.
///
/// * `src` must point to a properly initialized value of type `T`.
///
/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
/// value and the value at `*src` can [violate memory safety][read-ownership].
/// However, storing non-[`Copy`] types in volatile memory is almost certainly
/// incorrect.
/// * Reading from `src` must produce a properly initialized value of type `T`.
///
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
///
/// [valid]: self#safety
/// [read-ownership]: read#ownership-of-the-returned-value
///
/// Just like in C, whether an operation is volatile has no bearing whatsoever
/// on questions involving concurrent access from multiple threads. Volatile
/// accesses behave exactly like non-atomic accesses in that regard. In particular,
/// a race between a `read_volatile` and any write operation to the same location
/// is undefined behavior.
///
/// # Examples
///
/// Basic usage:
Expand All @@ -2090,63 +2102,70 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
unsafe {
ub_checks::assert_unsafe_precondition!(
check_language_ub,
"ptr::read_volatile requires that the pointer argument is aligned and non-null",
"ptr::read_volatile requires that the pointer argument is aligned",
(
addr: *const () = src as *const (),
align: usize = align_of::<T>(),
is_zst: bool = T::IS_ZST,
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
) => ub_checks::maybe_is_aligned(addr, align)
);
intrinsics::volatile_load(src)
}
}

/// Performs a volatile write of a memory location with the given value without
/// reading or dropping the old value.
///
/// Volatile operations are intended to act on I/O memory, and are guaranteed
/// to not be elided or reordered by the compiler across other volatile
/// operations.
///
/// `write_volatile` does not drop the contents of `dst`. This is safe, but it
/// could leak allocations or resources, so care should be taken not to overwrite
/// an object that should be dropped.
///
/// Additionally, it does not drop `src`. Semantically, `src` is moved into the
/// location pointed to by `dst`.
///
/// # Notes
///
/// Rust does not currently have a rigorously and formally defined memory model,
/// so the precise semantics of what "volatile" means here is subject to change
/// over time. That being said, the semantics will almost always end up pretty
/// similar to [C11's definition of volatile][c11].
///
/// The compiler shouldn't change the relative order or number of volatile
/// memory operations. However, volatile memory operations on zero-sized types
/// (e.g., if a zero-sized type is passed to `write_volatile`) are noops
/// and may be ignored.
///
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
/// Performs a volatile write of a memory location with the given value without reading or dropping
/// the old value.
///
/// Volatile operations are intended to act on I/O memory. As such, they are considered externally
/// observable events (just like syscalls), and are guaranteed to not be elided or reordered by the
/// compiler across other externally observable events. With this in mind, there are two cases of
/// usage that need to be distinguished:
///
/// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like
/// [`write`][write()], except for the additional guarantee that it won't be elided or reordered
/// (see above). This implies that the operation will actually access memory and not e.g. be
/// lowered to a register access. Other than that, all the usual rules for memory accesses apply
/// (including provenance). In particular, just like in C, whether an operation is volatile has no
/// bearing whatsoever on questions involving concurrent access from multiple threads. Volatile
/// accesses behave exactly like non-atomic accesses in that regard.
///
/// - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust
/// allocation. In this use-case, the pointer does *not* have to be [valid] for writes. This is
/// typically used for CPU and peripheral registers that must be accessed via an I/O memory
/// mapping, most commonly at fixed addresses reserved by the hardware. These often have special
/// semantics associated to their manipulation, and cannot be used as general purpose memory.
/// Here, any address value is possible, including 0 and [`usize::MAX`], so long as the semantics
/// of such a write are well-defined by the target hardware. The provenance of the pointer is
/// irrelevant, and it can be created with [`without_provenance`]. The access must not trap. It
/// can cause side-effects, but those must not affect Rust-allocated memory in any way. This
/// access is still not considered [atomic], and as such it cannot be used for inter-thread
/// synchronization.
///
/// Note that volatile memory operations on zero-sized types (e.g., if a zero-sized type is passed
/// to `write_volatile`) are noops and may be ignored.
///
/// `write_volatile` does not drop the contents of `dst`. This is safe, but it could leak
/// allocations or resources, so care should be taken not to overwrite an object that should be
/// dropped when operating on Rust memory. Additionally, it does not drop `src`. Semantically, `src`
/// is moved into the location pointed to by `dst`.
///
/// [allocation]: crate::ptr#allocated-object
/// [atomic]: crate::sync::atomic#memory-model-for-atomic-accesses
///
/// # Safety
///
/// Behavior is undefined if any of the following conditions are violated:
///
/// * `dst` must be [valid] for writes.
/// * `dst` must be either [valid] for writes, or it must point to memory outside of all Rust
/// allocations and writing to that memory must:
/// - not trap, and
/// - not cause any memory inside a Rust allocation to be modified.
///
/// * `dst` must be properly aligned.
///
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
///
/// [valid]: self#safety
///
/// Just like in C, whether an operation is volatile has no bearing whatsoever
/// on questions involving concurrent access from multiple threads. Volatile
/// accesses behave exactly like non-atomic accesses in that regard. In particular,
/// a race between a `write_volatile` and any other operation (reading or writing)
/// on the same location is undefined behavior.
///
/// # Examples
///
/// Basic usage:
Expand All @@ -2170,12 +2189,11 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
unsafe {
ub_checks::assert_unsafe_precondition!(
check_language_ub,
"ptr::write_volatile requires that the pointer argument is aligned and non-null",
"ptr::write_volatile requires that the pointer argument is aligned",
(
addr: *mut () = dst as *mut (),
align: usize = align_of::<T>(),
is_zst: bool = T::IS_ZST,
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
) => ub_checks::maybe_is_aligned(addr, align)
);
intrinsics::volatile_store(dst, src);
}
Expand Down
18 changes: 15 additions & 3 deletions library/core/src/ub_checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,25 @@ pub(crate) const fn maybe_is_aligned_and_not_null(
align: usize,
is_zst: bool,
) -> bool {
// This is just for safety checks so we can const_eval_select.
maybe_is_aligned(ptr, align) && (is_zst || !ptr.is_null())
}

/// Checks whether `ptr` is properly aligned with respect to the given alignment.
///
/// In `const` this is approximate and can fail spuriously. It is primarily intended
/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
/// check is anyway not executed in `const`.
#[inline]
#[rustc_allow_const_fn_unstable(const_eval_select)]
pub(crate) const fn maybe_is_aligned(ptr: *const (), align: usize) -> bool {
// This is just for safety checks so we can const_eval_select.
const_eval_select!(
@capture { ptr: *const (), align: usize, is_zst: bool } -> bool:
@capture { ptr: *const (), align: usize } -> bool:
if const {
is_zst || !ptr.is_null()
true
} else {
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
ptr.is_aligned_to(align)
}
)
}
Expand Down
5 changes: 2 additions & 3 deletions tests/ui/lint/invalid_null_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ unsafe fn null_ptr() {
let _a: A = ptr::read_unaligned(ptr::null_mut());
//~^ ERROR calling this function with a null pointer is undefined behavior

// These two should *not* fire the lint.
let _a: A = ptr::read_volatile(ptr::null());
//~^ ERROR calling this function with a null pointer is undefined behavior
let _a: A = ptr::read_volatile(ptr::null_mut());
//~^ ERROR calling this function with a null pointer is undefined behavior

let _a: A = ptr::replace(ptr::null_mut(), v);
//~^ ERROR calling this function with a null pointer is undefined behavior
Expand All @@ -82,8 +81,8 @@ unsafe fn null_ptr() {
ptr::write_unaligned(ptr::null_mut(), v);
//~^ ERROR calling this function with a null pointer is undefined behavior

// This one should *not* fire the lint.
ptr::write_volatile(ptr::null_mut(), v);
//~^ ERROR calling this function with a null pointer is undefined behavior

ptr::write_bytes::<usize>(ptr::null_mut(), 42, 0);
//~^ ERROR calling this function with a null pointer is undefined behavior
Expand Down
Loading