CVE-2021-38190
Description
The nalgebra crate before 0.27.1 allows out-of-bounds memory access via crafted deserialization of VecStorage due to missing length invariant check.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
The nalgebra crate before 0.27.1 allows out-of-bounds memory access via crafted deserialization of VecStorage due to missing length invariant check.
Vulnerability
The nalgebra crate for Rust, prior to version 0.27.1, contains a vulnerability in the VecStorage type's Deserialize implementation. The deserialization logic did not enforce the invariant that the number of stored elements must equal the product of the row count and column count (nrows * ncols). This flaw was introduced in version 0.11.0 and persisted through 0.27.0 [4]. The official description confirms that this allows out-of-bounds memory access [3].
Exploitation
An attacker can craft a serialized matrix (e.g., via serde) where the number of elements in the underlying vector does not match the declared dimensions. When the matrix is deserialized, the VecStorage will be constructed with an inconsistent state, leading to out-of-bounds reads or writes when the matrix is subsequently accessed. No special privileges are required; exploitation depends on the application deserializing untrusted data [2][4].
Impact
Successful exploitation can result in out-of-bounds memory access, potentially leading to memory corruption or information disclosure. The RustSec advisory categorizes this as a memory-corruption and memory-exposure vulnerability [4].
Mitigation
The issue is fixed in nalgebra version 0.27.1 [4]. Users should update to at least 0.27.1. Versions prior to 0.11.0 are unaffected. No workaround is available for affected versions; upgrading is the recommended mitigation [4].
AI Insight generated on May 21, 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.
| Package | Affected versions | Patched versions |
|---|---|---|
nalgebracrates.io | >= 0.11.0, < 0.27.1 | 0.27.1 |
Affected products
2- nalgebra/nalgebradescription
Patches
1a803271fcce7Merge pull request #889 from dimforge/dvector_deserialize
2 files changed · +76 −3
src/base/vec_storage.rs+48 −1 modified@@ -13,6 +13,12 @@ use crate::base::storage::{ }; use crate::base::{Scalar, Vector}; +#[cfg(feature = "serde-serialize-no-std")] +use serde::{ + de::{Deserialize, Deserializer, Error}, + ser::{Serialize, Serializer}, +}; + #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; @@ -24,13 +30,54 @@ use abomonation::Abomonation; /// A Vec-based matrix data storage. It may be dynamically-sized. #[repr(C)] #[derive(Eq, Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct VecStorage<T, R: Dim, C: Dim> { data: Vec<T>, nrows: R, ncols: C, } +#[cfg(feature = "serde-serialize")] +impl<T, R: Dim, C: Dim> Serialize for VecStorage<T, R, C> +where + T: Serialize, + R: Serialize, + C: Serialize, +{ + fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> + where + Ser: Serializer, + { + (&self.data, &self.nrows, &self.ncols).serialize(serializer) + } +} + +#[cfg(feature = "serde-serialize")] +impl<'a, T, R: Dim, C: Dim> Deserialize<'a> for VecStorage<T, R, C> +where + T: Deserialize<'a>, + R: Deserialize<'a>, + C: Deserialize<'a>, +{ + fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> + where + Des: Deserializer<'a>, + { + let (data, nrows, ncols): (Vec<T>, R, C) = Deserialize::deserialize(deserializer)?; + + // SAFETY: make sure the data we deserialize have the + // correct number of elements. + if nrows.value() * ncols.value() != data.len() { + return Err(Des::Error::custom(format!( + "Expected {} components, found {}", + nrows.value() * ncols.value(), + data.len() + ))); + } + + Ok(Self { data, nrows, ncols }) + } +} + #[deprecated(note = "renamed to `VecStorage`")] /// Renamed to [VecStorage]. pub type MatrixVec<T, R, C> = VecStorage<T, R, C>;
tests/core/serde.rs+28 −2 modified@@ -1,8 +1,8 @@ #![cfg(feature = "serde-serialize")] use na::{ - DMatrix, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix3x4, Point2, Point3, - Quaternion, Rotation2, Rotation3, Similarity2, Similarity3, SimilarityMatrix2, + DMatrix, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix2x3, Matrix3x4, Point2, + Point3, Quaternion, Rotation2, Rotation3, Similarity2, Similarity3, SimilarityMatrix2, SimilarityMatrix3, Translation2, Translation3, Unit, Vector2, }; use rand; @@ -27,6 +27,32 @@ fn serde_dmatrix() { let serialized = serde_json::to_string(&v).unwrap(); let deserialized: DMatrix<f32> = serde_json::from_str(&serialized).unwrap(); assert_eq!(v, deserialized); + + let m = DMatrix::from_column_slice(2, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); + let mat_str = "[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0],2,3]"; + let deserialized: DMatrix<f32> = serde_json::from_str(&mat_str).unwrap(); + assert_eq!(m, deserialized); + + let m = Matrix2x3::from_column_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); + let mat_str = "[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]"; + let deserialized: Matrix2x3<f32> = serde_json::from_str(&mat_str).unwrap(); + assert_eq!(m, deserialized); +} + +#[test] +#[should_panic] +fn serde_dmatrix_invalid_len() { + // This must fail: we attempt to deserialize a 2x3 with only 5 elements. + let mat_str = "[[1.0, 2.0, 3.0, 4.0, 5.0],2,3]"; + let _: DMatrix<f32> = serde_json::from_str(&mat_str).unwrap(); +} + +#[test] +#[should_panic] +fn serde_smatrix_invalid_len() { + // This must fail: we attempt to deserialize a 2x3 with only 5 elements. + let mat_str = "[1.0, 2.0, 3.0, 4.0, 5.0]"; + let _: Matrix2x3<f32> = serde_json::from_str(&mat_str).unwrap(); } test_serde!(
Vulnerability mechanics
Root cause
"Missing validation during deserialization allows a `VecStorage` object to be created with a data vector length that is inconsistent with its row and column dimensions."
Attack vector
An attacker can trigger this vulnerability by providing a maliciously crafted serialized input to an application using the `nalgebra` crate for deserialization. By supplying a `VecStorage` object where the number of elements in the data vector does not equal the product of the row and column counts, the attacker can cause out-of-bounds memory access [patch_id=16381]. This typically occurs when an application deserializes untrusted data into a matrix structure.
Affected code
The vulnerability is located in `src/base/vec_storage.rs` within the `VecStorage` struct's deserialization logic. The implementation failed to validate that the length of the deserialized data vector matched the expected dimensions of the matrix [patch_id=16381].
What the fix does
The patch modifies `src/base/vec_storage.rs` to implement a custom `Deserialize` trait for `VecStorage` [patch_id=16381]. This implementation explicitly checks if the product of the row count and column count matches the length of the deserialized data vector. If the lengths do not match, the deserialization process returns an error, preventing the creation of an invalid `VecStorage` instance and subsequent out-of-bounds memory access.
Preconditions
- inputThe application must use the `nalgebra` crate to deserialize data using `serde`.
Generated on May 17, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-3w8g-xr3f-2mp8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-38190ghsaADVISORY
- github.com/dimforge/nalgebra/blob/dev/CHANGELOG.mdghsaWEB
- github.com/dimforge/nalgebra/commit/a803271fcce75b7c151e92aa099dfa546db4adc5ghsaWEB
- github.com/dimforge/nalgebra/issues/883ghsaWEB
- github.com/dimforge/nalgebra/pull/889ghsaWEB
- raw.githubusercontent.com/rustsec/advisory-db/main/crates/nalgebra/RUSTSEC-2021-0070.mdmitrex_refsource_MISC
- rustsec.org/advisories/RUSTSEC-2021-0070.htmlghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.