Skip to content

feat(relay): Add a bounded deserializer for prost protobuffers - #6302

Open
klochek wants to merge 3 commits into
masterfrom
christopherklochek/relay_serializer_prost_derive
Open

feat(relay): Add a bounded deserializer for prost protobuffers#6302
klochek wants to merge 3 commits into
masterfrom
christopherklochek/relay_serializer_prost_derive

Conversation

@klochek

@klochek klochek commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Adds a new derive macro to emit the required scaffolding code to bound prost protobuffer messages.

An impl is generated for a new BoundedMessage trait (for each protobuffer message that's compiled to a struct/enum). For example:

impl BoundedMessage for SomeValue {
  fn desc() -> &'static [Nested] {
    &[Nested::Field(2, <SomeOtherValue as BoundedMessage>::desc)],
  }
}

And then, during serialization, when we find a tag that corresponds to the value in that array, we invoke the static function the we placed there, retrieving that nested message's array of nested messages--and so on.
Function pointers seem to be the only good way to do this (by generating something on the trait impl, which allows us to use the rust type system to reach around the compiled proto namespace and reference other types.)

@klochek
klochek requested a review from a team as a code owner August 12, 2026 03:32

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit faa352a. Configure here.

Comment thread relay-serialization-derive/src/lib.rs
Comment thread relay-serialization/src/prost/scan.rs
Comment thread relay-serialization/src/serde/de.rs

@Dav1dde Dav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Comment thread relay-serialization/src/prost/scan.rs Outdated

/// A trait for prost Messages to implement, allowing them to self-describe the kinds of nested
/// fields that have on themselves.
pub trait BoundedMessage {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub trait BoundedMessage {
pub trait BoundedMessage: prost::Message {

I think that should work and makes a few of the bounds checks down below simpler.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, enums aren't derived as prost::Message, but prost::Oneof, so this doesn't quite work. (Kind of makes me think BoundedMessage isn't the right name--maybe BoundedItem? BoundedProtoValue?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like BoundedProtoValue but it's not really that the value itself is bounded, it's more a descriptor, right?

So maybe something like SelfDescribingProtoValue (okay this is ... a mouthful, but you get the idea)

@@ -0,0 +1,196 @@
mod prost {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the nested module?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added because as I was testing, I disliked how the output read:

running 10 tests
test test_scan_accepts_what_prost_accepts ... ok
test test_scan_bounds_its_own_recursion ... ok

but using the submodule, it reads like

running 10 tests
test prost::test_scan_accepts_what_prost_accepts ... ok
test prost::test_scan_bounds_its_own_recursion ... ok

which I thought was nicer. I'm fine to revert if you don't like it, though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

Comment thread relay-serialization/src/prost/scan.rs
}

loop {
let (tag, wire_type) = key(reader)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The scan_group function incorrectly increments the operation budget for END_GROUP markers, which are not fields, potentially causing incorrect rejection of valid proto2 messages.
Severity: LOW

Suggested Fix

Modify the loop in scan_group to check for the END_GROUP wire type before spending from the budget meter. The cost for a FIELD should only be applied to actual field tags, not to the group terminator.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: relay-serialization/src/prost/scan.rs#L205

Potential issue: In the `scan_group` function, the budget meter is unconditionally
incremented for each tag read from the wire. This includes the `END_GROUP` marker, which
is a structural terminator for a proto2 group and not a field occurrence. According to
the metering logic's intent, only field occurrences should be counted. This
over-counting by one operation per group can lead to premature budget exhaustion. As a
result, valid proto2 messages containing groups might be incorrectly rejected with an
`Error::LimitExceeded` when processed with a bounded budget.

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants