Skip to content

Commit 1c57b0c

Browse files
as-comdwrensha
authored andcommitted
Forbid use of as_slice when unaligned feature is enabled
1 parent edac915 commit 1c57b0c

File tree

3 files changed

+62
-3
lines changed

3 files changed

+62
-3
lines changed

capnp/src/primitive_list.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,14 @@ impl<'a, T: PrimitiveElement> Reader<'a, T> {
112112
}
113113
}
114114

115-
#[cfg(target_endian = "little")]
115+
const _CHECK_SLICE: () = check_slice_supported::<T>();
116+
116117
/// Returns something if the slice is as expected in memory.
118+
///
119+
/// If the target is not little-endian or the `unaligned` feature is enabled, this function
120+
/// will only be available for types that are 1 byte or smaller.
117121
pub fn as_slice(&self) -> Option<&[T]> {
122+
let () = Self::_CHECK_SLICE;
118123
if self.reader.get_element_size() == T::element_size() {
119124
let bytes = self.reader.into_raw_bytes();
120125
let bits_per_element = data_bits_per_element(T::element_size()) as usize;
@@ -137,6 +142,17 @@ impl<'a, T: PrimitiveElement> Reader<'a, T> {
137142
}
138143
}
139144

145+
const fn check_slice_supported<T: PrimitiveElement>() {
146+
if core::mem::size_of::<T>() > 1 {
147+
if !cfg!(target_endian = "little") {
148+
panic!("cannot call as_slice on primitive list of multi-byte elements on non-little endian targets");
149+
}
150+
if cfg!(feature = "unaligned") {
151+
panic!("cannot call as_slice on primitive list of multi-byte elements when unaligned feature is enabled");
152+
}
153+
}
154+
}
155+
140156
impl<'a, T> crate::traits::IntoInternalListReader<'a> for Reader<'a, T>
141157
where
142158
T: PrimitiveElement,
@@ -178,8 +194,10 @@ where
178194
PrimitiveElement::set(&self.builder, index, value);
179195
}
180196

181-
#[cfg(target_endian = "little")]
197+
const _CHECK_SLICE: () = check_slice_supported::<T>();
198+
182199
pub fn as_slice(&mut self) -> Option<&mut [T]> {
200+
let () = Self::_CHECK_SLICE;
183201
if self.builder.get_element_size() == T::element_size() {
184202
let bytes = self.builder.as_raw_bytes();
185203
let bits_per_element = data_bits_per_element(T::element_size()) as usize;

capnp/tests/primitive_list_as_slice.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
#![cfg(all(target_endian = "little", feature = "alloc"))]
1+
#![cfg(all(
2+
target_endian = "little",
3+
feature = "alloc",
4+
not(feature = "unaligned")
5+
))]
26

37
use capnp::{message, primitive_list};
48

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![cfg(feature = "alloc")]
2+
3+
use capnp::{message, primitive_list};
4+
5+
#[test]
6+
pub fn primitive_list_as_slice_small_values() {
7+
let mut msg = message::Builder::new_default();
8+
9+
{
10+
let mut void_list = msg.initn_root::<primitive_list::Builder<()>>(0);
11+
assert_eq!(void_list.as_slice().unwrap().len(), 0);
12+
assert_eq!(void_list.into_reader().as_slice().unwrap().len(), 0);
13+
}
14+
15+
{
16+
let mut void_list = msg.initn_root::<primitive_list::Builder<()>>(5);
17+
assert_eq!(void_list.as_slice().unwrap(), &[(), (), (), (), ()]);
18+
assert_eq!(
19+
void_list.into_reader().as_slice().unwrap(),
20+
&[(), (), (), (), ()]
21+
);
22+
}
23+
24+
{
25+
let mut u8list = msg.initn_root::<primitive_list::Builder<u8>>(0);
26+
assert_eq!(u8list.as_slice().unwrap().len(), 0);
27+
assert_eq!(u8list.into_reader().as_slice().unwrap().len(), 0);
28+
}
29+
30+
{
31+
let mut u8list = msg.initn_root::<primitive_list::Builder<u8>>(3);
32+
u8list.set(0, 0);
33+
u8list.set(1, 1);
34+
u8list.set(2, 2);
35+
assert_eq!(u8list.as_slice().unwrap(), &[0, 1, 2]);
36+
}
37+
}

0 commit comments

Comments
 (0)