Skip to content

Commit fb952ab

Browse files
authored
wasmi_ir2: make Decode trait safe (#1662)
* make Decode trait and impls inherently safe An unsafe and more efficient implement of Decode and Decoder will have to wrap these safe APIs and avoid checks via APIs such as unwrap_unchecked etc. if needed. * add module docs to decode module
1 parent bb71307 commit fb952ab

File tree

5 files changed

+222
-165
lines changed

5 files changed

+222
-165
lines changed

crates/ir2/build/display/decode.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl<const N: usize> Display for DisplayDecode<&'_ GenericOp<N>> {
191191
.iter()
192192
.map(|field| field.ident)
193193
.map(SnakeCase)
194-
.map(|ident| (indent.inc_by(3), ident, ": Decode::decode(decoder)"))
194+
.map(|ident| (indent.inc_by(3), ident, ": Decode::decode(decoder)?"))
195195
.map(DisplayConcat),
196196
);
197197
write!(
@@ -201,10 +201,10 @@ impl<const N: usize> Display for DisplayDecode<&'_ GenericOp<N>> {
201201
{fields}\n\
202202
{indent}}}\n\
203203
{indent}impl Decode for {camel_ident} {{\n\
204-
{indent} unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {{\n\
205-
{indent} Self {{\n\
204+
{indent} fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {{\n\
205+
{indent} Ok(Self {{\n\
206206
{constructors}\n\
207-
{indent} }}\n\
207+
{indent} }})\n\
208208
{indent} }}\n\
209209
{indent}}}\n\
210210
"

crates/ir2/build/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ fn generate_encode_rs(config: &Config, isa: &Isa, contents: &mut String) -> Resu
121121

122122
fn generate_decode_rs(config: &Config, isa: &Isa, contents: &mut String) -> Result<(), Error> {
123123
let expected_size = match config.simd {
124-
true => 50_000,
124+
true => 55_000,
125125
false => 35_000,
126126
};
127127
write_to_buffer(contents, expected_size, |buffer| {

crates/ir2/src/decode/mod.rs

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![allow(non_camel_case_types)]
22

3+
//! Definitions and utilities to decode encoded byte streams.
4+
35
mod op;
46

57
use self::op::{
@@ -42,56 +44,76 @@ use crate::{
4244
Slot,
4345
SlotSpan,
4446
};
45-
use core::{mem, num::NonZero};
47+
use core::{
48+
error::Error as CoreError,
49+
fmt,
50+
mem::{self, MaybeUninit},
51+
num::NonZero,
52+
};
4653

4754
/// Types that can be used to decode types implementing [`Decode`].
4855
pub trait Decoder {
4956
/// Reads enough bytes from `self` to populate `buffer`.
50-
fn read_bytes(&mut self, buffer: &mut [u8]);
57+
fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<(), DecodeError>;
58+
}
59+
60+
/// An error that may be returned when decoding items.
61+
#[derive(Debug, Copy, Clone)]
62+
pub enum DecodeError {
63+
/// The decoder ran out of bytes.
64+
OutOfBytes,
65+
/// The decoder found an invalid bit pattern.
66+
InvalidBitPattern,
67+
}
68+
69+
impl CoreError for DecodeError {}
70+
impl fmt::Display for DecodeError {
71+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72+
let s = match self {
73+
DecodeError::OutOfBytes => "ran out of bytes to decode",
74+
DecodeError::InvalidBitPattern => "encountered invalid bit pattern",
75+
};
76+
f.write_str(s)
77+
}
5178
}
5279

5380
/// Types that can be decoded using a type that implements [`Decoder`].
54-
pub trait Decode {
81+
pub trait Decode: Sized {
5582
/// Decodes `Self` via `decoder`.
56-
///
57-
/// # Safety
58-
///
59-
/// It is the callers responsibility to ensure that the decoder
60-
/// decodes items in the order they have been encoded and on valid
61-
/// positions within the decode stream.
62-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self;
83+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError>;
6384
}
6485

6586
impl Decode for BoundedSlotSpan {
66-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
67-
let span = SlotSpan::decode(decoder);
68-
let len = u16::decode(decoder);
69-
Self::new(span, len)
87+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
88+
let span = SlotSpan::decode(decoder)?;
89+
let len = u16::decode(decoder)?;
90+
Ok(Self::new(span, len))
7091
}
7192
}
7293

7394
impl<const N: u16> Decode for FixedSlotSpan<N> {
74-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
75-
Self::new_unchecked(SlotSpan::decode(decoder))
95+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
96+
let span = SlotSpan::decode(decoder)?;
97+
Self::new(span).map_err(|_| DecodeError::InvalidBitPattern)
7698
}
7799
}
78100

79101
impl Decode for BranchTableTarget {
80-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
81-
let results = SlotSpan::decode(decoder);
82-
let offset = BranchOffset::decode(decoder);
83-
Self::new(results, offset)
102+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
103+
let results = SlotSpan::decode(decoder)?;
104+
let offset = BranchOffset::decode(decoder)?;
105+
Ok(Self::new(results, offset))
84106
}
85107
}
86108

87109
macro_rules! impl_decode_for_primitive {
88110
( $($ty:ty),* $(,)? ) => {
89111
$(
90112
impl Decode for $ty {
91-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
113+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
92114
let mut bytes = [0_u8; mem::size_of::<$ty>()];
93-
decoder.read_bytes(&mut bytes);
94-
Self::from_ne_bytes(bytes)
115+
decoder.read_bytes(&mut bytes)?;
116+
Ok(Self::from_ne_bytes(bytes))
95117
}
96118
}
97119
)*
@@ -105,8 +127,8 @@ macro_rules! impl_decode_using {
105127
( $($ty:ty as $as:ty = $e:expr),* $(,)? ) => {
106128
$(
107129
impl Decode for $ty {
108-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
109-
$e(<$as as Decode>::decode(decoder))
130+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
131+
Ok($e(<$as as Decode>::decode(decoder)?))
110132
}
111133
}
112134
)*
@@ -127,32 +149,66 @@ impl_decode_using! {
127149
Data as u32 = Into::into,
128150
Elem as u32 = Into::into,
129151

130-
Address as u64 = |address| unsafe { Address::try_from(address).unwrap_unchecked() },
131152
Sign<f32> as bool = Sign::new,
132153
Sign<f64> as bool = Sign::new,
133154
SlotSpan as Slot = SlotSpan::new,
134-
NonZero<i32> as i32 = |value| unsafe { NonZero::new_unchecked(value) },
135-
NonZero<i64> as i64 = |value| unsafe { NonZero::new_unchecked(value) },
136-
NonZero<u32> as u32 = |value| unsafe { NonZero::new_unchecked(value) },
137-
NonZero<u64> as u64 = |value| unsafe { NonZero::new_unchecked(value) },
138-
TrapCode as u8 = |code: u8| -> TrapCode {
139-
TrapCode::try_from(code).unwrap_unchecked()
155+
}
156+
157+
macro_rules! impl_decode_fallible_using {
158+
( $($ty:ty as $as:ty = $e:expr),* $(,)? ) => {
159+
$(
160+
impl Decode for $ty {
161+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
162+
$e(<$as as Decode>::decode(decoder)?)
163+
}
164+
}
165+
)*
166+
};
167+
}
168+
impl_decode_fallible_using! {
169+
Address as u64 = |address| {
170+
Address::try_from(address).map_err(|_| DecodeError::InvalidBitPattern)
171+
},
172+
NonZero<i32> as i32 = |value| {
173+
NonZero::new(value).ok_or(DecodeError::InvalidBitPattern)
174+
},
175+
NonZero<i64> as i64 = |value| {
176+
NonZero::new(value).ok_or(DecodeError::InvalidBitPattern)
177+
},
178+
NonZero<u32> as u32 = |value| {
179+
NonZero::new(value).ok_or(DecodeError::InvalidBitPattern)
180+
},
181+
NonZero<u64> as u64 = |value| {
182+
NonZero::new(value).ok_or(DecodeError::InvalidBitPattern)
183+
},
184+
TrapCode as u8 = |code: u8| {
185+
TrapCode::try_from(code).map_err(|_| DecodeError::InvalidBitPattern)
140186
},
141-
OpCode as u16 = |code: u16| -> OpCode {
142-
OpCode::try_from(code).unwrap_unchecked()
187+
OpCode as u16 = |code: u16| {
188+
OpCode::try_from(code).map_err(|_| DecodeError::InvalidBitPattern)
143189
}
144190
}
145191

146192
impl<const N: usize, T: Decode> Decode for [T; N] {
147-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
148-
core::array::from_fn(|_| <T as Decode>::decode(decoder))
193+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
194+
let mut array = <MaybeUninit<[T; N]>>::uninit();
195+
// Safety: we are going to decode and initialize all array items and won't read any.
196+
let items = unsafe { &mut *array.as_mut_ptr() };
197+
for item in items {
198+
*item = <T as Decode>::decode(decoder)?;
199+
}
200+
// Safety: we have decoded and thus initialized all array items.
201+
let array = unsafe { array.assume_init() };
202+
Ok(array)
149203
}
150204
}
151205

152206
#[cfg(feature = "simd")]
153207
impl<const N: u8> Decode for ImmLaneIdx<N> {
154-
unsafe fn decode<D: Decoder>(decoder: &mut D) -> Self {
155-
ImmLaneIdx::try_from(u8::decode(decoder)).unwrap_unchecked()
208+
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
209+
let byte = u8::decode(decoder)?;
210+
let lane = ImmLaneIdx::try_from(byte).map_err(|_| DecodeError::InvalidBitPattern)?;
211+
Ok(lane)
156212
}
157213
}
158214

0 commit comments

Comments
 (0)