fix: don't panic on volatile access to null
According to https://discourse.llvm.org/t/rfc-volatile-access-to-non-dereferenceable-memory-may-be-well-defined/86303/4, LLVM allows volatile operations on null and handles it correctly. This should be allowed in Rust as well, because I/O memory may be hard-coded to address 0 in some cases, like the AVR chip ATtiny1626. A test case that ensured a failure when passing null to volatile was removed, since it's now valid. Due to the addition of `maybe_is_aligned` to `ub_checks`, `maybe_is_aligned_and_not_null` was refactored to use it. docs: revise restrictions on volatile operations A distinction between usage on Rust memory vs. non-Rust memory was introduced. Documentation was reworded to explain what that means, and make explicit that: - No trapping can occur from volatile operations; - On Rust memory, all safety rules must be respected; - On Rust memory, the primary difference from regular access is that volatile always involves a memory dereference; - On Rust memory, the only data affected by an operation is the one pointed to in the argument(s) of the function; - On Rust memory, provenance follows the same rules as non-volatile access; - On non-Rust memory, any address known to not contain Rust memory is valid (including 0 and usize::MAX); - On non-Rust memory, no Rust memory may be affected (it is implicit that any other non-Rust memory may be affected, though, even if not referenced by the pointer). This should be relevant when, for example, reading register A causes a flag to change in register B, or writing to A causes B to change in some way. Everything affected mustn't be inside an allocation. - On non-Rust memory, provenance is irrelevant and a pointer with none can be used in a valid way. fix: don't lint null as UB for volatile Also remove a now-unneeded `allow` line. fix: additional wording nits
This commit is contained in:
parent
bf5e6cc7a7
commit
8a8717e971
7 changed files with 116 additions and 127 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 +
|
||||
|
|
@ -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.
|
||||
/// Performs a volatile read of the value from `src` without moving it.
|
||||
///
|
||||
/// 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.
|
||||
/// 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:
|
||||
///
|
||||
/// # Notes
|
||||
/// - 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.
|
||||
///
|
||||
/// 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].
|
||||
/// - 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.
|
||||
///
|
||||
/// 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.
|
||||
/// Note that volatile memory operations where T is a zero-sized type are noops and may be ignored.
|
||||
///
|
||||
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
|
||||
/// [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:
|
||||
|
|
@ -2090,50 +2102,63 @@ 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.
|
||||
/// 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.
|
||||
/// 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:
|
||||
///
|
||||
/// `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 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.
|
||||
///
|
||||
/// Additionally, it does not drop `src`. Semantically, `src` is moved into the
|
||||
/// location pointed to by `dst`.
|
||||
/// - 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.
|
||||
///
|
||||
/// # Notes
|
||||
/// 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.
|
||||
///
|
||||
/// 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].
|
||||
/// `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`.
|
||||
///
|
||||
/// 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
|
||||
/// [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.
|
||||
///
|
||||
|
|
@ -2141,12 +2166,6 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
|||
///
|
||||
/// [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:
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -164,27 +164,7 @@ LL | let _a: A = ptr::read_unaligned(ptr::null_mut());
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:61:17
|
||||
|
|
||||
LL | let _a: A = ptr::read_volatile(ptr::null());
|
||||
| ^^^^^^^^^^^^^^^^^^^-----------^
|
||||
| |
|
||||
| null pointer originates from here
|
||||
|
|
||||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:63:17
|
||||
|
|
||||
LL | let _a: A = ptr::read_volatile(ptr::null_mut());
|
||||
| ^^^^^^^^^^^^^^^^^^^---------------^
|
||||
| |
|
||||
| null pointer originates from here
|
||||
|
|
||||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:66:17
|
||||
--> $DIR/invalid_null_args.rs:65:17
|
||||
|
|
||||
LL | let _a: A = ptr::replace(ptr::null_mut(), v);
|
||||
| ^^^^^^^^^^^^^---------------^^^^
|
||||
|
|
@ -194,7 +174,7 @@ LL | let _a: A = ptr::replace(ptr::null_mut(), v);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:69:5
|
||||
--> $DIR/invalid_null_args.rs:68:5
|
||||
|
|
||||
LL | ptr::swap::<A>(ptr::null_mut(), &mut v);
|
||||
| ^^^^^^^^^^^^^^^---------------^^^^^^^^^
|
||||
|
|
@ -204,7 +184,7 @@ LL | ptr::swap::<A>(ptr::null_mut(), &mut v);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:71:5
|
||||
--> $DIR/invalid_null_args.rs:70:5
|
||||
|
|
||||
LL | ptr::swap::<A>(&mut v, ptr::null_mut());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^---------------^
|
||||
|
|
@ -214,7 +194,7 @@ LL | ptr::swap::<A>(&mut v, ptr::null_mut());
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:74:5
|
||||
--> $DIR/invalid_null_args.rs:73:5
|
||||
|
|
||||
LL | ptr::swap_nonoverlapping::<A>(ptr::null_mut(), &mut v, 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^^^^^^^^^
|
||||
|
|
@ -224,7 +204,7 @@ LL | ptr::swap_nonoverlapping::<A>(ptr::null_mut(), &mut v, 0);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:76:5
|
||||
--> $DIR/invalid_null_args.rs:75:5
|
||||
|
|
||||
LL | ptr::swap_nonoverlapping::<A>(&mut v, ptr::null_mut(), 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^
|
||||
|
|
@ -234,7 +214,7 @@ LL | ptr::swap_nonoverlapping::<A>(&mut v, ptr::null_mut(), 0);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:79:5
|
||||
--> $DIR/invalid_null_args.rs:78:5
|
||||
|
|
||||
LL | ptr::write(ptr::null_mut(), v);
|
||||
| ^^^^^^^^^^^---------------^^^^
|
||||
|
|
@ -244,7 +224,7 @@ LL | ptr::write(ptr::null_mut(), v);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:82:5
|
||||
--> $DIR/invalid_null_args.rs:81:5
|
||||
|
|
||||
LL | ptr::write_unaligned(ptr::null_mut(), v);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^---------------^^^^
|
||||
|
|
@ -254,17 +234,7 @@ LL | ptr::write_unaligned(ptr::null_mut(), v);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:85:5
|
||||
|
|
||||
LL | ptr::write_volatile(ptr::null_mut(), v);
|
||||
| ^^^^^^^^^^^^^^^^^^^^---------------^^^^
|
||||
| |
|
||||
| null pointer originates from here
|
||||
|
|
||||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:88:5
|
||||
--> $DIR/invalid_null_args.rs:87:5
|
||||
|
|
||||
LL | ptr::write_bytes::<usize>(ptr::null_mut(), 42, 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^^^^^
|
||||
|
|
@ -274,7 +244,7 @@ LL | ptr::write_bytes::<usize>(ptr::null_mut(), 42, 0);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:93:18
|
||||
--> $DIR/invalid_null_args.rs:92:18
|
||||
|
|
||||
LL | let _a: u8 = ptr::read(const_ptr);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -287,7 +257,7 @@ LL | let null_ptr = ptr::null_mut();
|
|||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:100:5
|
||||
--> $DIR/invalid_null_args.rs:99:5
|
||||
|
|
||||
LL | std::slice::from_raw_parts::<()>(ptr::null(), 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^
|
||||
|
|
@ -297,7 +267,7 @@ LL | std::slice::from_raw_parts::<()>(ptr::null(), 0);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:102:5
|
||||
--> $DIR/invalid_null_args.rs:101:5
|
||||
|
|
||||
LL | std::slice::from_raw_parts::<Zst>(ptr::null(), 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^
|
||||
|
|
@ -307,7 +277,7 @@ LL | std::slice::from_raw_parts::<Zst>(ptr::null(), 0);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:104:5
|
||||
--> $DIR/invalid_null_args.rs:103:5
|
||||
|
|
||||
LL | std::slice::from_raw_parts_mut::<()>(ptr::null_mut(), 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^
|
||||
|
|
@ -317,7 +287,7 @@ LL | std::slice::from_raw_parts_mut::<()>(ptr::null_mut(), 0);
|
|||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: calling this function with a null pointer is undefined behavior, even if the result of the function is unused
|
||||
--> $DIR/invalid_null_args.rs:106:5
|
||||
--> $DIR/invalid_null_args.rs:105:5
|
||||
|
|
||||
LL | std::slice::from_raw_parts_mut::<Zst>(ptr::null_mut(), 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^
|
||||
|
|
@ -326,5 +296,5 @@ LL | std::slice::from_raw_parts_mut::<Zst>(ptr::null_mut(), 0);
|
|||
|
|
||||
= help: for more information, visit <https://doc.rust-lang.org/std/ptr/index.html> and <https://doc.rust-lang.org/reference/behavior-considered-undefined.html>
|
||||
|
||||
error: aborting due to 31 previous errors
|
||||
error: aborting due to 28 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
|
||||
//@ error-pattern: unsafe precondition(s) violated: ptr::read_volatile requires
|
||||
//@ revisions: null misaligned
|
||||
|
||||
#![allow(invalid_null_arguments)]
|
||||
//@ revisions: misaligned
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -11,8 +9,6 @@ fn main() {
|
|||
let src = [0u16; 2];
|
||||
let src = src.as_ptr();
|
||||
unsafe {
|
||||
#[cfg(null)]
|
||||
ptr::read_volatile(ptr::null::<u8>());
|
||||
#[cfg(misaligned)]
|
||||
ptr::read_volatile(src.byte_add(1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
|
||||
//@ error-pattern: unsafe precondition(s) violated: ptr::write_volatile requires
|
||||
//@ revisions: null misaligned
|
||||
|
||||
#![allow(invalid_null_arguments)]
|
||||
//@ revisions: misaligned
|
||||
|
||||
use std::ptr;
|
||||
|
||||
|
|
@ -11,8 +9,6 @@ fn main() {
|
|||
let mut dst = [0u16; 2];
|
||||
let mut dst = dst.as_mut_ptr();
|
||||
unsafe {
|
||||
#[cfg(null)]
|
||||
ptr::write_volatile(ptr::null_mut::<u8>(), 1u8);
|
||||
#[cfg(misaligned)]
|
||||
ptr::write_volatile(dst.byte_add(1), 1u16);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue