VYPR
High severityNVD Advisory· Published Jul 27, 2025· Updated Jul 28, 2025

CVE-2024-58264

CVE-2024-58264

Description

The serde-json-wasm crate before 1.0.1 for Rust allows stack consumption via deeply nested JSON data.

AI Insight

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

serde-json-wasm before 1.0.1 and 0.5.2 allows stack overflow via deeply nested JSON due to missing recursion limit.

Vulnerability

Description

The serde-json-wasm crate for Rust, used for JSON serialization/deserialization in WebAssembly environments, is vulnerable to stack consumption caused by unbounded recursion. Prior to versions 1.0.1 and 0.5.2, the deserializer did not enforce a recursion depth limit. An attacker could craft a deeply nested JSON structure (e.g., arrays containing arrays) that triggers recursive parsing, leading to excessive stack usage and a potential stack overflow [1][2].

Exploitation

Exploitation requires the ability to supply a JSON input to an application that uses the vulnerable crate. No authentication or special network position is needed beyond the ability to send the malicious payload. The attack is trivial to execute by simply providing a JSON document with deeply nested objects or arrays (e.g., thousands of levels of nesting). The deserializer's recursive descent parser will recurse until the call stack is exhausted, causing a crash or denial of service [1][2].

Impact

Successful exploitation results in a denial of service (DoS) condition, as the application process terminates due to stack overflow. In environments where robustness is critical (e.g., smart contract execution in CosmWasm), this could disrupt service availability or be used to crash validator nodes. The vulnerability does not allow arbitrary code execution but can be used to repeatedly crash services [1][2].

Mitigation

The issue is fixed in versions 1.0.1 and 0.5.2 of the crate. The fix introduces a recursion limit of 128 and adds a new Error::RecursionLimitExceeded variant. Users should update to the latest patched version. No workarounds are available other than upgrading [1][2]. The CVE is not known to be on the CISA KEV list as of publication.

AI Insight generated on May 19, 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
serde-json-wasmcrates.io
>= 1.0.0, < 1.0.11.0.1
serde-json-wasmcrates.io
< 0.5.20.5.2

Affected products

2

Patches

2
a9a9b9bf2438

Add recursion limit

https://github.com/CosmWasm/serde-json-wasmChristoph OtterJan 23, 2024via ghsa
5 files changed · +78 14
  • Cargo.lock+1 1 modified
    @@ -40,7 +40,7 @@ checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
     
     [[package]]
     name = "serde-json-wasm"
    -version = "0.5.1"
    +version = "0.5.2"
     dependencies = [
      "serde",
      "serde_derive",
    
  • Cargo.toml+1 1 modified
    @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
     name = "serde-json-wasm"
     readme = "README.md"
     repository = "https://github.com/CosmWasm/serde-json-wasm"
    -version = "0.5.1"
    +version = "0.5.2"
     exclude = [
         ".cargo/",
         ".github/",
    
  • src/de/errors.rs+4 0 modified
    @@ -68,6 +68,9 @@ pub enum Error {
         /// JSON has a comma after the last value in an array or map.
         TrailingComma,
     
    +    /// JSON is nested too deeply, exceeeded the recursion limit.
    +    RecursionLimitExceeded,
    +
         /// Custom error message from serde
         Custom(String),
     }
    @@ -132,6 +135,7 @@ impl fmt::Display for Error {
                          value."
                     }
                     Error::TrailingComma => "JSON has a comma after the last value in an array or map.",
    +                Error::RecursionLimitExceeded => "JSON is nested too deeply, exceeeded the recursion limit.",
                     Error::Custom(msg) => msg,
                 }
             )
    
  • src/de/mod.rs+47 12 modified
    @@ -20,6 +20,9 @@ use std::str::from_utf8;
     pub struct Deserializer<'b> {
         slice: &'b [u8],
         index: usize,
    +
    +    /// Remaining depth until we hit the recursion limit
    +    remaining_depth: u8,
     }
     
     enum StringLike<'a> {
    @@ -29,7 +32,11 @@ enum StringLike<'a> {
     
     impl<'a> Deserializer<'a> {
         fn new(slice: &'a [u8]) -> Deserializer<'_> {
    -        Deserializer { slice, index: 0 }
    +        Deserializer {
    +            slice,
    +            index: 0,
    +            remaining_depth: 128,
    +        }
         }
     
         fn eat_char(&mut self) {
    @@ -286,16 +293,22 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
                     }
                 }
                 b'[' => {
    -                self.eat_char();
    -                let ret = visitor.visit_seq(SeqAccess::new(self))?;
    +                check_recursion! {
    +                    self.eat_char();
    +                    let ret = visitor.visit_seq(SeqAccess::new(self));
    +                }
    +                let ret = ret?;
     
                     self.end_seq()?;
     
                     Ok(ret)
                 }
                 b'{' => {
    -                self.eat_char();
    -                let ret = visitor.visit_map(MapAccess::new(self))?;
    +                check_recursion! {
    +                    self.eat_char();
    +                    let ret = visitor.visit_map(MapAccess::new(self));
    +                }
    +                let ret = ret?;
     
                     self.end_map()?;
     
    @@ -548,8 +561,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
         {
             match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
                 b'[' => {
    -                self.eat_char();
    -                let ret = visitor.visit_seq(SeqAccess::new(self))?;
    +                check_recursion! {
    +                    self.eat_char();
    +                    let ret = visitor.visit_seq(SeqAccess::new(self));
    +                }
    +                let ret = ret?;
     
                     self.end_seq()?;
     
    @@ -585,9 +601,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
             let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
     
             if peek == b'{' {
    -            self.eat_char();
    -
    -            let ret = visitor.visit_map(MapAccess::new(self))?;
    +            check_recursion! {
    +                self.eat_char();
    +                let ret = visitor.visit_map(MapAccess::new(self));
    +            }
    +            let ret = ret?;
     
                 self.end_map()?;
     
    @@ -623,8 +641,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
                 b'"' => visitor.visit_enum(UnitVariantAccess::new(self)),
                 // if it is a struct enum
                 b'{' => {
    -                self.eat_char();
    -                visitor.visit_enum(StructVariantAccess::new(self))
    +                check_recursion! {
    +                    self.eat_char();
    +                    let value = visitor.visit_enum(StructVariantAccess::new(self));
    +                }
    +                value
                 }
                 _ => Err(Error::ExpectedSomeIdent),
             }
    @@ -684,6 +705,20 @@ where
         from_slice(s.as_bytes())
     }
     
    +macro_rules! check_recursion {
    +    ($this:ident $($body:tt)*) => {
    +        $this.remaining_depth -= 1;
    +        if $this.remaining_depth == 0 {
    +            return Err($crate::de::Error::RecursionLimitExceeded);
    +        }
    +
    +        $this $($body)*
    +
    +        $this.remaining_depth += 1;
    +    };
    +}
    +pub(crate) use check_recursion;
    +
     #[cfg(test)]
     mod tests {
         use super::from_str;
    
  • src/lib.rs+25 0 modified
    @@ -214,4 +214,29 @@ mod test {
                 item
             );
         }
    +
    +    #[test]
    +    fn no_stack_overflow() {
    +        const AMOUNT: usize = 2000;
    +        let mut json = String::from(r#"{"":"#);
    +
    +        #[derive(Debug, Deserialize, Serialize)]
    +        pub struct Person {
    +            name: String,
    +            age: u8,
    +            phones: Vec<String>,
    +        }
    +
    +        for _ in 0..AMOUNT {
    +            json.push('[');
    +        }
    +        for _ in 0..AMOUNT {
    +            json.push(']');
    +        }
    +
    +        json.push_str(r#"]        }[[[[[[[[[[[[[[[[[[[[[   ""","age":35,"phones":["#);
    +
    +        let err = from_str::<Person>(&json).unwrap_err();
    +        assert_eq!(err, crate::de::Error::RecursionLimitExceeded);
    +    }
     }
    
e78f9e28b3a2

Add recursion limit

https://github.com/CosmWasm/serde-json-wasmChristoph OtterJan 23, 2024via ghsa
5 files changed · +78 14
  • Cargo.lock+1 1 modified
    @@ -40,7 +40,7 @@ checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890"
     
     [[package]]
     name = "serde-json-wasm"
    -version = "1.0.0"
    +version = "1.0.1"
     dependencies = [
      "serde",
      "serde_derive",
    
  • Cargo.toml+1 1 modified
    @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
     name = "serde-json-wasm"
     readme = "README.md"
     repository = "https://github.com/CosmWasm/serde-json-wasm"
    -version = "1.0.0"
    +version = "1.0.1"
     exclude = [
         ".cargo/",
         ".github/",
    
  • src/de/errors.rs+4 0 modified
    @@ -75,6 +75,9 @@ pub enum Error {
         /// JSON has a comma after the last value in an array or map.
         TrailingComma,
     
    +    /// JSON is nested too deeply, exceeeded the recursion limit.
    +    RecursionLimitExceeded,
    +
         /// Custom error message from serde
         Custom(String),
     }
    @@ -128,6 +131,7 @@ impl core::fmt::Display for Error {
                          value."
                     }
                     Error::TrailingComma => "JSON has a comma after the last value in an array or map.",
    +                Error::RecursionLimitExceeded => "JSON is nested too deeply, exceeeded the recursion limit.",
                     Error::Custom(msg) => msg,
                 }
             )
    
  • src/de/mod.rs+47 12 modified
    @@ -21,6 +21,9 @@ use self::seq::SeqAccess;
     pub struct Deserializer<'b> {
         slice: &'b [u8],
         index: usize,
    +
    +    /// Remaining depth until we hit the recursion limit
    +    remaining_depth: u8,
     }
     
     enum StringLike<'a> {
    @@ -30,7 +33,11 @@ enum StringLike<'a> {
     
     impl<'a> Deserializer<'a> {
         fn new(slice: &'a [u8]) -> Deserializer<'_> {
    -        Deserializer { slice, index: 0 }
    +        Deserializer {
    +            slice,
    +            index: 0,
    +            remaining_depth: 128,
    +        }
         }
     
         fn eat_char(&mut self) {
    @@ -287,16 +294,22 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
                     }
                 }
                 b'[' => {
    -                self.eat_char();
    -                let ret = visitor.visit_seq(SeqAccess::new(self))?;
    +                check_recursion! {
    +                    self.eat_char();
    +                    let ret = visitor.visit_seq(SeqAccess::new(self));
    +                }
    +                let ret = ret?;
     
                     self.end_seq()?;
     
                     Ok(ret)
                 }
                 b'{' => {
    -                self.eat_char();
    -                let ret = visitor.visit_map(MapAccess::new(self))?;
    +                check_recursion! {
    +                    self.eat_char();
    +                    let ret = visitor.visit_map(MapAccess::new(self));
    +                }
    +                let ret = ret?;
     
                     self.end_map()?;
     
    @@ -513,8 +526,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
         {
             match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
                 b'[' => {
    -                self.eat_char();
    -                let ret = visitor.visit_seq(SeqAccess::new(self))?;
    +                check_recursion! {
    +                    self.eat_char();
    +                    let ret = visitor.visit_seq(SeqAccess::new(self));
    +                }
    +                let ret = ret?;
     
                     self.end_seq()?;
     
    @@ -550,9 +566,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
             let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
     
             if peek == b'{' {
    -            self.eat_char();
    -
    -            let ret = visitor.visit_map(MapAccess::new(self))?;
    +            check_recursion! {
    +                self.eat_char();
    +                let ret = visitor.visit_map(MapAccess::new(self));
    +            }
    +            let ret = ret?;
     
                 self.end_map()?;
     
    @@ -588,8 +606,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
                 b'"' => visitor.visit_enum(UnitVariantAccess::new(self)),
                 // if it is a struct enum
                 b'{' => {
    -                self.eat_char();
    -                visitor.visit_enum(StructVariantAccess::new(self))
    +                check_recursion! {
    +                    self.eat_char();
    +                    let value = visitor.visit_enum(StructVariantAccess::new(self));
    +                }
    +                value
                 }
                 _ => Err(Error::ExpectedSomeIdent),
             }
    @@ -649,6 +670,20 @@ where
         from_slice(s.as_bytes())
     }
     
    +macro_rules! check_recursion {
    +    ($this:ident $($body:tt)*) => {
    +        $this.remaining_depth -= 1;
    +        if $this.remaining_depth == 0 {
    +            return Err($crate::de::Error::RecursionLimitExceeded);
    +        }
    +
    +        $this $($body)*
    +
    +        $this.remaining_depth += 1;
    +    };
    +}
    +pub(crate) use check_recursion;
    +
     #[cfg(test)]
     mod tests {
         use super::from_str;
    
  • src/lib.rs+25 0 modified
    @@ -227,4 +227,29 @@ mod test {
                 item
             );
         }
    +
    +    #[test]
    +    fn no_stack_overflow() {
    +        const AMOUNT: usize = 2000;
    +        let mut json = String::from(r#"{"":"#);
    +
    +        #[derive(Debug, Deserialize, Serialize)]
    +        pub struct Person {
    +            name: String,
    +            age: u8,
    +            phones: Vec<String>,
    +        }
    +
    +        for _ in 0..AMOUNT {
    +            json.push('[');
    +        }
    +        for _ in 0..AMOUNT {
    +            json.push(']');
    +        }
    +
    +        json.push_str(r#"]        }[[[[[[[[[[[[[[[[[[[[[   ""","age":35,"phones":["#);
    +
    +        let err = from_str::<Person>(&json).unwrap_err();
    +        assert_eq!(err, crate::de::Error::RecursionLimitExceeded);
    +    }
     }
    

Vulnerability mechanics

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

References

6

News mentions

0

No linked articles in our index yet.