VYPR
Moderate severityNVD Advisory· Published Jan 2, 2024· Updated Feb 13, 2025

`serde` deserialization for `FamStructWrapper` lacks bound checks that could potentially lead to out-of-bounds memory access

CVE-2023-50711

Description

vmm-sys-util is a collection of modules that provides helpers and utilities used by multiple rust-vmm components. Starting in version 0.5.0 and prior to version 0.12.0, an issue in the FamStructWrapper::deserialize implementation provided by the crate for vmm_sys_util::fam::FamStructWrapper can lead to out of bounds memory accesses. The deserialization does not check that the length stored in the header matches the flexible array length. Mismatch in the lengths might allow out of bounds memory access through Rust-safe methods. The issue was corrected in version 0.12.0 by inserting a check that verifies the lengths of compared flexible arrays are equal for any deserialized header and aborting deserialization otherwise. Moreover, the API was changed so that header length can only be modified through Rust-unsafe code. This ensures that users cannot trigger out-of-bounds memory access from Rust-safe code.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A missing length check in FamStructWrapper::deserialize in vmm-sys-util before 0.12.0 can cause out-of-bounds memory access from Rust-safe code.

Vulnerability

CVE-2023-50711 affects the vmm-sys-util crate, versions 0.5.0 up to but not including 0.12.0. The issue lies in the serde deserialization implementation for FamStructWrapper::deserialize, which does not verify that the header length matches the actual flexible array length. This inconsistency can lead to out-of-bounds memory accesses, even through Rust-safe methods, because the length field can be manipulated without bounds checking during deserialization [1][3].

Exploitation

The attack surface is local, with low complexity and no privileges required. An attacker can supply a crafted serialized payload that contains a mismatched header length, causing deserialization to proceed with an incorrect array size. Since the length can be set via safe code prior to the patch, a malicious input can trigger memory access beyond the allocated region [2][3].

Impact

Successful exploitation may result in integrity and availability impacts (CVSS 5.7). An attacker could potentially read or write out-of-bounds memory, leading to data corruption or potential crashes. The scope is changed because the vulnerability can affect resources beyond the original security context [2][3].

Mitigation

The fix was applied in version 0.12.0, which adds a check during deserialization to ensure the header length matches the flexible array length and aborts deserialization on mismatch. Additionally, the set_len method in the FamStruct trait was changed to unsafe, preventing safe code from setting an inconsistent length. Users should update to v0.12.0 or later. Fedora package updates are also available [1][3][4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
vmm-sys-utilcrates.io
>= 0.5.0, < 0.12.00.12.0

Affected products

12

Patches

1
30172fca2a8e

fix: deserialization issue of FamStructWrapper with serde

https://github.com/rust-vmm/vmm-sys-utilBabis ChaliosNov 13, 2023via ghsa
2 files changed · +59 10
  • Cargo.toml+2 1 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "vmm-sys-util"
    -version = "0.11.2"
    +version = "0.12.0"
     authors = ["Intel Virtualization Team <vmm-maintainers@intel.com>"]
     description = "A system utility set"
     repository = "https://github.com/rust-vmm/vmm-sys-util"
    @@ -26,3 +26,4 @@ bitflags = "1.0"
     
     [dev-dependencies]
     serde_json = "1.0.9"
    +bincode = "1.3.3"
    
  • src/fam.rs+57 9 modified
    @@ -99,7 +99,7 @@ impl fmt::Display for Error {
     ///         self.len as usize
     ///     }
     ///
    -///     fn set_len(&mut self, len: usize) {
    +///     unsafe fn set_len(&mut self, len: usize) {
     ///         self.len = len as u32
     ///     }
     ///
    @@ -135,7 +135,12 @@ pub unsafe trait FamStruct {
         ///
         /// These type of structures contain a member that holds the FAM length.
         /// This method will set the value of that member.
    -    fn set_len(&mut self, len: usize);
    +    ///
    +    /// # Safety
    +    ///
    +    /// The caller needs to ensure that `len` here reflects the correct number of entries of the
    +    /// flexible array part of the struct.
    +    unsafe fn set_len(&mut self, len: usize);
     
         /// Get max allowed FAM length
         ///
    @@ -220,7 +225,11 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
                 // SAFETY: Safe as long T follows the requirements of being POD.
                 mem_allocator.push(unsafe { mem::zeroed() })
             }
    -        mem_allocator[0].set_len(num_elements);
    +        // SAFETY: The flexible array part of the struct has `num_elements` capacity. We just
    +        // initialized this in `mem_allocator`.
    +        unsafe {
    +            mem_allocator[0].set_len(num_elements);
    +        }
     
             Ok(FamStructWrapper { mem_allocator })
         }
    @@ -276,8 +285,8 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
             &self.mem_allocator[0]
         }
     
    -    /// Get a mut reference to the actual [`FamStruct`](trait.FamStruct.html) instance.
    -    pub fn as_mut_fam_struct(&mut self) -> &mut T {
    +    // Get a mut reference to the actual [`FamStruct`](trait.FamStruct.html) instance.
    +    fn as_mut_fam_struct(&mut self) -> &mut T {
             &mut self.mem_allocator[0]
         }
     
    @@ -395,7 +404,11 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
                 self.mem_allocator[i] = unsafe { mem::zeroed() }
             }
             // Update the len of the underlying `FamStruct`.
    -        self.as_mut_fam_struct().set_len(len);
    +        // SAFETY: We just adjusted the memory for the underlying `mem_allocator` to hold `len`
    +        // entries.
    +        unsafe {
    +            self.as_mut_fam_struct().set_len(len);
    +        }
     
             // If the len needs to be decreased, deallocate unnecessary memory
             if additional_elements < 0 {
    @@ -540,13 +553,23 @@ where
                 {
                     use serde::de::Error;
     
    -                let header = seq
    +                let header: X = seq
                         .next_element()?
                         .ok_or_else(|| de::Error::invalid_length(0, &self))?;
                     let entries: Vec<X::Entry> = seq
                         .next_element()?
                         .ok_or_else(|| de::Error::invalid_length(1, &self))?;
     
    +                if header.len() != entries.len() {
    +                    let msg = format!(
    +                        "Mismatch between length of FAM specified in FamStruct header ({}) \
    +                         and actual size of FAM ({})",
    +                        header.len(),
    +                        entries.len()
    +                    );
    +                    return Err(V::Error::custom(msg));
    +                }
    +
                     let mut result: Self::Value = FamStructWrapper::from_entries(entries.as_slice())
                         .map_err(|e| V::Error::custom(format!("{:?}", e)))?;
                     result.mem_allocator[0] = header;
    @@ -570,7 +593,7 @@ macro_rules! generate_fam_struct_impl {
                     self.$field_name as usize
                 }
     
    -            fn set_len(&mut self, len: usize) {
    +            unsafe fn set_len(&mut self, len: usize) {
                     self.$field_name = len as $field_type;
                 }
     
    @@ -603,7 +626,7 @@ mod tests {
         const MAX_LEN: usize = 100;
     
         #[repr(C)]
    -    #[derive(Default, PartialEq, Eq)]
    +    #[derive(Default, Debug, PartialEq, Eq)]
         pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
         impl<T> __IncompleteArrayField<T> {
             #[inline]
    @@ -1078,4 +1101,29 @@ mod tests {
             assert_eq!(wrapper2.as_mut_fam_struct().flags, 2);
             assert_eq!(wrapper2.as_slice(), [0, 0, 0, 3, 14, 0, 0, 1]);
         }
    +
    +    #[cfg(feature = "with-serde")]
    +    #[test]
    +    fn test_bad_deserialize() {
    +        #[repr(C)]
    +        #[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
    +        struct Foo {
    +            pub len: u32,
    +            pub padding: u32,
    +            pub entries: __IncompleteArrayField<u32>,
    +        }
    +
    +        generate_fam_struct_impl!(Foo, u32, entries, u32, len, 100);
    +
    +        let state = FamStructWrapper::<Foo>::new(0).unwrap();
    +        let mut bytes = bincode::serialize(&state).unwrap();
    +
    +        // The `len` field of the header is the first to be serialized.
    +        // Writing at position 0 of the serialized data should change its value.
    +        bytes[0] = 255;
    +
    +        assert!(
    +            matches!(bincode::deserialize::<FamStructWrapper<Foo>>(&bytes).map_err(|boxed| *boxed), Err(bincode::ErrorKind::Custom(s)) if s == *"Mismatch between length of FAM specified in FamStruct header (255) and actual size of FAM (0)")
    +        );
    +    }
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.