Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invariant-parameterize Ptr and make is_bit_valid safe #699

Merged
merged 1 commit into from
Jan 18, 2024
Merged

Conversation

jswrenn
Copy link
Collaborator

@jswrenn jswrenn commented Dec 8, 2023

Closes #715.

@jswrenn jswrenn changed the title [wip] Don't Ptrs point to validly-aligned data [wip] Don't require Ptrs point to validly-aligned data Dec 8, 2023
@jswrenn jswrenn force-pushed the ptr-unaligned branch 2 times, most recently from a6b1487 to c1141bf Compare December 11, 2023 19:29
src/util.rs Outdated Show resolved Hide resolved
Copy link
Member

@joshlf joshlf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some invariants are now invalid - e.g., in as_ref, the safety comment assumes that self.ptr is aligned even though it's inside a block in which ASSUME_ALIGN can be either true or false.

src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated
Comment on lines 55 to 56
/// The argument to `TryFromBytes::is_bit_valid`.
pub type MaybeValid<'a, T> = Ptr<'a, T, false>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the name MaybeValid implies that the only thing that's not guaranteed is validity, but here lifetime and aliasing are both not guaranteed. I think we should just use Ptr directly since it's more clear that the user should go look at the type rather than guess its semantics.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ptr does guarantee lifetime (item 8):

/// INVARIANTS:
/// 1. `ptr` is derived from some valid Rust allocation, `A`
/// 2. `ptr` has the same provenance as `A`
/// 3. `ptr` addresses a byte range which is entirely contained in `A`
/// 4. `ptr` addresses a byte range whose length fits in an `isize`
/// 5. `ptr` addresses a byte range which does not wrap around the address
///     space
/// 6. If `ASSUME_ALIGNED`, `ptr` is validly-aligned for `T`
/// 7. `A` is guaranteed to live for at least `'a`
/// 8. `T: 'a`

...but not aliasing. However, this type only appears as an argument to is_bit_valid (and is documented as such), and is_bit_valid, as a pre-condition, guarantees the appropriate aliasing requirements. The name MaybeValid, then, does convey the salient quality of the argument.

src/util.rs Outdated Show resolved Hide resolved
src/util.rs Outdated Show resolved Hide resolved
@jswrenn jswrenn changed the title [wip] Don't require Ptrs point to validly-aligned data Invariant-parameterize Ptr and make is_bit_valid safe Dec 26, 2023
@jswrenn jswrenn force-pushed the ptr-unaligned branch 4 times, most recently from b3a0929 to 200757e Compare December 26, 2023 23:48
@jswrenn jswrenn marked this pull request as ready for review December 26, 2023 23:48
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/macros.rs Show resolved Hide resolved
src/pointer.rs Outdated

/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
/// to [`TryFromBytes::is_bit_valid`][crate::TryFromBytes::is_bit_valid].
pub type MaybeValid<'a, T> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this MaybeAlignedValid or something? I was confused by the naming when reading lib.rs until I came and looked at this doc comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about Maybe<T>? Its reading is its meaning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to Maybe<T>. >:]

I'm signing off for the night, but feel free to bikeshed the name without me!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a separate #[doc(hidden)] attribute here (so that it remains even if we remove the outer one on the module) and leave a TODO comment to bikeshed this name if we're planning on making this public?

src/pointer.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
Comment on lines -3628 to -3636
// SAFETY: We uphold the safety contract of `is_bit_valid(elem)`, by
// precondition on the surrounding call to `is_bit_valid`. The
// memory referenced by `elem` is contained entirely within `c`, and
// satisfies the preconditions satisfied by `c`. By axiom, we assume
// that `Iterator:all` does not invalidate these preconditions
// (e.g., by writing to `elem`.) Since `elem` is derived from `c`,
// it is only possible for uninitialized bytes to occur in `elem` at
// the same bytes they occur within `c`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3

src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
src/pointer.rs Outdated Show resolved Hide resolved
src/pointer/ptr.rs Outdated Show resolved Hide resolved
Comment on lines 148 to 152
/// Note that this method does not consume `self`. Callers should be
/// aware that safe code may assume that the aliasing invariants of
/// `self` are not violated, and that the `NonNull` may be used in ways
/// that violate those assumptions.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. I usually expect to see "assume" used in the context of unsafe code because it's the behavior of the code that matters (by contrast, with safe code, even things unrelated to the code's behavior matter because the compiler itself can make assumptions that could be broken).

I also think that the "Non null may be used" phrasing is confusing - I read it as "is allowed to be used", but I think you meant it to be read "could be used".

Maybe just something like:

Note that this method does not consume self. The caller should watch out for unsafe code which uses the returned NonNull in a way that violates the safety invariants of self.

(I originally said "it is the caller's responsibility...", but that feels weird given that this method is safe, and safe methods are unsound if their callers are required to do anything in order to uphold safety.)

Comment on lines 143 to 155
mod sealed {
pub trait Sealed {}

/// No aliasing invariant.
pub const AnyAliasing: Aliasing = 1 << 0;
impl<$($set,)*> Sealed for ($($set,)*)
where
$($set: super::$set,)*
{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also just put a single Sealed trait in util::sealed or something. We already have a top-level sealed module which we could move to util::sealed.

@jswrenn jswrenn changed the title Invariant-parameterize Ptr and make is_bit_valid safe Invariant-parameterize Ptr and make is_bit_valid safe Dec 28, 2023
@jswrenn jswrenn force-pushed the ptr-unaligned branch 6 times, most recently from 2439de6 to f8fb38a Compare January 3, 2024 18:18
src/pointer/mod.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
src/pointer/mod.rs Outdated Show resolved Hide resolved
src/util.rs Outdated
Comment on lines 524 to 528
let raw = self.ptr.ptr.as_ptr();
// SAFETY: By invariant on `MaybeAligned`, `raw` contains
// validly-initialized data for `T`. The value is safe to read and
// return, because `T` is copy.
unsafe { core::ptr::read_unaligned(raw) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you leave a TODO comment on read_unaligned to consider loosening the requirements at some point in the future? In particular, I could see us introducing a trait which represents "at least shared" aliasing, implementing it for both invariant::Shared and invariant::Exclusive, and using that trait (rather than a particular concrete type) as the aliasing bound.

src/pointer/mod.rs Outdated Show resolved Hide resolved
src/pointer/mod.rs Show resolved Hide resolved
src/pointer.rs Outdated
#[inline]
pub fn read_unaligned(self) -> T
where
T: Copy,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe let's open an issue to track having Ptr store the "original" type of its data? Probably not worth holding up this PR for that, and I suspect there are a lot of subtleties.

@joshlf joshlf enabled auto-merge January 18, 2024 19:53
@joshlf joshlf added this pull request to the merge queue Jan 18, 2024
Merged via the queue into main with commit 644cdb8 Jan 18, 2024
127 checks passed
@joshlf joshlf deleted the ptr-unaligned branch January 18, 2024 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Invariant-parameterize Ptr
2 participants