Skip to content

Commit a02f5ce

Browse files
committed
Invariant-parameterize Ptr and make is_bit_valid safe
Closes #715.
1 parent e56a42b commit a02f5ce

File tree

7 files changed

+1231
-880
lines changed

7 files changed

+1231
-880
lines changed

src/lib.rs

+78-220
Large diffs are not rendered by default.

src/macros.rs

+24-31
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,14 @@ macro_rules! safety_comment {
5252
/// $ty>` which satisfies the preconditions of
5353
/// `TryFromBytes::<$ty>::is_bit_valid`, it must be guaranteed that the
5454
/// memory referenced by that `Ptr` always contains a valid `$repr`.
55-
/// - The alignment of `$repr` is less than or equal to the alignment of
56-
/// `$ty`.
5755
/// - The impl of `is_bit_valid` must only return `true` for its argument
5856
/// `Ptr<$repr>` if the original `Ptr<$ty>` refers to a valid `$ty`.
5957
macro_rules! unsafe_impl {
6058
// Implement `$trait` for `$ty` with no bounds.
61-
($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => {
59+
($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident: MaybeAligned<$repr:ty>| $is_bit_valid:expr)?) => {
6260
$(#[$attr])*
6361
unsafe impl $trait for $ty {
64-
unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?);
62+
unsafe_impl!(@method $trait $(; |$candidate: MaybeAligned<$repr>| $is_bit_valid)?);
6563
}
6664
};
6765
// Implement all `$traits` for `$ty` with no bounds.
@@ -97,93 +95,88 @@ macro_rules! unsafe_impl {
9795
$(#[$attr:meta])*
9896
const $constname:ident : $constty:ident $(,)?
9997
$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
100-
=> $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)?
98+
=> $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: MaybeValid<$ptr_repr:ty>)?| $is_bit_valid:expr)?
10199
) => {
102100
unsafe_impl!(
103101
@inner
104102
$(#[$attr])*
105103
@const $constname: $constty,
106104
$($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
107-
=> $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)?
105+
=> $trait for $ty $(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: MaybeValid<$ptr_repr>)?| $is_bit_valid)?
108106
);
109107
};
110108
(
111109
$(#[$attr:meta])*
112110
$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
113-
=> $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)?
111+
=> $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: MaybeValid<$ptr_repr:ty>)?| $is_bit_valid:expr)?
114112
) => {
115113
unsafe_impl!(
116114
@inner
117115
$(#[$attr])*
118116
$($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
119-
=> $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)?
117+
=> $trait for $ty $(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: MaybeValid<$ptr_repr>)?| $is_bit_valid)?
120118
);
121119
};
122120
(
123121
@inner
124122
$(#[$attr:meta])*
125123
$(@const $constname:ident : $constty:ident,)*
126124
$($tyvar:ident $(: $(? $optbound:ident +)* + $($bound:ident +)* )?,)*
127-
=> $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)?
125+
=> $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: MaybeValid<$ptr_repr:ty>)?| $is_bit_valid:expr)?
128126
) => {
129127
$(#[$attr])*
130128
unsafe impl<$(const $constname: $constty,)* $($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> $trait for $ty {
131-
unsafe_impl!(@method $trait $(; |$candidate: $(&$ref_repr)? $(Ptr<$ptr_repr>)?| $is_bit_valid)?);
129+
unsafe_impl!(@method $trait $(; |$candidate: $(MaybeAligned<$ref_repr>)? $(MaybeValid<$ptr_repr>)?| $is_bit_valid)?);
132130
}
133131
};
134132

135-
(@method TryFromBytes ; |$candidate:ident: &$repr:ty| $is_bit_valid:expr) => {
133+
(@method TryFromBytes ; |$candidate:ident: MaybeAligned<$repr:ty>| $is_bit_valid:expr) => {
136134
#[allow(clippy::missing_inline_in_public_items)]
137135
fn only_derive_is_allowed_to_implement_this_trait() {}
138136

139137
#[inline]
140-
unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool {
138+
fn is_bit_valid(candidate: MaybeValid<'_, Self>) -> bool {
141139
// SAFETY:
142140
// - The argument to `cast_unsized` is `|p| p as *mut _` as required
143141
// by that method's safety precondition.
144142
// - The caller has promised that the cast results in an object of
145143
// equal or lesser size.
146-
// - The caller has promised that `$repr`'s alignment is less than
147-
// or equal to `Self`'s alignment.
148144
#[allow(clippy::as_conversions)]
149145
let candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) };
150-
// SAFETY:
151-
// - The caller has promised that the referenced memory region will
152-
// contain a valid `$repr` for `'a`.
153-
// - The memory may not be referenced by any mutable references.
154-
// This is a precondition of `is_bit_valid`.
155-
// - The memory may not be mutated even via `UnsafeCell`s. This is a
156-
// precondition of `is_bit_valid`.
157-
// - There must not exist any references to the same memory region
158-
// which contain `UnsafeCell`s at byte ranges which are not
159-
// identical to the byte ranges at which `T` contains
160-
// `UnsafeCell`s. This is a precondition of `is_bit_valid`.
161-
let $candidate: &$repr = unsafe { candidate.as_ref() };
146+
147+
// SAFETY: The caller has promised that the referenced memory region
148+
// will contain a valid `$repr`.
149+
let candidate = unsafe { candidate.assume_valid() };
150+
151+
let $candidate = MaybeAligned::from_ptr(candidate);
162152
$is_bit_valid
163153
}
164154
};
165-
(@method TryFromBytes ; |$candidate:ident: Ptr<$repr:ty>| $is_bit_valid:expr) => {
155+
(@method TryFromBytes ; |$candidate:ident: MaybeValid<$repr:ty>| $is_bit_valid:expr) => {
166156
#[allow(clippy::missing_inline_in_public_items)]
167157
fn only_derive_is_allowed_to_implement_this_trait() {}
168158

169159
#[inline]
170-
unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool {
160+
fn is_bit_valid(candidate: MaybeValid<'_, Self>) -> bool {
171161
// SAFETY:
172162
// - The argument to `cast_unsized` is `|p| p as *mut _` as required
173163
// by that method's safety precondition.
174164
// - The caller has promised that the cast results in an object of
175165
// equal or lesser size.
176-
// - The caller has promised that `$repr`'s alignment is less than
177-
// or equal to `Self`'s alignment.
178166
#[allow(clippy::as_conversions)]
179167
let $candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) };
168+
169+
// SAFETY: The caller has promised that the referenced memory region
170+
// will contain a valid `$repr`.
171+
let $candidate = unsafe { $candidate.assume_as_initialized() };
172+
180173
$is_bit_valid
181174
}
182175
};
183176
(@method TryFromBytes) => {
184177
#[allow(clippy::missing_inline_in_public_items)]
185178
fn only_derive_is_allowed_to_implement_this_trait() {}
186-
#[inline(always)] unsafe fn is_bit_valid(_: Ptr<'_, Self>) -> bool { true }
179+
#[inline(always)] fn is_bit_valid(_: MaybeValid<'_, Self>) -> bool { true }
187180
};
188181
(@method $trait:ident) => {
189182
#[allow(clippy::missing_inline_in_public_items)]

src/pointer.rs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! Abstractions over raw pointers.
2+
3+
mod ptr;
4+
5+
pub use maybe_aligned::MaybeAligned;
6+
pub use ptr::{invariant, Ptr};
7+
8+
/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
9+
/// to [`TryFromBytes::is_bit_valid`][crate::TryFromBytes::is_bit_valid].
10+
pub type MaybeValid<'a, T> =
11+
Ptr<'a, T, { invariant::Shared }, { invariant::AnyAlignment }, { invariant::AsInitialized }>;
12+
13+
/// A semi-user-facing wrapper type representing a maybe-aligned reference, for
14+
/// use in [`TryFromBytes::is_bit_valid`][crate::TryFromBytes::is_bit_valid].
15+
mod maybe_aligned {
16+
use super::{ptr::invariant, Ptr};
17+
use crate::Unaligned;
18+
use core::fmt::{Debug, Formatter};
19+
20+
/// A reference to a validly-initialized `T` of lifetime `'a` that might
21+
/// *not* be validly aligned for `T`.
22+
// NOTE: Although this type is called `MaybeAligned`, that name is only
23+
// appropriate in its sole place of use: `TryFromBytes::is_bit_valid`.
24+
// There, the preconditions on the function's argument ensure that
25+
#[doc(hidden)]
26+
#[allow(unreachable_pub)] // false positive?
27+
pub struct MaybeAligned<'a, T: ?Sized> {
28+
ptr: Ptr<'a, T, { invariant::Shared }, { invariant::AnyAlignment }, { invariant::Valid }>,
29+
}
30+
31+
impl<'a, T> MaybeAligned<'a, T>
32+
where
33+
T: 'a + ?Sized,
34+
{
35+
/// Constructs a `MaybeAligned` from a [`Ptr`].
36+
///
37+
/// The `Ptr`'s referent must be validly-initialized for `T`.
38+
#[doc(hidden)]
39+
#[inline]
40+
pub const fn from_ptr(
41+
ptr: Ptr<
42+
'a,
43+
T,
44+
{ invariant::Shared },
45+
{ invariant::AnyAlignment },
46+
{ invariant::Valid },
47+
>,
48+
) -> Self {
49+
Self { ptr }
50+
}
51+
52+
/// Reads the value from `MaybeAligned`.
53+
///
54+
/// This is only available if `T` is [`Copy`].
55+
#[inline]
56+
pub fn read_unaligned(self) -> T
57+
where
58+
T: Copy,
59+
{
60+
let raw = self.ptr.as_non_null().as_ptr();
61+
// SAFETY: By invariant on `MaybeAligned`, `raw` contains
62+
// validly-initialized data for `T`. The value is safe to read and
63+
// return, because `T` is copy.
64+
unsafe { core::ptr::read_unaligned(raw) }
65+
}
66+
67+
/// Views the value as an aligned reference.
68+
///
69+
/// This is only available if `T` is [`Unaligned`].
70+
#[inline]
71+
pub fn as_ref(self) -> &'a T
72+
where
73+
T: Unaligned,
74+
{
75+
// SAFETY: The alignment of `T` is 1 and thus is always aligned
76+
// because `T: Unaligned`.
77+
let ptr = unsafe { self.ptr.assume_aligned() };
78+
ptr.as_ref()
79+
}
80+
}
81+
82+
impl<'a, T: 'a + ?Sized> Debug for MaybeAligned<'a, T> {
83+
#[inline]
84+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
85+
self.ptr.fmt(f)
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)