Skip to content

Commit a57a051

Browse files
committed
Add missing bounds check to FamStructWrapper::deserialize
An issue was discovered in the `Versionize::deserialize` implementation provided by the `versionize` crate for `vmm_sys_utils::fam::FamStructWrapper`, which can lead to out of bounds memory accesses. Objects of this type are used to model structures containing C-style flexible array members [1]. These structures contain a memory allocation that is prefixed by a header containing the size of the allocation. Due to treating the header and the memory allocation as two objects, `Versionize`'s data format stores the size of the allocation twice: once in the header and then again as its own metadata of the memory allocation. A serialized `FamStructWrapper` thus looks as follows: +------------------------------------------------------------+\ | header (containing length of flexible array member `len1`) |\ +------------------------------------------------------------+\ +---------------------------------------+-----------------------+ | length of flexible array member`len2` | array member contents | +---------------------------------------+-----------------------+ During deserialization, the library separately deserializes the header and the memory allocation. It allocates `len2` bytes of memory, and then prefixes it with the separately deserialized header. Since `len2` is an implementation detail of the `Versionize` implementation, it is forgotten about at the end of the deserialize `function`, and all subsequent operations on the `FamStructWrapper` assume the memory allocated to have size `len1`. If deserialization input was malformed such that `len1 != len2`, then this can lead to (safe) functions on ´FamStructWrapper` to read past the end of allocated memory (if `len1 > len2`). The issue was corrected by inserting a check that verifies that these two lengths are equal, and aborting deserialization otherwise. [1]: https://en.wikipedia.org/wiki/Flexible_array_member Signed-off-by: Patrick Roy <roypat@amazon.co.uk>
1 parent 732eb81 commit a57a051

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

src/primitives.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,18 @@ where
369369
let entries: Vec<<T as FamStruct>::Entry> =
370370
Vec::deserialize(reader, version_map, app_version)
371371
.map_err(|ref err| VersionizeError::Deserialize(format!("{:?}", err)))?;
372+
373+
if header.len() != entries.len() {
374+
let msg = format!(
375+
"Mismatch between length of FAM specified in FamStruct header ({}) \
376+
and actual size of FAM ({})",
377+
header.len(),
378+
entries.len()
379+
);
380+
381+
return Err(VersionizeError::Deserialize(msg));
382+
}
383+
372384
// Construct the object from the array items.
373385
// Header(T) fields will be initialized by Default trait impl.
374386
let mut object = FamStructWrapper::from_entries(&entries)

tests/test.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,32 @@ impl<T> Versionize for __IncompleteArrayField<T> {
13231323
type MessageFamStructWrapper = FamStructWrapper<Message>;
13241324
type Message2FamStructWrapper = FamStructWrapper<Message2>;
13251325

1326+
#[test]
1327+
fn test_deserialize_famstructwrapper_invalid_len() {
1328+
let mut vm = VersionMap::new();
1329+
vm.new_version()
1330+
.set_type_version(Message::type_id(), 2)
1331+
.new_version()
1332+
.set_type_version(Message::type_id(), 3)
1333+
.new_version()
1334+
.set_type_version(Message::type_id(), 4);
1335+
1336+
// Create FamStructWrapper with len 2
1337+
let state = MessageFamStructWrapper::new(0).unwrap();
1338+
let mut buffer = [0; 256];
1339+
1340+
state.serialize(&mut buffer.as_mut_slice(), &vm, 2).unwrap();
1341+
1342+
// the `len` field of the header is the first serialized field.
1343+
// Let's corrupt it by making it bigger than the actual number of serialized elements
1344+
buffer[0] = 255;
1345+
1346+
assert_eq!(
1347+
MessageFamStructWrapper::deserialize(&mut buffer.as_slice(), &vm, 2).unwrap_err(),
1348+
VersionizeError::Deserialize("Mismatch between length of FAM specified in FamStruct header (255) and actual size of FAM (0)".to_string())
1349+
);
1350+
}
1351+
13261352
#[test]
13271353
fn test_versionize_famstructwrapper() {
13281354
let mut vm = VersionMap::new();

0 commit comments

Comments
 (0)