VYPR
Critical severityNVD Advisory· Published Dec 31, 2020· Updated Aug 4, 2024

CVE-2020-35858

CVE-2020-35858

Description

In prost before 0.6.1, a crafted message causes stack consumption leading to denial of service or potential remote code execution.

AI Insight

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

In prost before 0.6.1, a crafted message causes stack consumption leading to denial of service or potential remote code execution.

The vulnerability in the prost crate (versions prior to 0.6.1) arises from unconstrained recursion when skipping unknown fields during message decoding. The skip_field function, used when a field tag is not recognized, does not enforce a recursion limit, allowing deeply nested crafted messages to exhaust the call stack [3]. This bug affects the Rust implementation of Protocol Buffers, which is widely used for serialization in Rust applications [1].

Exploitation requires no authentication or special privileges; an attacker only needs to send a specially crafted Protocol Buffers message to an application using an affected prost version. The attack vector is network-based, with low complexity, as the vulnerable code is triggered automatically during parsing [4]. On x86 platforms, the primary impact is a denial of service via stack overflow. However, on ARM platforms, the advisory notes the possibility of remote code execution due to differences in memory corruption behavior [2].

The vulnerability has been addressed in prost version 0.6.1 by introducing a recursion limit parameter ctx to the skip_field function [3]. Users are strongly advised to update to 0.6.1 or later. The RustSec advisory (RUSTSEC-2020-0002) assigns a CVSS v3.1 score of 9.8 (Critical) due to the potential for complete compromise of confidentiality, integrity, and availability [4]. No workarounds were published; upgrading is the recommended mitigation.

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.

PackageAffected versionsPatched versions
prostcrates.io
< 0.6.10.6.1

Affected products

2

Patches

2
9ec361458027

release 0.6.1

https://github.com/danburkert/prostDan BurkertJan 16, 2020via osv
8 files changed · +12 12
  • Cargo.toml+2 2 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "prost"
    -version = "0.6.0"
    +version = "0.6.1"
     authors = ["Dan Burkert <dan@danburkert.com>"]
     license = "Apache-2.0"
     repository = "https://github.com/danburkert/prost"
    @@ -41,7 +41,7 @@ no-recursion-limit = []
     
     [dependencies]
     bytes = "0.5"
    -prost-derive = { version = "0.6.0", path = "prost-derive", optional = true }
    +prost-derive = { version = "0.6.1", path = "prost-derive", optional = true }
     
     [dev-dependencies]
     criterion = "0.3"
    
  • prost-build/Cargo.toml+3 3 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "prost-build"
    -version = "0.6.0"
    +version = "0.6.1"
     authors = ["Dan Burkert <dan@danburkert.com>"]
     license = "Apache-2.0"
     repository = "https://github.com/danburkert/prost"
    @@ -16,8 +16,8 @@ itertools = "0.8"
     log = "0.4"
     multimap = { version = "0.8", default-features = false }
     petgraph = { version = "0.5", default-features = false }
    -prost = { version = "0.6.0", path = ".." }
    -prost-types = { version = "0.6.0", path = "../prost-types" }
    +prost = { version = "0.6.1", path = ".." }
    +prost-types = { version = "0.6.1", path = "../prost-types" }
     tempfile = "3"
     
     [build-dependencies]
    
  • prost-build/src/lib.rs+1 1 modified
    @@ -1,4 +1,4 @@
    -#![doc(html_root_url = "https://docs.rs/prost-build/0.6.0")]
    +#![doc(html_root_url = "https://docs.rs/prost-build/0.6.1")]
     
     //! `prost-build` compiles `.proto` files into Rust.
     //!
    
  • prost-derive/Cargo.toml+1 1 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "prost-derive"
    -version = "0.6.0"
    +version = "0.6.1"
     authors = ["Dan Burkert <dan@danburkert.com>"]
     license = "Apache-2.0"
     repository = "https://github.com/danburkert/prost"
    
  • prost-derive/src/lib.rs+1 1 modified
    @@ -1,4 +1,4 @@
    -#![doc(html_root_url = "https://docs.rs/prost-derive/0.6.0")]
    +#![doc(html_root_url = "https://docs.rs/prost-derive/0.6.1")]
     // The `quote!` macro requires deep recursion.
     #![recursion_limit = "4096"]
     
    
  • prost-types/Cargo.toml+2 2 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "prost-types"
    -version = "0.6.0"
    +version = "0.6.1"
     authors = ["Dan Burkert <dan@danburkert.com>"]
     license = "Apache-2.0"
     repository = "https://github.com/danburkert/prost"
    @@ -15,4 +15,4 @@ test = false
     
     [dependencies]
     bytes = "0.5"
    -prost = { version = "0.6.0", path = ".." }
    +prost = { version = "0.6.1", path = ".." }
    
  • prost-types/src/lib.rs+1 1 modified
    @@ -1,4 +1,4 @@
    -#![doc(html_root_url = "https://docs.rs/prost-types/0.6.0")]
    +#![doc(html_root_url = "https://docs.rs/prost-types/0.6.1")]
     
     //! Protocol Buffers well-known types.
     //!
    
  • src/lib.rs+1 1 modified
    @@ -1,4 +1,4 @@
    -#![doc(html_root_url = "https://docs.rs/prost/0.6.0")]
    +#![doc(html_root_url = "https://docs.rs/prost/0.6.1")]
     
     mod error;
     mod message;
    
04091d3e745c

apply recursion limit when skipping fields

https://github.com/danburkert/prostDan BurkertJan 16, 2020via ghsa
4 files changed · +26 15
  • prost-derive/src/lib.rs+1 1 modified
    @@ -194,7 +194,7 @@ fn try_message(input: TokenStream) -> Result<TokenStream, Error> {
                     #struct_name
                     match tag {
                         #(#merge)*
    -                    _ => ::prost::encoding::skip_field(wire_type, tag, buf),
    +                    _ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx),
                     }
                 }
     
    
  • src/encoding.rs+4 3 modified
    @@ -381,10 +381,11 @@ where
         Ok(())
     }
     
    -pub fn skip_field<B>(wire_type: WireType, tag: u32, buf: &mut B) -> Result<(), DecodeError>
    +pub fn skip_field<B>(wire_type: WireType, tag: u32, buf: &mut B, ctx: DecodeContext) -> Result<(), DecodeError>
     where
         B: Buf,
     {
    +    ctx.limit_reached()?;
         let len = match wire_type {
             WireType::Varint => decode_varint(buf).map(|_| 0)?,
             WireType::ThirtyTwoBit => 4,
    @@ -399,7 +400,7 @@ where
                         }
                         break 0;
                     }
    -                _ => skip_field(inner_wire_type, inner_tag, buf)?,
    +                _ => skip_field(inner_wire_type, inner_tag, buf, ctx.enter_recursion())?,
                 }
             },
             WireType::EndGroup => return Err(DecodeError::new("unexpected end group tag")),
    @@ -1219,7 +1220,7 @@ macro_rules! map {
                         match tag {
                             1 => key_merge(wire_type, key, buf, ctx),
                             2 => val_merge(wire_type, val, buf, ctx),
    -                        _ => skip_field(wire_type, tag, buf),
    +                        _ => skip_field(wire_type, tag, buf, ctx),
                         }
                     },
                 )?;
    
  • src/types.rs+11 11 modified
    @@ -38,7 +38,7 @@ impl Message for bool {
             if tag == 1 {
                 bool::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -76,7 +76,7 @@ impl Message for u32 {
             if tag == 1 {
                 uint32::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -114,7 +114,7 @@ impl Message for u64 {
             if tag == 1 {
                 uint64::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -152,7 +152,7 @@ impl Message for i32 {
             if tag == 1 {
                 int32::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -190,7 +190,7 @@ impl Message for i64 {
             if tag == 1 {
                 int64::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -228,7 +228,7 @@ impl Message for f32 {
             if tag == 1 {
                 float::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -266,7 +266,7 @@ impl Message for f64 {
             if tag == 1 {
                 double::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -304,7 +304,7 @@ impl Message for String {
             if tag == 1 {
                 string::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -342,7 +342,7 @@ impl Message for Vec<u8> {
             if tag == 1 {
                 bytes::merge(wire_type, self, buf, ctx)
             } else {
    -            skip_field(wire_type, tag, buf)
    +            skip_field(wire_type, tag, buf, ctx)
             }
         }
         fn encoded_len(&self) -> usize {
    @@ -369,12 +369,12 @@ impl Message for () {
             tag: u32,
             wire_type: WireType,
             buf: &mut B,
    -        _ctx: DecodeContext,
    +        ctx: DecodeContext,
         ) -> Result<(), DecodeError>
         where
             B: Buf,
         {
    -        skip_field(wire_type, tag, buf)
    +        skip_field(wire_type, tag, buf, ctx)
         }
         fn encoded_len(&self) -> usize {
             0
    
  • tests/src/lib.rs+10 0 modified
    @@ -462,6 +462,16 @@ mod tests {
             };
         }
     
    +    #[test]
    +    fn test_267_regression() {
    +        // Checks that skip_field will error appropriately when given a big stack of StartGroup
    +        // tags.
    +        //
    +        // https://github.com/danburkert/prost/issues/267
    +        let buf = vec![b'C'; 1 << 20];
    +        <() as Message>::decode(&buf[..]).err().unwrap();
    +    }
    +
         #[test]
         fn test_default_enum() {
             let msg = default_enum_value::Test::default();
    

Vulnerability mechanics

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

References

5

News mentions

0

No linked articles in our index yet.