Skip to content

Commit 446775c

Browse files
authored
pod: fix length panic on PodSlice::unpack (#84)
1 parent c6edb7f commit 446775c

File tree

1 file changed

+75
-14
lines changed

1 file changed

+75
-14
lines changed

pod/src/slice.rs

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<'data, T: Pod> PodSlice<'data, T> {
2929
}
3030
let (length, data) = data.split_at(LENGTH_SIZE);
3131
let length = pod_from_bytes::<PodU32>(length)?;
32-
let _max_length = max_len_for_type::<T>(data.len())?;
32+
let _max_length = max_len_for_type::<T>(data.len(), u32::from(*length) as usize)?;
3333
let data = pod_slice_from_bytes(data)?;
3434
Ok(Self { length, data })
3535
}
@@ -70,7 +70,7 @@ impl<'data, T: Pod> PodSliceMut<'data, T> {
7070
if init {
7171
*length = 0.into();
7272
}
73-
let max_length = max_len_for_type::<T>(data.len())?;
73+
let max_length = max_len_for_type::<T>(data.len(), u32::from(*length) as usize)?;
7474
let data = pod_slice_from_bytes_mut(data)?;
7575
Ok(Self {
7676
length,
@@ -109,22 +109,30 @@ impl<'data, T: Pod> PodSliceMut<'data, T> {
109109
}
110110
}
111111

112-
fn max_len_for_type<T>(data_len: usize) -> Result<usize, ProgramError> {
113-
let size: usize = std::mem::size_of::<T>();
112+
fn max_len_for_type<T>(data_len: usize, length_val: usize) -> Result<usize, ProgramError> {
113+
let item_size = std::mem::size_of::<T>();
114114
let max_len = data_len
115-
.checked_div(size)
115+
.checked_div(item_size)
116116
.ok_or(PodSliceError::CalculationFailure)?;
117-
// check that it isn't over or under allocated
118-
if max_len.saturating_mul(size) != data_len {
117+
118+
// Make sure the max length that can be stored in the buffer isn't less
119+
// than the length value.
120+
if max_len < length_val {
121+
Err(PodSliceError::BufferTooSmall)?
122+
}
123+
124+
// Make sure the buffer is cleanly divisible by `size_of::<T>`; not over or
125+
// under allocated.
126+
if max_len.saturating_mul(item_size) != data_len {
119127
if max_len == 0 {
120128
// Size of T is greater than buffer size
121-
Err(PodSliceError::BufferTooSmall.into())
129+
Err(PodSliceError::BufferTooSmall)?
122130
} else {
123-
Err(PodSliceError::BufferTooLarge.into())
131+
Err(PodSliceError::BufferTooLarge)?
124132
}
125-
} else {
126-
Ok(max_len)
127133
}
134+
135+
Ok(max_len)
128136
}
129137

130138
#[cfg(test)]
@@ -171,9 +179,11 @@ mod tests {
171179

172180
#[test]
173181
fn test_pod_slice_buffer_too_large() {
174-
// 1 `TestStruct` + length = 37 bytes
175-
// we pass 38 to trigger BufferTooLarge
176-
let pod_slice_bytes = [1; 38];
182+
// Length is 1. We pass one test struct with 6 trailing bytes to
183+
// trigger BufferTooLarge.
184+
let data_len = LENGTH_SIZE + std::mem::size_of::<TestStruct>() + 6;
185+
let mut pod_slice_bytes = vec![1; data_len];
186+
pod_slice_bytes[0..4].copy_from_slice(&[1, 0, 0, 0]);
177187
let err = PodSlice::<TestStruct>::unpack(&pod_slice_bytes)
178188
.err()
179189
.unwrap();
@@ -184,6 +194,32 @@ mod tests {
184194
);
185195
}
186196

197+
#[test]
198+
fn test_pod_slice_buffer_larger_than_length_value() {
199+
// If the buffer is longer than the u32 length value declares, it
200+
// should still unpack successfully, as long as the length of the rest
201+
// of the buffer can be divided by `size_of::<T>`.
202+
let length: u32 = 12;
203+
let length_le = length.to_le_bytes();
204+
205+
// First set up the data to have room for extra items.
206+
let data_len = PodSlice::<TestStruct>::size_of(length as usize + 2).unwrap();
207+
let mut data = vec![0; data_len];
208+
209+
// Now write the bogus length - which is smaller - into the first 4
210+
// bytes.
211+
data[..LENGTH_SIZE].copy_from_slice(&length_le);
212+
213+
let pod_slice = PodSlice::<TestStruct>::unpack(&data).unwrap();
214+
let pod_slice_len = u32::from(*pod_slice.length);
215+
let data = pod_slice.data();
216+
let data_vec = data.to_vec();
217+
218+
assert_eq!(pod_slice_len, length);
219+
assert_eq!(data.len(), length as usize);
220+
assert_eq!(data_vec.len(), length as usize);
221+
}
222+
187223
#[test]
188224
fn test_pod_slice_buffer_too_small() {
189225
// 1 `TestStruct` + length = 37 bytes
@@ -199,6 +235,31 @@ mod tests {
199235
);
200236
}
201237

238+
#[test]
239+
fn test_pod_slice_buffer_shorter_than_length_value() {
240+
// If the buffer is shorter than the u32 length value declares, we
241+
// should get a BufferTooSmall error.
242+
let length: u32 = 12;
243+
let length_le = length.to_le_bytes();
244+
for num_items in 0..length {
245+
// First set up the data to have `num_elements` items.
246+
let data_len = PodSlice::<TestStruct>::size_of(num_items as usize).unwrap();
247+
let mut data = vec![0; data_len];
248+
249+
// Now write the bogus length - which is larger - into the first 4
250+
// bytes.
251+
data[..LENGTH_SIZE].copy_from_slice(&length_le);
252+
253+
// Expect an error on unpacking.
254+
let err = PodSlice::<TestStruct>::unpack(&data).err().unwrap();
255+
assert_eq!(
256+
err,
257+
PodSliceError::BufferTooSmall.into(),
258+
"Expected an `PodSliceError::BufferTooSmall` error"
259+
);
260+
}
261+
}
262+
202263
#[test]
203264
fn test_pod_slice_mut() {
204265
// slice can fit 2 `TestStruct`

0 commit comments

Comments
 (0)