From eaec844201fd1ee229c6af68440cbc202f68eccc Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Thu, 28 May 2026 15:20:27 -0400 Subject: [PATCH] [pointer] `Ptr::iter` takes `self` by value This fixes a prior soundness hole - `Ptr::iter` took `&self`, permitting multiple overlapping `Exclusive` `Ptr`s to be created at the same time. In CI, when running `cargo-semver-checks`, don't pass `--cfg zerocopy_unstable_ptr`, as we don't want to semver-check unstable APIs. Release 0.8.50. Fixes #3419 gherrit-pr-id: Ibb7d512d9e12ecfd118bb018bcae10d17279c2ed --- .github/workflows/ci.yml | 4 ++++ Cargo.lock | 4 ++-- Cargo.toml | 8 ++++---- src/pointer/ptr.rs | 30 +++++++++++++++++++++++++++--- zerocopy-derive/Cargo.toml | 2 +- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 327f1ac153..2a11210cad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -640,6 +640,10 @@ jobs: # SIMD type impls), and so we need to run on each target. - name: Check semver compatibility uses: obi1kenobi/cargo-semver-checks-action@6b69fcf40e9b5fb17adeb57e4b6ecd020649a239 # v2.9 + env: + # `zerocopy_unstable_ptr` exposes unstable API, so it should not be + # included in semver checks. + RUSTDOCFLAGS: -Dwarnings with: # Don't semver check zerocopy-derive; as a proc macro, it doesn't have # an API that cargo-semver-checks can understand. diff --git a/Cargo.lock b/Cargo.lock index 3042ef0186..bc6dd84ea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,7 +412,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zerocopy" -version = "0.8.49" +version = "0.8.50" dependencies = [ "elain", "itertools", @@ -426,7 +426,7 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.49" +version = "0.8.50" dependencies = [ "dissimilar", "prettyplease", diff --git a/Cargo.toml b/Cargo.toml index 912eaf2635..ce24a106fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ [package] edition = "2021" name = "zerocopy" -version = "0.8.49" +version = "0.8.50" authors = [ "Joshua Liebow-Feeser ", "Jack Wrenn ", @@ -124,13 +124,13 @@ __internal_use_only_features_that_work_on_stable = [ ] [dependencies] -zerocopy-derive = { version = "=0.8.49", path = "zerocopy-derive", optional = true } +zerocopy-derive = { version = "=0.8.50", path = "zerocopy-derive", optional = true } # The "associated proc macro pattern" ensures that the versions of zerocopy and # zerocopy-derive remain equal, even if the 'derive' feature isn't used. # See: https://github.com/matklad/macro-dep-test [target.'cfg(any())'.dependencies] -zerocopy-derive = { version = "=0.8.49", path = "zerocopy-derive" } +zerocopy-derive = { version = "=0.8.50", path = "zerocopy-derive" } [dev-dependencies] # FIXME(#381) Remove this dependency once we have our own layout gadgets. @@ -142,4 +142,4 @@ rustversion = "1.0" static_assertions = "1.1" testutil = { path = "testutil" } # In tests, unlike in production, zerocopy-derive is not optional -zerocopy-derive = { version = "=0.8.49", path = "zerocopy-derive" } +zerocopy-derive = { version = "=0.8.50", path = "zerocopy-derive" } diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index edfeb90a56..307b2aee63 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -1268,10 +1268,17 @@ mod _project { { /// Iteratively projects the elements `Ptr` from `Ptr<[T]>`. #[inline] - pub fn iter(&self) -> impl Iterator> { + pub fn iter(self) -> impl Iterator> { // SAFETY: - // 0. `elem` conforms to the aliasing invariant of `I::Aliasing` - // because projection does not impact the aliasing invariant. + // 0. `elem` conforms to the aliasing invariant of `I::Aliasing`: + // - `Exclusive`: `self` is consumed by value, and therefore + // cannot be used to access the slice while any yielded + // element `Ptr` is live. Each non-zero-sized element is a + // disjoint byte range within the slice, and zero-sized + // elements address no bytes, so distinct yielded element + // `Ptr`s do not alias each other. + // - `Shared`: It is sound for multiple shared `Ptr`s to exist + // simultaneously which reference the same memory. // 1. `elem`, conditionally, conforms to the validity invariant of // `I::Alignment`. If `elem` is projected from data well-aligned // for `[T]`, `elem` will be valid for `T`. @@ -1557,4 +1564,21 @@ mod tests { Err(e) => panic!("wrong error type: {:?}", e), } } + + #[test] + fn test_iter_exclusive_yields_disjoint_ptrs() { + let mut arr = [0u8, 1, 2, 3]; + + { + let mut iter = Ptr::from_mut(&mut arr[..]).iter(); + let first = iter.next().unwrap().as_mut(); + let second = iter.next().unwrap().as_mut(); + + *first = 10; + *second = 20; + *first = 30; + } + + assert_eq!(arr, [30, 20, 2, 3]); + } } diff --git a/zerocopy-derive/Cargo.toml b/zerocopy-derive/Cargo.toml index 343edf87bb..01336de48f 100644 --- a/zerocopy-derive/Cargo.toml +++ b/zerocopy-derive/Cargo.toml @@ -9,7 +9,7 @@ [package] edition = "2021" name = "zerocopy-derive" -version = "0.8.49" +version = "0.8.50" authors = ["Joshua Liebow-Feeser ", "Jack Wrenn "] description = "Custom derive for traits from the zerocopy crate" license = "BSD-2-Clause OR Apache-2.0 OR MIT"