feat(relay): Add a bounded deserializer for prost protobuffers - #6302
feat(relay): Add a bounded deserializer for prost protobuffers#6302klochek wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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.
|
|
||
| /// A trait for prost Messages to implement, allowing them to self-describe the kinds of nested | ||
| /// fields that have on themselves. | ||
| pub trait BoundedMessage { |
There was a problem hiding this comment.
| pub trait BoundedMessage { | |
| pub trait BoundedMessage: prost::Message { |
I think that should work and makes a few of the bounds checks down below simpler.
There was a problem hiding this comment.
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?)
There was a problem hiding this comment.
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 { | |||
There was a problem hiding this comment.
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.
| } | ||
|
|
||
| loop { | ||
| let (tag, wire_type) = key(reader)?; |
There was a problem hiding this comment.
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.

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:
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.)