Null pointer copy
| Vulnerability potential | High |
| DDoS potential | Medium |
Copying with a null source or destination is undefined behavior
Impact
std::ptr::copy, copy_nonoverlapping, swap, and replace with a null source
or destination are undefined behavior. These are Rust’s memmove/memcpy
equivalents: copy_nonoverlapping(src, dst, count) moves count * size_of::<T>()
bytes between the two pointers. If either is null — or null plus a count that
makes the access span into mapped memory — the operation reads from and/or writes
to invalid addresses.
The effect depends on which side is null and on count. A null destination
write corrupts or crashes much like a bulk null write; a null source read can
copy garbage or out-of-bounds bytes into a valid buffer (an info leak) or crash.
Because the count is multiplied by the element size, a single bad call can touch
a large, attacker-influenced span of memory, making this typically more
damaging than a single-element null read or write.
Vulnerability potential
- Bulk memory corruption → RCE. A null (or near-null) destination with a
non-trivial
countoverwrites a contiguous range of memory; if the destination offset and length are influenced by input this is a powerful write primitive for overwriting control data and achieving code execution. - Information disclosure. A null/invalid source copied into a buffer that is later returned, logged, or sent to a client leaks whatever bytes were read from the bad region — an over-read in the spirit of Heartbleed.
- Denial of service. The straightforward outcome — copying to/from an
unmapped zero page — faults and crashes the process (kernel panic in
no_std), giving a reliable crash DoS when reachable. - Count/length confusion. Mixing element count with byte count, or pairing a valid pointer with a length derived from elsewhere, amplifies any of the above into a large over-read or over-write.
A bulk copy through a null/invalid pointer under partial attacker control is highly exploitable, hence the High vulnerability rating.
Technical details
The ptr::copy* family requires that both src and dst be non-null, properly
aligned, and valid for reads/writes of count elements respectively (and
copy_nonoverlapping additionally forbids overlapping regions; using it on
overlapping ranges is itself UB). A null endpoint, an oversized count, or
misalignment each independently makes the call UB.
copy vs. copy_nonoverlapping
copy permits overlapping regions (like memmove); copy_nonoverlapping (like
memcpy) does not and is UB if they overlap. Both still demand non-null, valid,
in-bounds endpoints. swap/replace are built on the same machinery and inherit
the same requirements. None of them perform any null or bounds check at runtime.
Catching the issue
Dynamic detection
Miri detects null/dangling/misaligned/out-of-bounds endpoints and overlap
violations in copy*. AddressSanitizer (-Z sanitizer=address) catches the
faulting or out-of-bounds bulk access in native runs; a crash produces a
SIGSEGV/core dump for debugger triage.
Static and review
Validate or construct lengths and pointers from a single source of truth; prefer
safe slice operations (slice::copy_from_slice, clone_from_slice,
<[T]>::copy_within) which carry their own bounds, and NonNull<T> to encode
non-nullness. Confine copy* to small unsafe blocks with a # Safety comment
proving both endpoints’ validity and the element/byte count. Clippy flags some
obviously-invalid pointer uses.
How to reproduce
Run the following; observe a segmentation fault (or precise detection under Miri). The copy writes through a null destination.
fn main() {
let src = [1u8, 2, 3, 4];
let dst: *mut u8 = std::ptr::null_mut();
unsafe {
// bulk write to a null destination -> SIGSEGV / corruption
std::ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
}
println!("done");
}