Skip to content

Commit a8037b4

Browse files
committed
Make pointer Read witnesses unsafe
The public Read witness is trusted by pointer conversions to prove that unsynchronized reads are permitted, but downstream code could previously implement it without taking responsibility for that invariant. Make Read an unsafe trait, document its implementor obligation, and justify the two built-in implementations. Add a compile-fail regression showing that the original safe witness implementations now require unsafe impls. Closes #3613 *Authored by an AI agent acting on Josh Liebow-Feeser's behalf.* gherrit-pr-id: Gugofjuaaownqfvbulg6lh72eul7phdgb
1 parent 2dad389 commit a8037b4

5 files changed

Lines changed: 103 additions & 5 deletions

File tree

zerocopy/src/pointer/invariant.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,20 @@ unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Initialized, Initialized> f
253253
///
254254
/// # Safety
255255
///
256-
/// `T: Read<A, R>` if either of the following conditions holds:
256+
/// Implementors must ensure that either of the following conditions holds:
257257
/// - `A` is [`Exclusive`]
258-
/// - `T` implements [`Immutable`](crate::Immutable)
258+
/// - `Self` implements [`Immutable`](crate::Immutable)
259259
///
260260
/// As a consequence, if `T: Read<A, R>`, then any `Ptr<T, (A, ...)>` is
261261
/// permitted to perform unsynchronized reads from its referent.
262-
pub trait Read<A: Aliasing, R> {}
262+
pub unsafe trait Read<A: Aliasing, R> {}
263263

264-
impl<A: Aliasing, T: ?Sized + crate::Immutable> Read<A, BecauseImmutable> for T {}
265-
impl<T: ?Sized> Read<Exclusive, BecauseExclusive> for T {}
264+
// SAFETY: `T: Immutable`, satisfying the second condition of `Read`'s safety
265+
// contract.
266+
unsafe impl<A: Aliasing, T: ?Sized + crate::Immutable> Read<A, BecauseImmutable> for T {}
267+
// SAFETY: The aliasing argument is `Exclusive`, satisfying the first condition
268+
// of `Read`'s safety contract.
269+
unsafe impl<T: ?Sized> Read<Exclusive, BecauseExclusive> for T {}
266270

267271
/// Unsynchronized reads are permitted because only one live [`Ptr`](crate::Ptr)
268272
/// or reference may exist to the referent bytes at a time.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0200]: the trait `zerocopy::pointer::Read<Shared, CallerReason>` requires an `unsafe impl` declaration
2+
--> $DIR/read-requires-unsafe-impl.rs:19:1
3+
|
4+
19 | impl Read<Shared, CallerReason> for Cell<Vec<u8>> {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error[E0200]: the trait `zerocopy::pointer::Read<Shared, CallerReason>` requires an `unsafe impl` declaration
8+
--> $DIR/read-requires-unsafe-impl.rs:22:1
9+
|
10+
22 | impl Read<Shared, CallerReason> for Vec<u8> {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0200`.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0200]: the trait `zerocopy::invariant::Read<Shared, CallerReason>` requires an `unsafe impl` declaration
2+
--> $DIR/read-requires-unsafe-impl.rs:19:1
3+
|
4+
19 | impl Read<Shared, CallerReason> for Cell<Vec<u8>> {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: the trait `zerocopy::invariant::Read<Shared, CallerReason>` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
8+
help: add `unsafe` to this trait implementation
9+
|
10+
19 | unsafe impl Read<Shared, CallerReason> for Cell<Vec<u8>> {}
11+
| ++++++
12+
13+
error[E0200]: the trait `zerocopy::invariant::Read<Shared, CallerReason>` requires an `unsafe impl` declaration
14+
--> $DIR/read-requires-unsafe-impl.rs:22:1
15+
|
16+
22 | impl Read<Shared, CallerReason> for Vec<u8> {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
|
19+
= note: the trait `zerocopy::invariant::Read<Shared, CallerReason>` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
20+
help: add `unsafe` to this trait implementation
21+
|
22+
22 | unsafe impl Read<Shared, CallerReason> for Vec<u8> {}
23+
| ++++++
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0200`.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2026 The Fuchsia Authors
2+
//
3+
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4+
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5+
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6+
// This file may not be copied, modified, or distributed except according to
7+
// those terms.
8+
9+
#![forbid(unsafe_code)]
10+
11+
include!("../include.rs");
12+
13+
use std::{cell::Cell, vec::Vec};
14+
15+
use zerocopy::pointer::invariant::{Read, Shared};
16+
17+
struct CallerReason;
18+
19+
impl Read<Shared, CallerReason> for Cell<Vec<u8>> {}
20+
//~[msrv, stable, nightly]^ ERROR: requires an `unsafe impl` declaration
21+
22+
impl Read<Shared, CallerReason> for Vec<u8> {}
23+
//~[msrv, stable, nightly]^ ERROR: requires an `unsafe impl` declaration
24+
25+
fn main() {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0200]: the trait `zerocopy::invariant::Read<Shared, CallerReason>` requires an `unsafe impl` declaration
2+
--> $DIR/read-requires-unsafe-impl.rs:19:1
3+
|
4+
19 | impl Read<Shared, CallerReason> for Cell<Vec<u8>> {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: the trait `zerocopy::invariant::Read<Shared, CallerReason>` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
8+
help: add `unsafe` to this trait implementation
9+
|
10+
19 | unsafe impl Read<Shared, CallerReason> for Cell<Vec<u8>> {}
11+
| ++++++
12+
13+
error[E0200]: the trait `zerocopy::invariant::Read<Shared, CallerReason>` requires an `unsafe impl` declaration
14+
--> $DIR/read-requires-unsafe-impl.rs:22:1
15+
|
16+
22 | impl Read<Shared, CallerReason> for Vec<u8> {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
|
19+
= note: the trait `zerocopy::invariant::Read<Shared, CallerReason>` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
20+
help: add `unsafe` to this trait implementation
21+
|
22+
22 | unsafe impl Read<Shared, CallerReason> for Vec<u8> {}
23+
| ++++++
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0200`.

0 commit comments

Comments
 (0)