tokio-rs / prost

PROST! a Protocol Buffers implementation for the Rust Language
Apache License 2.0
3.97k stars 513 forks source link

Stack overflow when decoding a recursive message with `proto2` syntax #924

Open EFanZh opened 1 year ago

EFanZh commented 1 year ago

Suppose I have a proto file with a recursive message:

syntax = "proto2";

package foo;

message Foo { required Foo x = 1; }

prost-build will generate this struct:

#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Foo {
    #[prost(message, required, boxed, tag = "1")]
    pub x: ::prost::alloc::boxed::Box<Foo>,
}

If I use the Foo type to decode an empty byte slice, I get an stack overflow error:

use prost::Message;

include!(concat!(env!("OUT_DIR"), "/foo.rs"));

fn main() {
    let _ = Foo::decode("".as_bytes());
}

I guess the behavior is caused by prost attempting to construct a Foo object, which is not possible, since constructing it recursively depends on constructing a new object of the same type.

caspermeijn commented 4 months ago

The way I see it, struct Foo can never be constructed. prost will create a default instance of Foo during decoding, which will stack overflow.

The C++ implementation solves this by using the same API for optional and required fields and doing lazy initialization. That way, it doesn't have to create the full instance. https://protobuf.dev/reference/cpp/cpp-generated/#embeddedmessage

I don't know how to solve this besides throwing an error during prost-build. Is this a real-world example, or did you find out about this during experimentation?

EFanZh commented 4 months ago

I kind of forget then exact context of this issue. But I think is is not a very urgent issue.