Headline
CVE-2023-28448: Add missing bounds check to `<FamStructWrapper as Versionize>::deserialize` by roypat · Pull Request #53 · firecracker-microvm/versionize
Versionize is a framework for version tolerant serializion/deserialization of Rust data structures, designed for usecases that need fast deserialization times and minimal size overhead. 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. The impact started with version 0.1.1. The issue was corrected in version 0.1.10 by inserting a check that verifies, for any deserialized header, the lengths of compared flexible arrays are equal and aborting deserialization otherwise.
Conversation
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 [email protected]
Signed-off-by: Patrick Roy [email protected]
Related news
### Impact An issue was discovered in the `Versionize::deserialize` implementation provided by the `versionize` crate for `vmm_sys_util::fam::FamStructWrapper`, which can lead to out of bounds memory accesses. ### Patches The impact started with version 0.1.1. The issue was corrected in version 0.1.10 by inserting a check that verifies, for any deserialized header, the lengths of compared flexible arrays are equal and aborting deserialization otherwise. ### Workarounds \- ### References - https://github.com/firecracker-microvm/versionize/pull/53