Skip to content

Commit 238ff8b

Browse files
authored
Merge pull request #1657 from joshlf/unsafe-cleanup
Document or remove some uses of `unsafe`
2 parents bc5d2c3 + 4056fb9 commit 238ff8b

File tree

5 files changed

+77
-18
lines changed

5 files changed

+77
-18
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ name = "ring"
168168
[dependencies]
169169
getrandom = { version = "0.2.8" }
170170
untrusted = { version = "0.9" }
171+
zerocopy = { version = "0.7.5", features = ["derive"] }
171172

172173
[target.'cfg(any(target_arch = "x86",target_arch = "x86_64", all(any(target_arch = "aarch64", target_arch = "arm"), any(target_os = "android", target_os = "fuchsia", target_os = "linux", target_os = "windows"))))'.dependencies]
173174
spin = { version = "0.9.2", default-features = false, features = ["once"] }

src/aead/gcm.rs

+6
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ impl Context {
126126
debug_assert!(input_bytes > 0);
127127

128128
let input = input.as_ptr() as *const [u8; BLOCK_LEN];
129+
// SAFETY:
130+
// - `[[u8; BLOCK_LEN]]` has the same bit validity as `[u8]`.
131+
// - `[[u8; BLOCK_LEN]]` has the same alignment requirement as `[u8]`.
132+
// - `input_bytes / BLOCK_LEN` ensures that the total length in bytes of
133+
// the new `[[u8; BLOCK_LEN]]` will not be longer than the original
134+
// `[u8]`.
129135
let input = unsafe { core::slice::from_raw_parts(input, input_bytes / BLOCK_LEN) };
130136

131137
// Although these functions take `Xi` and `h_table` as separate

src/digest.rs

+39-11
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,9 @@
2424
// The goal for this implementation is to drive the overhead as close to zero
2525
// as possible.
2626

27-
use crate::{
28-
c, cpu, debug,
29-
endian::{ArrayEncoding, BigEndian},
30-
polyfill,
31-
};
27+
use crate::{c, cpu, debug, endian::BigEndian, polyfill};
3228
use core::num::Wrapping;
29+
use zerocopy::{AsBytes, FromBytes};
3330

3431
mod sha1;
3532
mod sha2;
@@ -114,7 +111,9 @@ impl BlockContext {
114111

115112
Digest {
116113
algorithm: self.algorithm,
117-
value: (self.algorithm.format_output)(self.state),
114+
// SAFETY: Invariant on this field promises that this is the correct
115+
// format function for this algorithm's block size.
116+
value: unsafe { (self.algorithm.format_output)(self.state) },
118117
}
119118
}
120119
}
@@ -248,8 +247,11 @@ impl Digest {
248247
impl AsRef<[u8]> for Digest {
249248
#[inline(always)]
250249
fn as_ref(&self) -> &[u8] {
251-
let as64 = unsafe { &self.value.as64 };
252-
&as64.as_byte_array()[..self.algorithm.output_len]
250+
let data = (&self.value as *const Output).cast::<u8>();
251+
// SAFETY: So long as `self.algorithm` is the correct algorithm, all
252+
// code initializes all bytes of `self.value` in the range `[0,
253+
// self.algorithm.output_len)`.
254+
unsafe { core::slice::from_raw_parts(data, self.algorithm.output_len) }
253255
}
254256
}
255257

@@ -270,7 +272,9 @@ pub struct Algorithm {
270272
len_len: usize,
271273

272274
block_data_order: unsafe extern "C" fn(state: &mut State, data: *const u8, num: c::size_t),
273-
format_output: fn(input: State) -> Output,
275+
// INVARIANT: This is always set to the correct output for the algorithm's
276+
// block size.
277+
format_output: unsafe fn(input: State) -> Output,
274278

275279
initial_state: State,
276280

@@ -474,14 +478,38 @@ pub const MAX_OUTPUT_LEN: usize = 512 / 8;
474478
/// algorithms in this module.
475479
pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
476480

477-
fn sha256_format_output(input: State) -> Output {
481+
fn sha256_format_output(input: State) -> Output
482+
where
483+
[BigEndian<u32>; 256 / 8 / core::mem::size_of::<BigEndian<u32>>()]: FromBytes,
484+
[BigEndian<u64>; 512 / 8 / core::mem::size_of::<BigEndian<u64>>()]: AsBytes,
485+
{
486+
// SAFETY: There are two cases:
487+
// - The union is initialized as `as32`, in which case this is trivially
488+
// sound.
489+
// - The union is initialized as `as64`. In this case, the `as64` variant is
490+
// longer than the `as32` variant, so all bytes of `as32` are initialized
491+
// as they are in the prefix of `as64`. Since `as64`'s type is `AsBytes`
492+
// (see the where bound on this function), all of its bytes are
493+
// initialized (ie, none are padding). Since `as32`'s type is `FromBytes`,
494+
// any initialized sequence of bytes constitutes a valid instance of the
495+
// type, so this is sound.
478496
let input = unsafe { &input.as32 };
479497
Output {
480498
as32: input.map(BigEndian::from),
481499
}
482500
}
483501

484-
fn sha512_format_output(input: State) -> Output {
502+
/// # Safety
503+
///
504+
/// The caller must ensure that all bytes of `State` have been initialized.
505+
unsafe fn sha512_format_output(input: State) -> Output
506+
where
507+
[BigEndian<u64>; 512 / 8 / core::mem::size_of::<BigEndian<u64>>()]: FromBytes,
508+
{
509+
// SAFETY: Caller has promised that all bytes are initialized. Since
510+
// `input.as64` is `FromBytes`, we know that this is sufficient to guarantee
511+
// that the input has been initialized to a valid instance of the type of
512+
// `input.as64`.
485513
let input = unsafe { &input.as64 };
486514
Output {
487515
as64: input.map(BigEndian::from),

src/endian.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use core::num::Wrapping;
1+
use core::{mem, num::Wrapping};
2+
3+
use zerocopy::{AsBytes, FromBytes, FromZeroes};
24

35
/// An `Encoding` of a type `T` can be converted to/from its byte
46
/// representation without any byte swapping or other computation.
57
///
68
/// The `Self: Copy` constraint addresses `clippy::declare_interior_mutable_const`.
7-
pub trait Encoding<T>: From<T> + Into<T>
9+
pub trait Encoding<T>: From<T> + Into<T> + FromBytes + AsBytes
810
where
911
Self: Copy,
1012
{
@@ -25,7 +27,7 @@ pub trait FromByteArray<T> {
2527

2628
macro_rules! define_endian {
2729
($endian:ident) => {
28-
#[derive(Clone, Copy)]
30+
#[derive(Clone, Copy, FromZeroes, FromBytes, AsBytes)]
2931
#[repr(transparent)]
3032
pub struct $endian<T>(T);
3133

@@ -48,7 +50,7 @@ macro_rules! impl_from_byte_array {
4850
{
4951
#[inline]
5052
fn from_byte_array(a: &[u8; $elems * core::mem::size_of::<$base>()]) -> Self {
51-
unsafe { core::mem::transmute_copy(a) }
53+
zerocopy::transmute!(*a)
5254
}
5355
}
5456
};
@@ -58,11 +60,24 @@ macro_rules! impl_array_encoding {
5860
($endian:ident, $base:ident, $elems:expr) => {
5961
impl ArrayEncoding<[u8; $elems * core::mem::size_of::<$base>()]>
6062
for [$endian<$base>; $elems]
63+
where
64+
Self: AsBytes,
6165
{
6266
#[inline]
6367
fn as_byte_array(&self) -> &[u8; $elems * core::mem::size_of::<$base>()] {
68+
const _: () = assert!(
69+
mem::size_of::<[$endian<$base>; $elems]>()
70+
== $elems * core::mem::size_of::<$base>()
71+
);
6472
let as_bytes_ptr =
6573
self.as_ptr() as *const [u8; $elems * core::mem::size_of::<$base>()];
74+
// SAFETY:
75+
// - `Self: AsBytes`, so it's sound to observe the bytes of
76+
// `self` and to have a reference to those bytes alive at the
77+
// same time as `&self`.
78+
// - As confirmed by the preceding assertion, the sizes of
79+
// `Self` and the return type are equal.
80+
// - `[u8; N]` has no alignment requirement.
6681
unsafe { &*as_bytes_ptr }
6782
}
6883
}

src/rsa/public_exponent.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ impl PublicExponent {
1212

1313
// TODO: Use `NonZeroU64::new(...).unwrap()` when `feature(const_panic)` is
1414
// stable.
15-
pub(super) const _3: Self = Self(unsafe { NonZeroU64::new_unchecked(3) });
16-
pub(super) const _65537: Self = Self(unsafe { NonZeroU64::new_unchecked(65537) });
15+
pub(super) const _3: Self = Self(match NonZeroU64::new(3) {
16+
Some(nz) => nz,
17+
None => unreachable!(),
18+
});
19+
pub(super) const _65537: Self = Self(match NonZeroU64::new(65537) {
20+
Some(nz) => nz,
21+
None => unreachable!(),
22+
});
1723

1824
// This limit was chosen to bound the performance of the simple
1925
// exponentiation-by-squaring implementation in `elem_exp_vartime`. In
@@ -29,7 +35,10 @@ impl PublicExponent {
2935
//
3036
// TODO: Use `NonZeroU64::new(...).unwrap()` when `feature(const_panic)` is
3137
// stable.
32-
const MAX: Self = Self(unsafe { NonZeroU64::new_unchecked((1u64 << 33) - 1) });
38+
const MAX: Self = Self(match NonZeroU64::new((1u64 << 33) - 1) {
39+
Some(nz) => nz,
40+
None => unreachable!(),
41+
});
3342

3443
pub(super) fn from_be_bytes(
3544
input: untrusted::Input,

0 commit comments

Comments
 (0)